Skip to content

Commit

Permalink
Merge pull request #2880 from alphagov/fix-jest-watch
Browse files Browse the repository at this point in the history
Fix Jest setup/teardown for test server awaiting `SIGTERM`
  • Loading branch information
colinrotherham authored Sep 27, 2022
2 parents 3a312de + 7a08cbb commit f6b8e8a
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 41 deletions.
6 changes: 4 additions & 2 deletions config/jest/browser/open.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { setup } from 'jest-environment-puppeteer'

/**
* Open browser
*
* @param {import('jest').Config} jestConfig
*/
export default async function browserOpen () {
export default async function browserOpen (jestConfig) {
await serverStart() // Wait for web server
await setup() // Open browser
await setup(jestConfig) // Open browser
}
19 changes: 12 additions & 7 deletions config/jest/server/start.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getServers, setup } from 'jest-dev-server'
import waitOn from 'wait-on'
import serverStop from './stop.mjs'

import configPaths from '../../paths.js'
const { PORT = configPaths.ports.test } = process.env
Expand All @@ -8,22 +9,26 @@ const { PORT = configPaths.ports.test } = process.env
* Start web server
*/
export default async function serverStart () {
const [server] = await getServers()
const servers = await getServers()

// Server already running
if (server) {
return
// Server start timeout
let timeout = 1000

// Server stopping?
if (servers.some(({ signalCode }) => signalCode === 'SIGTERM')) {
await serverStop() // Wait for server to stop
timeout = 0 // No need to wait for start
}

// Wait until ready
// Wait until ready check
const ready = ({ timeout = 30000 } = {}) => waitOn({
resources: [`tcp:${PORT}`],
timeout
})

// Start server
// Wait until ready (or start up)
try {
await ready({ timeout: 1000 })
await ready({ timeout })
} catch (error) {
await setup({ command: `PORT=${PORT} node app/start.js`, port: PORT })
await ready()
Expand Down
2 changes: 1 addition & 1 deletion jest-puppeteer.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
browserContext: 'incognito',
dumpio: true,
browserPerWorker: true,
launch: {
// we use --no-sandbox --disable-setuid-sandbox as a workaround for the
// 'No usable sandbox! Update your kernel' error
Expand Down
4 changes: 3 additions & 1 deletion lib/jest-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ function renderTemplate (params = {}, blocks = {}) {
* browser to execute arbitrary initialisation. Receives an object with the
* passed configuration as `config` and the PascalCased component name as
* `componentClassName`
* @returns {Promise}
* @returns {Promise<import('puppeteer').Page>}
*/
async function renderAndInitialise (componentName, options = {}) {
await page.goto(`${options.baseUrl}/tests/boilerplate`, { waitUntil: 'load' })
Expand All @@ -128,6 +128,8 @@ async function renderAndInitialise (componentName, options = {}) {
componentClassName: componentNameToJavaScriptClassName(componentName)
})
}

return page
}

/**
Expand Down
66 changes: 42 additions & 24 deletions src/govuk/components/button/button.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ describe('/components/button', () => {
*
* Examples don't do this and we need it to have something to submit
*/
async function trackClicks () {
page.evaluate(() => {
function trackClicks (page) {
return page.evaluate(() => {
const $button = document.querySelector('button')
const $form = document.createElement('form')
$button.parentNode.appendChild($form)
Expand All @@ -80,40 +80,52 @@ describe('/components/button', () => {
*
* @returns {Number}
*/
function getClicksCount () {
function getClicksCount (page) {
return page.evaluate(() => window.__SUBMIT_EVENTS)
}

describe('not enabled', () => {
let page

beforeEach(async () => {
page = await browser.newPage()
})

it('does not prevent multiple submissions', async () => {
await page.goto(baseUrl + '/components/button/preview', {
waitUntil: 'load'
})

await trackClicks()
await trackClicks(page)

await page.click('button')
await page.click('button')

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(2)
})
})

describe('using data-attributes', () => {
let page

beforeEach(async () => {
page = await browser.newPage()
})

it('prevents unintentional submissions when in a form', async () => {
await page.goto(
baseUrl + '/components/button/prevent-double-click/preview',
{ waitUntil: 'load' }
)

await trackClicks()
await trackClicks(page)

await page.click('button')
await page.click('button')

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(1)
})
Expand All @@ -124,12 +136,12 @@ describe('/components/button', () => {
{ waitUntil: 'load' }
)

await trackClicks()
await trackClicks(page)

await page.click('button')
await page.click('button', { delay: 1000 })

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(2)
})
Expand All @@ -140,7 +152,7 @@ describe('/components/button', () => {
{ waitUntil: 'load' }
)

await trackClicks()
await trackClicks(page)

// Clone button to have two buttons on the page
await page.evaluate(() => {
Expand All @@ -153,31 +165,33 @@ describe('/components/button', () => {
await page.click('button:nth-child(1)')
await page.click('button:nth-child(2)')

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(2)
})
})

describe('using JavaScript configuration', () => {
let page

// To ensure
beforeEach(async () => {
await renderAndInitialise('button', {
page = await renderAndInitialise('button', {
baseUrl,
nunjucksParams: examples.default,
javascriptConfig: {
preventDoubleClick: true
}
})

await trackClicks()
await trackClicks(page)
})

it('prevents unintentional submissions when in a form', async () => {
await page.click('button')
await page.click('button')

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(1)
})
Expand All @@ -186,7 +200,7 @@ describe('/components/button', () => {
await page.click('button')
await page.click('button', { delay: 1000 })

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(2)
})
Expand All @@ -203,37 +217,41 @@ describe('/components/button', () => {
await page.click('button:nth-child(1)')
await page.click('button:nth-child(2)')

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(2)
})
})

describe('using JavaScript configuration, but cancelled by data-attributes', () => {
let page

it('does not prevent multiple submissions', async () => {
await renderAndInitialise('button', {
page = await renderAndInitialise('button', {
baseUrl,
nunjucksParams: examples["don't prevent double click"],
javascriptConfig: {
preventDoubleClick: true
}
})

await trackClicks()
await trackClicks(page)

await page.click('button')
await page.click('button')

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(2)
})
})

describe('using `initAll`', () => {
let page

// To ensure
beforeEach(async () => {
await renderAndInitialise('button', {
page = await renderAndInitialise('button', {
baseUrl,
nunjucksParams: examples.default,
initialiser () {
Expand All @@ -245,14 +263,14 @@ describe('/components/button', () => {
}
})

await trackClicks()
await trackClicks(page)
})

it('prevents unintentional submissions when in a form', async () => {
await page.click('button')
await page.click('button')

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(1)
})
Expand All @@ -261,7 +279,7 @@ describe('/components/button', () => {
await page.click('button')
await page.click('button', { delay: 1000 })

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(2)
})
Expand All @@ -278,7 +296,7 @@ describe('/components/button', () => {
await page.click('button:nth-child(1)')
await page.click('button:nth-child(2)')

const clicksCount = await getClicksCount()
const clicksCount = await getClicksCount(page)

expect(clicksCount).toBe(2)
})
Expand Down
12 changes: 9 additions & 3 deletions src/govuk/components/error-summary/error-summary.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ describe('Error Summary', () => {
})

describe('using JavaScript configuration', () => {
let page

beforeAll(async () => {
await renderAndInitialise('error-summary', {
page = await renderAndInitialise('error-summary', {
baseUrl,
nunjucksParams: examples.default,
javascriptConfig: {
Expand Down Expand Up @@ -104,8 +106,10 @@ describe('Error Summary', () => {
})

describe('using JavaScript configuration, but enabled via data-attributes', () => {
let page

beforeAll(async () => {
await renderAndInitialise('error-summary', {
page = await renderAndInitialise('error-summary', {
baseUrl,
nunjucksParams: examples['autofocus explicitly enabled']
})
Expand All @@ -127,8 +131,10 @@ describe('Error Summary', () => {
})

describe('using `initAll`', () => {
let page

beforeAll(async () => {
await renderAndInitialise('error-summary', {
page = await renderAndInitialise('error-summary', {
baseUrl,
nunjucksParams: examples.default,
initialiser () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ describe('Notification banner, when type is set to "success"', () => {
})

describe('and auto-focus is disabled using JavaScript configuration', () => {
let page

beforeAll(async () => {
await renderAndInitialise('notification-banner', {
page = await renderAndInitialise('notification-banner', {
baseUrl,
nunjucksParams:
examples['with type as success'],
Expand All @@ -81,8 +83,10 @@ describe('Notification banner, when type is set to "success"', () => {
})

describe('and auto-focus is disabled using options passed to initAll', () => {
let page

beforeAll(async () => {
await renderAndInitialise('notification-banner', {
page = await renderAndInitialise('notification-banner', {
baseUrl,
nunjucksParams:
examples['with type as success'],
Expand Down Expand Up @@ -110,8 +114,10 @@ describe('Notification banner, when type is set to "success"', () => {
})

describe('and autofocus is disabled in JS but enabled in data attributes', () => {
let page

beforeAll(async () => {
await renderAndInitialise('notification-banner', {
page = await renderAndInitialise('notification-banner', {
baseUrl,
nunjucksParams: examples['auto-focus explicitly enabled, with type as success'],
javascriptConfig: {
Expand Down

0 comments on commit f6b8e8a

Please sign in to comment.