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,31 @@ 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 (
67+ `"manifestHref":"/docs/_next/static/${ buildId } /_offline-navigation-manifest.json"`
68+ )
69+ expect ( serviceWorkerScript ) . toContain ( 'skipWaiting' )
70+ expect ( serviceWorkerScript ) . toContain ( 'clients.claim' )
71+ expect ( serviceWorkerScript ) . not . toContain ( 'respondWith' )
72+
5573 expect ( manifestJson ) . toEqual ( {
5674 version : 1 ,
5775 buildId,
5876 basePath : '/docs' ,
59- assetPrefix : 'https://cdn.example.com /app-assets' ,
77+ assetPrefix : '/app-assets' ,
6078 trailingSlash : true ,
6179 output : 'default' ,
6280 scope : '/docs/' ,
@@ -69,9 +87,72 @@ describe('offlineNavigations build artifacts', () => {
6987 path : `static/${ buildId } /_offline-navigation-fallback.html` ,
7088 href : `/docs/_next/static/${ buildId } /_offline-navigation-fallback.html` ,
7189 } ,
90+ serviceWorker : {
91+ path : `static/_offline-navigation-service-worker.js` ,
92+ href : `/docs/_next/static/_offline-navigation-service-worker.js` ,
93+ } ,
7294 } )
7395 } )
7496
97+ it ( 'registers the pass-through service worker when enabled' , async ( ) => {
98+ const buildResult = await next . build ( )
99+ expect ( buildResult . exitCode ) . toBe ( 0 )
100+
101+ await next . start ( { skipBuild : true } )
102+
103+ try {
104+ const swResponse = await next . fetch (
105+ `/docs/_next/static/_offline-navigation-service-worker.js${ next . getDeploymentIdQuery ( ) } `
106+ )
107+ expect ( swResponse . status ) . toBe ( 200 )
108+ expect ( swResponse . headers . get ( 'cache-control' ) ) . toBe (
109+ 'no-cache, must-revalidate'
110+ )
111+ expect ( swResponse . headers . get ( 'service-worker-allowed' ) ) . toBe ( '/docs/' )
112+
113+ const browser = await next . browser ( '/docs' )
114+ await retry ( async ( ) => {
115+ const registration = await browser . eval ( async ( ) => {
116+ if ( ! ( 'serviceWorker' in navigator ) ) {
117+ return null
118+ }
119+
120+ const registrations = await navigator . serviceWorker . getRegistrations ( )
121+ const registration = registrations . find ( ( registration ) =>
122+ registration . scope . endsWith ( '/docs/' )
123+ )
124+
125+ if ( ! registration ) {
126+ return null
127+ }
128+
129+ return {
130+ scope : registration . scope ,
131+ scriptURL : registration . active ?. scriptURL ?? null ,
132+ }
133+ } )
134+
135+ expect ( registration ) . toEqual ( {
136+ scope : `${ next . url } /docs/` ,
137+ scriptURL : `${ next . url } /docs/_next/static/_offline-navigation-service-worker.js${ next . getDeploymentIdQuery ( ) } ` ,
138+ } )
139+ } )
140+
141+ await browser . eval ( async ( ) => {
142+ if ( ! ( 'serviceWorker' in navigator ) ) {
143+ return
144+ }
145+
146+ const registrations = await navigator . serviceWorker . getRegistrations ( )
147+ await Promise . all (
148+ registrations . map ( ( registration ) => registration . unregister ( ) )
149+ )
150+ } )
151+ } finally {
152+ await next . stop ( )
153+ }
154+ } )
155+
75156 it ( 'does not emit offline navigation artifacts when disabled' , async ( ) => {
76157 await next . patchFile ( 'next.config.js' , ( content ) =>
77158 content . replace ( 'offlineNavigations: true' , 'offlineNavigations: false' )
@@ -80,9 +161,10 @@ describe('offlineNavigations build artifacts', () => {
80161 const buildResult = await next . build ( )
81162 expect ( buildResult . exitCode ) . toBe ( 0 )
82163
83- const { fallbackDocument, manifest } =
164+ const { fallbackDocument, manifest, serviceWorker } =
84165 await getOfflineNavigationArtifactPaths ( )
85166 expect ( existsSync ( fallbackDocument . absolutePath ) ) . toBe ( false )
86167 expect ( existsSync ( manifest . absolutePath ) ) . toBe ( false )
168+ expect ( existsSync ( serviceWorker . absolutePath ) ) . toBe ( false )
87169 } )
88170} )
0 commit comments