This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-7333-b75e82138278daac68dcec6ab6adfb18179c0f45 in repository https://gitbox.apache.org/repos/asf/texera.git
commit c76d7b3f864808ba95d5847f661731bfcc8fd248 Author: Meng Wang <[email protected]> AuthorDate: Wed Aug 5 22:45:58 2026 -0700 test(frontend): render ConsoleFrameComponent template branches for coverage (#7333) ### What changes were proposed in this PR? Extends the `ConsoleFrameComponent` spec so its **template** actually renders, covering the `.component.html` branches the class-level tests never exercised (`frontend/src/app/workspace/component/result-panel/console-frame/`). The class was already well covered; only the template was low. No production code changed. > Path note: the component lives under `result-panel/console-frame/`, not the > `console-frame/` path the issue lists. +4 tests, each seeding component state then calling `detectChanges()` so the template executes: - One row per console message — the `*ngFor` list, the collapse panel (non-empty message) vs the plain-title branch (empty message), and the source / timestamp / worker tags. Only the message that carries a `workerId` renders the worker tag. - The source and timestamp tags disappear when `showSource` / `showTimestamp` are toggled off (the `*ngIf` false arms). - The debug input group is absent when `consoleInputEnabled` is false and present when true; clicking each of the four action buttons reaches its handler, and pressing enter in the command input submits the command through the websocket. Per the component's determinism constraints: - the timestamp cell is rendered through the `| date` pipe but its formatted string is **not** asserted (it is timezone-dependent under a UTC CI runner) — the assertions check the tag's presence and other cell text instead; - no fake timers are introduced for the `ngAfterViewChecked` auto-scroll `setTimeout` (a synchronous test body never lets it fire), and nothing asserts on layout/geometry (`scrollHeight` etc., which are zero under jsdom). ### Any related issues, documentation, discussions? Closes #7329 ### How was this PR tested? Extended unit tests, run locally in `frontend/` (all green; the failure path was verified by breaking an assertion to confirm the suite goes red): ``` ng test --watch=false --include src/app/workspace/component/result-panel/console-frame/console-frame.component.spec.ts # Test Files 1 passed (1) | Tests 23 passed (23) prettier --write <spec> # clean eslint <spec> # clean ``` ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.8 [1M context]) --- .../console-frame/console-frame.component.spec.ts | 90 ++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/frontend/src/app/workspace/component/result-panel/console-frame/console-frame.component.spec.ts b/frontend/src/app/workspace/component/result-panel/console-frame/console-frame.component.spec.ts index 34205e2ae7..20392ce767 100644 --- a/frontend/src/app/workspace/component/result-panel/console-frame/console-frame.component.spec.ts +++ b/frontend/src/app/workspace/component/result-panel/console-frame/console-frame.component.spec.ts @@ -18,6 +18,7 @@ */ import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; import { Subject } from "rxjs"; import { ConsoleFrameComponent } from "./console-frame.component"; import { OperatorMetadataService } from "../../../service/operator-metadata/operator-metadata.service"; @@ -289,4 +290,93 @@ describe("ConsoleFrameComponent", () => { expect(component.consoleMessages).toEqual([consoleMessage("PRINT")]); }); }); + + // The tests above drive the class directly; these render the template so its + // *ngFor / *ngIf / (click) / [(ngModel)] branches actually execute. + describe("template rendering", () => { + // A message with a body (renders the collapse panel) that carries a worker id, + // and one with an empty body (renders the plain title branch) and no worker. + const withBody: ConsoleMessage = { + ...consoleMessage("PRINT"), + message: "hello body", + title: "header A", + workerId: "w-0", + source: "srcA", + }; + const noBody: ConsoleMessage = { + ...consoleMessage("ERROR"), + message: "", + title: "plain B", + workerId: "", + source: "srcB", + }; + + it("renders one row per message with its body, source, timestamp and worker tags", () => { + component.consoleMessages = [withBody, noBody]; + component.showSource = true; + component.showTimestamp = true; + fixture.detectChanges(); + + const rows = fixture.debugElement.queryAll(By.css(".console-message-entry")); + expect(rows.length).toBe(2); + + // non-empty message -> collapse header; empty message -> plain title + const text = fixture.nativeElement.textContent as string; + const collapseHeader = fixture.debugElement.query(By.css(".collapse-message-header")); + expect(collapseHeader).toBeTruthy(); + expect(collapseHeader.nativeElement.textContent).toContain("header A"); + expect(text).toContain("plain B"); + + // both rows show a source tag; both show a timestamp tag (the rendered date + // string is intentionally NOT asserted — it is timezone-dependent) + expect(fixture.debugElement.queryAll(By.css(".source-tag")).length).toBe(2); + expect(fixture.debugElement.queryAll(By.css(".timestamp-tag")).length).toBe(2); + // only the message with a worker id renders the worker tag + expect(fixture.debugElement.queryAll(By.css(".worker-tag")).length).toBe(1); + }); + + it("hides the source and timestamp tags when the toggles are off", () => { + component.consoleMessages = [withBody, noBody]; + component.showSource = false; + component.showTimestamp = false; + fixture.detectChanges(); + + expect(fixture.debugElement.queryAll(By.css(".console-message-entry")).length).toBe(2); + expect(fixture.debugElement.queryAll(By.css(".source-tag")).length).toBe(0); + expect(fixture.debugElement.queryAll(By.css(".timestamp-tag")).length).toBe(0); + }); + + it("does not render the debug input group when console input is disabled", () => { + component.consoleInputEnabled = false; + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css(".console-input-container"))).toBeNull(); + }); + + it("renders the debug input group and wires its buttons and command input when enabled", () => { + component.operatorId = "op1"; + component.workerIds = ["w-0", "w-1"]; + component.targetWorker = component.ALL_WORKERS; + component.consoleInputEnabled = true; + fixture.detectChanges(); + + expect(fixture.debugElement.query(By.css(".console-input-container"))).toBeTruthy(); + + // clicking each action button reaches its handler / service + const buttons = fixture.debugElement.queryAll(By.css(".console-input-container button")); + expect(buttons.length).toBe(4); + buttons.forEach(button => button.triggerEventHandler("click", null)); + expect(skipTuples).toHaveBeenCalled(); + expect(retryExecution).toHaveBeenCalled(); + expect(doStep).toHaveBeenCalled(); + expect(doContinue).toHaveBeenCalled(); + + // entering a command and pressing enter submits it through the websocket + // (target input[nz-input] specifically — the nz-select renders its own input too) + component.command = "break"; + fixture.debugElement.query(By.css("input[nz-input]")).triggerEventHandler("keyup.enter", null); + // targetWorker defaults to ALL_WORKERS, so the command is broadcast to every worker id + expect(send).toHaveBeenCalledWith("DebugCommandRequest", { operatorId: "op1", workerId: "w-0", cmd: "break" }); + expect(send).toHaveBeenCalledWith("DebugCommandRequest", { operatorId: "op1", workerId: "w-1", cmd: "break" }); + }); + }); });
