11import { existsSync } from 'fs'
22import { join } from 'path'
33import { nextTestSetup } from 'e2e-utils'
4+ import { retry } from 'next-test-utils'
45
56describe ( 'offlineNavigations build artifacts' , ( ) => {
67 const { next } = nextTestSetup ( {
@@ -23,6 +24,15 @@ describe('offlineNavigations build artifacts', () => {
2324 ) ,
2425 relativePath : `.next/static/${ buildId } /_offline-navigation-fallback.html` ,
2526 } ,
27+ serviceWorker : {
28+ absolutePath : join (
29+ next . testDir ,
30+ '.next' ,
31+ 'static' ,
32+ '_offline-navigation-service-worker.js'
33+ ) ,
34+ relativePath : `.next/static/_offline-navigation-service-worker.js` ,
35+ } ,
2636 manifest : {
2737 absolutePath : join (
2838 next . testDir ,
@@ -40,23 +50,32 @@ describe('offlineNavigations build artifacts', () => {
4050 const buildResult = await next . build ( )
4151 expect ( buildResult . exitCode ) . toBe ( 0 )
4252
43- const { buildId, fallbackDocument, manifest } =
53+ const { buildId, fallbackDocument, manifest, serviceWorker } =
4454 await getOfflineNavigationArtifactPaths ( )
4555 const html = await next . readFile ( fallbackDocument . relativePath )
4656 const manifestJson = JSON . parse ( await next . readFile ( manifest . relativePath ) )
57+ const serviceWorkerScript = await next . readFile ( serviceWorker . relativePath )
4758
4859 expect ( html ) . toContain ( 'data-next-offline-navigation-fallback' )
4960 expect ( html ) . toContain ( 'id="__NEXT_OFFLINE_NAVIGATION_FALLBACK"' )
5061 expect ( html ) . toContain ( `"buildId":"${ buildId } "` )
5162 expect ( html ) . toContain ( 'self.__next_f' )
52- expect ( html ) . toContain ( 'https://cdn.example.com /app-assets/_next/static/' )
63+ expect ( html ) . toContain ( '/app-assets/_next/static/' )
5364 expect ( html ) . not . toContain ( 'offline navigations page' )
5465
66+ expect ( serviceWorkerScript ) . toContain ( `"buildId":"${ buildId } "` )
67+ expect ( serviceWorkerScript ) . toContain (
68+ `"manifestHref":"/docs/_next/static/${ buildId } /_offline-navigation-manifest.json"`
69+ )
70+ expect ( serviceWorkerScript ) . toContain ( 'skipWaiting' )
71+ expect ( serviceWorkerScript ) . toContain ( 'clients.claim' )
72+ expect ( serviceWorkerScript ) . not . toContain ( 'respondWith' )
73+
5574 expect ( manifestJson ) . toEqual ( {
5675 version : 1 ,
5776 buildId,
5877 basePath : '/docs' ,
59- assetPrefix : 'https://cdn.example.com /app-assets' ,
78+ assetPrefix : '/app-assets' ,
6079 trailingSlash : true ,
6180 output : 'default' ,
6281 scope : '/docs/' ,
@@ -69,9 +88,72 @@ describe('offlineNavigations build artifacts', () => {
6988 path : `static/${ buildId } /_offline-navigation-fallback.html` ,
7089 href : `/docs/_next/static/${ buildId } /_offline-navigation-fallback.html` ,
7190 } ,
91+ serviceWorker : {
92+ path : `static/_offline-navigation-service-worker.js` ,
93+ href : `/docs/_next/static/_offline-navigation-service-worker.js` ,
94+ } ,
7295 } )
7396 } )
7497
98+ it ( 'registers the pass-through service worker when enabled' , async ( ) => {
99+ const buildResult = await next . build ( )
100+ expect ( buildResult . exitCode ) . toBe ( 0 )
101+
102+ await next . start ( { skipBuild : true } )
103+
104+ try {
105+ const swResponse = await next . fetch (
106+ `/docs/_next/static/_offline-navigation-service-worker.js${ next . getDeploymentIdQuery ( ) } `
107+ )
108+ expect ( swResponse . status ) . toBe ( 200 )
109+ expect ( swResponse . headers . get ( 'cache-control' ) ) . toBe (
110+ 'no-cache, must-revalidate'
111+ )
112+ expect ( swResponse . headers . get ( 'service-worker-allowed' ) ) . toBe ( '/docs/' )
113+
114+ const browser = await next . browser ( '/docs' )
115+ await retry ( async ( ) => {
116+ const registration = await browser . eval ( async ( ) => {
117+ if ( ! ( 'serviceWorker' in navigator ) ) {
118+ return null
119+ }
120+
121+ const registrations = await navigator . serviceWorker . getRegistrations ( )
122+ const registration = registrations . find ( ( registration ) =>
123+ registration . scope . endsWith ( '/docs/' )
124+ )
125+
126+ if ( ! registration ) {
127+ return null
128+ }
129+
130+ return {
131+ scope : registration . scope ,
132+ scriptURL : registration . active ?. scriptURL ?? null ,
133+ }
134+ } )
135+
136+ expect ( registration ) . toEqual ( {
137+ scope : `${ next . url } /docs/` ,
138+ scriptURL : `${ next . url } /docs/_next/static/_offline-navigation-service-worker.js${ next . getDeploymentIdQuery ( ) } ` ,
139+ } )
140+ } )
141+
142+ await browser . eval ( async ( ) => {
143+ if ( ! ( 'serviceWorker' in navigator ) ) {
144+ return
145+ }
146+
147+ const registrations = await navigator . serviceWorker . getRegistrations ( )
148+ await Promise . all (
149+ registrations . map ( ( registration ) => registration . unregister ( ) )
150+ )
151+ } )
152+ } finally {
153+ await next . stop ( )
154+ }
155+ } )
156+
75157 it ( 'does not emit offline navigation artifacts when disabled' , async ( ) => {
76158 await next . patchFile ( 'next.config.js' , ( content ) =>
77159 content . replace ( 'offlineNavigations: true' , 'offlineNavigations: false' )
@@ -80,9 +162,10 @@ describe('offlineNavigations build artifacts', () => {
80162 const buildResult = await next . build ( )
81163 expect ( buildResult . exitCode ) . toBe ( 0 )
82164
83- const { fallbackDocument, manifest } =
165+ const { fallbackDocument, manifest, serviceWorker } =
84166 await getOfflineNavigationArtifactPaths ( )
85167 expect ( existsSync ( fallbackDocument . absolutePath ) ) . toBe ( false )
86168 expect ( existsSync ( manifest . absolutePath ) ) . toBe ( false )
169+ expect ( existsSync ( serviceWorker . absolutePath ) ) . toBe ( false )
87170 } )
88171} )
0 commit comments