Skip to content

Commit

Permalink
Update Puppeteer tests to use ES modules not window globals
Browse files Browse the repository at this point in the history
An import map has been added to resolve `govuk-frontend` to `/javascripts/all.bundle.min.mjs` when run inside Puppeteer `page.evaluate()`
  • Loading branch information
colinrotherham committed Jun 20, 2023
1 parent 3b2b9c0 commit a67967a
Show file tree
Hide file tree
Showing 8 changed files with 31 additions and 47 deletions.
4 changes: 1 addition & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/govuk-frontend-review/src/views/layouts/_generic.njk
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<link rel="stylesheet" href="/stylesheets/app.min.css">

{% block styles %}{% endblock %}

<script type="importmap">
{ "imports": { "govuk-frontend": "/javascripts/all.bundle.min.mjs" } }
</script>
{% endblock %}

{% block bodyStart %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ describe('/components/button', () => {
it('does not prevent further JavaScript from running', async () => {
await goTo(page, '/tests/boilerplate')

const result = await page.evaluate((component) => {
const namespace = 'GOVUKFrontend' in window
? window.GOVUKFrontend
: {}
const result = await page.evaluate(async (exportName) => {
const namespace = await import('govuk-frontend')

// `undefined` simulates the element being missing,
// from an unchecked `document.querySelector` for example
new namespace[component](undefined).init()
new namespace[exportName](undefined).init()

// If our component initialisation breaks, this won't run
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -648,17 +648,14 @@ describe('Character count', () => {
// Override maxlength to 10
maxlength: 10
},
initialiser ({ config, namespace }) {
const $component = document.querySelector('[data-module]')

initialiser ($module) {
// Set locale to Welsh, which expects translations for 'one', 'two',
// 'few' 'many' and 'other' forms – with the default English strings
// provided we only have translations for 'one' and 'other'.
//
// We want to make sure we handle this gracefully in case users have
// an existing character count inside an incorrect locale.
$component.setAttribute('lang', 'cy')
new namespace.CharacterCount($component, config).init()
$module.setAttribute('lang', 'cy')
}
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { goToComponent, goToExample, renderAndInitialise } = require('govuk-frontend-helpers/puppeteer')
const { goToComponent, goToExample, renderAndInitialise, goTo } = require('govuk-frontend-helpers/puppeteer')
const { getExamples } = require('govuk-frontend-lib/files')

describe('Error Summary', () => {
Expand Down Expand Up @@ -82,14 +82,14 @@ describe('Error Summary', () => {

describe('using JavaScript configuration, with no elements on the page', () => {
it('does not prevent further JavaScript from running', async () => {
const result = await page.evaluate((component) => {
const namespace = 'GOVUKFrontend' in window
? window.GOVUKFrontend
: {}
await goTo(page, '/tests/boilerplate')

const result = await page.evaluate(async (exportName) => {
const namespace = await import('govuk-frontend')

// `undefined` simulates the element being missing,
// from an unchecked `document.querySelector` for example
new namespace[component](undefined).init()
new namespace[exportName](undefined).init()

// If our component initialisation breaks, this won't run
return true
Expand Down
20 changes: 7 additions & 13 deletions packages/govuk-frontend/src/govuk/components/globals.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,17 @@ describe('GOV.UK Frontend', () => {
beforeEach(async () => {
await goTo(page, '/')

// Exported via global (e.g GOVUKFrontend.initAll)
exported = await page.evaluate(() => 'GOVUKFrontend' in window
? Object.keys(window.GOVUKFrontend)
: undefined
// Exports available via browser dynamic import
exported = await page.evaluate(async () =>
Object.keys(await import('govuk-frontend'))
)
})

it('exports `initAll` function', async () => {
await goTo(page, '/')

const typeofInitAll = await page.evaluate((utility) => {
const namespace = 'GOVUKFrontend' in window
? window.GOVUKFrontend
: {}

const typeofInitAll = await page.evaluate(async (utility) => {
const namespace = await import('govuk-frontend')
return typeof namespace[utility]
}, 'initAll')

Expand Down Expand Up @@ -53,10 +49,8 @@ describe('GOV.UK Frontend', () => {
const components = exported
.filter(method => !['initAll', 'version'].includes(method))

const componentsWithoutInitFunctions = await page.evaluate((components) => {
const namespace = 'GOVUKFrontend' in window
? window.GOVUKFrontend
: {}
const componentsWithoutInitFunctions = await page.evaluate(async (components) => {
const namespace = await import('govuk-frontend')

return components.filter(component => {
const prototype = namespace[component].prototype
Expand Down
1 change: 1 addition & 0 deletions shared/helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"devDependencies": {
"@axe-core/puppeteer": "^4.7.3",
"cheerio": "^1.0.0-rc.12",
"govuk-frontend": "*",
"govuk-frontend-config": "*",
"govuk-frontend-lib": "*",
"jest-axe": "^7.0.1",
Expand Down
22 changes: 7 additions & 15 deletions shared/helpers/puppeteer.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,8 @@ async function axe (page, overrides = {}) {
* @param {object} options - Render and initialise options
* @param {object} options.params - Nunjucks macro params
* @param {object} [options.config] - Component instantiation config
* @param {(context: { config?: object, namespace: object }) => void} [options.initialiser] - A function that'll run in the
* browser to execute arbitrary initialisation. Receives an object with the
* passed configuration as `config` and the GOVUKFrontend global as `namespace`
* @param {($module: Element) => void} [options.initialiser] - A function that'll run in the
* browser to execute arbitrary initialisation
* @returns {Promise<import('puppeteer').Page>} Puppeteer page object
*/
async function renderAndInitialise (page, componentName, options) {
Expand All @@ -92,22 +91,15 @@ async function renderAndInitialise (page, componentName, options) {
}, html)

// Run a script to init the JavaScript component
await page.evaluate((componentClassName, options) => {
const $component = document.querySelector('[data-module]')

// Check for window global
if (!('GOVUKFrontend' in window) || !window.GOVUKFrontend[componentClassName]) {
throw new Error(`Global 'window.GOVUKFrontend.${componentClassName}' not found`)
}
await page.evaluate(async (exportName, options) => {
const $module = document.querySelector('[data-module]')

if (options.initialiser) {
return options.initialiser({
config: options.config,
namespace: window.GOVUKFrontend
})
options.initialiser($module)
}

new window.GOVUKFrontend[componentClassName]($component, options.config).init()
const namespace = await import('govuk-frontend')
new namespace[exportName]($module, options.config).init()
}, componentNameToClassName(componentName), options)

return page
Expand Down

0 comments on commit a67967a

Please sign in to comment.