diff --git a/__tests__/accessiblity-audit.test.js b/__tests__/accessiblity-audit.test.js
index 73b0421aa4..2cd660d8a6 100644
--- a/__tests__/accessiblity-audit.test.js
+++ b/__tests__/accessiblity-audit.test.js
@@ -6,6 +6,7 @@ async function analyze (page, path) {
await goTo(page, path)
const axe = new AxePuppeteer(page)
+ .setLegacyMode(true) // Share single page via iframe
.include('body')
// axe reports there is "no label associated with the text field", when there is one.
.exclude('#app-site-search__input')
@@ -16,6 +17,8 @@ async function analyze (page, path) {
.exclude('.govuk-skip-link')
// axe reports that the back to top button is not inside a landmark, which is intentional.
.exclude('.app-back-to-top')
+ // axe reports that the Browsersync banner is not inside a landmark, which is intentional.
+ .exclude('#__bs_notify__')
// axe reports that the frame "does not have a main landmark" and example
headings
// violate "Heading levels should only increase by one", which is intentional.
// https://github.com/alphagov/govuk-design-system/pull/2442#issuecomment-1326600528
diff --git a/__tests__/cookie-banner.test.js b/__tests__/cookie-banner.test.js
index ff6731c916..06242c1aac 100644
--- a/__tests__/cookie-banner.test.js
+++ b/__tests__/cookie-banner.test.js
@@ -17,7 +17,7 @@ describe('Cookie banner', () => {
const cookieParam = {
name: 'design_system_cookies_policy',
value: JSON.stringify({ analytics: true, version: 1 }),
- url: `http://localhost:${configPaths.testPort}`
+ url: `http://localhost:${configPaths.port}`
}
async function setup (page) {
diff --git a/__tests__/cookies-page.test.js b/__tests__/cookies-page.test.js
index 3403fc09ff..f437b47bd8 100644
--- a/__tests__/cookies-page.test.js
+++ b/__tests__/cookies-page.test.js
@@ -19,7 +19,7 @@ describe('Cookies page', () => {
beforeEach(async () => {
await page.deleteCookie({
name: 'design_system_cookies_policy',
- url: `http://localhost:${configPaths.testPort}`
+ url: `http://localhost:${configPaths.port}`
})
await page.setJavaScriptEnabled(true)
diff --git a/config/paths.json b/config/paths.json
index 6fd592cad2..4273f4b20e 100644
--- a/config/paths.json
+++ b/config/paths.json
@@ -15,6 +15,5 @@
"views": "views/",
"layouts": "views/layouts/",
"partials": "views/partials/",
- "port": 3000,
- "testPort": 3333
+ "port": 3000
}
diff --git a/jest-puppeteer.config.js b/jest-puppeteer.config.js
index 86cd7b2023..777858e26a 100644
--- a/jest-puppeteer.config.js
+++ b/jest-puppeteer.config.js
@@ -1,9 +1,34 @@
const configPaths = require('./lib/paths.js')
+/**
+ * @type {import('jest-environment-puppeteer').JestPuppeteerConfig}
+ */
module.exports = {
browserContext: 'incognito',
+
+ /**
+ * Puppeteer launch options
+ */
+ launch: {
+ /**
+ * Allow headless mode switching using `HEADLESS=false`
+ * but default to `new` to skip deprecation warning
+ *
+ * {@link https://developer.chrome.com/articles/new-headless/}
+ */
+ headless: process.env.HEADLESS !== 'false'
+ ? 'new'
+ : false
+ },
+
+ /**
+ * Development server options
+ */
server: {
command: 'npm run serve',
- port: configPaths.testPort
+ port: configPaths.port,
+
+ // Skip when already running
+ usedPortAction: 'ignore'
}
}
diff --git a/jest.config.mjs b/jest.config.mjs
index 366f55689b..8c5da8eb13 100644
--- a/jest.config.mjs
+++ b/jest.config.mjs
@@ -1,6 +1,21 @@
+import jestPuppeteerConfig from './jest-puppeteer.config.js'
+
+// Detect when browser has been launched headless
+const { headless } = jestPuppeteerConfig.launch
+
+/**
+ * Jest config
+ *
+ * @type {import('@jest/types').Config.InitialOptions}
+ */
export default {
preset: 'jest-puppeteer',
+ // Reduce CPU usage during project test runs
+ maxWorkers: headless
+ ? '50%' // Matches Jest default (50%) via `--watch`
+ : 1, // Use only 1x browser window when headless
+
// Custom matchers
setupFilesAfterEnv: ['./config/jest/matchers.js'],
diff --git a/lib/puppeteer-helpers.js b/lib/puppeteer-helpers.js
index d7feed91f0..086ccb421a 100644
--- a/lib/puppeteer-helpers.js
+++ b/lib/puppeteer-helpers.js
@@ -1,7 +1,5 @@
const configPaths = require('../lib/paths.js')
-const PORT = configPaths.testPort
-
/**
* Navigate to path
*
@@ -10,7 +8,7 @@ const PORT = configPaths.testPort
* @returns {Promise} Puppeteer page object
*/
async function goTo (page, path) {
- const { href } = new URL(path, `http://localhost:${PORT}`)
+ const { href } = new URL(path, `http://localhost:${configPaths.port}`)
// Navigate to blank page first to fix same page
// hash fragment changes (window.location.hash)
diff --git a/package-lock.json b/package-lock.json
index c90fe81443..12dad7384b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -55,7 +55,7 @@
"metalsmith-renamer": "^0.5.217",
"nunjucks": "^3.2.4",
"outdent": "^0.8.0",
- "puppeteer": "^19.10.1",
+ "puppeteer": "^20.2.0",
"rollup": "^3.21.7",
"sitemap": "^7.1.1",
"slash": "^3.0.0",
@@ -2731,13 +2731,14 @@
}
},
"node_modules/@puppeteer/browsers": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-0.5.0.tgz",
- "integrity": "sha512-Uw6oB7VvmPRLE4iKsjuOh8zgDabhNX67dzo8U/BB0f9527qx+4eeUs+korU98OhG5C4ubg7ufBgVi63XYwS6TQ==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz",
+ "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==",
"dev": true,
"dependencies": {
"debug": "4.3.4",
"extract-zip": "2.0.1",
+ "http-proxy-agent": "5.0.0",
"https-proxy-agent": "5.0.1",
"progress": "2.0.3",
"proxy-from-env": "1.1.0",
@@ -2749,7 +2750,7 @@
"browsers": "lib/cjs/main-cli.js"
},
"engines": {
- "node": ">=14.1.0"
+ "node": ">=16.0.0"
},
"peerDependencies": {
"typescript": ">= 4.7.4"
@@ -4877,9 +4878,9 @@
"dev": true
},
"node_modules/chromium-bidi": {
- "version": "0.4.6",
- "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.6.tgz",
- "integrity": "sha512-TQOkWRaLI/IWvoP8XC+7jO4uHTIiAUiklXU1T0qszlUFEai9LgKXIBXy3pOS3EnQZ3bQtMbKUPkug0fTAEHCSw==",
+ "version": "0.4.9",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.9.tgz",
+ "integrity": "sha512-u3DC6XwgLCA9QJ5ak1voPslCmacQdulZNCPsI3qNXxSnEcZS7DFIbww+5RM2bznMEje7cc0oydavRLRvOIZtHw==",
"dev": true,
"dependencies": {
"mitt": "3.0.0"
@@ -5555,9 +5556,9 @@
}
},
"node_modules/devtools-protocol": {
- "version": "0.0.1107588",
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz",
- "integrity": "sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==",
+ "version": "0.0.1120988",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz",
+ "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==",
"dev": true
},
"node_modules/diff": {
@@ -12012,40 +12013,32 @@
}
},
"node_modules/puppeteer": {
- "version": "19.10.1",
- "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.10.1.tgz",
- "integrity": "sha512-HqpY8sWqz28JfyZE8cGG9kBPgASD7iRHn/ryWLvVLXE10poR5AyU/mMsLmL175qoYl/chwMTN2pxKSz7HobaCg==",
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-20.2.1.tgz",
+ "integrity": "sha512-XWHlNCJf1u+i5AzakXj1PlNOTYcFcAwAunQj/urFKzKaZ7WTChlAo710W+TlEE2SJKshEZTwilMDDkVvwadLTQ==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
- "@puppeteer/browsers": "0.5.0",
+ "@puppeteer/browsers": "1.3.0",
"cosmiconfig": "8.1.3",
- "https-proxy-agent": "5.0.1",
- "progress": "2.0.3",
- "proxy-from-env": "1.1.0",
- "puppeteer-core": "19.10.1"
+ "puppeteer-core": "20.2.1"
}
},
"node_modules/puppeteer-core": {
- "version": "19.10.1",
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.10.1.tgz",
- "integrity": "sha512-vD4ojslBtnIWd56IQIEADIcAWrNel/Qt7YGlAxcSNB0b33U3tYe0A+0FLmPNgSa7UTlCOCCVEmzXi5QlDtLGjQ==",
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.2.1.tgz",
+ "integrity": "sha512-HmNMcL+g9ght0nCzS5BmEvom2IVhp1/xhlQ9O+fmihQx0EYwYUqgCQhs8Hbv7IapiqKhahKCC8PKfCNTXXsKbQ==",
"dev": true,
"dependencies": {
- "@puppeteer/browsers": "0.5.0",
- "chromium-bidi": "0.4.6",
+ "@puppeteer/browsers": "1.3.0",
+ "chromium-bidi": "0.4.9",
"cross-fetch": "3.1.5",
"debug": "4.3.4",
- "devtools-protocol": "0.0.1107588",
- "extract-zip": "2.0.1",
- "https-proxy-agent": "5.0.1",
- "proxy-from-env": "1.1.0",
- "tar-fs": "2.1.1",
- "unbzip2-stream": "1.4.3",
+ "devtools-protocol": "0.0.1120988",
"ws": "8.13.0"
},
"engines": {
- "node": ">=14.14.0"
+ "node": ">=16.0.0"
},
"peerDependencies": {
"typescript": ">= 4.7.4"
@@ -17434,13 +17427,14 @@
"optional": true
},
"@puppeteer/browsers": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-0.5.0.tgz",
- "integrity": "sha512-Uw6oB7VvmPRLE4iKsjuOh8zgDabhNX67dzo8U/BB0f9527qx+4eeUs+korU98OhG5C4ubg7ufBgVi63XYwS6TQ==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.3.0.tgz",
+ "integrity": "sha512-an3QdbNPkuU6qpxpbssxAbjRLJcF+eP4L8UqIY3+6n0sbaVxw5pz7PiCLy9g32XEZuoamUlV5ZQPnA6FxvkIHA==",
"dev": true,
"requires": {
"debug": "4.3.4",
"extract-zip": "2.0.1",
+ "http-proxy-agent": "5.0.0",
"https-proxy-agent": "5.0.1",
"progress": "2.0.3",
"proxy-from-env": "1.1.0",
@@ -19094,9 +19088,9 @@
"dev": true
},
"chromium-bidi": {
- "version": "0.4.6",
- "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.6.tgz",
- "integrity": "sha512-TQOkWRaLI/IWvoP8XC+7jO4uHTIiAUiklXU1T0qszlUFEai9LgKXIBXy3pOS3EnQZ3bQtMbKUPkug0fTAEHCSw==",
+ "version": "0.4.9",
+ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.9.tgz",
+ "integrity": "sha512-u3DC6XwgLCA9QJ5ak1voPslCmacQdulZNCPsI3qNXxSnEcZS7DFIbww+5RM2bznMEje7cc0oydavRLRvOIZtHw==",
"dev": true,
"requires": {
"mitt": "3.0.0"
@@ -19608,9 +19602,9 @@
"dev": true
},
"devtools-protocol": {
- "version": "0.0.1107588",
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz",
- "integrity": "sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==",
+ "version": "0.0.1120988",
+ "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1120988.tgz",
+ "integrity": "sha512-39fCpE3Z78IaIPChJsP6Lhmkbf4dWXOmzLk/KFTdRkNk/0JymRIfUynDVRndV9HoDz8PyalK1UH21ST/ivwW5Q==",
"dev": true
},
"diff": {
@@ -24276,17 +24270,14 @@
"dev": true
},
"puppeteer": {
- "version": "19.10.1",
- "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.10.1.tgz",
- "integrity": "sha512-HqpY8sWqz28JfyZE8cGG9kBPgASD7iRHn/ryWLvVLXE10poR5AyU/mMsLmL175qoYl/chwMTN2pxKSz7HobaCg==",
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-20.2.1.tgz",
+ "integrity": "sha512-XWHlNCJf1u+i5AzakXj1PlNOTYcFcAwAunQj/urFKzKaZ7WTChlAo710W+TlEE2SJKshEZTwilMDDkVvwadLTQ==",
"dev": true,
"requires": {
- "@puppeteer/browsers": "0.5.0",
+ "@puppeteer/browsers": "1.3.0",
"cosmiconfig": "8.1.3",
- "https-proxy-agent": "5.0.1",
- "progress": "2.0.3",
- "proxy-from-env": "1.1.0",
- "puppeteer-core": "19.10.1"
+ "puppeteer-core": "20.2.1"
},
"dependencies": {
"argparse": {
@@ -24331,21 +24322,16 @@
}
},
"puppeteer-core": {
- "version": "19.10.1",
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.10.1.tgz",
- "integrity": "sha512-vD4ojslBtnIWd56IQIEADIcAWrNel/Qt7YGlAxcSNB0b33U3tYe0A+0FLmPNgSa7UTlCOCCVEmzXi5QlDtLGjQ==",
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.2.1.tgz",
+ "integrity": "sha512-HmNMcL+g9ght0nCzS5BmEvom2IVhp1/xhlQ9O+fmihQx0EYwYUqgCQhs8Hbv7IapiqKhahKCC8PKfCNTXXsKbQ==",
"dev": true,
"requires": {
- "@puppeteer/browsers": "0.5.0",
- "chromium-bidi": "0.4.6",
+ "@puppeteer/browsers": "1.3.0",
+ "chromium-bidi": "0.4.9",
"cross-fetch": "3.1.5",
"debug": "4.3.4",
- "devtools-protocol": "0.0.1107588",
- "extract-zip": "2.0.1",
- "https-proxy-agent": "5.0.1",
- "proxy-from-env": "1.1.0",
- "tar-fs": "2.1.1",
- "unbzip2-stream": "1.4.3",
+ "devtools-protocol": "0.0.1120988",
"ws": "8.13.0"
},
"dependencies": {
diff --git a/package.json b/package.json
index 4e91a92136..5c838cf22f 100644
--- a/package.json
+++ b/package.json
@@ -74,7 +74,7 @@
"metalsmith-renamer": "^0.5.217",
"nunjucks": "^3.2.4",
"outdent": "^0.8.0",
- "puppeteer": "^19.10.1",
+ "puppeteer": "^20.2.0",
"rollup": "^3.21.7",
"sitemap": "^7.1.1",
"slash": "^3.0.0",
diff --git a/tasks/serve.js b/tasks/serve.js
index 1ddfbdf07d..b7e3db5f75 100644
--- a/tasks/serve.js
+++ b/tasks/serve.js
@@ -9,6 +9,6 @@ const paths = require('../lib/paths.js')
const app = connect().use(serveStatic(paths.public))
const server = createServer(app)
-server.listen(paths.testPort, () => {
- console.log(`Server started at http://localhost:${paths.testPort}`)
+server.listen(paths.port, () => {
+ console.log(`Server started at http://localhost:${paths.port}`)
})