11// @vitest -environment node
2- import { beforeEach , afterEach , describe , expect , it } from 'vitest'
3- import { Window } from 'happy-dom '
2+
3+ import { beforeEach , describe , expect , it } from 'vitest '
44import { experimental_AstroContainer as AstroContainer } from 'astro/container'
5- import ConsentBanner from '@components/Consent/Banner/index.astro'
5+ import BannerFixture from '@components/Consent/Banner/client/__tests__/banner.fixture.astro'
6+ import type { ConsentBannerElement } from '@components/Consent/Banner/client'
7+ import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
8+ import {
9+ executeRender ,
10+ withJsdomEnvironment ,
11+ } from '@test/unit/helpers/litRuntime'
612import {
713 getConsentWrapper ,
814 getConsentCloseBtn ,
@@ -11,69 +17,99 @@ import {
1117} from '@components/Consent/Banner/client/selectors'
1218import { ClientScriptError } from '@components/scripts/errors'
1319
14- const attachDom = ( html : string ) : Window => {
15- const windowInstance = new Window ( )
16- windowInstance . document . body . innerHTML = html
20+ type ConsentBannerModule = WebComponentModule < ConsentBannerElement >
1721
18- globalThis . window = windowInstance as unknown as typeof globalThis . window
19- globalThis . document = windowInstance . document as unknown as Document
20- globalThis . Node = windowInstance . Node as unknown as typeof globalThis . Node
22+ const CONSENT_READY_TIMEOUT_MS = 2_000
23+ const BANNER_READY_EVENT = 'consent-banner:ready'
2124
22- return windowInstance
23- }
25+ const waitForBannerReady = async ( element : ConsentBannerElement ) => {
26+ if ( element . isInitialized ) {
27+ return
28+ }
2429
25- describe ( 'Consent Banner Selectors' , ( ) => {
26- let container : AstroContainer
27- let windowInstance : Window
30+ await new Promise < void > ( ( resolve , reject ) => {
31+ const timeoutId = setTimeout ( ( ) => {
32+ element . removeEventListener ( BANNER_READY_EVENT , onReady )
33+ reject ( new Error ( 'Consent banner never finished initializing' ) )
34+ } , CONSENT_READY_TIMEOUT_MS )
2835
29- beforeEach ( async ( ) => {
30- container = await AstroContainer . create ( )
31- const markup = await container . renderToString ( ConsentBanner )
32- windowInstance = attachDom ( markup )
36+ function onReady ( ) {
37+ clearTimeout ( timeoutId )
38+ resolve ( )
39+ }
40+
41+ element . addEventListener ( BANNER_READY_EVENT , onReady , { once : true } )
3342 } )
43+ }
44+
45+ const renderConsentBanner = async (
46+ assertion : ( ) => Promise < void > | void ,
47+ ) => {
48+ const container = await AstroContainer . create ( )
49+
50+ await executeRender < ConsentBannerModule > ( {
51+ container,
52+ component : BannerFixture ,
53+ moduleSpecifier : '@components/Consent/Banner/client/index' ,
54+ selector : 'consent-banner' ,
55+ waitForReady : waitForBannerReady ,
56+ assert : async ( ) => assertion ( ) ,
57+ } )
58+ }
3459
35- afterEach ( ( ) => {
36- windowInstance . happyDOM ?. cancelAsync ?.( )
37- delete ( globalThis as { document ?: Document } ) . document
38- delete ( globalThis as { window ?: typeof globalThis . window } ) . window
39- delete ( globalThis as { Node ?: typeof globalThis . Node } ) . Node
60+ describe ( 'Consent Banner Selectors' , ( ) => {
61+ beforeEach ( async ( ) => {
62+ await withJsdomEnvironment ( ( { window } ) => {
63+ window . sessionStorage . clear ( )
64+ window . localStorage . clear ( )
65+ } )
4066 } )
4167
42- it ( 'returns consent modal wrapper with expected attributes' , ( ) => {
43- const wrapper = getConsentWrapper ( )
68+ it ( 'returns consent modal wrapper with expected attributes' , async ( ) => {
69+ await renderConsentBanner ( ( ) => {
70+ const wrapper = getConsentWrapper ( )
4471
45- expect ( wrapper . id ) . toBe ( 'consent-modal-id' )
46- expect ( wrapper . getAttribute ( 'role' ) ) . toBe ( 'dialog' )
47- expect ( wrapper . getAttribute ( 'aria-label' ) ) . toBe ( 'Cookie consent dialog' )
72+ expect ( wrapper . id ) . toBe ( 'consent-modal-id' )
73+ expect ( wrapper . getAttribute ( 'role' ) ) . toBe ( 'dialog' )
74+ expect ( wrapper . getAttribute ( 'aria-label' ) ) . toBe ( 'Cookie consent dialog' )
75+ } )
4876 } )
4977
50- it ( 'locates the close button' , ( ) => {
51- const closeBtn = getConsentCloseBtn ( )
78+ it ( 'locates the close button' , async ( ) => {
79+ await renderConsentBanner ( ( ) => {
80+ const closeBtn = getConsentCloseBtn ( )
5281
53- expect ( closeBtn ) . toBeTruthy ( )
54- expect ( closeBtn . classList . contains ( 'consent-modal__close-btn' ) ) . toBe ( true )
55- expect ( closeBtn . getAttribute ( 'aria-label' ) ) . toMatch ( / c l o s e c o o k i e c o n s e n t d i a l o g / i)
82+ expect ( closeBtn ) . toBeTruthy ( )
83+ expect ( closeBtn . classList . contains ( 'consent-modal__close-btn' ) ) . toBe ( true )
84+ expect ( closeBtn . getAttribute ( 'aria-label' ) ) . toMatch ( / c l o s e c o o k i e c o n s e n t d i a l o g / i)
85+ } )
5686 } )
5787
58- it ( 'locates the allow-all button' , ( ) => {
59- const allowBtn = getConsentAllowBtn ( )
88+ it ( 'locates the allow-all button' , async ( ) => {
89+ await renderConsentBanner ( ( ) => {
90+ const allowBtn = getConsentAllowBtn ( )
6091
61- expect ( allowBtn ) . toBeTruthy ( )
62- expect ( allowBtn . classList . contains ( 'consent-modal__btn-allow' ) ) . toBe ( true )
63- expect ( allowBtn . textContent ?. trim ( ) ) . toBe ( 'Allow All' )
92+ expect ( allowBtn ) . toBeTruthy ( )
93+ expect ( allowBtn . classList . contains ( 'consent-modal__btn-allow' ) ) . toBe ( true )
94+ expect ( allowBtn . textContent ?. trim ( ) ) . toBe ( 'Allow All' )
95+ } )
6496 } )
6597
66- it ( 'locates the customize button' , ( ) => {
67- const customizeBtn = getConsentCustomizeBtn ( )
98+ it ( 'locates the customize button' , async ( ) => {
99+ await renderConsentBanner ( ( ) => {
100+ const customizeBtn = getConsentCustomizeBtn ( )
68101
69- expect ( customizeBtn ) . toBeTruthy ( )
70- expect ( customizeBtn . classList . contains ( 'consent-modal__btn-customize' ) ) . toBe ( true )
71- expect ( customizeBtn . textContent ?. trim ( ) ) . toBe ( 'Customize' )
102+ expect ( customizeBtn ) . toBeTruthy ( )
103+ expect ( customizeBtn . classList . contains ( 'consent-modal__btn-customize' ) ) . toBe ( true )
104+ expect ( customizeBtn . textContent ?. trim ( ) ) . toBe ( 'Customize' )
105+ } )
72106 } )
73107
74- it ( 'throws a ClientScriptError when wrapper is missing' , ( ) => {
75- document . getElementById ( 'consent-modal-id' ) ?. remove ( )
108+ it ( 'throws a ClientScriptError when wrapper is missing' , async ( ) => {
109+ await renderConsentBanner ( ( ) => {
110+ document . getElementById ( 'consent-modal-id' ) ?. remove ( )
76111
77- expect ( ( ) => getConsentWrapper ( ) ) . toThrowError ( ClientScriptError )
112+ expect ( ( ) => getConsentWrapper ( ) ) . toThrowError ( ClientScriptError )
113+ } )
78114 } )
79115} )
0 commit comments