feat: complete TechLog Studio publication flow
This commit is contained in:
@@ -1,58 +0,0 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import { SERVER_STATE_PROFILES } from "../../src/contracts/server-state.ts";
|
||||
import { ROUTE_REGISTRY } from "../../src/features/installed-feature-contracts.ts";
|
||||
|
||||
/**
|
||||
* The overview is a projection of the installed registries. These assertions
|
||||
* read the same registries the shipped bundle was built from, so they keep
|
||||
* meaning after a feature is added or removed.
|
||||
*/
|
||||
|
||||
test("projects the installed route registry into the shipped bundle", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/examples/platform");
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "플랫폼 구성", level: 1 }),
|
||||
).toBeVisible();
|
||||
|
||||
const table = page.getByRole("table", { name: "설치된 라우트 목록" });
|
||||
await expect(table.locator("tbody tr")).toHaveCount(
|
||||
Object.keys(ROUTE_REGISTRY).length,
|
||||
);
|
||||
for (const routeId of Object.keys(ROUTE_REGISTRY)) {
|
||||
await expect(table.getByText(routeId, { exact: true })).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test("shows every fixed server-state profile", async ({ page }) => {
|
||||
await page.goto("/examples/platform");
|
||||
|
||||
const table = page.getByRole("table", { name: "서버 상태 프로파일" });
|
||||
await expect(table.locator("tbody tr")).toHaveCount(
|
||||
Object.keys(SERVER_STATE_PROFILES).length,
|
||||
);
|
||||
});
|
||||
|
||||
test("states the release contract identity verified at boot", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/examples/platform");
|
||||
|
||||
const release = page.locator(
|
||||
"section[aria-labelledby='platform-release-title']",
|
||||
);
|
||||
await expect(release.getByText(/^sha256:/)).toBeVisible();
|
||||
});
|
||||
|
||||
test("reports an unselected capability without claiming it was disabled", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/examples/platform");
|
||||
|
||||
const capabilities = page.locator(
|
||||
"section[aria-labelledby='platform-capabilities-title']",
|
||||
);
|
||||
await expect(capabilities.getByText("미선택")).toHaveCount(4);
|
||||
await expect(capabilities.getByText("운영자가 비활성화함")).toHaveCount(0);
|
||||
});
|
||||
@@ -1,50 +0,0 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
|
||||
async function openReferenceForm(page: Page) {
|
||||
await page.goto("/examples/reference-resources/new");
|
||||
await page.getByRole("button", { name: "로그인 시작" }).click();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Reference resource 만들기" }),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
test("validates a reference form and focuses the first invalid field", async ({
|
||||
page,
|
||||
}) => {
|
||||
await openReferenceForm(page);
|
||||
await page.getByRole("button", { name: "저장" }).click();
|
||||
|
||||
const firstField = page.getByRole("textbox", { name: /새 항목 이름/ });
|
||||
await expect(firstField).toBeFocused();
|
||||
await expect(firstField).toHaveAttribute("aria-invalid", "true");
|
||||
await expect(page.getByRole("alert")).toContainText("입력 내용을 확인해 주세요.");
|
||||
});
|
||||
|
||||
test("guards dirty cancellation and restores focus when writing continues", async ({
|
||||
page,
|
||||
}) => {
|
||||
await openReferenceForm(page);
|
||||
await page
|
||||
.getByRole("textbox", { name: /새 항목 이름/ })
|
||||
.fill("Unsaved reference");
|
||||
const cancel = page.getByRole("button", { name: "취소" });
|
||||
await cancel.click();
|
||||
|
||||
await expect(
|
||||
page.getByRole("dialog", { name: "저장하지 않은 변경이 있습니다." }),
|
||||
).toBeVisible();
|
||||
await page.getByRole("button", { name: "계속 작성" }).click();
|
||||
await expect(cancel).toBeFocused();
|
||||
await expect(page).toHaveURL(/\/examples\/reference-resources\/new$/);
|
||||
});
|
||||
|
||||
test("keeps the form template within a 320px viewport", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 320, height: 720 });
|
||||
await openReferenceForm(page);
|
||||
const viewport = await page.evaluate(() => ({
|
||||
clientWidth: document.documentElement.clientWidth,
|
||||
scrollWidth: document.documentElement.scrollWidth,
|
||||
}));
|
||||
expect(viewport.scrollWidth).toBeLessThanOrEqual(viewport.clientWidth);
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import { ROUTE_REGISTRY } from "../../src/features/installed-feature-contracts.ts";
|
||||
|
||||
test("opens the protected integration route through the local demo seam", async ({
|
||||
page,
|
||||
}) => {
|
||||
const protectedRoute = ROUTE_REGISTRY.REFERENCE_RESOURCE_LIST;
|
||||
await page.route(
|
||||
"http://localhost:8080/api/reference-resources?*",
|
||||
(route) =>
|
||||
route.fulfill({
|
||||
json: [
|
||||
{
|
||||
id: "browser-reference",
|
||||
name: "Browser reference",
|
||||
createdAt: "2026-07-26T00:00:00.000Z",
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
await page.goto(protectedRoute.path);
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "세션이 필요합니다." }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole("button", { name: "로그인 시작" }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole("heading", { name: protectedRoute.title }),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText("인증됨")).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { FIXTURE_IDS } from "../../src/features/tech-log/adapters/mock/fixtures.ts";
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
|
||||
test("validates, previews, publishes, and unpublishes one Studio working copy", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto(
|
||||
`/studio/documents/${FIXTURE_IDS.stateNonceReference}/validation`,
|
||||
);
|
||||
await expect(
|
||||
page.getByText("현재 저장 버전의 검증을 통과했습니다"),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole("link", { name: "Public Preview 만들기" }).click();
|
||||
await page
|
||||
.getByRole("button", { name: "Public Preview 만들기" })
|
||||
.click();
|
||||
await expect(
|
||||
page.getByText("현재 저장 버전의 Public Preview입니다."),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole("link", { name: "게시 준비로 이동" }).click();
|
||||
await page.getByRole("button", { name: "게시" }).click();
|
||||
await expect(page).toHaveURL(
|
||||
/\/studio\/publications\/[0-9a-f-]+\/preview$/,
|
||||
);
|
||||
await expect(
|
||||
page.getByRole("heading", {
|
||||
level: 1,
|
||||
name: "Authorization Code Flow에서 state와 nonce의 경계",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole("link", { name: "게시 기록으로 돌아가기" }).click();
|
||||
await page
|
||||
.getByRole("button", {
|
||||
name: /Authorization Code Flow에서 state와 nonce의 경계 게시 취소/,
|
||||
})
|
||||
.click();
|
||||
await expect(
|
||||
page.getByRole("dialog", { name: "게시를 취소할까요?" }),
|
||||
).toContainText("작업본과 이전 Snapshot은 보존됩니다.");
|
||||
await page.getByRole("button", { name: "게시 취소 확인" }).click();
|
||||
await expect(page.getByRole("status")).toContainText(
|
||||
"게시를 취소했습니다.",
|
||||
);
|
||||
});
|
||||
|
||||
test("renders an immutable historical snapshot and Studio-local missing event", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto(
|
||||
`/studio/publications/${FIXTURE_IDS.fetchPublishedEvent}/preview`,
|
||||
);
|
||||
await expect(
|
||||
page.getByText(/반환된 20건 뒤에서 전체 컬렉션이 로드되는 과정/),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText("게시 후 본문 측정값을 보완한 저장본입니다."),
|
||||
).toHaveCount(0);
|
||||
|
||||
await page.goto(
|
||||
"/studio/publications/99999999-9999-4999-8999-999999999999/preview",
|
||||
);
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "게시 기록을 찾을 수 없습니다" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("navigation", { name: "Studio 주 탐색" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
|
||||
test("validates and reports the common text-field flow", async ({ page }) => {
|
||||
await page.goto("/examples/ui");
|
||||
await page.getByRole("button", { name: "입력 확인" }).click();
|
||||
|
||||
const field = page.getByRole("textbox", { name: "프로젝트 이름" });
|
||||
await expect(field).toHaveAttribute("aria-invalid", "true");
|
||||
await expect(field).toHaveAccessibleDescription(
|
||||
/프로젝트 이름을 입력해 주세요/,
|
||||
);
|
||||
|
||||
await field.fill("Starter");
|
||||
await page.getByRole("button", { name: "입력 확인" }).click();
|
||||
await expect(page.getByText("“Starter” 입력을 확인했습니다.")).toBeVisible();
|
||||
});
|
||||
|
||||
test("traps modal interaction and restores focus to the trigger", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/examples/ui");
|
||||
const trigger = page.getByRole("button", { name: "모달 열기" });
|
||||
await trigger.click();
|
||||
|
||||
const dialog = page.getByRole("dialog", { name: "연동 확인" });
|
||||
await expect(dialog).toBeVisible();
|
||||
await expect(
|
||||
dialog.getByRole("button", { name: "연동 확인 닫기" }),
|
||||
).toBeFocused();
|
||||
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(dialog).toBeHidden();
|
||||
await expect(trigger).toBeFocused();
|
||||
});
|
||||
Reference in New Issue
Block a user