feat: add removable reference feature vertical slice

This commit is contained in:
donghyeon-ka
2026-07-26 14:56:34 +09:00
parent 980981bc86
commit c11be43f20
87 changed files with 1881 additions and 1114 deletions
+11 -74
View File
@@ -2,34 +2,29 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { describe, expect, it } from "vitest";
import {
createAnonymousSessionAdapter,
createDemoSessionAdapter,
} from "../../src/adapters/auth/external-session-adapter.js";
import { createAnonymousSessionAdapter } from "../../src/adapters/auth/external-session-adapter.js";
import { ApplicationProvider } from "../../src/presentation/providers/application-provider.js";
import { AppRouter } from "../../src/presentation/routes/app-router.jsx";
import { createTestApplication } from "../helpers/create-test-application.js";
/**
* @param {import("../../src/application/ports/auth-session-port.js").AuthSessionPort} session
* @param {Parameters<typeof createTestApplication>[0]} [overrides]
*/
function renderRouter(session, overrides = {}) {
function renderRouter() {
return render(
<ApplicationProvider
application={createTestApplication({ ...overrides, session })}
application={createTestApplication({
session: createAnonymousSessionAdapter(),
})}
>
<AppRouter />
</ApplicationProvider>,
);
}
describe("application router", () => {
it("renders the app shell and not-found route without an API request", async () => {
describe("generic application router", () => {
it("renders the app shell and not-found route without a feature input", async () => {
window.history.pushState({}, "", "/missing");
renderRouter(createAnonymousSessionAdapter());
renderRouter();
expect(
await screen.findByRole("heading", {
@@ -40,10 +35,10 @@ describe("application router", () => {
expect(screen.getByRole("main")).toBeVisible();
});
it("navigates between registry-backed example routes", async () => {
it("navigates between registry-backed platform routes", async () => {
const user = userEvent.setup();
window.history.pushState({}, "", "/");
renderRouter(createAnonymousSessionAdapter());
renderRouter();
await user.click(
await screen.findByRole("link", { name: "UI 구성요소" }),
@@ -58,62 +53,4 @@ describe("application router", () => {
screen.getByRole("heading", { name: "UI 구성요소", level: 1 }),
).toHaveFocus();
});
it("reacts to demo sign-in and opens the protected integration route", async () => {
const user = userEvent.setup();
const authSession = createDemoSessionAdapter();
window.history.pushState({}, "", "/sample/resources");
renderRouter(authSession);
expect(
await screen.findByRole("heading", { name: "세션이 필요합니다." }),
).toBeVisible();
await user.click(screen.getByRole("button", { name: "로그인 시작" }));
expect(
await screen.findByRole("heading", { name: "보호된 연동 지점" }),
).toBeVisible();
expect(screen.getByText("인증됨")).toBeVisible();
});
it("fails closed when the auth integration does not change state", async () => {
const user = userEvent.setup();
window.history.pushState({}, "", "/sample/resources");
renderRouter(createAnonymousSessionAdapter());
await user.click(
await screen.findByRole("button", { name: "로그인 시작" }),
);
expect(
screen.getByRole("heading", { name: "세션이 필요합니다." }),
).toBeVisible();
});
it("rejects invalid route search before any application query runs", async () => {
const getCurrent = vi.fn(async () => ({
buildId: "test-build",
releaseId: "test-release",
configSchemaVersion: "1",
apiContractVersion: "1",
assetManifestHash: "test-hash",
routeChunks: { "route-sample-resources": "assets/sample.js" },
}));
window.history.pushState({}, "", "/sample/resources?limit=invalid");
renderRouter(createDemoSessionAdapter("authenticated"), {
releaseInfo: { getCurrent, refresh: getCurrent },
});
expect(
await screen.findByRole("heading", {
name: "올바르지 않은 주소입니다.",
}),
).toBeVisible();
expect(screen.getAllByRole("heading", { level: 1 })).toHaveLength(1);
expect(screen.getByText("안전한 탐색 링크를 사용해 주세요.")).toHaveAttribute(
"data-route-error",
"ROUTE_SEARCH_INVALID",
);
expect(getCurrent).not.toHaveBeenCalled();
});
});