Skip to content

Commit f7c9f60

Browse files
committed
Fix broken test case for Consent Checkbox test
1 parent 1b8f5bf commit f7c9f60

5 files changed

Lines changed: 337 additions & 394 deletions

File tree

‎src/components/Consent/Checkbox/client/__tests__/checkbox.fixture.astro‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,23 @@ import ConsentCheckbox, { type Props as ConsentCheckboxProps } from '@components
33
44
export interface CheckboxFixtureProps extends ConsentCheckboxProps {
55
formId?: string
6+
wrapInForm?: boolean
67
}
78
8-
const props: CheckboxFixtureProps = {
9+
const {
10+
wrapInForm = true,
11+
...props
12+
} = {
913
purpose: 'Responding to your inquiry',
14+
wrapInForm: true,
1015
...Astro.props,
11-
}
16+
} as CheckboxFixtureProps
1217
---
13-
14-
<form id="gdpr-consent-form" data-testid="gdpr-consent-form">
18+
{wrapInForm ? (
19+
<form id="gdpr-consent-form" data-testid="gdpr-consent-form">
20+
<ConsentCheckbox {...props} />
21+
<button type="submit">Submit</button>
22+
</form>
23+
) : (
1524
<ConsentCheckbox {...props} />
16-
<button type="submit">Submit</button>
17-
</form>
25+
)}

‎src/components/Consent/Checkbox/client/__tests__/index.spec.ts‎

Lines changed: 31 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
// @vitest-environment node
22

3-
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
4-
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
5-
import CheckboxFixture from '@components/Consent/Checkbox/client/__tests__/checkbox.fixture.astro'
6-
import type { ConsentCheckboxElement } from '@components/Consent/Checkbox/client'
7-
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
8-
import { executeRender, withJsdomEnvironment } from '@test/unit/helpers/litRuntime'
9-
import { ClientScriptError } from '@components/scripts/errors'
3+
import { beforeEach, describe, expect, it, vi } from 'vitest'
4+
import { renderConsentCheckbox } from '@components/Consent/Checkbox/client/__tests__/testUtils'
5+
import { withJsdomEnvironment } from '@test/unit/helpers/litRuntime'
106
import * as consentStore from '@components/scripts/store'
117

128
vi.mock('@components/scripts/store', () => {
@@ -50,77 +46,12 @@ const consentStoreMock = consentStore as ConsentStoreMock
5046
const updateConsentMock = vi.mocked(consentStore.updateConsent)
5147
const subscribeToFunctionalConsentMock = vi.mocked(consentStore.subscribeToFunctionalConsent)
5248

53-
type ConsentClientModule = typeof import('@components/Consent/Checkbox/client')
54-
let consentClientModule: ConsentClientModule | null = null
55-
56-
const getConsentClientModule = (): ConsentClientModule => {
57-
if (!consentClientModule) {
58-
throw new Error('Consent checkbox client module not initialized')
59-
}
60-
61-
return consentClientModule
62-
}
63-
64-
type ConsentCheckboxModule = WebComponentModule<ConsentCheckboxElement>
65-
66-
const defaultProps = {
67-
id: 'gdpr-consent',
68-
formId: 'contact-form',
69-
purpose: 'Responding to your inquiry',
70-
}
71-
72-
beforeAll(async () => {
73-
await withJsdomEnvironment(async () => {
74-
consentClientModule = await import('@components/Consent/Checkbox/client')
75-
})
76-
})
77-
78-
const CONSENT_READY_TIMEOUT_MS = 2_000
79-
8049
beforeEach(() => {
8150
consentStoreMock.__test.resetConsent()
8251
updateConsentMock.mockClear()
8352
subscribeToFunctionalConsentMock.mockClear()
8453
})
8554

86-
const waitForConsentReady = async (element: ConsentCheckboxElement) => {
87-
await new Promise<void>((resolve, reject) => {
88-
if (element.isInitialized) {
89-
resolve()
90-
return
91-
}
92-
93-
const timeoutId = setTimeout(() => {
94-
element.removeEventListener('consent-checkbox:ready', onReady)
95-
reject(new Error('Consent checkbox never finished initializing'))
96-
}, CONSENT_READY_TIMEOUT_MS)
97-
98-
function onReady() {
99-
clearTimeout(timeoutId)
100-
resolve()
101-
}
102-
103-
element.addEventListener('consent-checkbox:ready', onReady, { once: true })
104-
})
105-
}
106-
107-
const renderConsentCheckbox = async (
108-
assertion: (_context: { element: ConsentCheckboxElement; window: any }) => Promise<void> | void,
109-
props: Record<string, unknown> = {},
110-
) => {
111-
const container = await AstroContainer.create()
112-
113-
await executeRender<ConsentCheckboxModule>({
114-
container,
115-
component: CheckboxFixture,
116-
moduleSpecifier: '@components/Consent/Checkbox/client/index',
117-
args: { props: { ...defaultProps, ...props } },
118-
selector: 'consent-checkbox',
119-
waitForReady: waitForConsentReady,
120-
assert: async ({ element, window }) => assertion({ element, window }),
121-
})
122-
}
123-
12455
describe('ConsentCheckboxElement', () => {
12556
it('requests consent updates when the checkbox is checked', async () => {
12657
await renderConsentCheckbox(({ element, window }) => {
@@ -219,74 +150,52 @@ describe('ConsentCheckboxElement', () => {
219150
expect(focusSpy).toHaveBeenCalled()
220151
})
221152
})
222-
})
223153

224-
describe('Validation helpers', () => {
225-
it('validateConsent shows and clears error messages', async () => {
226-
const { validateConsent } = getConsentClientModule()
227-
228-
await renderConsentCheckbox(({ element }) => {
154+
it('continues to operate when the checkbox is not inside a form', async () => {
155+
await renderConsentCheckbox(({ element, window }) => {
156+
const form = element.closest('form')
229157
const checkbox = element.querySelector<HTMLInputElement>('input[type="checkbox"]')
230-
const errorElement = element.querySelector<HTMLDivElement>('#gdpr-consent-error')
231158

159+
expect(form).toBeNull()
232160
expect(checkbox).toBeTruthy()
233-
expect(errorElement).toBeTruthy()
234-
235-
checkbox!.checked = false
236-
expect(validateConsent(checkbox!, errorElement!)).toBe(false)
237-
expect(errorElement!.textContent).toContain('You must consent to data processing')
238161

239162
checkbox!.checked = true
240-
expect(validateConsent(checkbox!, errorElement!)).toBe(true)
241-
expect(errorElement!.textContent).toBe('')
242-
})
243-
})
244-
245-
it('isConsentValid reflects the checkbox state', async () => {
246-
const { isConsentValid } = getConsentClientModule()
247-
248-
await renderConsentCheckbox(({ element }) => {
249-
const checkbox = element.querySelector<HTMLInputElement>('input[type="checkbox"]')
250-
expect(checkbox).toBeTruthy()
251-
252-
checkbox!.checked = false
253-
expect(isConsentValid('gdpr-consent')).toBe(false)
163+
checkbox!.dispatchEvent(new window.Event('change', { bubbles: true }))
254164

255-
checkbox!.checked = true
256-
expect(isConsentValid('gdpr-consent')).toBe(true)
257-
})
165+
expect(updateConsentMock).toHaveBeenCalledWith('functional', true)
166+
}, { wrapInForm: false })
258167
})
259168
})
260169

261-
describe('initGDPRConsent', () => {
262-
it('throws ClientScriptError when elements are missing', async () => {
263-
await withJsdomEnvironment(async ({ window }) => {
264-
window.document.body.innerHTML = '<div></div>'
265-
266-
const { initGDPRConsent } = getConsentClientModule()
267-
expect(() => initGDPRConsent('missing', 'contact')).toThrow(ClientScriptError)
170+
describe('ConsentCheckbox web component module contract', () => {
171+
it('exposes metadata for registration', async () => {
172+
await withJsdomEnvironment(async () => {
173+
const { webComponentModule, ConsentCheckboxElement } = await import('@components/Consent/Checkbox/client')
174+
expect(webComponentModule.registeredName).toBe('consent-checkbox')
175+
expect(webComponentModule.componentCtor).toBe(ConsentCheckboxElement)
268176
})
269177
})
270178

271-
it('handles checkboxes that are not inside a form', async () => {
179+
it('registers the custom element when window is available', async () => {
272180
await withJsdomEnvironment(async ({ window }) => {
273-
window.document.body.innerHTML = `
274-
<div id="gdpr-consent-container">
275-
<input type="checkbox" id="gdpr-consent" />
276-
<div id="gdpr-consent-error"></div>
277-
</div>
278-
`
181+
const { registerConsentCheckboxWebComponent, ConsentCheckboxElement } = await import('@components/Consent/Checkbox/client')
182+
const uniqueTag = `consent-checkbox-${Math.random().toString(36).slice(2)}`
279183

280-
const { initGDPRConsent } = getConsentClientModule()
281-
const cleanup = initGDPRConsent('gdpr-consent', 'contact')
184+
const originalGet = window.customElements.get.bind(window.customElements)
185+
const getSpy = vi.spyOn(window.customElements, 'get').mockImplementation((tagName: string) => {
186+
if (tagName === uniqueTag) {
187+
return undefined
188+
}
189+
return originalGet(tagName)
190+
})
191+
const defineSpy = vi.spyOn(window.customElements, 'define').mockImplementation(() => undefined)
282192

283-
const checkbox = window.document.getElementById('gdpr-consent') as HTMLInputElement
284-
checkbox.checked = true
285-
checkbox.dispatchEvent(new window.Event('change'))
193+
registerConsentCheckboxWebComponent(uniqueTag)
286194

287-
expect(updateConsentMock).toHaveBeenCalledWith('functional', true)
195+
expect(defineSpy).toHaveBeenCalledWith(uniqueTag, ConsentCheckboxElement)
288196

289-
cleanup()
197+
getSpy.mockRestore()
198+
defineSpy.mockRestore()
290199
})
291200
})
292201
})

‎src/components/Consent/Checkbox/client/__tests__/selectors.spec.ts‎

Lines changed: 4 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,23 @@
11
// @vitest-environment node
22

33
import { describe, expect, it } from 'vitest'
4-
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
5-
import CheckboxFixture from '@components/Consent/Checkbox/client/__tests__/checkbox.fixture.astro'
64
import {
75
getConsentCheckbox,
86
getConsentContainer,
97
getConsentDescription,
108
getConsentError,
119
} from '@components/Consent/Checkbox/client/selectors'
1210
import type { ConsentCheckboxElement } from '@components/Consent/Checkbox/client'
13-
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
14-
import { executeRender } from '@test/unit/helpers/litRuntime'
1511
import { ClientScriptError } from '@components/scripts/errors'
16-
17-
type ConsentCheckboxModule = WebComponentModule<ConsentCheckboxElement>
18-
19-
const defaultProps = {
20-
id: 'gdpr-consent',
21-
purpose: 'Responding to your inquiry',
22-
}
23-
24-
const CONSENT_READY_TIMEOUT_MS = 2_000
25-
26-
const waitForConsentReady = async (element: ConsentCheckboxElement) => {
27-
await new Promise<void>((resolve, reject) => {
28-
if (element.isInitialized) {
29-
resolve()
30-
return
31-
}
32-
33-
const timeoutId = setTimeout(() => {
34-
element.removeEventListener('consent-checkbox:ready', onReady)
35-
reject(new Error('Consent checkbox never finished initializing'))
36-
}, CONSENT_READY_TIMEOUT_MS)
37-
38-
function onReady() {
39-
clearTimeout(timeoutId)
40-
resolve()
41-
}
42-
43-
element.addEventListener('consent-checkbox:ready', onReady, { once: true })
44-
})
45-
}
12+
import { renderConsentCheckbox } from '@components/Consent/Checkbox/client/__tests__/testUtils'
4613

4714
const renderSelectors = async (
4815
assertion: (_context: { element: ConsentCheckboxElement }) => Promise<void> | void,
4916
props: Record<string, unknown> = {},
5017
) => {
51-
const container = await AstroContainer.create()
52-
53-
await executeRender<ConsentCheckboxModule>({
54-
container,
55-
component: CheckboxFixture,
56-
moduleSpecifier: '@components/Consent/Checkbox/client/index',
57-
args: { props: { ...defaultProps, ...props } },
58-
selector: 'consent-checkbox',
59-
waitForReady: waitForConsentReady,
60-
assert: async ({ element }) => assertion({ element }),
61-
})
18+
await renderConsentCheckbox(async ({ element }) => {
19+
await assertion({ element })
20+
}, props)
6221
}
6322

6423
describe('Consent checkbox selectors', () => {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { expect } from 'vitest'
2+
import { experimental_AstroContainer as AstroContainer } from 'astro/container'
3+
import CheckboxFixture, { type CheckboxFixtureProps } from '@components/Consent/Checkbox/client/__tests__/checkbox.fixture.astro'
4+
import type { ConsentCheckboxElement } from '@components/Consent/Checkbox/client'
5+
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
6+
import { executeRender } from '@test/unit/helpers/litRuntime'
7+
8+
export type ConsentCheckboxModule = WebComponentModule<ConsentCheckboxElement>
9+
10+
const CONSENT_READY_TIMEOUT_MS = 2_000
11+
12+
export const consentCheckboxReadyEvent = 'consent-checkbox:ready'
13+
14+
export const waitForConsentReady = async (element: ConsentCheckboxElement): Promise<void> => {
15+
if (element.isInitialized) {
16+
return
17+
}
18+
19+
await new Promise<void>((resolve, reject) => {
20+
const timeoutId = setTimeout(() => {
21+
element.removeEventListener(consentCheckboxReadyEvent, onReady)
22+
reject(new Error('Consent checkbox never finished initializing'))
23+
}, CONSENT_READY_TIMEOUT_MS)
24+
25+
function onReady() {
26+
clearTimeout(timeoutId)
27+
resolve()
28+
}
29+
30+
element.addEventListener(consentCheckboxReadyEvent, onReady, { once: true })
31+
})
32+
}
33+
34+
const defaultProps: CheckboxFixtureProps = {
35+
id: 'gdpr-consent',
36+
formId: 'contact-form',
37+
purpose: 'Responding to your inquiry',
38+
wrapInForm: true,
39+
}
40+
41+
export interface RenderConsentCheckboxContext {
42+
element: ConsentCheckboxElement
43+
window: Window & typeof globalThis
44+
module: ConsentCheckboxModule
45+
}
46+
47+
export type RenderConsentCheckboxAssertion = (
48+
_context: RenderConsentCheckboxContext,
49+
) => Promise<void> | void
50+
51+
export const renderConsentCheckbox = async (
52+
assertion: RenderConsentCheckboxAssertion,
53+
props: Partial<CheckboxFixtureProps> = {},
54+
): Promise<void> => {
55+
const container = await AstroContainer.create()
56+
57+
await executeRender<ConsentCheckboxModule>({
58+
container,
59+
component: CheckboxFixture,
60+
moduleSpecifier: '@components/Consent/Checkbox/client/index',
61+
args: { props: { ...defaultProps, ...props } },
62+
selector: 'consent-checkbox',
63+
waitForReady: waitForConsentReady,
64+
assert: async ({ element, window, module, renderResult }) => {
65+
if (!window) {
66+
throw new Error('Consent checkbox tests require a browser-like window environment')
67+
}
68+
69+
expect(renderResult).toContain(`<${module.registeredName}`)
70+
await assertion({ element, window: window as Window & typeof globalThis, module })
71+
},
72+
})
73+
}

0 commit comments

Comments
 (0)