관리자 설정 로딩과 부트 진단 복구
This commit is contained in:
parent
d58eb67764
commit
fb640a75ac
7 changed files with 767 additions and 100 deletions
|
|
@ -232,9 +232,15 @@ test.describe("full sweep — shared shell, GNB, and routing guards", () => {
|
|||
|
||||
await page.goto("/learn");
|
||||
|
||||
await expect(page.getByText(BOOT_SCREEN_TEXT)).toBeVisible();
|
||||
const bootScreen = page.getByText(BOOT_SCREEN_TEXT);
|
||||
await expect(bootScreen).toBeVisible();
|
||||
await expect(bootScreen).toHaveAttribute("data-vignette-boot-loading", "true");
|
||||
// 부트스트랩이 끝나지 않는 동안은 로그인으로 이동하지 않는다
|
||||
await expect(page).toHaveURL(/\/learn$/);
|
||||
// 3.5초와 8초 watchdog은 화면에 실제로 보이는 bounded BootScreen을
|
||||
// 빈 React root로 오인하지 않는다.
|
||||
await page.waitForTimeout(8_600);
|
||||
await expect(page.locator("#vignette-boot-diagnostic")).toHaveCount(0);
|
||||
|
||||
releaseMe();
|
||||
|
||||
|
|
@ -242,6 +248,154 @@ test.describe("full sweep — shared shell, GNB, and routing guards", () => {
|
|||
await expect(page.getByRole("button", { name: /Google 계정으로 로그인/ })).toBeVisible();
|
||||
});
|
||||
|
||||
test("removes a watchdog panel when the delayed boot screen becomes visible", async ({ page }) => {
|
||||
test.setTimeout(15_000);
|
||||
let releaseMain!: () => void;
|
||||
const mainGate = new Promise<void>((resolve) => {
|
||||
releaseMain = resolve;
|
||||
});
|
||||
let releaseMe!: () => void;
|
||||
const meGate = new Promise<void>((resolve) => {
|
||||
releaseMe = resolve;
|
||||
});
|
||||
await page.route("**/src/main.tsx*", async (route) => {
|
||||
await mainGate;
|
||||
await route.continue();
|
||||
});
|
||||
await page.route("**/api/auth/me", async (route) => {
|
||||
await meGate;
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ detail: "not authenticated" }),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/learn", { waitUntil: "commit" });
|
||||
await expect(page.locator("#root")).toBeEmpty();
|
||||
await expect(page.locator("#vignette-boot-diagnostic")).toBeVisible({ timeout: 4_500 });
|
||||
|
||||
releaseMain();
|
||||
|
||||
await expect(page.getByText(BOOT_SCREEN_TEXT)).toHaveAttribute(
|
||||
"data-vignette-boot-loading",
|
||||
"true",
|
||||
);
|
||||
await expect(page.locator("#vignette-boot-diagnostic")).toHaveCount(0, { timeout: 1_500 });
|
||||
|
||||
releaseMe();
|
||||
await expect(page.getByRole("button", { name: /Google 계정으로 로그인/ })).toBeVisible();
|
||||
});
|
||||
|
||||
test("reports a boot screen that remains visible through the 20-second watchdog", async ({ page }) => {
|
||||
test.setTimeout(35_000);
|
||||
let releaseChunk!: () => void;
|
||||
const gate = new Promise<void>((resolve) => {
|
||||
releaseChunk = resolve;
|
||||
});
|
||||
await page.route("**/src/pages/Login.tsx*", async (route) => {
|
||||
await gate;
|
||||
await route.continue();
|
||||
});
|
||||
await page.route("**/api/auth/me", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ detail: "not authenticated" }),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/auth/config", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
google_oauth_configured: true,
|
||||
saml_configured: false,
|
||||
providers: [
|
||||
{
|
||||
provider: "google",
|
||||
configured: true,
|
||||
enabled: true,
|
||||
login_path: "/auth/login?provider=google",
|
||||
},
|
||||
],
|
||||
allowed_email_domains: [],
|
||||
redirect_uri: "http://127.0.0.1:8000/auth/callback",
|
||||
dev_login_enabled: false,
|
||||
}),
|
||||
});
|
||||
});
|
||||
const meResolved = page.waitForResponse((response) => response.url().includes("/api/auth/me"));
|
||||
await page.goto("/login", { waitUntil: "domcontentloaded" });
|
||||
await meResolved;
|
||||
await expect(page.getByText(BOOT_SCREEN_TEXT).filter({ visible: true })).toHaveAttribute(
|
||||
"data-vignette-boot-loading",
|
||||
"true",
|
||||
);
|
||||
await page.waitForTimeout(8_600);
|
||||
await expect(page.locator("#vignette-boot-diagnostic")).toHaveCount(0);
|
||||
await expect(page.locator("#vignette-boot-diagnostic")).toBeVisible({ timeout: 14_000 });
|
||||
await expect(page.locator("#vignette-boot-diagnostic")).toHaveAttribute(
|
||||
"data-vignette-boot-reason",
|
||||
"watchdog",
|
||||
);
|
||||
|
||||
releaseChunk();
|
||||
await expect(page.getByRole("button", { name: /Google 계정으로 로그인/ })).toBeVisible();
|
||||
});
|
||||
|
||||
test("keeps manual and runtime diagnostics when a delayed boot screen appears after watchdog", async ({ page }) => {
|
||||
test.setTimeout(15_000);
|
||||
let releaseMain!: () => void;
|
||||
const mainGate = new Promise<void>((resolve) => {
|
||||
releaseMain = resolve;
|
||||
});
|
||||
let releaseMe!: () => void;
|
||||
const meGate = new Promise<void>((resolve) => {
|
||||
releaseMe = resolve;
|
||||
});
|
||||
await page.route("**/src/main.tsx*", async (route) => {
|
||||
await mainGate;
|
||||
await route.continue();
|
||||
});
|
||||
await page.route("**/api/auth/me", async (route) => {
|
||||
await meGate;
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ detail: "not authenticated" }),
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto("/learn", { waitUntil: "commit" });
|
||||
const diagnostic = page.locator("#vignette-boot-diagnostic");
|
||||
await expect(diagnostic).toBeVisible({ timeout: 4_500 });
|
||||
await expect(diagnostic).toHaveAttribute("data-vignette-boot-reason", "watchdog");
|
||||
|
||||
await page.evaluate(() => {
|
||||
(window as Window & { __VIGNETTE_BOOT_DIAG__?: { check(): void } }).__VIGNETTE_BOOT_DIAG__?.check();
|
||||
});
|
||||
await expect(diagnostic).toHaveAttribute("data-vignette-boot-reason", "manual");
|
||||
await page.evaluate(() => {
|
||||
window.dispatchEvent(new ErrorEvent("error", { message: "boot diagnostic runtime fixture" }));
|
||||
});
|
||||
await expect(diagnostic).toHaveAttribute("data-vignette-boot-reason", "runtime-error");
|
||||
|
||||
releaseMain();
|
||||
await expect(page.getByText(BOOT_SCREEN_TEXT)).toHaveAttribute(
|
||||
"data-vignette-boot-loading",
|
||||
"true",
|
||||
);
|
||||
await page.waitForTimeout(250);
|
||||
await expect(diagnostic).toBeVisible();
|
||||
await expect(diagnostic).toHaveAttribute("data-vignette-boot-reason", "runtime-error");
|
||||
await expect(diagnostic.locator("dd").first()).toHaveText("runtime-error");
|
||||
|
||||
releaseMe();
|
||||
await expect(page.getByRole("button", { name: /Google 계정으로 로그인/ })).toBeVisible();
|
||||
});
|
||||
|
||||
// checklist: shell-suspense-fallback
|
||||
// 모든 페이지가 lazy import이므로 라우트 청크가 로드되는 동안
|
||||
// Suspense fallback(BootScreen)이 표시된다. Vite dev 모듈 URL을 지연시켜 재현.
|
||||
|
|
@ -254,7 +408,35 @@ test.describe("full sweep — shared shell, GNB, and routing guards", () => {
|
|||
await gate;
|
||||
await route.continue();
|
||||
});
|
||||
await page.route("**/api/auth/me", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ detail: "not authenticated" }),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/auth/config", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
google_oauth_configured: true,
|
||||
saml_configured: false,
|
||||
providers: [
|
||||
{
|
||||
provider: "google",
|
||||
configured: true,
|
||||
enabled: true,
|
||||
login_path: "/auth/login?provider=google",
|
||||
},
|
||||
],
|
||||
allowed_email_domains: [],
|
||||
redirect_uri: "http://127.0.0.1:8000/auth/callback",
|
||||
dev_login_enabled: false,
|
||||
}),
|
||||
});
|
||||
});
|
||||
const meResolved = page.waitForResponse(
|
||||
(res) => res.url().includes("/api/auth/me"),
|
||||
{ timeout: 15_000 },
|
||||
|
|
@ -285,7 +467,35 @@ test.describe("full sweep — shared shell, GNB, and routing guards", () => {
|
|||
page.on("request", (request) => {
|
||||
if (request.isNavigationRequest()) documentRequests += 1;
|
||||
});
|
||||
await page.route("**/api/auth/me", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 401,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({ detail: "not authenticated" }),
|
||||
});
|
||||
});
|
||||
|
||||
await page.route("**/api/auth/config", async (route) => {
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body: JSON.stringify({
|
||||
google_oauth_configured: true,
|
||||
saml_configured: false,
|
||||
providers: [
|
||||
{
|
||||
provider: "google",
|
||||
configured: true,
|
||||
enabled: true,
|
||||
login_path: "/auth/login?provider=google",
|
||||
},
|
||||
],
|
||||
allowed_email_domains: [],
|
||||
redirect_uri: "http://127.0.0.1:8000/auth/callback",
|
||||
dev_login_enabled: false,
|
||||
}),
|
||||
});
|
||||
});
|
||||
await page.goto("/login");
|
||||
await expect(page.getByRole("button", { name: /Google 계정으로 로그인/ })).toBeVisible();
|
||||
expect(documentRequests).toBe(1);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue