diff --git a/__tests__/service-worker/service-worker.test.js b/__tests__/service-worker/service-worker.test.js index 8aa341a..43c9601 100644 --- a/__tests__/service-worker/service-worker.test.js +++ b/__tests__/service-worker/service-worker.test.js @@ -1592,6 +1592,246 @@ describe('service-worker', async () => { assertEquals(await response.json(), { test: "success" }) }); + it("should use return a 4xx error directly from the last plugin, regardless of previous plugin errors or rejection", async () => { + window.LibResilientConfig = { + plugins: [{ + name: 'reject-all' + },{ + name: 'error-out' + },{ + name: 'return-418' + }], + loggedComponents: [ + 'service-worker' + ] + } + + let rejectingFetch = spy( + (request, init)=>{ return Promise.reject('reject-all rejecting a request for: ' + request); } + ) + + window.LibResilientPluginConstructors.set('reject-all', ()=>{ + return { + name: 'reject-all', + description: 'Reject all requests.', + version: '0.0.1', + fetch: rejectingFetch + } + }) + + let throwingFetch = spy( + (request, init)=>{ throw new Error('error-out throwing an Error for: ' + request); } + ) + + window.LibResilientPluginConstructors.set('error-out', ()=>{ + return { + name: 'error-out', + description: 'Throws.', + version: '0.0.1', + fetch: throwingFetch + } + }) + + let mock_response_data = { + data: JSON.stringify({text: "success"}), + status: 418, + statusText: "Im A Teapot" + } + window.fetch = spy(window.getMockedFetch(mock_response_data)) + + + window.LibResilientPluginConstructors.set('return-418', ()=>{ + return { + name: 'return-418', + description: 'Return 418 HTTP Error.', + version: '0.0.1', + fetch: fetch + } + }) + + await import("../../service-worker.js?" + window.test_id); + await self.dispatchEvent(new Event('install')) + await self.waitForSWInstall() + + let fetch_event = new FetchEvent('test.json') + window.dispatchEvent(fetch_event) + let response = await fetch_event.waitForResponse() + + assertSpyCalls(window.fetch, 2); // two, because the first one is for config.json + assertSpyCalls(rejectingFetch, 1); + assertSpyCalls(throwingFetch, 1); + assertSpyCall(window.fetch, 1, { args: [ + "https://test.resilient.is/test.json", + { + cache: undefined, + integrity: undefined, + method: "GET", + redirect: "follow", + referrer: undefined, + }] + }) + assertEquals(response.status, 418) + assertEquals(response.statusText, 'Im A Teapot') + assertEquals(await response.json(), { text: "success" }) + }); + + it("should use return a 4xx error directly from a plugin, regardless of any following plugins", async () => { + window.LibResilientConfig = { + plugins: [{ + name: 'return-418' + },{ + name: 'reject-all' + },{ + name: 'error-out' + }], + loggedComponents: [ + 'service-worker' + ] + } + + let rejectingFetch = spy( + (request, init)=>{ return Promise.reject('reject-all rejecting a request for: ' + request); } + ) + + window.LibResilientPluginConstructors.set('reject-all', ()=>{ + return { + name: 'reject-all', + description: 'Reject all requests.', + version: '0.0.1', + fetch: rejectingFetch + } + }) + + let throwingFetch = spy( + (request, init)=>{ throw new Error('error-out throwing an Error for: ' + request); } + ) + + window.LibResilientPluginConstructors.set('error-out', ()=>{ + return { + name: 'error-out', + description: 'Throws.', + version: '0.0.1', + fetch: throwingFetch + } + }) + + let mock_response_data = { + data: JSON.stringify({text: "success"}), + status: 418, + statusText: "Im A Teapot" + } + window.fetch = spy(window.getMockedFetch(mock_response_data)) + + + window.LibResilientPluginConstructors.set('return-418', ()=>{ + return { + name: 'return-418', + description: 'Return 418 HTTP Error.', + version: '0.0.1', + fetch: fetch + } + }) + + await import("../../service-worker.js?" + window.test_id); + await self.dispatchEvent(new Event('install')) + await self.waitForSWInstall() + + let fetch_event = new FetchEvent('test.json') + window.dispatchEvent(fetch_event) + let response = await fetch_event.waitForResponse() + + assertSpyCalls(window.fetch, 2); // two, because the first one is for config.json + assertSpyCalls(rejectingFetch, 0); + assertSpyCalls(throwingFetch, 0); + assertSpyCall(window.fetch, 1, { args: [ + "https://test.resilient.is/test.json", + { + cache: undefined, + integrity: undefined, + method: "GET", + redirect: "follow", + referrer: undefined, + }] + }) + assertEquals(response.status, 418) + assertEquals(response.statusText, 'Im A Teapot') + assertEquals(await response.json(), { text: "success" }) + }); + + it("should use treat a 5xx error from a plugin as internal error and try following plugins", async () => { + window.LibResilientConfig = { + plugins: [{ + name: 'return-500' + },{ + name: 'reject-all' + },{ + name: 'error-out' + }], + loggedComponents: [ + 'service-worker' + ] + } + + let rejectingFetch = spy( + (request, init)=>{ return Promise.reject('reject-all rejecting a request for: ' + request); } + ) + + window.LibResilientPluginConstructors.set('reject-all', ()=>{ + return { + name: 'reject-all', + description: 'Reject all requests.', + version: '0.0.1', + fetch: rejectingFetch + } + }) + + let throwingFetch = spy( + (request, init)=>{ throw new Error('error-out throwing an Error for: ' + request); } + ) + + window.LibResilientPluginConstructors.set('error-out', ()=>{ + return { + name: 'error-out', + description: 'Throws.', + version: '0.0.1', + fetch: throwingFetch + } + }) + + let mock_response_data = { + data: JSON.stringify({text: "success"}), + status: 500, + statusText: "Internal Server Error" + } + window.fetch = spy(window.getMockedFetch(mock_response_data)) + + window.LibResilientPluginConstructors.set('return-500', ()=>{ + return { + name: 'return-500', + description: 'Return 500 HTTP Error.', + version: '0.0.1', + fetch: fetch + } + }) + + await import("../../service-worker.js?" + window.test_id); + await self.dispatchEvent(new Event('install')) + await self.waitForSWInstall() + + let fetch_event = new FetchEvent('test.json') + window.dispatchEvent(fetch_event) + let response = fetch_event.waitForResponse() + assertRejects( async () => { + return await response + }) + // wait for the response to resolve + await response.catch((e)=>{}) + + assertSpyCalls(window.fetch, 2); + assertSpyCalls(rejectingFetch, 1); + assertSpyCalls(throwingFetch, 1); + }); + it("should normalize query params in requested URLs by default", async () => { console.log(self.LibResilientConfig) @@ -2883,7 +3123,7 @@ describe('service-worker', async () => { assertEquals(await response.json(), { test: "success" }) }) - it("should return a 404 Not Found HTTP response object when handling a failed navigation request", async () => { + it("should return a 404 Not Found HTTP response object with an error screen when handling a rejected navigation request", async () => { window.LibResilientConfig = { plugins: [{ name: 'reject-all' @@ -2920,7 +3160,7 @@ describe('service-worker', async () => { assertEquals((await response.text()).slice(0, 57), 'Loading failed.') }) - it("should not return a 404 Not Found HTTP response object when handling a rejected non-navigation request", async () => { + it("should not return a 404 Not Found HTTP response object with an error screen when handling a rejected non-navigation request", async () => { window.LibResilientConfig = { plugins: [{ name: 'reject-all' @@ -2952,7 +3192,7 @@ describe('service-worker', async () => { assertRejects(async ()=>{ await fetch_event.waitForResponse() }) }) - it("should not return a 404 Not Found HTTP response object when handling a non-navigation request that throws an error", async () => { + it("should not return a 404 Not Found HTTP response object with an error screen when handling a non-navigation request that throws an error", async () => { window.LibResilientConfig = { plugins: [{ name: 'error-out'