import { expect, test, type Page } from './fixtures'; // The finished-game export: the history header's πŸ“€ button opens a format chooser β€” // the GCG file or the rendered PNG image (lib/gameimage) β€” as a native Telegram popup // inside Telegram and as the app's own modal elsewhere. g3 ("vs Rick") is the seeded // finished game. Web Share is force-disabled per test so every engine deterministically // exercises the download (plain browser) or preview (in-app webview) branch. async function disableWebShare(page: Page): Promise { await page.addInitScript(() => { Object.defineProperty(navigator, 'canShare', { value: () => false, configurable: true }); Object.defineProperty(navigator, 'share', { value: undefined, configurable: true }); }); } async function openFinishedHistory(page: Page): Promise { await page.goto('/'); await page.getByRole('button', { name: /guest/i }).click(); await page.getByRole('button', { name: /Rick/ }).click(); await expect(page.locator('[data-cell]').first()).toBeVisible(); await page.locator('.scoreboard').click(); await expect(page.getByRole('button', { name: 'Export game' })).toBeVisible(); } test('the export chooser downloads the PNG image on a plain browser', async ({ page }) => { await disableWebShare(page); await openFinishedHistory(page); await page.getByRole('button', { name: 'Export game' }).click(); // The app's own chooser modal (not inside Telegram) offers both formats. await expect(page.getByRole('button', { name: 'GCG file' })).toBeVisible(); const download = page.waitForEvent('download'); await page.getByRole('button', { name: 'Image (PNG)' }).click(); expect((await download).suggestedFilename()).toMatch(/^game-.+\.png$/); }); test('the export chooser downloads the GCG file on a plain browser', async ({ page }) => { await disableWebShare(page); await openFinishedHistory(page); await page.getByRole('button', { name: 'Export game' }).click(); const download = page.waitForEvent('download'); await page.getByRole('button', { name: 'GCG file' }).click(); expect((await download).suggestedFilename()).toMatch(/^game-.+\.gcg$/); }); test('inside Telegram the chooser is the native popup and the image lands in the preview modal', async ({ page, }) => { await disableWebShare(page); // A Telegram WebApp stub whose showPopup records its params and picks the image option β€” // the native-chooser path. With no Web Share in the webview, the image must land in the // long-press preview modal (the Android Telegram delivery), never a dead . await page.addInitScript(() => { Object.assign(window, { Telegram: { WebApp: { initData: 'query_id=test&user=%7B%22id%22%3A1%7D&auth_date=1&hash=deadbeef', initDataUnsafe: {}, ready() {}, expand() {}, showPopup(params: unknown, cb: (id: string) => void) { (window as unknown as { __popup?: unknown }).__popup = params; setTimeout(() => cb('image'), 0); }, }, }, }); }); await page.goto('/'); await expect(page.getByText('Your turn')).toBeVisible(); // auto-authenticated lobby await page.getByRole('button', { name: /Rick/ }).click(); await expect(page.locator('[data-cell]').first()).toBeVisible(); await page.locator('.scoreboard').click(); await page.getByRole('button', { name: 'Export game' }).click(); // The native popup carried both formats plus cancel (≀3 buttons, Bot API 6.2)… const popup = await page.evaluate( () => (window as unknown as { __popup?: { buttons?: { id?: string; type?: string }[] } }).__popup, ); expect(popup?.buttons?.map((b) => b.id ?? b.type)).toEqual(['image', 'gcg', 'cancel']); // …and no in-app chooser modal was mounted. await expect(page.getByRole('button', { name: 'GCG file' })).toHaveCount(0); // The preview modal shows the rendered PNG as a data: URL (long-press-saveable) + the hint. const img = page.locator('.export-preview'); await expect(img).toBeVisible(); expect(await img.getAttribute('src')).toMatch(/^data:image\/png/); await expect(page.locator('.export-hint')).toBeVisible(); });