| 1 | // Run: node --import ./scripts/svg-stub-register.mjs --import tsx src/__tests__/file-navigation-revision.test.tsx |
| 2 | // |
| 3 | // A dock record's revision counter belongs to that record: when the dock reads |
| 4 | // a different record, its counter starts over, and the panel must not mistake |
| 5 | // the new record's first command for a revision it already applied. |
| 6 | |
| 7 | import assert from "node:assert/strict"; |
| 8 | import React, { act } from "react"; |
| 9 | import { renderFilesWorkspace } from "./workspace-panel-test-harness"; |
| 10 | import { WorkspaceDockRegion, type WorkspaceDockRegionProps } from "../app-shell/WorkspaceDockRegion"; |
| 11 | import { LocaleProvider } from "../lib/i18n"; |
| 12 | import { performResourceAction, fileNavigationOwner } from "../lib/fileNavigationCommands"; |
| 13 | import { fileNavigationKey } from "../lib/fileNavigationOwner"; |
| 14 | import { useActivityBarStore } from "../store/activityBar"; |
| 15 | |
| 16 | const { dom, root, dockTabId } = await renderFilesWorkspace({ |
| 17 | ReadPresentedFileForTab: async (_tab, _tool, path) => ({ path, body: `CONTENT ${path}`, size: 20, truncated: false, binary: false }), |
| 18 | ListDirForTab: async () => [], |
| 19 | }); |
| 20 | const props: WorkspaceDockRegionProps = { |
| 21 | visible: true, overlay: false, mode: "files", showContext: false, |
| 22 | t: key => key, onPickEntry: () => {}, remote: { onClose: () => {} }, context: {} as WorkspaceDockRegionProps["context"], |
| 23 | workspace: { open: true, tabId: "tab-one", cwd: "/repo", maximized: false, onClose: () => {}, onToggleMaximized: () => {} }, |
| 24 | workspaceKey: "revision-test", workspaceRoot: "/repo", |
| 25 | }; |
| 26 | const sessionTabId = "tab-one"; |
| 27 | const paint = () => act(async () => root.render( |
| 28 | <LocaleProvider><WorkspaceDockRegion {...props} workspace={{ ...props.workspace, tabId: sessionTabId }} visible /></LocaleProvider>, |
| 29 | )); |
| 30 | const record = () => fileNavigationOwner().getSnapshot(fileNavigationKey({ sessionTabId, dockTabId })); |
| 31 | const open = async (path: string) => { |
| 32 | await act(async () => performResourceAction({ hostId: "local", tabId: sessionTabId, path, source: "presented", toolCallId: "call" }, "preview")); |
| 33 | }; |
| 34 | const onChangesView = () => document.querySelector(".workspace-panel--changed-overview") !== null; |
| 35 | |
| 36 | await paint(); |
| 37 | await open("first.md"); |
| 38 | await paint(); |
| 39 | assert.equal(record()?.navigation?.revision, 1, "the dock's first navigation is revision one"); |
| 40 | |
| 41 | // The user moves to the Changes view, then the dock tab is closed and reopened: |
| 42 | // the dock reads a new record, and the revision is one sequence for the whole |
| 43 | // instance, so that record cannot hand out a revision the panel already applied. |
| 44 | const changesView = [...document.querySelectorAll<HTMLButtonElement>("button")] |
| 45 | .find(button => /^changes$/i.test((button.textContent ?? "").trim())); |
| 46 | assert(changesView, "the dock exposes its Changes view"); |
| 47 | await act(async () => { changesView!.dispatchEvent(new window.MouseEvent("click", { bubbles: true })); }); |
| 48 | assert(onChangesView(), "the dock is on the Changes view"); |
| 49 | const closedGeneration = record()!.generation; |
| 50 | await act(async () => { |
| 51 | useActivityBarStore.getState().closeTab(dockTabId); |
| 52 | fileNavigationOwner().retain([]); |
| 53 | await Promise.resolve(); |
| 54 | }); |
| 55 | assert.equal(record(), null, "a closed dock tab keeps no record"); |
| 56 | await act(async () => useActivityBarStore.getState().reopenTab(dockTabId)); |
| 57 | await paint(); |
| 58 | assert.equal(record()?.generation, closedGeneration + 1, "reopening starts a new dock lifetime"); |
| 59 | await open("second.md"); |
| 60 | await paint(); |
| 61 | assert.equal(record()?.navigation?.revision, 2, "the new record's navigation continues the instance's sequence"); |
| 62 | assert.equal(record()?.selected?.resource.path, "second.md"); |
| 63 | assert(!onChangesView(), "that command is applied: the dock returns to its file preview"); |
| 64 | assert(document.body.textContent?.includes("CONTENT second.md") === true, "and shows the file it opened"); |
| 65 | |
| 66 | await act(async () => root.unmount()); |
| 67 | dom.window.close(); |
| 68 | console.log("PASS a dock reading another record still applies that record's first navigation"); |
| 69 |