feat: migrate legacy long-lived database records
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import { expect, test, type Page, type Route } from '@playwright/test';
|
||||
|
||||
const response = (data: unknown) => ({ result: { data } });
|
||||
const operationNames = (route: Route) =>
|
||||
decodeURIComponent(new URL(route.request().url()).pathname.split('/trpc/')[1] ?? '').split(',');
|
||||
|
||||
const installArchive = async (page: Page) => {
|
||||
await page.addInitScript(() => {
|
||||
localStorage.setItem('sammo-game-token', 'ga_archive');
|
||||
localStorage.setItem('sammo-game-profile', 'che:default');
|
||||
});
|
||||
await page.route('**/che/api/trpc/**', async (route) => {
|
||||
const results = operationNames(route).map((operation) => {
|
||||
if (operation === 'lobby.info') return response({ myGeneral: null });
|
||||
if (operation === 'archive.myPastPlays') {
|
||||
return response({
|
||||
seasons: [
|
||||
{
|
||||
serverId: 'che_2024_01',
|
||||
date: '2024-01-31T00:00:00.000Z',
|
||||
season: 51,
|
||||
scenario: 2,
|
||||
scenarioName: '천하쟁패',
|
||||
generals: [
|
||||
{
|
||||
generalNo: 17,
|
||||
name: '관우',
|
||||
lastYearMonth: 21403,
|
||||
nationId: 2,
|
||||
nationName: '촉',
|
||||
nationColor: '#800000',
|
||||
leadership: 91,
|
||||
strength: 98,
|
||||
intel: 77,
|
||||
experience: 23000,
|
||||
dedication: 1200,
|
||||
officerLevel: 12,
|
||||
personal: '대담',
|
||||
special: '상재',
|
||||
special2: '신산',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
return { error: { message: `unhandled ${operation}`, data: { code: 'BAD_REQUEST' } } };
|
||||
});
|
||||
await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(results) });
|
||||
});
|
||||
};
|
||||
|
||||
test('past plays is available without a current general and preserves desktop interaction geometry', async ({
|
||||
page,
|
||||
}) => {
|
||||
await installArchive(page);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
await page.goto('past-plays');
|
||||
|
||||
const root = page.locator('.past-plays-page');
|
||||
await expect(root).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: '내 지난 플레이 보기' })).toBeVisible();
|
||||
await expect(page.getByText('천하쟁패 · 51기')).toBeVisible();
|
||||
await expect(page.locator('.general-name')).toHaveText('관우');
|
||||
|
||||
const geometry = await root.evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const titleRect = element.querySelector('.title-row')!.getBoundingClientRect();
|
||||
const tableRect = element.querySelector('table')!.getBoundingClientRect();
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
x: rect.x,
|
||||
width: rect.width,
|
||||
minHeight: rect.height,
|
||||
title: { x: titleRect.x, y: titleRect.y, width: titleRect.width },
|
||||
tableWidth: tableRect.width,
|
||||
color: style.color,
|
||||
backgroundColor: style.backgroundColor,
|
||||
};
|
||||
});
|
||||
expect(geometry).toMatchObject({
|
||||
x: 100,
|
||||
width: 1000,
|
||||
title: { x: 100, y: 0, width: 1000 },
|
||||
tableWidth: 1000,
|
||||
color: 'rgb(238, 238, 238)',
|
||||
backgroundColor: 'rgb(21, 21, 21)',
|
||||
});
|
||||
|
||||
const refresh = page.getByRole('button', { name: '새로고침' });
|
||||
await refresh.hover();
|
||||
expect(await refresh.evaluate((element) => getComputedStyle(element).color)).toBe('rgb(135, 206, 235)');
|
||||
await refresh.focus();
|
||||
expect(await refresh.evaluate((element) => document.activeElement === element)).toBe(true);
|
||||
|
||||
const artifactRoot = process.env.PAST_PLAYS_ARTIFACT_DIR;
|
||||
if (artifactRoot) {
|
||||
const output = resolve(artifactRoot);
|
||||
await mkdir(output, { recursive: true });
|
||||
await writeFile(resolve(output, 'desktop-computed-dom.json'), `${JSON.stringify(geometry, null, 2)}\n`);
|
||||
await page.screenshot({ path: resolve(output, 'desktop.png'), fullPage: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('past plays keeps the legacy-width table scrollable on a mobile viewport', async ({ page }) => {
|
||||
await installArchive(page);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto('past-plays');
|
||||
|
||||
const scroll = page.locator('.table-scroll');
|
||||
const metrics = await scroll.evaluate((element) => ({
|
||||
clientWidth: element.clientWidth,
|
||||
scrollWidth: element.scrollWidth,
|
||||
overflowX: getComputedStyle(element).overflowX,
|
||||
}));
|
||||
// The shared legacy shell keeps its historical 500 px minimum canvas.
|
||||
expect(metrics).toEqual({ clientWidth: 500, scrollWidth: 820, overflowX: 'auto' });
|
||||
await expect(page.locator('.title-row')).toHaveCSS('flex-direction', 'column');
|
||||
});
|
||||
@@ -16,6 +16,7 @@ export default defineConfig({
|
||||
'inGameMenus.spec.ts',
|
||||
'nationOffices.spec.ts',
|
||||
'directoryLists.spec.ts',
|
||||
'pastPlays.spec.ts',
|
||||
'nationGeneralSecret.spec.ts',
|
||||
'npcPolicy.spec.ts',
|
||||
'auction.spec.ts',
|
||||
|
||||
Reference in New Issue
Block a user