130 lines
7.2 KiB
TypeScript
130 lines
7.2 KiB
TypeScript
import { expect, test, type Page } from '@playwright/test';
|
|
|
|
async function activeScenes(page: Page): Promise<string[]> {
|
|
return page.evaluate(() => window.__HONEY_GAME__.scene.getScenes(true).map((scene) => scene.scene.key));
|
|
}
|
|
|
|
test('title, movement, maps, corruption controls, and ending flow', async ({ page }) => {
|
|
const errors: string[] = [];
|
|
page.on('console', (message) => { if (message.type() === 'error') errors.push(message.text()); });
|
|
page.on('pageerror', (error) => errors.push(error.message));
|
|
|
|
await page.goto('/');
|
|
await expect(page.locator('.name-form')).toBeVisible();
|
|
await expect(page.locator('#player-name')).toHaveAttribute('placeholder', '小满');
|
|
await page.screenshot({ path: '.qa/title.png' });
|
|
await page.locator('#player-name').fill('验收员');
|
|
await page.getByRole('button', { name: '进入蜜糖村' }).click();
|
|
await expect.poll(() => activeScenes(page)).toContain('HouseScene');
|
|
await expect.poll(() => activeScenes(page)).toContain('UIScene');
|
|
|
|
const startX = await page.evaluate(() => (window.__HONEY_GAME__.scene.getScene('HouseScene') as unknown as { player: { x: number } }).player.x);
|
|
await page.keyboard.down('ArrowRight'); await page.waitForTimeout(250); await page.keyboard.up('ArrowRight');
|
|
const movedX = await page.evaluate(() => (window.__HONEY_GAME__.scene.getScene('HouseScene') as unknown as { player: { x: number } }).player.x);
|
|
expect(movedX).toBeGreaterThan(startX);
|
|
for (const [key, animation] of [['w', 'walk-up'], ['s', 'walk-down'], ['a', 'walk-left'], ['d', 'walk-right']] as const) {
|
|
await page.keyboard.down(key); await page.waitForTimeout(80);
|
|
const activeAnimation = await page.evaluate(() => (window.__HONEY_GAME__.scene.getScene('HouseScene') as unknown as { player: { anims: { currentAnim?: { key: string } } } }).player.anims.currentAnim?.key);
|
|
expect(activeAnimation).toBe(animation);
|
|
await page.keyboard.up(key);
|
|
}
|
|
await page.keyboard.down('ArrowLeft'); await page.waitForTimeout(1600); await page.keyboard.up('ArrowLeft');
|
|
const boundedX = await page.evaluate(() => (window.__HONEY_GAME__.scene.getScene('HouseScene') as unknown as { player: { x: number } }).player.x);
|
|
expect(boundedX).toBeGreaterThanOrEqual(5);
|
|
|
|
await page.keyboard.press('Backquote');
|
|
await expect(page.locator('.debug-panel')).toBeVisible();
|
|
await page.locator('[data-field="happiness"]').fill('100');
|
|
await page.locator('[data-action="happiness"]').click();
|
|
await page.locator('[data-field="awareness"]').fill('72');
|
|
await page.locator('[data-action="awareness"]').click();
|
|
await page.locator('[data-field="day"]').fill('7');
|
|
await page.locator('[data-action="day"]').click();
|
|
const state = await page.evaluate(() => {
|
|
const d = window.__HONEY_GAME__.registry.get('director') as { snapshot: { happiness: number; awareness: number; day: number }; stage: string };
|
|
return { ...d.snapshot, stage: d.stage };
|
|
});
|
|
expect(state).toMatchObject({ happiness: 100, awareness: 72, day: 7, stage: 'S4' });
|
|
await page.screenshot({ path: '.qa/debug.png' });
|
|
|
|
const maps = ['HouseScene', 'PlazaScene', 'BakeryScene', 'ShopScene', 'ClinicScene', 'LakeScene', 'FieldScene', 'WellScene', 'MistScene', 'WhiteScene'];
|
|
for (const key of maps) {
|
|
await page.locator('[data-field="scene"]').selectOption(key);
|
|
await page.locator('[data-action="teleport"]').click();
|
|
await expect.poll(() => activeScenes(page)).toContain(key);
|
|
}
|
|
|
|
await page.locator('[data-field="scene"]').selectOption('PlazaScene');
|
|
await page.locator('[data-action="teleport"]').click();
|
|
await expect.poll(() => activeScenes(page)).toContain('PlazaScene');
|
|
await page.keyboard.press('Backquote');
|
|
await page.screenshot({ path: '.qa/plaza-s4.png' });
|
|
await page.keyboard.press('Backquote');
|
|
await page.locator('[data-field="happiness"]').fill('40');
|
|
await page.locator('[data-action="happiness"]').click();
|
|
await page.locator('[data-field="day"]').fill('1');
|
|
await page.locator('[data-action="day"]').click();
|
|
await page.keyboard.press('Backquote');
|
|
await page.waitForTimeout(2000);
|
|
await page.screenshot({ path: '.qa/plaza-s1.png' });
|
|
await page.evaluate(() => {
|
|
const ui = window.__HONEY_GAME__.scene.getScene('UIScene') as unknown as { showDialogue: (options: object) => Promise<unknown> };
|
|
void ui.showDialogue({ speaker: '阿福', text: '早上好呀!今天的幸福任务贴在板上啦。祝你开心!', portrait: 2 });
|
|
});
|
|
await page.waitForTimeout(100);
|
|
await page.screenshot({ path: '.qa/dialogue.png' });
|
|
await page.keyboard.press('e');
|
|
await page.keyboard.press('Backquote');
|
|
await page.locator('[data-action="clues"]').click();
|
|
const clueCount = await page.evaluate(() => (window.__HONEY_GAME__.registry.get('director') as { snapshot: { clues: Set<string> } }).snapshot.clues.size);
|
|
expect(clueCount).toBe(9);
|
|
await page.locator('[data-ending="E3"]').click();
|
|
await expect.poll(() => activeScenes(page)).toContain('EndingScene');
|
|
await page.waitForTimeout(3200);
|
|
await page.screenshot({ path: '.qa/e3.png' });
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test('integer scaling stays sharp at desktop and compact viewport', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.locator('#player-name').press('Enter');
|
|
await expect.poll(() => activeScenes(page)).toContain('HouseScene');
|
|
const desktop = await page.locator('canvas').boundingBox();
|
|
expect(desktop?.width).toBe(960);
|
|
expect(desktop?.height).toBe(540);
|
|
await page.setViewportSize({ width: 640, height: 360 });
|
|
await page.waitForTimeout(250);
|
|
const compact = await page.locator('canvas').boundingBox();
|
|
expect(compact?.width).toBe(640);
|
|
expect(compact?.height).toBe(360);
|
|
});
|
|
|
|
test('all four forced endings render their specified evidence', async ({ page }) => {
|
|
const startEnding = async (ending: string, name = '小满') => {
|
|
await page.goto('/');
|
|
await page.locator('#player-name').fill(name);
|
|
await page.locator('#player-name').press('Enter');
|
|
await expect.poll(() => activeScenes(page)).toContain('HouseScene');
|
|
await page.evaluate((id) => (window.__HONEY_GAME__.registry.get('director') as { emit: (event: string, ending: string) => void }).emit('force-ending', id), ending);
|
|
await expect.poll(() => activeScenes(page)).toContain('EndingScene');
|
|
};
|
|
const texts = () => page.evaluate(() => (window.__HONEY_GAME__.scene.getScene('EndingScene') as unknown as { children: { list: Array<{ type?: string; text?: string }> } }).children.list
|
|
.filter((child) => child.type === 'Text')
|
|
.map((text) => text.text ?? ''));
|
|
|
|
await startEnding('E1');
|
|
expect((await texts()).join('\n')).toContain('从此,每一天都很幸福。\n\n每一天。');
|
|
await startEnding('E2'); await page.waitForTimeout(2200);
|
|
expect((await texts()).join('\n')).toContain('疗程重新开始。');
|
|
await startEnding('E3', '真实姓名'); await page.waitForTimeout(3200);
|
|
const record = (await texts()).join('\n');
|
|
expect(record).toContain('蜜糖谷情绪疗养中心');
|
|
expect(record).toContain('姓名:真实姓名');
|
|
expect(record).toContain('编号:PT-07');
|
|
expect(record).toMatch(/疗程时长:\d{2}:\d{2}/);
|
|
await page.waitForTimeout(2900);
|
|
expect((await texts()).join('\n')).toContain('微笑消失了。');
|
|
await startEnding('E4');
|
|
expect((await texts()).join('\n')).toContain('系统检测到疗程无效。');
|
|
});
|