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-7407-dce17e8446358cabf6b8910705cd7a9c7737f159 in repository https://gitbox.apache.org/repos/asf/texera.git
commit 2c57707a762486a7935860d8fee7b8eb0ff89a7e Author: Xinyuan Lin <[email protected]> AuthorDate: Fri Aug 7 18:31:43 2026 -0700 test(frontend): render the version list's collapse and selection rules (#7407) ### What changes were proposed in this PR? The version list keeps its display rules in the template, and the spec never rendered it — the existing tests drive `collapse()` and `getDisplayedVersionId()` directly. The template was at roughly **4%** of statements locally. The rule worth pinning is the row predicate: ```html <tr *ngIf="(!row.importance && row.expand) || row.importance"> ``` A minor version stays folded away until its important parent is expanded — that is the whole point of the collapse, and it exists only in the template. Adds 9 tests: that predicate in both directions, the descending version numbering, the `selected-row` highlight, the expand control appearing only on important versions, the three arguments the timestamp button passes to `getVersion`, the date format, the column headings, and the table being absent until versions load. **Verified by mutation**, all reverted (template diff empty): | Mutation | Result | |---|---| | make the row predicate always true | red | | drop `expand` from the predicate | red | | key `selected-row` off the row count instead of the index | red | | number versions by index instead of count − index | red | | hand `getVersion` a fixed index | red | | show the expand control on every row | red | | change the date format | red | | render the table before versions load | red | Local coverage for the component directory: **~4% → 96.55%** of statements. The spec was already set up for this — it deliberately skips `detectChanges()` in `beforeEach` and notes that tests needing the rendered template should call it locally. One gotcha worth recording: `nz-table` renders its own expand-icon `<button>`, so `querySelector("button")` finds that rather than the version link. The tests select `button.version-link`. No production file is touched. ### Any related issues, documentation, discussions? Closes #7404 ### How was this PR tested? ``` npx ng test --watch=false --include="**/versions-list.component.spec.ts" ``` ``` Test Files 1 passed (1) Tests 20 passed (20) ``` 9 new on top of the existing 11. `yarn format:ci` passes. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 5) --- .../versions-list/versions-list.component.spec.ts | 83 ++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/frontend/src/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts b/frontend/src/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts index 7517e1547a..c9a2af8af8 100644 --- a/frontend/src/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts +++ b/frontend/src/app/workspace/component/left-panel/versions-list/versions-list.component.spec.ts @@ -216,4 +216,87 @@ describe("VersionsListComponent", () => { expect(component.selectedRowIndex).toBe(0); }); }); + /** + * The table's own logic lives in the template: which rows survive the collapse predicate, the + * descending version number, and the selection highlight. The class-level specs above drive + * collapse() and getDisplayedVersionId() directly and never render, so none of it was pinned. + */ + describe("rendered table", () => { + /** Renders the given entries and returns the rows that survived the *ngIf. */ + function renderRows(entries: ReturnType<typeof makeEntry>[]): HTMLTableRowElement[] { + component.versionsList = entries as any; + fixture.detectChanges(); + return Array.from(fixture.nativeElement.querySelectorAll("tbody tr")); + } + + it("renders no table at all until the versions have loaded", () => { + component.versionsList = undefined as any; + fixture.detectChanges(); + + expect(fixture.nativeElement.querySelector("#versions-list")).toBeNull(); + }); + + it("labels the columns from versionTableHeaders", () => { + renderRows([makeEntry(1, true)]); + + const headers = Array.from(fixture.nativeElement.querySelectorAll("thead th")).map(th => + (th as HTMLElement).textContent?.trim() + ); + expect(headers).toEqual(component.versionTableHeaders); + }); + + it("hides an unimportant version while it is collapsed", () => { + // The predicate is (!importance && expand) || importance: minor versions stay folded away + // until their important parent is expanded, which is the whole point of the collapse. + const rows = renderRows([makeEntry(3, true), makeEntry(2, false, false), makeEntry(1, false, false)]); + + expect(rows).toHaveLength(1); + }); + + it("reveals an unimportant version once it is expanded", () => { + const rows = renderRows([makeEntry(3, true), makeEntry(2, false, true), makeEntry(1, false, false)]); + + expect(rows).toHaveLength(2); + }); + + it("numbers the versions downwards, newest first", () => { + const rows = renderRows([makeEntry(3, true), makeEntry(2, true), makeEntry(1, true)]); + + const numbers = rows.map(r => r.querySelectorAll("td")[0].textContent?.trim()); + expect(numbers).toEqual(["3", "2", "1"]); + }); + + it("marks only the selected row", () => { + component.selectedRowIndex = 1; + const rows = renderRows([makeEntry(2, true), makeEntry(1, true)]); + + expect(rows[0].classList).not.toContain("selected-row"); + expect(rows[1].classList).toContain("selected-row"); + }); + + it("offers the expand control only on an important version", () => { + const rows = renderRows([makeEntry(2, true), makeEntry(1, false, true)]); + + expect(rows[0].querySelector("[nztableexpand], .ant-table-row-expand-icon")).not.toBeNull(); + expect(rows[1].querySelector(".ant-table-row-expand-icon")).toBeNull(); + }); + + it("asks for the version behind the row that was clicked", () => { + const getVersion = vi.spyOn(component, "getVersion").mockImplementation(() => {}); + const rows = renderRows([makeEntry(30, true), makeEntry(20, true)]); + + rows[1].querySelector<HTMLButtonElement>("button.version-link")!.click(); + + // vId of the clicked row, its displayed (descending) number, and its index. + expect(getVersion).toHaveBeenCalledWith(20, 1, 1); + }); + + it("shows the timestamp in the compact date format", () => { + const rows = renderRows([makeEntry(1, true)]); + + expect(rows[0].querySelector("button.version-link")!.textContent?.trim()).toMatch( + /^\d{2}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}$/ + ); + }); + }); });
