diff --git a/examples/sveltekit-ts-assets-generator/client-test/offline.spec.ts b/examples/sveltekit-ts-assets-generator/client-test/offline.spec.ts new file mode 100644 index 0000000..770f5e1 --- /dev/null +++ b/examples/sveltekit-ts-assets-generator/client-test/offline.spec.ts @@ -0,0 +1,38 @@ +import {test, expect} from '@playwright/test'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import {generateSW} from "../pwa.mjs"; + +test('Test offline and trailing slashes', async ({ browser}) => { + // test offline + trailing slashes routes + const context = await browser.newContext() + const offlinePage = await context.newPage() + await offlinePage.goto('/') + const offlineSwURL = await offlinePage.evaluate(async () => { + const registration = await Promise.race([ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + navigator.serviceWorker.ready, + new Promise((_, reject) => setTimeout(() => reject(new Error('Service worker registration failed: time out')), 10000)) + ]) + // @ts-expect-error registration is of type unknown + return registration.active?.scriptURL + }); + const offlineSwName = generateSW ? 'sw.js' : 'prompt-sw.js' + expect(offlineSwURL).toBe(`http://localhost:4173/${offlineSwName}`); + await context.setOffline(true) + const aboutAnchor = offlinePage.getByRole('link', { name: 'About' }) + expect(await aboutAnchor.getAttribute('href')).toBe('/about') + await aboutAnchor.click({ noWaitAfter: false }) + const url = await offlinePage.evaluate(async () => { + await new Promise(resolve => setTimeout(resolve, 3000)) + return location.href + }) + expect(url).toBe('http://localhost:4173/about') + expect(offlinePage.locator('li[aria-current="page"] a').getByText('About')).toBeTruthy() + await offlinePage.reload({ waitUntil: 'load' }) + expect(offlinePage.url()).toBe('http://localhost:4173/about') + expect(offlinePage.locator('li[aria-current="page"] a').getByText('About')).toBeTruthy() + // Dispose context once it's no longer needed. + await context.close(); +}); diff --git a/examples/sveltekit-ts-assets-generator/client-test/sw.spec.ts b/examples/sveltekit-ts-assets-generator/client-test/sw.spec.ts new file mode 100644 index 0000000..bf8df4e --- /dev/null +++ b/examples/sveltekit-ts-assets-generator/client-test/sw.spec.ts @@ -0,0 +1,51 @@ +import {test, expect} from '@playwright/test'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import {generateSW} from "../pwa.mjs"; + +test('The service worker is registered and cache storage is present', async ({ page}) => { + await page.goto('/'); + + const swURL = await page.evaluate(async () => { + const registration = await Promise.race([ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + navigator.serviceWorker.ready, + new Promise((_, reject) => setTimeout(() => reject(new Error('Service worker registration failed: time out')), 10000)) + ]) + // @ts-expect-error registration is of type unknown + return registration.active?.scriptURL + }); + const swName = generateSW ? 'sw.js' : 'prompt-sw.js' + expect(swURL).toBe(`http://localhost:4173/${swName}`); + + const cacheContents = await page.evaluate(async () => { + const cacheState: Record> = {}; + for (const cacheName of await caches.keys()) { + const cache = await caches.open(cacheName); + cacheState[cacheName] = (await cache.keys()).map((req) => req.url); + } + return cacheState; + }); + + expect(Object.keys(cacheContents).length).toEqual(1) + + const key = 'workbox-precache-v2-http://localhost:4173/' + + expect(Object.keys(cacheContents)[0]).toEqual(key) + + const urls = cacheContents[key].map(url => url.slice('http://localhost:4173/'.length)) + + /* + 'http://localhost:4173/about?__WB_REVISION__=38251751d310c9b683a1426c22c135a2', + 'http://localhost:4173/?__WB_REVISION__=073370aa3804305a787b01180cd6b8aa', + 'http://localhost:4173/manifest.webmanifest?__WB_REVISION__=27df2fa4f35d014b42361148a2207da3' + */ + expect(urls.some(url => url.startsWith('manifest.webmanifest?__WB_REVISION__='))).toEqual(true) + expect(urls.some(url => url.startsWith('?__WB_REVISION__='))).toEqual(true) + expect(urls.some(url => url.startsWith('about?__WB_REVISION__='))).toEqual(true) + // dontCacheBustURLsMatching: any asset in _app/immutable folder shouldn't have a revision (?__WB_REVISION__=) + expect(urls.some(url => url.startsWith('_app/immutable/') && url.endsWith('.css'))).toEqual(true) + expect(urls.some(url => url.startsWith('_app/immutable/') && url.endsWith('.js'))).toEqual(true) + expect(urls.some(url => url.includes('_app/version.json?__WB_REVISION__='))).toEqual(true) +}); diff --git a/examples/sveltekit-ts-assets-generator/test/build.test.ts b/examples/sveltekit-ts-assets-generator/test/build.test.ts new file mode 100644 index 0000000..89a68cb --- /dev/null +++ b/examples/sveltekit-ts-assets-generator/test/build.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest' +import { existsSync, readFileSync } from 'node:fs' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import { generateSW } from '../pwa.mjs' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +import { nodeAdapter } from '../adapter.mjs' + +describe(`test-build: ${nodeAdapter ? 'node' : 'static'} adapter`, () => { + it(`service worker is generated: ${generateSW ? 'sw.js' : 'prompt-sw.js'}`, () => { + const swName = `./build/${nodeAdapter ? 'client/': ''}${generateSW ? 'sw.js' : 'prompt-sw.js'}` + expect(existsSync(swName), `${swName} doesn't exist`).toBeTruthy() + const webManifest = `./build/${nodeAdapter ? 'client/': ''}manifest.webmanifest` + expect(existsSync(webManifest), `${webManifest} doesn't exist`).toBeTruthy() + const swContent = readFileSync(swName, 'utf-8') + let match: RegExpMatchArray | null + if (generateSW) { + match = swContent.match(/define\(\['\.\/(workbox-\w+)'/) + expect(match && match.length === 2, `workbox-***.js entry not found in ${swName}`).toBeTruthy() + const workboxName = `./build/${nodeAdapter ? 'client/': ''}${match?.[1]}.js` + expect(existsSync(workboxName),`${workboxName} doesn't exist`).toBeTruthy() + } + match = swContent.match(/"url":\s*"manifest\.webmanifest"/) + expect(match && match.length === 1, 'missing manifest.webmanifest in sw precache manifest').toBeTruthy() + match = swContent.match(/"url":\s*"\/"/) + expect(match && match.length === 1, 'missing entry point route (/) in sw precache manifest').toBeTruthy() + match = swContent.match(/"url":\s*"about"/) + expect(match && match.length === 1,'missing about route (/about) in sw precache manifest').toBeTruthy() + if (nodeAdapter) { + match = swContent.match(/"url":\s*"server\//) + expect(match === null, 'found server/ entries in sw precache manifest').toBeTruthy() + } + }) +}) diff --git a/package.json b/package.json index 9371b0b..27cebcc 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "lint-fix": "nr lint --fix", "prepublishOnly": "npm run build", "release": "bumpp && npm publish", - "test": "pnpm run -C examples/sveltekit-ts test" + "test": "pnpm run -C examples/sveltekit-ts test && pnpm run -C examples/sveltekit-ts-assets-generator test" }, "peerDependencies": { "@sveltejs/kit": "^1.3.1 || ^2.0.1",