diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4dc1a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/tests/**/www/** +node_modules \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..7de98fa --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Arthur Stolyar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..4636ab8 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "self-destructing-sw", + "version": "1.0.0", + "description": "", + "main": "sw.js", + "scripts": { + "test": "mocha tests/**/test.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "cpr": "^3.0.1", + "express": "^4.16.2", + "puppeteer": "^0.13.0" + } +} diff --git a/tests/service-worker/server.js b/tests/service-worker/server.js new file mode 100644 index 0000000..d494da6 --- /dev/null +++ b/tests/service-worker/server.js @@ -0,0 +1,40 @@ +var path = require('path'); + +var express = require('express'); +var app = express(); + +var WWW_FOLDER = path.join(__dirname, 'www'); + +app.get('/', serveIndex); + +app.get('/sw.js', serveServiceWorker); +app.use(express.static(WWW_FOLDER)); + +let indexPreload = 0; +let notFoundPreload = 0; + +function serveServiceWorker(req, res) { + res.sendFile(path.join(WWW_FOLDER, 'sw.js'), { + cacheControl: false, + acceptRanges: false, + headers: { + 'Cache-Control': 'no-cache' + } + }); +} + +function serveIndex(req, res) { + res.sendFile(path.join(WWW_FOLDER, 'index.html'), { + cacheControl: false, + acceptRanges: false, + headers: { + 'Cache-Control': 'no-cache' + } + }); +} + +module.exports = new Promise((resolve) => { + const server = app.listen('9191', '127.0.0.1', () => { + resolve(server); + }); +}); \ No newline at end of file diff --git a/tests/service-worker/test.js b/tests/service-worker/test.js new file mode 100644 index 0000000..0024efc --- /dev/null +++ b/tests/service-worker/test.js @@ -0,0 +1,160 @@ +const puppeteer = require('puppeteer'); +const { exec } = require('child_process'); +const path = require('path'); +const cpr = require('cpr'); +const server = require('./server'); + +const SERVER = `http://127.0.0.1:9191/`; + +module.exports = new Promise((resolve) => { + after(async () => { + (await server).close(); + resolve(); + }); +}); + +describe('testing install and fix', async () => { + let browser; + let page; + let first; + let second; + + before(async () => { + await server; + + await new Promise((resolve, reject) => { + cpr( + path.join(__dirname, 'www_broken'), + path.join(__dirname, 'www'), { + deleteFirst: true, + overwrite: true, + confirm: true, + }, (err) => { + if (err) { + reject(err); + return; + } + + resolve(); + } + ); + }); + + browser = await puppeteer.launch({ + headless: true + }); + + page = await browser.newPage(); + }); + + after(async () => { + await browser.close(); + }); + + it('should open page and install service-worker', async () => { + await page.goto(SERVER); + await page.evaluate(function() { + return navigator.serviceWorker.ready; + }); + }); + + it('should open 2 pages from the cache and reload current one', async () => { + ([ first, second ] = await Promise.all([ + browser.newPage(), browser.newPage() + ])); + + await Promise.all([ + first.goto(SERVER), + second.goto(SERVER), + page.reload() + ]); + + await Promise.all([ + await first.evaluate(function() { + const page = document.body.textContent.trim(); + + if (page !== 'Cached') { + throw new Error('Incorrect page:' + page); + } + }), + await second.evaluate(function() { + const page = document.body.textContent.trim(); + + if (page !== 'Cached') { + throw new Error('Incorrect page:' + page); + } + }), + await page.evaluate(function() { + const page = document.body.textContent.trim(); + + if (page !== 'Cached') { + throw new Error('Incorrect page:' + page); + } + }) + ]); + }); + + it('should write fixed code and reload main page, all pages should reload', async () => { + await new Promise((resolve, reject) => { + cpr( + path.join(__dirname, 'www_fixed'), + path.join(__dirname, 'www'), { + // deleteFirst: true, + overwrite: true, + confirm: true, + }, (err) => { + if (err) { + reject(err); + return; + } + + resolve(); + } + ); + }); + + await page.reload(); + + await new Promise(resolve => { + let reloads = 0; + + let onReload = () => { + reloads++; + + if (reloads >= 3) { + resolve(); + } + }; + + page.once('load', onReload); + first.once('load', onReload); + second.once('load', onReload); + }); + }); + + it('should load all pages from the network with fixed content', async () => { + await Promise.all([ + await first.evaluate(function() { + const page = document.body.textContent.trim(); + + if (page !== 'Fixed') { + throw new Error('Incorrect page:' + page); + } + }), + await second.evaluate(function() { + const page = document.body.textContent.trim(); + + if (page !== 'Fixed') { + throw new Error('Incorrect page:' + page); + } + }), + await page.evaluate(function() { + const page = document.body.textContent.trim(); + + if (page !== 'Fixed') { + throw new Error('Incorrect page:' + page); + } + }) + ]); + }); +}); \ No newline at end of file diff --git a/tests/service-worker/www_broken/index.html b/tests/service-worker/www_broken/index.html new file mode 100644 index 0000000..ed51c17 --- /dev/null +++ b/tests/service-worker/www_broken/index.html @@ -0,0 +1,5 @@ + + +Normal \ No newline at end of file diff --git a/tests/service-worker/www_broken/sw.js b/tests/service-worker/www_broken/sw.js new file mode 100644 index 0000000..ed7bfe0 --- /dev/null +++ b/tests/service-worker/www_broken/sw.js @@ -0,0 +1,23 @@ +self.addEventListener('install', (e) => { + e.waitUntil(self.skipWaiting()); +}); + +self.addEventListener('activate', (e) => { + e.waitUntil(self.clients.claim()); +}); + +self.addEventListener('fetch', (e) => { + e.respondWith(new Response(` + + + Cached + `, { + headers: { + 'Content-Type': 'text/html' + } + })); + + // fetch(e.request); +}); \ No newline at end of file diff --git a/tests/service-worker/www_fixed/index.html b/tests/service-worker/www_fixed/index.html new file mode 100644 index 0000000..37fe23f --- /dev/null +++ b/tests/service-worker/www_fixed/index.html @@ -0,0 +1,2 @@ + +Fixed \ No newline at end of file diff --git a/tests/service-worker/www_fixed/sw.js b/tests/service-worker/www_fixed/sw.js new file mode 100644 index 0000000..ebc8cc9 --- /dev/null +++ b/tests/service-worker/www_fixed/sw.js @@ -0,0 +1,13 @@ +self.addEventListener('install', function(e) { + self.skipWaiting(); +}); + +self.addEventListener('activate', function(e) { + self.registration.unregister() + .then(function() { + return self.clients.matchAll(); + }) + .then(function(clients) { + clients.forEach(client => client.navigate(client.url)) + }); +}); \ No newline at end of file