libresilient/__tests__/service-worker.test.js

1861 wiersze
69 KiB
JavaScript

const makeServiceWorkerEnv = require('service-worker-mock');
global.fetch = require('node-fetch');
jest.mock('node-fetch')
describe("service-worker", () => {
beforeEach(() => {
global.fetch.mockImplementation((url, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: url
})
);
});
Object.assign(global, makeServiceWorkerEnv());
global.self = new ServiceWorkerGlobalScope()
jest.resetModules();
self.LibResilientPlugins = new Array()
self.importScripts = jest.fn((url)=>{
console.debug(`importScripts('../${url}')`)
try {
require('../' + url);
} catch(e) {}
})
// TODO: pretty ugly, but necessary for some reason...
global.LibResilientPluginConstructors = new Map()
self.LibResilientPluginConstructors = global.LibResilientPluginConstructors
})
test("reality-check: Promise.any() polyfill should work", async () => {
self.LibResilientPlugins = false
self.LibResilientConfig = {
plugins: [],
loggedComponents: [
'service-worker'
]
}
expect.assertions(4)
// we want to make sure to actually test the polyfill
Promise.any = undefined
expect(typeof Promise.any).toEqual('undefined')
require("../service-worker.js");
expect(typeof Promise.any).toEqual('function')
expect(await Promise.any([
Promise.resolve('test resolve 1'),
Promise.reject('test reject 2')
])).toEqual('test resolve 1')
try {
await Promise.any([
Promise.reject('test reject 1'),
Promise.reject('test reject 2')
])
} catch (e) {
expect(e).toEqual([
"test reject 1",
"test reject 2"
])
}
})
test("basic set-up: LibResilientPlugins", async () => {
self.LibResilientPlugins = false
self.LibResilientConfig = {
plugins: [],
loggedComponents: [
'service-worker'
]
}
require("../service-worker.js");
expect(self.LibResilientPlugins).toBeInstanceOf(Array)
})
test("basic set-up: use default LibResilientConfig values when config.json missing", async () => {
self.LibResilientConfig = null
global.fetch.mockImplementation((url, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "fail" })],
{type: "application/json"}
),
{
status: 404,
statusText: "Not Found",
headers: {
'ETag': 'TestingETagHeader'
},
url: url
})
);
});
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(10000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "fetch"},{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'fetch', 'cache'])
expect(fetch).toHaveBeenCalled();
})
test("basic set-up: use default LibResilientConfig values when config.json not valid JSON", async () => {
self.LibResilientConfig = null
global.fetch.mockImplementation((url, init) => {
return Promise.resolve(
new Response(
new Blob(
["not a JSON"],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: url
})
);
});
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(10000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "fetch"},{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'fetch', 'cache'])
expect(fetch).toHaveBeenCalled();
})
test("basic set-up: use default LibResilientConfig values when no valid 'plugins' field in config.json", async () => {
self.LibResilientConfig = null
global.fetch.mockImplementation((url, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({loggedComponents: ['service-worker', 'fetch'], plugins: 'not a valid array'})],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: url
})
);
});
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(10000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "fetch"},{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'fetch', 'cache'])
expect(fetch).toHaveBeenCalled();
})
test("basic set-up: use default LibResilientConfig values when no valid 'loggedComponents' field in config.json", async () => {
self.LibResilientConfig = null
global.fetch.mockImplementation((url, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({loggedComponents: 'not a valid array', plugins: [{name: "fetch"}]})],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: url
})
);
});
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(10000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "fetch"},{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'fetch', 'cache'])
expect(fetch).toHaveBeenCalled();
})
test("basic set-up: use default LibResilientConfig values when 'defaultPluginTimeout' field in config.json contains an invalid value", async () => {
self.LibResilientConfig = null
global.fetch.mockImplementation((url, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({loggedComponents: ['service-worker', 'fetch'], plugins: [{name: "fetch"}], defaultPluginTimeout: 'not an integer'})],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: url
})
);
});
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(10000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "fetch"},{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'fetch', 'cache'])
expect(fetch).toHaveBeenCalled();
})
test("basic set-up: use config values from a valid config.json file", async () => {
self.LibResilientConfig = null
global.fetch.mockImplementation((url, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({loggedComponents: ['service-worker', 'cache'], plugins: [{name: "cache"}], defaultPluginTimeout: 5000})],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: url
})
);
});
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(5000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'cache'])
expect(fetch).toHaveBeenCalled();
})
test("basic set-up: instantiate a complex tree of configured plugins", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'plugin-1',
uses: [{
name: 'plugin-2',
uses: [{
name: 'plugin-3'
}]
},{
name: 'plugin-3'
}]
},{
name: 'plugin-2',
uses: [{
name: 'plugin-3'
}]
},{
name: 'plugin-3',
uses: [{
name: 'plugin-1'
},{
name: 'plugin-2',
uses: [{
name: 'plugin-1',
uses: [{
name: 'plugin-4'
}]
}]
}]
},{
name: 'plugin-4'
}
]
}
global.LibResilientPluginConstructors.set('plugin-1', (LRPC, config)=>{
return {
name: 'plugin-1',
description: 'Plugin Type 1',
version: '0.0.1',
fetch: (url)=>{return true},
uses: config.uses || []
}
})
global.LibResilientPluginConstructors.set('plugin-2', (LRPC, config)=>{
return {
name: 'plugin-2',
description: 'Plugin Type 2',
version: '0.0.1',
fetch: (url)=>{return true},
uses: config.uses || []
}
})
global.LibResilientPluginConstructors.set('plugin-3', (LRPC, config)=>{
return {
name: 'plugin-3',
description: 'Plugin Type 3',
version: '0.0.1',
fetch: (url)=>{return true},
uses: config.uses || []
}
})
global.LibResilientPluginConstructors.set('plugin-4', (LRPC, config)=>{
return {
name: 'plugin-4',
description: 'Plugin Type 4',
version: '0.0.1',
fetch: (url)=>{return true},
uses: config.uses || []
}
})
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
// basic stuff
expect(self.LibResilientPlugins.length).toEqual(4)
expect(self.LibResilientPlugins[0].name).toEqual('plugin-1')
expect(self.LibResilientPlugins[1].name).toEqual('plugin-2')
expect(self.LibResilientPlugins[2].name).toEqual('plugin-3')
expect(self.LibResilientPlugins[3].name).toEqual('plugin-4')
// first plugin dependencies
expect(self.LibResilientPlugins[0].uses.length).toEqual(2)
expect(self.LibResilientPlugins[0].uses[0].name).toEqual('plugin-2')
expect(self.LibResilientPlugins[0].uses[0].uses.length).toEqual(1)
expect(self.LibResilientPlugins[0].uses[0].uses[0].name).toEqual('plugin-3')
expect(self.LibResilientPlugins[0].uses[0].uses[0].uses.length).toEqual(0)
expect(self.LibResilientPlugins[0].uses[1].name).toEqual('plugin-3')
expect(self.LibResilientPlugins[0].uses[1].uses.length).toEqual(0)
// second plugin dependencies
expect(self.LibResilientPlugins[1].uses.length).toEqual(1)
expect(self.LibResilientPlugins[1].uses[0].name).toEqual('plugin-3')
expect(self.LibResilientPlugins[1].uses[0].uses.length).toEqual(0)
// third plugin dependencies
expect(self.LibResilientPlugins[2].uses.length).toEqual(2)
expect(self.LibResilientPlugins[2].uses[0].name).toEqual('plugin-1')
expect(self.LibResilientPlugins[2].uses[0].uses.length).toEqual(0)
expect(self.LibResilientPlugins[2].uses[1].name).toEqual('plugin-2')
expect(self.LibResilientPlugins[2].uses[1].uses.length).toEqual(1)
expect(self.LibResilientPlugins[2].uses[1].uses[0].name).toEqual('plugin-1')
expect(self.LibResilientPlugins[2].uses[1].uses[0].uses.length).toEqual(1)
expect(self.LibResilientPlugins[2].uses[1].uses[0].uses[0].name).toEqual('plugin-4')
expect(self.LibResilientPlugins[2].uses[1].uses[0].uses[0].uses.length).toEqual(0)
// fourth plugin dependencies
expect(self.LibResilientPlugins[3].uses.length).toEqual(0)
})
test("basic set-up: instantiate configured plugins; explicitly disabled plugins should not be instantiated", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'plugin-enabled'
},{
name: 'plugin-disabled',
enabled: false
},{
name: 'plugin-enabled',
enabled: true
}]
}
global.LibResilientPluginConstructors.set('plugin-enabled', ()=>{
return {
name: 'plugin-enabled',
description: 'Enabled plugin',
version: '0.0.1',
fetch: (url)=>{return true}
}
})
global.LibResilientPluginConstructors.set('plugin-disabled', ()=>{
return {
name: 'plugin-disabled',
description: 'Disabled plugin',
version: '0.0.1',
fetch: (url)=>{return true}
}
})
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientPlugins.length).toEqual(2)
expect(self.LibResilientPlugins[0].name).toEqual('plugin-enabled')
expect(self.LibResilientPlugins[1].name).toEqual('plugin-enabled')
})
test("basic set-up: instantiate configured plugins; explicitly disabled dependencies should not be instantiated", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'plugin-disabled',
enabled: false,
uses: [{
name: 'dependency-enabled'
}]
},{
name: 'plugin-enabled',
uses: [{
name: 'dependency-disabled',
enabled: false
}]
},{
name: 'plugin-enabled',
uses: [{
name: 'dependency-enabled',
enabled: true
}]
}],
loggedComponents: ['service-worker']
}
global.LibResilientPluginConstructors.set('plugin-enabled', (LRPC, config)=>{
return {
name: 'plugin-enabled',
description: 'Enabled plugin',
version: '0.0.1',
fetch: (url)=>{return true},
uses: config.uses || []
}
})
global.LibResilientPluginConstructors.set('plugin-disabled', (LRPC, config)=>{
return {
name: 'plugin-disabled',
description: 'Disabled plugin',
version: '0.0.1',
fetch: (url)=>{return true},
uses: config.uses || []
}
})
global.LibResilientPluginConstructors.set('dependency-disabled', (LRPC, config)=>{
return {
name: 'dependency-disabled',
description: 'Disabled dependency plugin',
version: '0.0.1',
fetch: (url)=>{return true},
uses: config.uses || []
}
})
global.LibResilientPluginConstructors.set('dependency-enabled', (LRPC, config)=>{
return {
name: 'dependency-enabled',
description: 'Enabled dependency plugin',
version: '0.0.1',
fetch: (url)=>{return true},
uses: config.uses || []
}
})
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientPlugins.length).toEqual(2)
expect(self.LibResilientPlugins[0].name).toEqual('plugin-enabled')
expect(self.LibResilientPlugins[0].uses.length).toEqual(0)
expect(self.LibResilientPlugins[1].name).toEqual('plugin-enabled')
expect(self.LibResilientPlugins[1].uses.length).toEqual(1)
expect(self.LibResilientPlugins[1].uses[0].name).toEqual('dependency-enabled')
expect(self.LibResilientPlugins[1].uses[0].uses.length).toEqual(0)
})
test("basic set-up: a valid config.json file gets cached", async () => {
self.LibResilientConfig = null
var configData = {loggedComponents: ['service-worker', 'cache'], plugins: [{name: "cache"}], defaultPluginTimeout: 5000}
global.fetch.mockImplementation((url, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify(configData)],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: url
})
);
});
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(5000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'cache'])
expect(fetch).toHaveBeenCalled();
expect (await caches.open('v1').then((cache)=>{
return cache.match(self.location.origin + '/config.json')
}).then((response)=>{
return response.json()
}).then((json)=>{
return json
})).toStrictEqual({loggedComponents: ['service-worker', 'cache'], plugins: [{name: "cache"}], defaultPluginTimeout: 5000})
})
test("basic set-up: a cached valid config.json file gets used, no fetch happens", async () => {
self.LibResilientConfig = null
var configData = {loggedComponents: ['service-worker', 'cache'], plugins: [{name: "cache"}], defaultPluginTimeout: 5000}
var configUrl = '/config.json'
var configResponse = new Response(
new Blob(
[JSON.stringify(configData)],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: configUrl
})
await caches
.open('v1')
.then((cache)=>{
return cache.put(configUrl, configResponse)
})
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(5000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'cache'])
expect(fetch).not.toHaveBeenCalled();
})
test("basic set-up: a stale cached valid config.json file gets used, no fetch happens, fresh config.json is retrieved using the configured plugins and cached", async () => {
self.LibResilientConfig = null
var configData = {loggedComponents: ['service-worker', 'cache', 'fetch'], plugins: [{name: "fetch"},{name: "cache"}], defaultPluginTimeout: 5000}
var configUrl = '/config.json'
var configResponse = new Response(
new Blob(
[JSON.stringify(configData)],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader',
// very stale date
'Date': new Date(0).toUTCString()
},
url: configUrl
})
await caches
.open('v1')
.then((cache)=>{
return cache.put(configUrl, configResponse)
})
var newConfigData = {loggedComponents: ['service-worker', 'fetch'], plugins: [{name: "fetch"}], defaultPluginTimeout: 2000}
let resolveConfigFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify(newConfigData)],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader',
// very current date
'Date': new Date().toUTCString()
},
url: configUrl
})
)
})
global.LibResilientPluginConstructors.set('fetch', ()=>{
return {
name: 'fetch',
description: 'Resolve with config data (pretending to be fetch).',
version: '0.0.1',
fetch: resolveConfigFetch
}
})
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
// verify current config (the one from the pre-cached stale `config.json`)
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(5000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "fetch"},{name: "cache"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'cache', 'fetch'])
expect(fetch).not.toHaveBeenCalled();
expect(resolveConfigFetch).toHaveBeenCalled();
// verify that the *new* config got cached
cdata = await caches
.open('v1')
.then((cache)=>{
return cache.match(configUrl)
})
.then((cresponse)=>{
return cresponse.json()
})
expect(cdata).toStrictEqual(newConfigData)
})
test("basic set-up: a stale cached valid config.json file gets used, no fetch happens; invalid config.json retrieved using the configured plugins is not cached", async () => {
self.LibResilientConfig = null
var configData = {loggedComponents: ['service-worker', 'cache', 'resolve-config'], plugins: [{name: "cache"}, {name: "resolve-config"}], defaultPluginTimeout: 5000}
var configUrl = '/config.json'
var configResponse = new Response(
new Blob(
[JSON.stringify(configData)],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader',
// very stale date
'Date': new Date(0).toUTCString()
},
url: configUrl
})
await caches
.open('v1')
.then((cache)=>{
return cache.put(configUrl, configResponse)
})
var newConfigData = {loggedComponentsInvalid: ['service-worker', 'resolve-config'], pluginsInvalid: [{name: "resolve-config"}], defaultPluginTimeoutInvalid: 2000}
let resolveConfigFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify(newConfigData)],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader',
// very current date
'Date': new Date().toUTCString()
},
url: configUrl
})
)
})
global.LibResilientPluginConstructors.set('resolve-config', ()=>{
return {
name: 'resolve-config',
description: 'Resolve with config data.',
version: '0.0.1',
fetch: resolveConfigFetch
}
})
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
// verify current config (the one from the pre-cached stale `config.json`)
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(5000)
expect(self.LibResilientConfig.plugins).toStrictEqual([{name: "cache"}, {name: "resolve-config"}])
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(['service-worker', 'cache', 'resolve-config'])
expect(fetch).not.toHaveBeenCalled();
expect(resolveConfigFetch).toHaveBeenCalled();
// verify that the *new* config got cached
cdata = await caches
.open('v1')
.then((cache)=>{
return cache.match(configUrl)
})
.then((cresponse)=>{
return cresponse.json()
})
expect(cdata).toStrictEqual(configData)
})
test("basic set-up: a stale cached valid config.json file gets used, no fetch happens; valid config.json (configuring additional plugins) is retrieved using the configured plugins other than fetch, and is not cached", async () => {
self.LibResilientConfig = null
var configData = {loggedComponents: ['service-worker', 'resolve-config'], plugins: [{name: "resolve-config"}], defaultPluginTimeout: 5000}
var configUrl = '/config.json'
var configResponse = new Response(
new Blob(
[JSON.stringify(configData)],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader',
// very stale date
'Date': new Date(0).toUTCString()
},
url: configUrl
})
await caches
.open('v1')
.then((cache)=>{
return cache.put(configUrl, configResponse)
})
var newConfigData = {loggedComponents: ['service-worker', 'resolve-config', 'cache'], plugins: [{name: "resolve-config"}, {name: "cache"}], defaultPluginTimeout: 2000}
let resolveConfigFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify(newConfigData)],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader',
// very current date
'Date': new Date().toUTCString()
},
url: configUrl
})
)
})
global.LibResilientPluginConstructors.set('resolve-config', ()=>{
return {
name: 'resolve-config',
description: 'Resolve with config data.',
version: '0.0.1',
fetch: resolveConfigFetch
}
})
try {
require("../service-worker.js");
} catch(e) {}
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
// verify current config (the one from the pre-cached stale `config.json`)
expect(typeof self.LibResilientConfig).toEqual('object')
expect(self.LibResilientConfig.defaultPluginTimeout).toBe(configData.defaultPluginTimeout)
expect(self.LibResilientConfig.plugins).toStrictEqual(configData.plugins)
expect(self.LibResilientConfig.loggedComponents).toStrictEqual(configData.loggedComponents)
expect(fetch).not.toHaveBeenCalled();
expect(resolveConfigFetch).toHaveBeenCalled();
// verify that the *new* config got cached
cdata = await caches
.open('v1')
.then((cache)=>{
return cache.match(configUrl)
})
.then((cresponse)=>{
return cresponse.json()
})
expect(cdata).toStrictEqual(configData)
})
test("failed fetch by first configured plugin should not affect a successful fetch by a second one", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'reject-all'
},{
name: 'resolve-all'
}],
loggedComponents: [
'service-worker'
]
}
let rejectingFetch = jest.fn((request, init)=>{ return Promise.reject(request); })
let resolvingFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
})
global.LibResilientPluginConstructors.set('reject-all', ()=>{
return {
name: 'reject-all',
description: 'Reject all requests.',
version: '0.0.1',
fetch: rejectingFetch
}
})
global.LibResilientPluginConstructors.set('resolve-all', ()=>{
return {
name: 'resolve-all',
description: 'Resolve all requests.',
version: '0.0.1',
fetch: resolvingFetch
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
var response = await self.trigger('fetch', new Request('/test.json'))
expect(rejectingFetch).toHaveBeenCalled();
expect(resolvingFetch).toHaveBeenCalled();
expect(await response.json()).toEqual({ test: "success" })
});
test("plugins should receive the Request() init data", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'reject-all'
},{
name: 'resolve-all'
}],
loggedComponents: [
'service-worker'
]
}
let rejectingFetch = jest.fn((request, init)=>{ return Promise.reject(request); })
let resolvingFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
})
global.LibResilientPluginConstructors.set('reject-all', ()=>{
return {
name: 'reject-all',
description: 'Reject all requests.',
version: '0.0.1',
fetch: rejectingFetch
}
})
global.LibResilientPluginConstructors.set('resolve-all', ()=>{
return {
name: 'resolve-all',
description: 'Resolve all requests.',
version: '0.0.1',
fetch: resolvingFetch
}
})
var initTest = {
method: "GET",
// TODO: ref. https://gitlab.com/rysiekpl/libresilient/-/issues/23
//headers: new Headers({"x-stub": "STUB"}),
//mode: "mode-stub",
//credentials: "credentials-stub",
cache: "cache-stub",
referrer: "referrer-stub",
// these are not implemented by service-worker-mock
// https://github.com/zackargyle/service-workers/blob/master/packages/service-worker-mock/models/Request.js#L20
redirect: undefined,
integrity: undefined,
cache: undefined
}
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
var response = await self.trigger('fetch', new Request('/test.json', initTest))
expect(rejectingFetch).toHaveBeenCalled();
expect(resolvingFetch).toHaveBeenCalled();
expect(await response.json()).toEqual({ test: "success" })
expect(rejectingFetch).toHaveBeenCalledWith('https://www.test.com/test.json', initTest)
expect(resolvingFetch).toHaveBeenCalledWith('https://www.test.com/test.json', initTest)
});
test("defaultPluginTimeout should be respected", async () => {
self.LibResilientConfig = {
defaultPluginTimeout: 100,
plugins: [{
name: 'resolve-with-timeout'
}],
loggedComponents: [
'service-worker',
]
}
let rwtCallback = jest.fn()
global.LibResilientPluginConstructors.set('resolve-with-timeout', ()=>{
return {
name: 'resolve-with-timeout',
description: 'Resolve all requests after a timeout.',
version: '0.0.1',
fetch: (request, init)=>{
return new Promise((resolve, reject)=>{
setTimeout(rwtCallback, 300)
})
}
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
var response = self.trigger('fetch', new Request('/test.json'))
expect.assertions(2)
try {
await response
} catch(e) {
expect(e.toString()).toBe("Error: LibResilient request using resolve-with-timeout timed out after 100ms.")
}
expect(rwtCallback).not.toHaveBeenCalled()
});
test("making an external request should work and not go through the plugins", async () => {
global.fetch.mockImplementation((request, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
method: 'GET',
url: request.url
})
);
});
self.LibResilientConfig = {
plugins: [{
name: 'reject-all'
}],
loggedComponents: [
'service-worker'
]
}
global.LibResilientPluginConstructors.set('reject-all', ()=>{
return {
name: 'reject-all',
description: 'Reject all requests.',
version: '0.0.1',
fetch: (request, init)=>{ return Promise.reject(request); }
}
})
require("../service-worker.js");
var response = await self.trigger('fetch', new Request('https://example.com/test.json'))
expect(await response.json()).toEqual({ test: "success" })
})
test("making a POST request should work and not go through the plugins", async () => {
global.fetch.mockImplementation((request, init) => {
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
method: 'POST',
url: request.url
})
);
});
self.LibResilientConfig = {
plugins: [{
name: 'reject-all'
}],
loggedComponents: [
'service-worker'
]
}
global.LibResilientPluginConstructors.set('reject-all', ()=>{
return {
name: 'reject-all',
description: 'Reject all requests.',
version: '0.0.1',
fetch: (request, init)=>{ return Promise.reject(request); }
}
})
require("../service-worker.js");
var response = await self.trigger('fetch', new Request('/test.json', {method: "POST"}))
expect(response.method).toEqual('POST')
expect(await response.json()).toEqual({ test: "success" })
})
test("stashing content after a successful fetch should work", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'fetch'
},{
name: 'cache'
}],
loggedComponents: [
'service-worker', 'fetch', 'cache'
]
}
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
var response = await self.trigger('fetch', new Request('/test.json'))
expect(await response.json()).toEqual({ test: "success" })
expect (await caches.open('v1').then((cache)=>{
return cache.keys()
}).then((keys)=>{
return keys[0].url
})).toEqual(self.location.origin + '/test.json')
expect (await caches.open('v1').then((cache)=>{
return cache.match(self.location.origin + '/test.json')
}).then((response)=>{
return response.json()
}).then((json)=>{
return json
})).toEqual({ test: "success" })
});
test("stashing should be skipped if content was retrieved from a stashing plugin", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'stashing-test'
},{
name: 'reject-all'
}],
loggedComponents: [
'service-worker'
]
}
let resolvingFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'X-LibResilient-Method': 'resolve-all',
'X-LibResilient-ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
})
let rejectingFetch = jest.fn((request, init)=>{ return Promise.reject(request); })
let stashingStash = jest.fn()
global.LibResilientPluginConstructors.set('stashing-test', ()=>{
return {
name: 'stashing-test',
description: 'Mock stashing plugin.',
version: '0.0.1',
fetch: resolvingFetch,
stash: stashingStash
}
})
global.LibResilientPluginConstructors.set('reject-all', ()=>{
return {
name: 'reject-all',
description: 'Reject all requests.',
version: '0.0.1',
fetch: rejectingFetch
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
var response = await self.trigger('fetch', new Request('/test.json'))
expect(resolvingFetch).toHaveBeenCalled();
expect(stashingStash).not.toHaveBeenCalled();
expect(rejectingFetch).toHaveBeenCalled();
expect(await response.json()).toEqual({ test: "success" })
});
test("content should be stashed if it was retrieved from a job after retrieval from a stashing plugin, and it differs from the stashed version", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'stashing-test'
},{
name: 'resolve-all'
}],
loggedComponents: [
'service-worker'
]
}
let resolvingFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'X-LibResilient-Method': 'resolve-all',
'X-LibResilient-ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
})
let resolvingFetch2 = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success2" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'NewTestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
})
let stashingStash = jest.fn(async (response, url)=>{
expect(await response.json()).toEqual({ test: "success2" })
expect(response.headers.get('ETag')).toEqual('NewTestingETagHeader')
})
global.LibResilientPluginConstructors.set('stashing-test', ()=>{
return {
name: 'stashing-test',
description: 'Mock stashing plugin.',
version: '0.0.1',
fetch: resolvingFetch,
stash: stashingStash
}
})
global.LibResilientPluginConstructors.set('resolve-all', ()=>{
return {
name: 'resolve-all',
description: 'Resolve all requests.',
version: '0.0.1',
fetch: resolvingFetch2
}
})
var testClient = new Client()
self.clients.clients.push(testClient)
var fetchedDiffersFound = false
testClient.addEventListener('message', event => {
if (event.data.fetchedDiffers) {
fetchedDiffersFound = true
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
var response = await self.trigger('fetch', {
request: new Request('/test.json'),
clientId: testClient.id
})
expect(resolvingFetch).toHaveBeenCalled();
expect(await response.json()).toEqual({ test: "success" })
expect(resolvingFetch2).toHaveBeenCalled();
expect(stashingStash).toHaveBeenCalled();
expect(fetchedDiffersFound).toEqual(true)
});
test("content should be stashed if it was retrieved from a job after retrieval from a stashing plugin, even it does not differ from the stashed version", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'stashing-test'
},{
name: 'resolve-all'
}],
loggedComponents: [
'service-worker'
]
}
let resolvingFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'X-LibResilient-Method': 'resolve-all',
'X-LibResilient-ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
})
let stashingStash = jest.fn()
global.LibResilientPluginConstructors.set('stashing-test', ()=>{
return {
name: 'stashing-test',
description: 'Mock stashing plugin.',
version: '0.0.1',
fetch: resolvingFetch,
stash: stashingStash
}
})
global.LibResilientPluginConstructors.set('resolve-all', ()=>{
return {
name: 'resolve-all',
description: 'Resolve all requests.',
version: '0.0.1',
fetch: resolvingFetch
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
var response = await self.trigger('fetch', new Request('/test.json'))
expect(resolvingFetch).toHaveBeenCalledTimes(2);
expect(await response.json()).toEqual({ test: "success" })
expect(stashingStash).toHaveBeenCalled();
});
test("stashing content explicitly should work", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'cache'
}],
loggedComponents: [
'service-worker', 'cache'
]
}
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
await self.trigger(
'message',
{
data:{
stash: [new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})]
}
})
// needed here also
await new Promise(resolve => setTimeout(resolve, 100));
expect (await caches.open('v1').then((cache)=>{
return cache.keys()
}).then((keys)=>{
return keys[0].url
})).toEqual(self.location.origin + '/test.json')
expect (await caches.open('v1').then((cache)=>{
return cache.match(self.location.origin + '/test.json')
}).then((response)=>{
return response.json()
}).then((json)=>{
return json
})).toEqual({ test: "success" })
});
test("after a retrieval from a stashing plugin, background plugin should receive the Request() init data", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'stashing-test'
},{
name: 'resolve-all'
}],
loggedComponents: [
'service-worker'
]
}
let resolvingFetch = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'X-LibResilient-Method': 'resolve-all',
'X-LibResilient-ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
})
let resolvingFetch2 = jest.fn((request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success2" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'NewTestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
})
let stashingStash = jest.fn(async (response, url)=>{
expect(await response.json()).toEqual({ test: "success2" })
expect(response.headers.get('ETag')).toEqual('NewTestingETagHeader')
})
global.LibResilientPluginConstructors.set('stashing-test', ()=>{
return {
name: 'stashing-test',
description: 'Mock stashing plugin.',
version: '0.0.1',
fetch: resolvingFetch,
stash: stashingStash
}
})
global.LibResilientPluginConstructors.set('resolve-all', ()=>{
return {
name: 'resolve-all',
description: 'Resolve all requests.',
version: '0.0.1',
fetch: resolvingFetch2
}
})
var testClient = new Client()
self.clients.clients.push(testClient)
var fetchedDiffersFound = false
testClient.addEventListener('message', event => {
if (event.data.fetchedDiffers) {
fetchedDiffersFound = true
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
var initTest = {
method: "GET",
// TODO: ref. https://gitlab.com/rysiekpl/libresilient/-/issues/23
//headers: new Headers({"x-stub": "STUB"}),
//mode: "mode-stub",
//credentials: "credentials-stub",
cache: "cache-stub",
referrer: "referrer-stub",
// these are not implemented by service-worker-mock
// https://github.com/zackargyle/service-workers/blob/master/packages/service-worker-mock/models/Request.js#L20
redirect: undefined,
integrity: undefined,
cache: undefined
}
var response = await self.trigger('fetch', {
request: new Request('/test.json', initTest),
clientId: testClient.id
})
expect(resolvingFetch).toHaveBeenCalled();
expect(await response.json()).toEqual({ test: "success" })
expect(resolvingFetch).toHaveBeenCalledWith('https://www.test.com/test.json', initTest);
expect(resolvingFetch2).toHaveBeenCalledWith('https://www.test.com/test.json', initTest);
});
test("unstashing content explicitly should work", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'cache'
}],
loggedComponents: [
'service-worker', 'cache'
]
}
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
await self.trigger(
'message',
{
data:{
stash: [new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})]
}
})
// needed here also
await new Promise(resolve => setTimeout(resolve, 100));
expect (await caches.open('v1').then((cache)=>{
return cache.keys()
}).then((keys)=>{
return keys[0].url
})).toEqual(self.location.origin + '/test.json')
expect (await caches.open('v1').then((cache)=>{
return cache.match(self.location.origin + '/test.json')
}).then((response)=>{
return response.json()
}).then((json)=>{
return json
})).toEqual({ test: "success" })
// now unstash
await self.trigger(
'message',
{
data:{
unstash: [self.location.origin + '/test.json']
}
})
expect (await caches.open('v1').then((cache)=>{
return cache.keys()
})).toEqual([])
});
test("publishing content explicitly should work (stub)", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'publish-test'
}],
loggedComponents: [
'service-worker'
]
}
var result = false
global.LibResilientPluginConstructors.set('publish-test', ()=>{
return {
name: 'publish-test',
description: 'Publish plugin fixture.',
version: '0.0.1',
publish: (request)=>{
result = 'publish-test success: ' + request.url
}
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
await self.trigger(
'message',
{
data:{
publish: [new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})]
}
})
expect(result).toEqual('publish-test success: ' + self.location.origin + '/test.json')
})
test("using plugins with dependencies should work", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'dependent-test',
uses: [{
name: 'dependency1-test'
},{
name: 'dependency2-test'
}]
}],
loggedComponents: [
'service-worker'
]
}
global.LibResilientPluginConstructors.set('dependent-test', ()=>{
return {
name: 'dependent-test',
description: 'Dependent plugin fixture.',
version: '0.0.1',
uses: [{
name: 'dependency1-test'
},{
name: 'dependency2-test'
}]
}
})
global.LibResilientPluginConstructors.set('dependency1-test', ()=>{
return {
name: 'dependency1-test',
description: 'First dependency plugin fixture.',
version: '0.0.1'
}
})
global.LibResilientPluginConstructors.set('dependency2-test', ()=>{
return {
name: 'dependency2-test',
description: 'Second dependency plugin fixture.',
version: '0.0.1'
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(self.LibResilientPlugins.map(p=>p.name)).toEqual(['dependent-test'])
expect(self.LibResilientPlugins[0].uses.map(p=>p.name)).toEqual(['dependency1-test', 'dependency2-test'])
})
test("using multiple instances of the same plugin should work", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'plugin-test',
},{
name: 'plugin-test',
},{
name: 'plugin-test',
}],
loggedComponents: [
'service-worker'
]
}
var pver = 0
global.LibResilientPluginConstructors.set('plugin-test', ()=>{
pver += 1
return {
name: 'plugin-test',
description: 'Simple plugin stub.',
version: '0.0.' + pver
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect(self.LibResilientPlugins.map(p=>p.name)).toEqual(['plugin-test', 'plugin-test', 'plugin-test'])
expect(self.LibResilientPlugins.map(p=>p.version)).toEqual(['0.0.1', '0.0.2', '0.0.3'])
})
test("should error out if all plugins fail", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'reject-all'
}],
loggedComponents: [
'service-worker'
]
}
global.LibResilientPluginConstructors.set('reject-all', ()=>{
return {
name: 'reject-all',
description: 'Reject all requests.',
version: '0.0.1',
fetch: (request, init)=>{ return Promise.reject(request); }
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
expect.assertions(1)
try {
await self.trigger('fetch', new Request('/test.json', {method: "GET"}))
} catch(e) {
expect(e).toEqual(self.location.origin + '/test.json')
}
})
test("should send clientId back if event.resultingClientId is set", async () => {
self.LibResilientConfig = {
plugins: [{
name: 'resolve-all'
}],
loggedComponents: [
'service-worker'
]
}
global.LibResilientPluginConstructors.set('resolve-all', ()=>{
return {
name: 'resolve-all',
description: 'Resolve all requests.',
version: '0.0.1',
fetch: (request, init)=>{
return Promise.resolve(
new Response(
new Blob(
[JSON.stringify({ test: "success" })],
{type: "application/json"}
),
{
status: 200,
statusText: "OK",
headers: {
'ETag': 'TestingETagHeader'
},
url: self.location.origin + '/test.json'
})
)
}
}
})
var testClient = new Client()
self.clients.clients.push(testClient)
// monkey-patching addEventListener so that we can add
// the resultingClientId field to the event in a fetch callback
self.oldAddEventListener = self.addEventListener
self.addEventListener = (eventName, callback) => {
if (eventName === 'fetch') {
return self.oldAddEventListener(eventName, event => {
event.resultingClientId = testClient.id
return callback(event)
})
} else {
return self.oldAddEventListener(eventName, callback)
}
}
expect.hasAssertions()
testClient.addEventListener('message', event => {
if (event.data.clientId) {
expect(event.data.clientId).toEqual(testClient.id)
}
})
require("../service-worker.js");
await self.trigger('install')
// this is silly but works, and is necessary because
// event.waitUntil() in the install event handler is not handled correctly in NodeJS
await new Promise(resolve => setTimeout(resolve, 100));
await self.trigger('activate')
await self.trigger('fetch', new Request('/test.json'))
})
});