diff --git a/__tests__/service-worker/service-worker.test.js b/__tests__/service-worker/service-worker.test.js index 6c19d1e..e34aecf 100644 --- a/__tests__/service-worker/service-worker.test.js +++ b/__tests__/service-worker/service-worker.test.js @@ -289,16 +289,16 @@ beforeEach(async ()=>{ // clear the caches await caches .has('v1') - .then(async (hasv1) => { - if (hasv1) { + .then(async (hasit) => { + if (hasit) { await caches.delete('v1') } }) await caches - .has('v1:temp') - .then(async (hasv1) => { - if (hasv1) { - await caches.delete('v1:temp') + .has('v1:verified') + .then(async (hasit) => { + if (hasit) { + await caches.delete('v1:verified') } }) // make sure we're starting with a clean slate in LibResilientPluginConstructors @@ -370,6 +370,44 @@ describe('service-worker', async () => { assertSpyCalls(self.fetch, 1) }) + it("should use default LibResilientConfig values when fetching config.json throws an exception", async () => { + + window.fetch = spy(() => { + throw new Error('Testing exception') + }) + + await import("../../service-worker.js?" + window.test_id); + await self.dispatchEvent(new Event('install')) + await self.waitForSWInstall() + + assertEquals(typeof self.LibResilientConfig, "object") + assertEquals(self.LibResilientConfig.defaultPluginTimeout, 10000) + assertEquals(self.LibResilientConfig.plugins, [{name: "fetch"},{name: "cache"}]) + assertEquals(self.LibResilientConfig.loggedComponents, ['service-worker', 'fetch', 'cache']) + assertEquals(self.LibResilientConfig.normalizeQueryParams, true) + assertEquals(self.LibResilientConfig.useMimeSniffingLibrary, false) + assertSpyCalls(self.fetch, 1) + }) + + it("should use default LibResilientConfig values when fetching config.json returns undefined", async () => { + + window.fetch = spy(() => { + return undefined; + }) + + await import("../../service-worker.js?" + window.test_id); + await self.dispatchEvent(new Event('install')) + await self.waitForSWInstall() + + assertEquals(typeof self.LibResilientConfig, "object") + assertEquals(self.LibResilientConfig.defaultPluginTimeout, 10000) + assertEquals(self.LibResilientConfig.plugins, [{name: "fetch"},{name: "cache"}]) + assertEquals(self.LibResilientConfig.loggedComponents, ['service-worker', 'fetch', 'cache']) + assertEquals(self.LibResilientConfig.normalizeQueryParams, true) + assertEquals(self.LibResilientConfig.useMimeSniffingLibrary, false) + assertSpyCalls(self.fetch, 1) + }) + it("should use default LibResilientConfig values when config.json not valid JSON", async () => { let mock_response_data = { @@ -490,7 +528,7 @@ describe('service-worker', async () => { assertSpyCalls(self.fetch, 1) }) - it("should use config values from a valid fetched config.json file, caching it", async () => { + it("should use config values from a valid fetched config.json file, caching it in both caches (v1, v1:verified)", async () => { let mock_response_data = { data: JSON.stringify({loggedComponents: ['service-worker', 'cache'], plugins: [{name: "cache"}], defaultPluginTimeout: 5000, normalizeQueryParams: false, useMimeSniffingLibrary: true}) } @@ -515,6 +553,10 @@ describe('service-worker', async () => { await window.waitForCacheAction(window.location.origin + 'config.json', 'v1'), mock_response_data.data ); + assertEquals( + await window.waitForCacheAction(window.location.origin + 'config.json', 'v1:verified'), + mock_response_data.data + ); }) it("should instantiate a complex tree of configured plugins", async () => { @@ -775,9 +817,17 @@ describe('service-worker', async () => { assertEquals(self.LibResilientConfig.defaultPluginTimeout, 5000) assertEquals(self.LibResilientConfig.plugins, [{name: "cache"}]) assertEquals(self.LibResilientConfig.loggedComponents, ['service-worker', 'cache']) + + // cacheConfigJSON() is called asynchronously in the Service Worker, + // if we don't make sure that the caching has completed, we will get an error. + // so we wait until config.json is cached, and use that to verify it is in fact cached + assertEquals( + await window.waitForCacheAction(window.location.origin + 'config.json', 'v1:verified'), + mock_response_data.data + ); }) - it("should use a stale cached valid config.json file without a fetch, then retrieve and cache a fresh config.json using the configured plugins", async () => { + it("should use a stale cached valid config.json file without a fetch, caching it in v1:verified cache, then retrieve and cache a fresh config.json using the configured plugins", async () => { // this does not change var config_url = window.location.origin + 'config.json' @@ -839,6 +889,14 @@ describe('service-worker', async () => { assertSpyCalls(self.fetch, 0) assertSpyCalls(resolveConfigFetch, 1) + // cacheConfigJSON() is called asynchronously in the Service Worker, + // if we don't make sure that the caching has completed, we will get an error. + // so we wait until config.json is cached, and use that to verify it is in fact cached + assertEquals( + await window.waitForCacheAction(window.location.origin + 'config.json', 'v1:verified'), + mock_response_data.data + ); + // wait until verify the *new* config got cached // running waitForCacheAdd only once might not be enough, as the cache // already contained config.json! @@ -847,7 +905,7 @@ describe('service-worker', async () => { // so as not to end up in a forever loop for (let i=0; i<100; i++) { // did we get the new config? - if (await window.waitForCacheAction(config_url, 'v1:temp') === mock_response_data2.data) { + if (await window.waitForCacheAction(config_url, 'v1') === mock_response_data2.data) { // we did! we're done return true; } @@ -855,7 +913,7 @@ describe('service-worker', async () => { throw new Error('New config failed to cache, apparently!') }) - it("should use a stale cached valid config.json file without a fetch; invalid config.json retrieved using the configured plugins should not be cached", async () => { + it("should use a stale cached valid config.json file without a fetch, caching it in v1:verified cache; invalid config.json retrieved using the configured plugins should not be cached", async () => { // this does not change var config_url = window.location.origin + 'config.json' @@ -917,6 +975,14 @@ describe('service-worker', async () => { assertSpyCalls(self.fetch, 0) assertSpyCalls(resolveConfigFetch, 1) + // cacheConfigJSON() is called asynchronously in the Service Worker, + // if we don't make sure that the caching has completed, we will get an error. + // so we wait until config.json is cached, and use that to verify it is in fact cached + assertEquals( + await window.waitForCacheAction(window.location.origin + 'config.json', 'v1:verified'), + mock_response_data.data + ); + // waiting for potential caching of the "new" config for (let i=0; i<100; i++) { // did we get the new config? @@ -927,7 +993,7 @@ describe('service-worker', async () => { } }) - it("should use a stale cached valid config.json file without a fetch; valid config.json retrieved using the configured plugins other than fetch should not be cached", async () => { + it("should use a stale cached valid config.json file without a fetch; invalid config.json retrieved using the configured plugins other than fetch should not be cached", async () => { // this does not change var config_url = window.location.origin + 'config.json' @@ -956,7 +1022,7 @@ describe('service-worker', async () => { console.log(await resp.text()) }) - // prepare the fresh invalid config request/response + // prepare the fresh valid config request/response let mock_response_data2 = { data: JSON.stringify({loggedComponents: ['service-worker', 'resolve-config', 'cache'], plugins: [{name: "resolve-config"}, {name: "cache"}], defaultPluginTimeout: 2000}), headers: { diff --git a/service-worker.js b/service-worker.js index fcbda17..681b107 100644 --- a/service-worker.js +++ b/service-worker.js @@ -459,7 +459,7 @@ let initServiceWorker = async () => { let lrpcBackup = new Map(self.LibResilientPluginConstructors) // caches to try: temp cache, main cache - let available_caches = ['v1:temp', 'v1'] + let available_caches = ['v1', 'v1:verified'] // keep track let config_executed = false @@ -548,12 +548,19 @@ let initServiceWorker = async () => { self.LibResilientConfig = config self.log('service-worker', 'config loaded.') - // we're good, let's cache the config if we need to - // that is, if it comes from the "v1:temp" cache + // we're good, let's cache the config as verified if we need to + // that is, if it comes from the "v1" cache... + if (use_cache === "v1") { + self.log('service-worker', `successfully loaded config.json; caching in cache: v1:verified`) + cacheConfigJSON(configURL, cresponse, 'v1:verified') // or, was fetch()ed and valid (no caching if we're going with defaults, obviously) - if ( (use_cache === "v1:temp") || ( (use_cache === undefined) && (cresponse !== false) ) ) { - self.log('service-worker', `successfully loaded config.json; caching in cache: v1`) - cacheConfigJSON(configURL, cresponse, 'v1') + } else if ( (use_cache === undefined) && (cresponse !== false) ) { + self.log('service-worker', `successfully loaded config.json; caching in cache: v1, v1:verified`) + // we want to cache to both, so that: + // 1. we get the extra bit of performance from using the v1 cache that is checked first + // 2. but we get the verified config already in the v1:verified cache for later + cacheConfigJSON(configURL, await cresponse.clone(), 'v1') + cacheConfigJSON(configURL, cresponse, 'v1:verified') } // inform @@ -622,7 +629,7 @@ let initServiceWorker = async () => { self.log('service-worker', `valid config.json successfully retrieved through plugins; caching.`) // cache it, asynchronously, in the temporary cache // as the config has not been "execute-tested" yet - cacheConfigJSON(configURL, cresponse, "v1:temp") + cacheConfigJSON(configURL, cresponse, "v1") } }) }