Skip to content
Source
Sign inSign up

projectScriptEditor.browser.test.tsx

88 lines2.9 KB

1import { act, render, type Screen } from "../../test/render";
2import { EnvironmentId } from "@t3tools/contracts";
3import { AsyncResult } from "effect/reactivity";
4import { StrictMode } from "react";
5import { describe, expect, it, vi } from "vite-plus/test";
6​
7vi.mock(import("~/state/session"), async (original) => ({
8 ...(await original()),
9 readEnvironmentScope: () => true,
10 useEnvironmentScope: () => true,
11}));
12​
13import {
14 EMPTY_PROJECT_SCRIPT_INPUT,
15 ProjectScriptEditorDialog,
16 type ProjectScriptActionResult,
17 type ProjectScriptEditorRequest,
18} from "./projectScriptEditor";
19​
20const onSubmit = vi.fn<Parameters<typeof ProjectScriptEditorDialog>[0]["onSubmit"]>();
21const onClose = vi.fn();
22const onDelete = vi.fn();
23​
24function request(name: string, error?: string): ProjectScriptEditorRequest {
25 return {
26 scriptId: name,
27 initial: { ...EMPTY_PROJECT_SCRIPT_INPUT, name, command: `run-${name}` },
28 ...(error === undefined ? {} : { error }),
29 };
31​
32const open = (screen: Screen, nextRequest: ProjectScriptEditorRequest) =>
33 screen.rerender(
34 <StrictMode>
35 <ProjectScriptEditorDialog
36 environmentId={EnvironmentId.make("test")}
37 request={nextRequest}
38 scripts={[]}
39 onSubmit={onSubmit}
40 onClose={onClose}
41 onDelete={onDelete}
42 />
43 </StrictMode>,
44 );
45​
46const submit = () => document.querySelector("form")!.requestSubmit();
47const saveButton = () => document.querySelector<HTMLButtonElement>('button[type="submit"]')!;
48​
49function deferredSave() {
50 let resolve!: (result: ProjectScriptActionResult) => void;
51 let reject!: (error: Error) => void;
52 const promise = new Promise<ProjectScriptActionResult>((resolveResult, rejectResult) => {
53 resolve = resolveResult;
54 reject = rejectResult;
55 });
56 return { promise, resolve, reject };
58​
59function setup() {
60 onSubmit.mockReset();
61 onClose.mockReset();
62 onDelete.mockReset();
64​
65describe("project action editor save lifecycle", () => {
66 it("does not close a replacement request or release its in-flight save", async () => {
67 setup();
68 await using screen = await render(null);
69 const first = deferredSave();
70 const second = deferredSave();
71 onSubmit.mockReturnValueOnce(first.promise).mockReturnValueOnce(second.promise);
72 await open(screen, request("build"));
73 await act(async () => submit());
74​
75 await open(screen, request("test"));
76 expect(saveButton().disabled).toBe(false);
77 expect(document.querySelector<HTMLInputElement>("#script-name")!.value).toBe("test");
78 await act(async () => submit());
79​
80 await act(async () => first.resolve(AsyncResult.success(undefined)));
81 expect(onClose).not.toHaveBeenCalled();
82 expect(saveButton().disabled).toBe(true);
83​
84 await act(async () => second.resolve(AsyncResult.success(undefined)));
85 expect(onClose).toHaveBeenCalledTimes(1);
86 expect(onSubmit.mock.calls.map(([scriptId]) => scriptId)).toEqual(["build", "test"]);
87 });
88});