Files
clean-architecture-frontend…/tests/features/reference-feature/reference-production-vertical.test.tsx
T

122 lines
3.9 KiB
TypeScript

// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {
afterAll,
afterEach,
beforeAll,
describe,
expect,
it,
vi,
} from "vitest";
import { createRuntimeComposition } from "../../../src/bootstrap/create-runtime-composition.ts";
import { RuntimeApplication } from "../../../src/bootstrap/runtime-application.tsx";
import { createBootstrapHandlers } from "../../mocks/handlers/bootstrap.ts";
import { createReferenceScenarioHandlers } from "../../mocks/handlers/reference-resources.ts";
import { createStrictMockServer } from "../../mocks/server.ts";
const runtimeConfig = {
APP_ENV: "local",
API_BASE_URL: "https://api.test",
REQUEST_TIMEOUT_MS: 10_000,
MAX_RETRY_ATTEMPTS: 0,
TELEMETRY_ENABLED: false,
AUTH_MODE: "demo",
CONFIG_SCHEMA_VERSION: "1",
API_CONTRACT_VERSION: "1",
RELEASE_MANIFEST_URL: "/release-manifest.json",
BUILD_ID: "local-build",
RELEASE_ID: "local-release",
};
const releaseManifest = {
schemaVersion: 1,
appVersion: "0.1.0",
buildId: "local-build",
commitSha: "local",
configSchemaVersion: "1",
apiContractVersion: "1",
assetManifestHash: "test-hash",
releaseId: "local-release",
builtAt: "2026-07-26T00:00:00.000Z",
routeChunks: {
"route-home": "assets/home.js",
"route-examples-ui": "assets/ui.js",
"route-examples-states": "assets/states.js",
"route-examples-auth": "assets/auth.js",
"route-reference-resources": "assets/reference.js",
"route-reference-resource-detail": "assets/reference-detail.js",
"route-reference-resource-form": "assets/reference-form.js",
"route-reference-resource-status": "assets/reference-status.js",
"route-not-found": "assets/not-found.js",
},
};
const listRequests = vi.fn();
const createRequests = vi.fn();
const resources = [{ id: "reference-1", name: "Existing" }];
const mockApi = createStrictMockServer(
...createBootstrapHandlers(runtimeConfig, releaseManifest),
...createReferenceScenarioHandlers({
resources,
onList: listRequests,
onCreate: createRequests,
}),
);
beforeAll(mockApi.listen);
afterEach(() => {
mockApi.reset();
listRequests.mockClear();
createRequests.mockClear();
resources.splice(1);
});
afterAll(mockApi.close);
const absoluteFetch: typeof fetch = (input, init) => {
if (input instanceof Request) return fetch(input, init);
const url = new URL(
input instanceof URL ? input.href : input,
"http://app.test",
);
return fetch(url, init);
};
describe("reference feature production vertical path", () => {
it("traverses bootstrap, router, application, HTTP schema/mapper and query cache", async () => {
const user = userEvent.setup();
const composition = await createRuntimeComposition({
fetcher: absoluteFetch,
host: {},
});
window.history.pushState(
{},
"",
"/examples/reference-resources?tags=open&tags=new&limit=5",
);
render(<RuntimeApplication composition={composition} />);
await user.click(
await screen.findByRole("button", { name: "로그인 시작" }),
);
expect(await screen.findByText("Existing")).toBeVisible();
expect(listRequests).toHaveBeenCalledWith(
"?limit=5&tags=open&tags=new",
);
await user.click(screen.getByRole("button", { name: "새 항목 만들기" }));
await user.type(
await screen.findByRole("textbox", { name: /새 항목 이름/ }),
" Created ",
);
await user.click(screen.getByRole("button", { name: "저장" }));
expect(await screen.findByText("저장했습니다.")).toBeVisible();
await user.click(screen.getByRole("button", { name: "목록으로 돌아가기" }));
expect(await screen.findByText("Created")).toBeVisible();
expect(createRequests).toHaveBeenCalledWith({ name: "Created" });
expect(listRequests.mock.calls.length).toBeGreaterThanOrEqual(2);
});
});