Skip to content
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
1 change: 1 addition & 0 deletions src/server/plugins/engine/views/summary.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ <h2 class="govuk-heading-m" id="declaration">{{ t('pages.summary.declaration') }
When a better fix is implemented, this class can be removed and preventDoubleClick should be set back to true. #}
{{ govukButton({
text: submitButtonText,
classes: "prevent-multiple-clicks",
name: "action",
value: "send",
preventDoubleClick: false
Expand Down
92 changes: 92 additions & 0 deletions test/form/summary-submit-button.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { join } from 'node:path'

import { initDebounceClick } from '~/src/client/javascripts/debounce-click.js'
import { FORM_PREFIX } from '~/src/server/constants.js'
import { createServer } from '~/src/server/index.js'
import { getFormMetadata } from '~/src/server/plugins/engine/services/formsService.js'
import * as fixtures from '~/test/fixtures/index.js'
import { renderResponse } from '~/test/helpers/component-helpers.js'
import { getCookie, getCookieHeader } from '~/test/utils/get-cookie.js'

const basePath = `${FORM_PREFIX}/minimal`

jest.mock('~/src/server/plugins/engine/services/formsService.js')
jest.mock('~/src/server/plugins/engine/services/formSubmissionService.js')

describe('Page: /summary submit button', () => {
/** @type {Server} */
let server

/** @type {ReturnType<typeof getCookieHeader>} */
let headers

beforeAll(async () => {
server = await createServer({
formFileName: 'minimal.js',
formFilePath: join(import.meta.dirname, 'definitions'),
enforceCsrf: true
})

await server.initialize()

const response = await server.inject({
url: `${basePath}/start`
})

const csrfToken = getCookie(response, 'crumb')
headers = getCookieHeader(response, ['session', 'crumb'])

await server.inject({
url: `${basePath}/start`,
method: 'POST',
headers,
payload: {
crumb: csrfToken,
field: 'value'
}
})
})

beforeEach(() => {
jest.mocked(getFormMetadata).mockResolvedValue(fixtures.form.metadata)
})

afterAll(async () => {
await server.stop()
})

afterEach(() => {
document.body.innerHTML = ''
})

it('only submits the form once when the submit button is clicked twice in quick succession', async () => {
const { container } = await renderResponse(server, {
url: `${basePath}/summary`,
headers
})

// Wire up the client-side JS against the real rendered page, as
// application.js does in the browser via initAll()
initDebounceClick()

const $form = /** @type {HTMLFormElement} */ (
container.getByRole('button', { name: 'Submit' }).closest('form')
)
const $submit = container.getByRole('button', { name: 'Submit' })

const onSubmit = jest.fn((/** @type {Event} */ event) =>
// Stop jsdom attempting a real navigation
event.preventDefault()
)
$form.addEventListener('submit', onSubmit)

$submit.click()
$submit.click()

expect(onSubmit).toHaveBeenCalledTimes(1)
})
})

/**
* @import { Server } from '@hapi/hapi'
*/
Loading