Skip to content

Commit

Permalink
Implement check for type of $module
Browse files Browse the repository at this point in the history
Simply replaces `return this` with explicit `throw` as trying to factor further leads to extra complexity figuring out inheritance and typing, which we can do after v5 is shipped.

request-checks: true
  • Loading branch information
romaricpascal committed Sep 7, 2023
1 parent 11c9270 commit eabdc84
Show file tree
Hide file tree
Showing 23 changed files with 242 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { I18n } from '../../i18n.mjs'
* @preserve
*/
export class Accordion extends GOVUKFrontendComponent {
/** @private */
$module

/**
* @private
* @type {AccordionConfig}
Expand Down Expand Up @@ -116,7 +119,9 @@ export class Accordion extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new TypeError(
'Expected `$module` to be an instance of `HTMLElement`'
)
}

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

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'accordion', {
params: examples.default,
beforeInitialisation() {
// Remove the root of the components as a way
// for the constructor to receive the wrong type for `$module`
document.querySelector('[data-module]').remove()
}
})
).rejects.toEqual({
name: 'TypeError',
message: 'Expected `$module` to be an instance of `HTMLElement`'
})
})
})
})
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const DEBOUNCE_TIMEOUT_IN_SECONDS = 1
* @preserve
*/
export class Button extends GOVUKFrontendComponent {
/** @private */
$module

/**
* @private
* @type {ButtonConfig}
Expand All @@ -31,7 +34,9 @@ export class Button extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new TypeError(
'Expected `$module` to be an instance of `HTMLElement`'
)
}

this.$module = $module
Expand Down
37 changes: 16 additions & 21 deletions packages/govuk-frontend/src/govuk/components/button/button.test.js
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,22 @@ describe('/components/button', () => {
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'button', {
params: examples.default,
beforeInitialisation() {
// Remove the root of the components as a way
// for the constructor to receive the wrong type for `$module`
document.querySelector('[data-module]').remove()
}
})
).rejects.toEqual({
name: 'TypeError',
message: 'Expected `$module` to be an instance of `HTMLElement`'
})
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { I18n } from '../../i18n.mjs'
* @preserve
*/
export class CharacterCount extends GOVUKFrontendComponent {
/** @private */
$module

/** @private */
$textarea

Expand Down Expand Up @@ -67,7 +70,9 @@ export class CharacterCount extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new TypeError(
'Expected `$module` to be an instance of `HTMLElement`'
)
}

const $textarea = $module.querySelector('.govuk-js-character-count')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,22 @@ describe('Character count', () => {
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'character-count', {
params: examples.default,
beforeInitialisation() {
// Remove the root of the components as a way
// for the constructor to receive the wrong type for `$module`
document.querySelector('[data-module]').remove()
}
})
).rejects.toEqual({
name: 'TypeError',
message: 'Expected `$module` to be an instance of `HTMLElement`'
})
})
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'
* @preserve
*/
export class Checkboxes extends GOVUKFrontendComponent {
/** @private */
$module

/** @private */
$inputs

Expand All @@ -27,7 +30,9 @@ export class Checkboxes extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new TypeError(
'Expected `$module` to be an instance of `HTMLElement`'
)
}

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

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'checkboxes', {
params: examples.default,
beforeInitialisation() {
// Remove the root of the components as a way
// for the constructor to receive the wrong type for `$module`
document.querySelector('[data-module]').remove()
}
})
).rejects.toEqual({
name: 'TypeError',
message: 'Expected `$module` to be an instance of `HTMLElement`'
})
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'
* @preserve
*/
export class ErrorSummary extends GOVUKFrontendComponent {
/** @private */
$module

/**
* @private
* @type {ErrorSummaryConfig}
Expand All @@ -24,7 +27,9 @@ export class ErrorSummary extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new TypeError(
'Expected `$module` to be an instance of `HTMLElement`'
)
}

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,21 @@ describe('Error Summary', () => {
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'error-summary', {
params: examples.default,
beforeInitialisation() {
// Remove the root of the components as a way
// for the constructor to receive the wrong type for `$module`
document.querySelector('[data-module]').remove()
}
})
).rejects.toEqual({
name: 'TypeError',
message: 'Expected `$module` to be an instance of `HTMLElement`'
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { I18n } from '../../i18n.mjs'
* @preserve
*/
export class ExitThisPage extends GOVUKFrontendComponent {
/** @private */
$module

/**
* @private
* @type {ExitThisPageConfig}
Expand Down Expand Up @@ -78,7 +81,9 @@ export class ExitThisPage extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new TypeError(
'Expected `$module` to be an instance of `HTMLElement`'
)
}

const $button = $module.querySelector('.govuk-exit-this-page__button')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,22 @@ describe('/components/exit-this-page', () => {
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'exit-this-page', {
params: examples.default,
beforeInitialisation() {
// Remove the root of the components as a way
// for the constructor to receive the wrong type for `$module`
document.querySelector('[data-module]').remove()
}
})
).rejects.toEqual({
name: 'TypeError',
message: 'Expected `$module` to be an instance of `HTMLElement`'
})
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'
* @preserve
*/
export class Header extends GOVUKFrontendComponent {
/** @private */
$module

/** @private */
$menuButton

Expand Down Expand Up @@ -41,7 +44,9 @@ export class Header extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new TypeError(
'Expected `$module` to be an instance of `HTMLElement`'
)
}

this.$module = $module
Expand Down
16 changes: 16 additions & 0 deletions packages/govuk-frontend/src/govuk/components/header/header.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,22 @@ describe('Header navigation', () => {
message: 'GOV.UK Frontend is not supported in this browser'
})
})

it('throws when receiving the wrong type for $module', async () => {
await expect(
renderAndInitialise(page, 'header', {
params: examples.default,
beforeInitialisation() {
// Remove the root of the components as a way
// for the constructor to receive the wrong type for `$module`
document.querySelector('[data-module]').remove()
}
})
).rejects.toEqual({
name: 'TypeError',
message: 'Expected `$module` to be an instance of `HTMLElement`'
})
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'
* @preserve
*/
export class NotificationBanner extends GOVUKFrontendComponent {
/** @private */
$module

/**
* @private
* @type {NotificationBannerConfig}
Expand All @@ -22,7 +25,9 @@ export class NotificationBanner extends GOVUKFrontendComponent {
super()

if (!($module instanceof HTMLElement)) {
return this
throw new TypeError(
'Expected `$module` to be an instance of `HTMLElement`'
)
}

this.$module = $module
Expand Down
Loading

0 comments on commit eabdc84

Please sign in to comment.