51147a1429
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 19s
CI / ui (pull_request) Successful in 1m8s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m43s
Make the web SPA an installable PWA and surface it to users: - manifest.webmanifest + an install-only service worker + 192/512/maskable icons (ui/public); PWA head tags in index.html; the .webmanifest MIME type registered in the gateway (the distroless image has no /etc/mime.types). - Platform-adaptive install CTA (components/InstallApp.svelte + lib/pwa): one-tap on Chromium, manual Add-to-Home-Screen instructions on iOS Safari, hidden elsewhere / once installed / inside a Mini App. Shown under the logged-out login card and at the bottom of Settings. - The landing gains a third entry linking /app/ (the brand tile), with a caption under all three (Telegram / VK / Веб-версия). The service worker is navigation-only (network-first, cached-shell fallback); hashed assets and the Connect stream are untouched. It exists to satisfy Chromium's installability requirement and is the single growth point for a future opt-in offline mode. Tests: pwa.ts unit tests; a webui probe (manifest/sw.js content-type + the SPA-fallback-to-HTML trap); e2e for the one-tap CTA, the iOS instructions modal and the landing web entry. Docs: ARCHITECTURE §13, FUNCTIONAL (+ru), gateway README.
60 lines
2.7 KiB
TypeScript
60 lines
2.7 KiB
TypeScript
import { expect, test } from './fixtures';
|
|
|
|
// The install CTA (components/InstallApp.svelte) is platform-adaptive: a one-tap button where the
|
|
// browser offers a beforeinstallprompt (Chromium), a manual instructions modal on iOS Safari, and
|
|
// nothing elsewhere / once installed / inside a Mini App. The native OS install dialog is
|
|
// browser-level and not drivable from Playwright, so these assert the CTA's presence and branch.
|
|
|
|
const IPHONE_SAFARI =
|
|
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
|
|
|
|
test('web: a captured install prompt shows the one-tap CTA and clicking it prompts', async ({ page }) => {
|
|
await page.goto('/');
|
|
// The web login screen is shown (an ordinary browser tab, not a Mini App).
|
|
await expect(page.getByRole('button', { name: /guest/i })).toBeVisible();
|
|
|
|
// Hidden until the browser offers installation (Playwright never fires beforeinstallprompt).
|
|
await expect(page.getByRole('button', { name: /Install the app/i })).toHaveCount(0);
|
|
|
|
// Simulate Chromium offering the install: dispatch a beforeinstallprompt the app can capture.
|
|
await page.evaluate(() => {
|
|
const e = new Event('beforeinstallprompt') as Event & {
|
|
prompt?: () => Promise<void>;
|
|
userChoice?: Promise<unknown>;
|
|
};
|
|
(window as unknown as { __promptCalled: boolean }).__promptCalled = false;
|
|
e.prompt = () => {
|
|
(window as unknown as { __promptCalled: boolean }).__promptCalled = true;
|
|
return Promise.resolve();
|
|
};
|
|
e.userChoice = Promise.resolve({ outcome: 'accepted', platform: '' });
|
|
window.dispatchEvent(e);
|
|
});
|
|
|
|
const cta = page.getByRole('button', { name: /Install the app/i });
|
|
await expect(cta).toBeVisible();
|
|
await cta.click();
|
|
// Clicking calls the captured prompt (the native dialog itself is not observable here).
|
|
await expect
|
|
.poll(() => page.evaluate(() => (window as unknown as { __promptCalled: boolean }).__promptCalled))
|
|
.toBe(true);
|
|
});
|
|
|
|
test.describe('iOS Safari', () => {
|
|
test.use({ userAgent: IPHONE_SAFARI });
|
|
|
|
test('shows manual Add-to-Home-Screen instructions (no programmatic install)', async ({ page }) => {
|
|
await page.goto('/');
|
|
await expect(page.getByRole('button', { name: /guest/i })).toBeVisible();
|
|
|
|
// On iOS Safari the CTA shows at once (no beforeinstallprompt is ever fired there).
|
|
const cta = page.getByRole('button', { name: /Install the app/i });
|
|
await expect(cta).toBeVisible();
|
|
await cta.click();
|
|
|
|
// It opens the app's own instructions modal (not a native / Telegram popup).
|
|
await expect(page.getByRole('heading', { name: 'Add to Home Screen' })).toBeVisible();
|
|
await expect(page.getByText(/Tap the Share button/)).toBeVisible();
|
|
});
|
|
});
|