UNPKG

auspice

Version:

Web app for visualizing pathogen evolution

152 lines (133 loc) 4.95 kB
/** * This exercises the actual offline behaviour of the service worker feature, * which can only be meaningfully verified in a real browser: it builds a * standalone copy of Auspice with `AUSPICE_ENABLE_SERVICE_WORKER=true` into a * temporary directory, serves it with `auspice view` on its own port * (independent of the default build / server used by the other smoke tests), * and drives it with Playwright. * * This is unrelated to the default build used by ../smoke-test/urls.test.js, so * it does not require `npm run get-data`. */ const {test, expect} = require('@playwright/test'); const {spawn} = require('child_process'); const fs = require('fs'); const os = require('os'); const path = require('path'); const webpack = require('webpack'); const REPO_ROOT = path.resolve(__dirname, '..', '..'); const PORT = process.env.SERVICE_WORKER_TEST_PORT || '4001'; const BASE_URL = `http://localhost:${PORT}`; test.use({baseURL: BASE_URL}); // serial so all tests can share one build test.describe.configure({mode: 'serial'}); let buildDir; let serverProcess; test.beforeAll(async () => { // build may take longer than the default timeout of 30s test.setTimeout(60_000); buildDir = fs.mkdtempSync(path.join(os.tmpdir(), 'auspice-service-worker-test-')); await buildWithServiceWorkerEnabled(buildDir); serverProcess = await startServer(buildDir); }); test("registers a service worker which serves the root path while offline", async ({page, context}) => { await page.goto('/'); await page.evaluate(() => navigator.serviceWorker.ready); await context.setOffline(true); const response = await page.reload(); expect(response.status()).toEqual(200); }); test("does not serve non-root navigations while offline", async ({page, context}) => { await page.goto('/'); await page.evaluate(() => navigator.serviceWorker.ready); await context.setOffline(true); await expect(page.goto('/foo')).rejects.toThrow(); }); test.afterAll(async () => { if (serverProcess) serverProcess.kill(); if (buildDir) fs.rmSync(buildDir, {recursive: true, force: true}); }); /** * Builds Auspice into `outputDir/dist` with the service worker enabled, by * calling the webpack config generator directly (bypassing `auspice build`'s * CLI/argparse layer, which we don't need here). */ function buildWithServiceWorkerEnabled(outputDir) { const generateConfig = require(path.join(REPO_ROOT, 'webpack.config.js')).default; // webpack.config.js reads from process.env to decide whether to include the service worker plugin. // Temporarily set it here, then restore the original value to avoid global env side effects. const originalEnv = process.env.AUSPICE_ENABLE_SERVICE_WORKER; process.env.AUSPICE_ENABLE_SERVICE_WORKER = 'true'; let config; try { config = generateConfig({devMode: false, customOutputPath: outputDir}); // Disable minimization and compression to speed up the test build config.optimization = { ...config.optimization, minimize: false, }; config.plugins = config.plugins.filter( (plugin) => plugin.constructor.name !== "CompressionPlugin" ); } finally { if (originalEnv === undefined) delete process.env.AUSPICE_ENABLE_SERVICE_WORKER; else process.env.AUSPICE_ENABLE_SERVICE_WORKER = originalEnv; } return new Promise((resolve, reject) => { webpack(config).run((err, stats) => { if (err) return reject(err); if (stats.hasErrors()) return reject(new Error(stats.toString({colors: false}))); resolve(); }); }); } /** * Starts `auspice view` serving `buildDir/dist`, with an empty dataset * directory (this test doesn't need real datasets). */ function startServer(dir) { const emptyDir = path.join(dir, 'data'); fs.mkdirSync(emptyDir); const proc = spawn( 'node', [path.join(REPO_ROOT, 'auspice.js'), 'view', '--customBuildOnly', '--datasetDir', emptyDir], {cwd: dir, env: {...process.env, PORT}} ); return waitForServer(proc); } function waitForServer(proc) { return new Promise((resolve, reject) => { let settled = false; const timer = setTimeout(() => { if (!settled) { settled = true; proc.kill(); reject(new Error(`Auspice server did not start listening on port ${PORT} in time`)); } }, 30_000); const onData = (data) => { if (settled) return; if (data.toString().includes('now running at')) { settled = true; clearTimeout(timer); proc.stdout.off('data', onData); resolve(proc); } }; proc.stdout.on('data', onData); proc.on('error', (err) => { if (!settled) { settled = true; clearTimeout(timer); reject(err); } }); proc.on('exit', (code) => { if (!settled) { settled = true; clearTimeout(timer); reject(new Error(`Auspice server exited early with code ${code}`)); } }); }); }