diff --git a/app/components/analysis/AnalysesTable.vue b/app/components/analysis/AnalysesTable.vue index 3ca79dc..bb7e8d1 100644 --- a/app/components/analysis/AnalysesTable.vue +++ b/app/components/analysis/AnalysesTable.vue @@ -822,15 +822,13 @@ const onCloseNavToast = () => { - No logs found... +
+ No logs found... +
@@ -312,7 +334,8 @@ async function copyToClipboard(isAnalysis: boolean) { } .log-scroll-panel { - font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Monaco, + font-family: + ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Monaco, "Courier New", monospace; font-size: 0.8125rem; height: 30em; @@ -369,6 +392,10 @@ async function copyToClipboard(isAnalysis: boolean) { overflow: hidden; } +.log-entry-empty { + margin: 2rem 0.25rem; +} + .log-message { display: block; white-space: nowrap; diff --git a/app/components/analysis/logs/ContainerLogs.vue b/app/components/analysis/logs/ContainerLogs.vue index eb21b4f..c26904a 100644 --- a/app/components/analysis/logs/ContainerLogs.vue +++ b/app/components/analysis/logs/ContainerLogs.vue @@ -30,6 +30,7 @@ const analysis = ref(null); const logChunks = useLogChunks(analysisId); const lastFetchedAt = ref(null); const showTimestamps = ref(false); +const showDebug = ref(false); await logChunks.initialize(); if (logChunks.httpError.value === 403) { @@ -149,6 +150,14 @@ function onRefreshToggle() { offIcon="pi pi-clock" class="log-timestamp-toggle" /> + @@ -237,7 +249,8 @@ function onRefreshToggle() { gap: 0.75em; } -.log-timestamp-toggle { +.log-timestamp-toggle, +.log-debug-toggle { font-size: 0.8em; } diff --git a/app/components/analysis/logs/RefreshSwitch.vue b/app/components/analysis/logs/RefreshSwitch.vue index a7bba3d..0ce6bbb 100644 --- a/app/components/analysis/logs/RefreshSwitch.vue +++ b/app/components/analysis/logs/RefreshSwitch.vue @@ -20,7 +20,7 @@ const tooltip = props.disabled :disabled="props.disabled" onLabel="Refresh Logs On" offLabel="Refresh Logs Off" - onIcon="pi pi-refresh" + onIcon="pi pi-spin pi-refresh" offIcon="pi pi-refresh" class="log-timestamp-toggle" v-tooltip.top="tooltip" diff --git a/app/services/Api.ts b/app/services/Api.ts index 3a48709..bb80cd9 100644 --- a/app/services/Api.ts +++ b/app/services/Api.ts @@ -51,8 +51,6 @@ export enum PodStatus { Started = "started", Stopping = "stopping", Stopped = "stopped", - Running = "running", - Finished = "finished", Executing = "executing", Executed = "executed", Stuck = "stuck", diff --git a/app/utils/count-analyses.ts b/app/utils/count-analyses.ts index 4fb4d53..a515bdc 100644 --- a/app/utils/count-analyses.ts +++ b/app/utils/count-analyses.ts @@ -29,7 +29,6 @@ export function countAnalysisContainers( break; case PodStatus.Executing: - case PodStatus.Running: counts.executing++; break; @@ -43,7 +42,6 @@ export function countAnalysisContainers( break; case PodStatus.Executed: - case PodStatus.Finished: counts.executed++; break; diff --git a/app/utils/status-tag-severity.ts b/app/utils/status-tag-severity.ts index a2caa89..51985bd 100644 --- a/app/utils/status-tag-severity.ts +++ b/app/utils/status-tag-severity.ts @@ -40,7 +40,6 @@ export const getExecutionStatusSeverity = (status: PodStatus) => { return "info"; case PodStatus.Executing: - case PodStatus.Running: return "contrast"; case PodStatus.Stopping: @@ -51,7 +50,6 @@ export const getExecutionStatusSeverity = (status: PodStatus) => { return "danger"; case PodStatus.Executed: - case PodStatus.Finished: return "success"; } }; diff --git a/test/components/analysis/AnalysisControlButtons.spec.ts b/test/components/analysis/AnalysisControlButtons.spec.ts index eaa1f1f..1fdabc1 100644 --- a/test/components/analysis/AnalysisControlButtons.spec.ts +++ b/test/components/analysis/AnalysisControlButtons.spec.ts @@ -16,6 +16,7 @@ interface ButtonStates { rerunActive: boolean; stopActive: boolean; deleteActive: boolean; + logsActive: boolean; } function isButtonVisible(wrapper: VueWrapper, selector: string): boolean { @@ -26,6 +27,17 @@ function isButtonVisible(wrapper: VueWrapper, selector: string): boolean { return !style || !style.includes("display: none"); } +// The logs button is wrapped in a NuxtLink that carries the v-show, so the +// `display: none` lands on the anchor rather than the button itself. +function isLogsBtnVisible(wrapper: VueWrapper): boolean { + const btn = wrapper.find(".logs-analysis-btn"); + if (!btn.exists()) return false; + + const anchor = btn.element.closest("a"); + const style = anchor?.getAttribute("style"); + return !style || !style.includes("display: none"); +} + describe("AnalysisControlButtons.vue", () => { let spy; let mockToast; @@ -105,6 +117,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: false, stopActive: true, deleteActive: true, + logsActive: false, }, "", PodStatus.Started, @@ -130,6 +143,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: false, stopActive: false, deleteActive: false, + logsActive: false, }, "", "", @@ -148,6 +162,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: false, stopActive: false, deleteActive: false, + logsActive: false, }, "", "", @@ -168,6 +183,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: true, stopActive: false, deleteActive: true, + logsActive: true, }, PodStatus.Executing, PodStatus.Stopped, @@ -186,6 +202,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: true, stopActive: false, deleteActive: true, + logsActive: true, }, PodStatus.Executing, PodStatus.Stopped, @@ -204,6 +221,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: false, stopActive: true, deleteActive: true, + logsActive: true, }, PodStatus.Executing, PodStatus.Executing, @@ -222,6 +240,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: false, stopActive: false, deleteActive: false, + logsActive: false, }, PodStatus.Executing, "", @@ -240,6 +259,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: false, stopActive: false, deleteActive: false, + logsActive: false, }, ); }); @@ -256,6 +276,7 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: false, stopActive: true, deleteActive: true, + logsActive: true, }, PodStatus.Executing, PodStatus.Executing, @@ -274,58 +295,37 @@ describe("AnalysisControlButtons.vue", () => { rerunActive: false, stopActive: false, deleteActive: false, + logsActive: false, }, "", ); }); - it("Log btn check", async () => { - const wrapper = mount(AnalysisControlButtons, { - props: { - analysisBuildStatus: ProcessStatus.Executed, - analysisExecutionStatus: null, - analysisNodeId: "8003eefe-e39b-4bd4-aec4-78046c63b39b", - analysisDistributionStatus: ProcessStatus.Executed, - analysisId: fakeAnalysisId, - approvalStatus: "approved", - projectId: "7f2f3b59-3b6d-4fb6-a900-2a4d5c2ea483", - nodeId: "e3b89572-327f-4936-8cf0-fbfbcc6336b7", - datastore: true, - requireDatastore: true, - }, - }); + it("Log btn - hidden until the pod has logs to show", async () => { + const wrapper = mountControls(null); expect(AnalysisControlButtons).toBeTruthy(); - // Success check - const logBtn = wrapper.find(".logs-analysis-btn"); - expect(logBtn.attributes("data-p-disabled")).toBe("true"); - - await wrapper.setProps({ - analysisExecutionStatus: PodStatus.Started, - }); - expect(logBtn.attributes("data-p-disabled")).toBe("false"); - - await wrapper.setProps({ - analysisExecutionStatus: PodStatus.Executing, - }); - expect(logBtn.attributes("data-p-disabled")).toBe("false"); - - await wrapper.setProps({ - analysisExecutionStatus: "", - }); - expect(logBtn.attributes("data-p-disabled")).toBe("true"); + // No run yet, so there is nothing to view + expect(isLogsBtnVisible(wrapper)).toBe(false); - await wrapper.setProps({ - analysisExecutionStatus: PodStatus.Failed, - }); - expect(logBtn.attributes("data-p-disabled")).toBe("false"); + // Shown for statuses that have produced logs + for (const status of [ + PodStatus.Failed, + PodStatus.Stopped, + PodStatus.Stopping, + PodStatus.Executing, + PodStatus.Executed, + ]) { + await wrapper.setProps({ analysisExecutionStatus: status }); + expect(isLogsBtnVisible(wrapper)).toBe(true); + } - await wrapper.setProps({ - analysisExecutionStatus: PodStatus.Executed, - }); - expect(logBtn.attributes("data-p-disabled")).toBe("false"); - // + // Hidden again for statuses without logs + for (const status of [PodStatus.Starting, PodStatus.Started, ""]) { + await wrapper.setProps({ analysisExecutionStatus: status }); + expect(isLogsBtnVisible(wrapper)).toBe(false); + } }); function mountControls(analysisExecutionStatus: string | null) { diff --git a/test/components/analysis/AnalysisTable.spec.ts b/test/components/analysis/AnalysisTable.spec.ts index 1c99929..6bad3c3 100644 --- a/test/components/analysis/AnalysisTable.spec.ts +++ b/test/components/analysis/AnalysisTable.spec.ts @@ -377,7 +377,7 @@ describe("AnalysesTable.vue - PO calls and Progress", () => { const runStatusCell = wrapper.findAll("tbody tr")[0]!.findAll("td")[ RUN_STATUS_COL ]!; - expect(runStatusCell.text()).toBe(PodStatus.Running); + expect(runStatusCell.text()).toBe(PodStatus.Executing); }); test("Non-terminal execution_status is cleared when analysis is absent from PodOrc", async () => { diff --git a/test/components/analysis/AnalysisUpdateButton.spec.ts b/test/components/analysis/AnalysisUpdateButton.spec.ts index 6329cee..de44aca 100644 --- a/test/components/analysis/AnalysisUpdateButton.spec.ts +++ b/test/components/analysis/AnalysisUpdateButton.spec.ts @@ -63,7 +63,7 @@ describe("AnalysisUpdateButton.vue", () => { ); expect(wrapper.emitted("updateAnalysisRunStatus")).toHaveLength(1); expect(wrapper.emitted("updateAnalysisRunStatus")![0]).toEqual([ - PodStatus.Running, + PodStatus.Executing, undefined, ]); }); diff --git a/test/components/analysis/ContainerCounter.spec.ts b/test/components/analysis/ContainerCounter.spec.ts index 27d7586..3d9554e 100644 --- a/test/components/analysis/ContainerCounter.spec.ts +++ b/test/components/analysis/ContainerCounter.spec.ts @@ -70,12 +70,7 @@ describe("ContainerCounter.vue", () => { expect(counterDiv.attributes("class")).toContain("opaque-badge"); } - if (executionStatus === "Executing" || executionStatus === "Executed") { - // Emit extra for running and finished - emitCounts += 2; - } else { - emitCounts++; - } + emitCounts++; // Clicking on badge emits correctly badgeDiv.trigger("click"); expect(wrapper.emitted()).toHaveProperty("applyExecutionStatusFilter"); diff --git a/test/mockapi/handlers.ts b/test/mockapi/handlers.ts index 3603921..9222c3e 100644 --- a/test/mockapi/handlers.ts +++ b/test/mockapi/handlers.ts @@ -148,7 +148,7 @@ export const handlers = [ http.get(`/po/status/${fakeAnalysisId}`, () => { return HttpResponse.json({ [fakeAnalysisId]: { - status: PodStatus.Running, + status: PodStatus.Executing, progress: undefined, }, }); @@ -157,7 +157,7 @@ export const handlers = [ http.get(`/po/status`, () => { return HttpResponse.json({ [fakeAnalysisId]: { - status: PodStatus.Running, + status: PodStatus.Executing, progress: undefined, }, }); diff --git a/test/utils/count-analyses.test.ts b/test/utils/count-analyses.test.ts index 422fb52..b31437c 100644 --- a/test/utils/count-analyses.test.ts +++ b/test/utils/count-analyses.test.ts @@ -29,12 +29,11 @@ describe("countAnalysisContainers", () => { expect(result.started).toBe(2); }); - it("counts Executing and Running as executing", () => { + it("counts Executing as executing", () => { const result = countAnalysisContainers([ makeNode(PodStatus.Executing), - makeNode(PodStatus.Running), ]); - expect(result.executing).toBe(2); + expect(result.executing).toBe(1); }); it("counts Stopping and Stopped as stopped", () => { @@ -50,12 +49,11 @@ describe("countAnalysisContainers", () => { expect(result.failed).toBe(1); }); - it("counts Executed and Finished as executed", () => { + it("counts Executed as executed", () => { const result = countAnalysisContainers([ makeNode(PodStatus.Executed), - makeNode(PodStatus.Finished), ]); - expect(result.executed).toBe(2); + expect(result.executed).toBe(1); }); it("skips nodes with null or undefined execution_status", () => { diff --git a/test/utils/status-tag-severity.test.ts b/test/utils/status-tag-severity.test.ts index 416ae64..52bdfbb 100644 --- a/test/utils/status-tag-severity.test.ts +++ b/test/utils/status-tag-severity.test.ts @@ -42,12 +42,10 @@ test("Analysis run status severity tag", () => { const expectations = { starting: "info", started: "info", - running: "contrast", // deprecated executing: "contrast", stopping: "warning", stopped: "warning", executed: "success", - finished: "success", // deprecated failed: "danger", };