Files
Native-Router-Vue/apps/demo/e2e/pwa.spec.ts

93 lines
4.6 KiB
TypeScript

import { expect, test } from '@playwright/test'
const iphoneUserAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 Version/18.5 Mobile/15E148 Safari/604.1'
async function dispatchTouchStart(page: import('@playwright/test').Page, clientX: number) {
return await page.locator('.nvr-navigator').evaluate((element, x) => {
const event = new Event('touchstart', { bubbles: true, cancelable: true })
Object.defineProperty(event, 'touches', {
value: [{ identifier: 7, clientX: x, clientY: 240 }],
})
return { dispatched: element.dispatchEvent(event), prevented: event.defaultPrevented }
}, clientX)
}
test('ships an installable standalone manifest and iOS metadata', async ({ page, request }) => {
await page.goto('/inbox')
await expect(page.locator('meta[name="apple-mobile-web-app-capable"]')).toHaveAttribute('content', 'yes')
await expect(page.locator('meta[name="apple-mobile-web-app-status-bar-style"]')).toHaveAttribute('content', 'black-translucent')
const touchIconPath = await page.locator('link[rel="apple-touch-icon"]').evaluate((link) =>
new URL(link.getAttribute('href') ?? '', document.baseURI).pathname,
)
expect(touchIconPath).toBe('/apple-touch-icon.png')
const manifestResponse = await request.get('/manifest.webmanifest')
expect(manifestResponse.ok()).toBe(true)
const manifest = await manifestResponse.json()
expect(manifest).toMatchObject({ id: '/', scope: '/', start_url: '/', display: 'standalone' })
expect(manifest.icons).toEqual(expect.arrayContaining([
expect.objectContaining({ src: '/pwa-192.png', sizes: '192x192', type: 'image/png' }),
expect.objectContaining({ src: '/pwa-512.png', sizes: '512x512', type: 'image/png' }),
]))
expect((await request.get('/apple-touch-icon.png')).headers()['content-type']).toContain('image/png')
const workerResponse = await request.get('/sw.js')
expect(workerResponse.ok()).toBe(true)
expect(workerResponse.headers()['cache-control']).toContain('no-cache')
const worker = await workerResponse.text()
expect(worker).toContain('self.skipWaiting()')
expect(worker).toContain('clientsClaim()')
})
test('does not interfere with Safari edge touches before Home Screen installation', async ({ page }) => {
await page.addInitScript((userAgent) => {
Object.defineProperty(navigator, 'userAgent', { configurable: true, value: userAgent })
Object.defineProperty(navigator, 'standalone', { configurable: true, value: false })
}, iphoneUserAgent)
await page.goto('/chat/maya')
await expect(page.locator('html')).toHaveAttribute('data-pwa-display-mode', 'browser')
await expect(page.locator('html')).toHaveAttribute('data-pwa-edge-guard', 'inactive')
expect((await dispatchTouchStart(page, 1)).prevented).toBe(false)
})
test('reserves only the leading edge in an installed iOS PWA', async ({ page }) => {
await page.addInitScript((userAgent) => {
Object.defineProperty(navigator, 'userAgent', { configurable: true, value: userAgent })
Object.defineProperty(navigator, 'standalone', { configurable: true, value: true })
}, iphoneUserAgent)
await page.goto('/chat/maya')
const root = page.locator('html')
await expect(root).toHaveAttribute('data-pwa-platform', 'ios')
await expect(root).toHaveAttribute('data-pwa-display-mode', 'standalone')
await expect(root).toHaveAttribute('data-pwa-edge-guard', 'active')
expect((await dispatchTouchStart(page, 80)).prevented).toBe(false)
expect((await dispatchTouchStart(page, 1)).prevented).toBe(true)
await expect(root).toHaveAttribute('data-pwa-edge-claims', '1')
})
test('registers and activates the offline service worker', async ({ page }) => {
await page.goto('/inbox')
const workerUrl = await page.evaluate(async () => {
const registration = await navigator.serviceWorker.ready
return registration.active?.scriptURL ?? ''
})
expect(workerUrl).toMatch(/\/sw\.js$/)
await expect(page.locator('html')).not.toHaveAttribute('data-pwa-update-checks', '0')
})
test('precaches lazily split routes for offline navigation', async ({ page }) => {
await page.goto('/inbox')
await page.evaluate(async () => { await navigator.serviceWorker.ready })
const cachedUrls = await page.evaluate(async () => {
const urls: string[] = []
for (const name of await caches.keys()) {
const cache = await caches.open(name)
urls.push(...(await cache.keys()).map((request) => request.url))
}
return urls
})
expect(cachedUrls.some((url) => /StoriesView-.*\.js$/.test(url))).toBe(true)
expect(cachedUrls.some((url) => /ProfileView-.*\.js$/.test(url))).toBe(true)
expect(cachedUrls.some((url) => /RuntimeLabView-.*\.js$/.test(url))).toBe(true)
})