fix: 배포 중 화면 유지와 자동 재연결 적용
정상 DEPLOY 중에는 기존 정적 프론트엔드를 유지하고 새 artifact 준비 후 전환합니다. 게임 화면은 일시적인 gateway 오류를 별도 상태로 표시하고 읽기 전용 probe로 제한적으로 복구합니다.
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { expect, test, type Page, type Route } from '@playwright/test';
|
||||
import { gameProfile, gameTrpcRoute } from './gameTestPaths.js';
|
||||
|
||||
const response = (data: unknown) => ({ result: { data } });
|
||||
|
||||
const operationNames = (route: Route): string[] => {
|
||||
const url = new URL(route.request().url());
|
||||
return decodeURIComponent(url.pathname.slice(url.pathname.lastIndexOf('/trpc/') + 6)).split(',');
|
||||
};
|
||||
|
||||
const installRecoveryFixture = async (page: Page) => {
|
||||
let unavailable = true;
|
||||
let lobbyRequests = 0;
|
||||
await page.addInitScript(
|
||||
({ token, profile }) => {
|
||||
window.localStorage.setItem('sammo-game-token', token);
|
||||
window.localStorage.setItem('sammo-game-profile', profile);
|
||||
window.addEventListener('sammo:game-server-reconnected', () => {
|
||||
const state = window as typeof window & { __gameServerReconnects?: number };
|
||||
state.__gameServerReconnects = (state.__gameServerReconnects ?? 0) + 1;
|
||||
});
|
||||
},
|
||||
{ token: 'ga_recovery', profile: gameProfile }
|
||||
);
|
||||
await page.route(gameTrpcRoute, async (route) => {
|
||||
const operations = operationNames(route);
|
||||
if (operations.includes('lobby.info')) {
|
||||
lobbyRequests += 1;
|
||||
if (unavailable) {
|
||||
await route.fulfill({
|
||||
status: 503,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify({ error: 'profile switch in progress' }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(
|
||||
operations.map((operation) => {
|
||||
if (operation === 'auth.status') return response({ userId: 'recovery-user' });
|
||||
if (operation === 'lobby.info') return response({ myGeneral: null });
|
||||
if (operation === 'join.getConfig') return response({});
|
||||
return response(null);
|
||||
})
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
recover: () => {
|
||||
unavailable = false;
|
||||
},
|
||||
lobbyRequests: () => lobbyRequests,
|
||||
};
|
||||
};
|
||||
|
||||
for (const viewport of [
|
||||
{ name: 'desktop', width: 1280, height: 800 },
|
||||
{ name: 'mobile', width: 390, height: 844 },
|
||||
]) {
|
||||
test(`keeps the current screen and reconnects after a transient profile switch on ${viewport.name}`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
||||
const fixture = await installRecoveryFixture(page);
|
||||
await page.goto('select-general');
|
||||
|
||||
const heading = page.locator('.page-title');
|
||||
await expect(heading).toContainText('장 수 선 택');
|
||||
const notice = page.getByTestId('game-server-connection-notice');
|
||||
await expect(notice).toBeVisible();
|
||||
await expect(notice).toContainText('화면을 유지한 채 자동으로 다시 연결합니다');
|
||||
await expect(notice).toHaveCSS('position', 'fixed');
|
||||
const noticeBox = await notice.boundingBox();
|
||||
expect(noticeBox).not.toBeNull();
|
||||
expect(noticeBox!.x).toBeGreaterThanOrEqual(0);
|
||||
expect(noticeBox!.x + noticeBox!.width).toBeLessThanOrEqual(viewport.width);
|
||||
const documentWidthDuringReconnect = await page.evaluate(() => document.documentElement.scrollWidth);
|
||||
await page.evaluate(() => {
|
||||
Object.assign(window, { __connectionRecoveryPageMarker: 'kept' });
|
||||
});
|
||||
|
||||
fixture.recover();
|
||||
await page.evaluate(() => window.dispatchEvent(new Event('online')));
|
||||
|
||||
await expect(notice).toHaveCount(0);
|
||||
await expect(heading).toContainText('장 수 선 택');
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(documentWidthDuringReconnect);
|
||||
expect(await page.evaluate(() => Reflect.get(window, '__connectionRecoveryPageMarker'))).toBe('kept');
|
||||
expect(await page.evaluate(() => Reflect.get(window, '__gameServerReconnects'))).toBe(1);
|
||||
expect(fixture.lobbyRequests()).toBeGreaterThanOrEqual(2);
|
||||
expect(await page.evaluate(() => window.localStorage.getItem('sammo-game-token'))).toBe('ga_recovery');
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user