Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw ElementError when the module is not of the expected type #4104

Merged
merged 7 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { mergeConfigs, extractConfigByNamespace } from '../../common/index.mjs'
import { normaliseDataset } from '../../common/normalise-dataset.mjs'
import { ElementError } from '../../errors/index.mjs'
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'
import { I18n } from '../../i18n.mjs'

Expand Down Expand Up @@ -119,7 +120,10 @@ export class Accordion extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new ElementError($module, {
componentName: 'Accordion',
identifier: '$module'
})
}

this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,35 @@ describe('/components/accordion', () => {
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when $module is not set', async () => {
await expect(
renderAndInitialise(page, 'accordion', {
params: examples.default,
beforeInitialisation($module) {
$module.remove()
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Accordion: $module not found'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'accordion', {
params: examples.default,
beforeInitialisation($module) {
// Replace with an `<svg>` element which is not an `HTMLElement` in the DOM (but an `SVGElement`)
$module.outerHTML = `<svg data-module="govuk-accordion"></svg>`
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Accordion: $module is not an instance of "HTMLElement"'
})
})
})
})
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { mergeConfigs } from '../../common/index.mjs'
import { normaliseDataset } from '../../common/normalise-dataset.mjs'
import { ElementError } from '../../errors/index.mjs'
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'

const KEY_SPACE = 32
Expand Down Expand Up @@ -34,7 +35,10 @@ export class Button extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new ElementError($module, {
componentName: 'Button',
identifier: '$module'
})
}

this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const {
goTo,
goToComponent,
renderAndInitialise
} = require('@govuk-frontend/helpers/puppeteer')
Expand All @@ -18,26 +17,6 @@ describe('/components/button', () => {
examples = await getExamples('button')
})

describe('mis-instantiation', () => {
it('does not prevent further JavaScript from running', async () => {
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
/* eslint-disable-next-line no-new */
new namespace[exportName](undefined)

// If our component initialisation breaks, this won't run
return true
}, 'Button')

expect(result).toBe(true)
})
})

describe('/components/button/link', () => {
beforeAll(async () => {
await goToComponent(page, 'button', {
Expand Down Expand Up @@ -359,6 +338,35 @@ describe('/components/button', () => {
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when $module is not set', async () => {
await expect(
renderAndInitialise(page, 'button', {
params: examples.default,
beforeInitialisation($module) {
$module.remove()
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Button: $module not found'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'button', {
params: examples.default,
beforeInitialisation($module) {
// Replace with an `<svg>` element which is not an `HTMLElement` in the DOM (but an `SVGElement`)
$module.outerHTML = `<svg data-module="govuk-button"></svg>`
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Button: $module is not an instance of "HTMLElement"'
})
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
validateConfig
} from '../../common/index.mjs'
import { normaliseDataset } from '../../common/normalise-dataset.mjs'
import { ConfigError } from '../../errors/index.mjs'
import { ConfigError, ElementError } from '../../errors/index.mjs'
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'
import { I18n } from '../../i18n.mjs'

Expand Down Expand Up @@ -75,7 +75,10 @@ export class CharacterCount extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new ElementError($module, {
componentName: 'Character count',
identifier: '$module'
})
}

const $textarea = $module.querySelector('.govuk-js-character-count')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,36 @@ describe('Character count', () => {
})
})

it('throws when $module is not set', async () => {
await expect(
renderAndInitialise(page, 'character-count', {
params: examples.default,
beforeInitialisation($module) {
$module.remove()
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Character count: $module not found'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'character-count', {
params: examples.default,
beforeInitialisation($module) {
// Replace with an `<svg>` element which is not an `HTMLElement` in the DOM (but an `SVGElement`)
$module.outerHTML = `<svg data-module="govuk-character-count"></svg>`
}
})
).rejects.toEqual({
name: 'ElementError',
message:
'Character count: $module is not an instance of "HTMLElement"'
})
})

it('throws when receiving invalid configuration', async () => {
await expect(
renderAndInitialise(page, 'character-count', {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ElementError } from '../../errors/index.mjs'
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'

/**
Expand Down Expand Up @@ -30,7 +31,10 @@ export class Checkboxes extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new ElementError($module, {
componentName: 'Checkboxes',
identifier: '$module'
})
}

/** @satisfies {NodeListOf<HTMLInputElement>} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,35 @@ describe('Checkboxes with multiple groups and a "None" checkbox and conditional
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when $module is not set', async () => {
await expect(
renderAndInitialise(page, 'checkboxes', {
params: examples.default,
beforeInitialisation($module) {
$module.remove()
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Checkboxes: $module not found'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'checkboxes', {
params: examples.default,
beforeInitialisation($module) {
// Replace with an `<svg>` element which is not an `HTMLElement` in the DOM (but an `SVGElement`)
$module.outerHTML = `<svg data-module="govuk-checkboxes"></svg>`
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Checkboxes: $module is not an instance of "HTMLElement"'
})
})
})
})
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { mergeConfigs } from '../../common/index.mjs'
import { normaliseDataset } from '../../common/normalise-dataset.mjs'
import { ElementError } from '../../errors/index.mjs'
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'

/**
Expand Down Expand Up @@ -27,7 +28,10 @@ export class ErrorSummary extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new ElementError($module, {
componentName: 'Error summary',
identifier: '$module'
})
}

this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
const {
goToComponent,
goToExample,
renderAndInitialise,
goTo
renderAndInitialise
} = require('@govuk-frontend/helpers/puppeteer')
const { getExamples } = require('@govuk-frontend/lib/components')

Expand Down Expand Up @@ -94,26 +93,6 @@ describe('Error Summary', () => {
})
})

describe('using JavaScript configuration, with no elements on the page', () => {
it('does not prevent further JavaScript from running', async () => {
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
/* eslint-disable-next-line no-new */
new namespace[exportName](undefined)

// If our component initialisation breaks, this won't run
return true
}, 'ErrorSummary')

expect(result).toBe(true)
})
})

describe('using JavaScript configuration, but enabled via data-attributes', () => {
beforeAll(async () => {
await renderAndInitialise(page, 'error-summary', {
Expand Down Expand Up @@ -251,5 +230,34 @@ describe('Error Summary', () => {
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when $module is not set', async () => {
await expect(
renderAndInitialise(page, 'error-summary', {
params: examples.default,
beforeInitialisation($module) {
$module.remove()
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Error summary: $module not found'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'error-summary', {
params: examples.default,
beforeInitialisation($module) {
// Replace with an `<svg>` element which is not an `HTMLElement` in the DOM (but an `SVGElement`)
$module.outerHTML = `<svg data-module="govuk-error-summary"></svg>`
}
})
).rejects.toEqual({
name: 'ElementError',
message: 'Error summary: $module is not an instance of "HTMLElement"'
})
})
})
})
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { mergeConfigs, extractConfigByNamespace } from '../../common/index.mjs'
import { normaliseDataset } from '../../common/normalise-dataset.mjs'
import { ElementError } from '../../errors/index.mjs'
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'
import { I18n } from '../../i18n.mjs'

/**
* Exit This Page component
* Exit this page component
*
* @preserve
*/
Expand Down Expand Up @@ -81,7 +82,10 @@ export class ExitThisPage extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new ElementError($module, {
componentName: 'Exit this page',
identifier: '$module'
})
}

const $button = $module.querySelector('.govuk-exit-this-page__button')
Expand Down
Loading