11// @vitest -environment node
22import { beforeEach , describe , expect , test } from 'vitest'
33import { experimental_AstroContainer as AstroContainer } from 'astro/container'
4- import { Window } from 'happy-dom'
54import TestWebComponentAstro from '@components/Test/webComponent.astro'
65import type { TestWebComponent as TestWebComponentInstance } from '@components/Test/webComponent'
6+ import {
7+ renderInHappyDom ,
8+ type RenderResult ,
9+ } from '@test/unit/helpers/litRuntime'
710
8- type RenderResult = Awaited < ReturnType < AstroContainer [ 'renderToString' ] > >
9-
10- type HappyDomGlobalKey = 'window' | 'document' | 'customElements' | 'HTMLElement'
11- type HappyDomGlobals = Partial < Record < HappyDomGlobalKey , unknown > >
12-
13- const setGlobalValue = ( key : HappyDomGlobalKey , value : unknown ) => {
14- if ( value === undefined ) {
15- delete ( globalThis as Record < string , unknown > ) [ key ]
16- return
17- }
18-
19- ; ( globalThis as Record < string , unknown > ) [ key ] = value
20- }
21-
22- const withHappyDomEnvironment = async < TReturn > (
23- callback : ( _context : { window : Window } ) => Promise < TReturn > | TReturn ,
24- ) : Promise < TReturn > => {
25- const window = new Window ( )
26- const globalObject = globalThis as Record < string , unknown >
27- const previousGlobals : HappyDomGlobals = {
28- window : globalObject [ 'window' ] ,
29- document : globalObject [ 'document' ] ,
30- customElements : globalObject [ 'customElements' ] ,
31- HTMLElement : globalObject [ 'HTMLElement' ] ,
32- }
33-
34- setGlobalValue ( 'window' , window )
35- setGlobalValue ( 'document' , window . document )
36- setGlobalValue ( 'customElements' , window . customElements )
37- setGlobalValue ( 'HTMLElement' , window . HTMLElement )
38-
39- try {
40- return await callback ( { window } )
41- } finally {
42- setGlobalValue ( 'window' , previousGlobals . window )
43- setGlobalValue ( 'document' , previousGlobals . document )
44- setGlobalValue ( 'customElements' , previousGlobals . customElements )
45- setGlobalValue ( 'HTMLElement' , previousGlobals . HTMLElement )
46- await window . happyDOM ?. whenAsyncComplete ?.( )
47- await window . happyDOM ?. close ?.( )
48- }
49- }
11+ type TestComponentModule = typeof import ( '@components/Test/webComponent' )
5012
5113describe ( 'TestWebComponent class behavior' , ( ) => {
5214 let container : AstroContainer
@@ -55,48 +17,49 @@ describe('TestWebComponent class behavior', () => {
5517 container = await AstroContainer . create ( )
5618 } )
5719
58- const renderInHappyDom = async (
20+ const componentSelector = 'test-web-component'
21+ const renderArgs = {
22+ props : {
23+ heading : 'Integration test' ,
24+ } ,
25+ }
26+
27+ const executeRender = async (
5928 assertion : ( _context : { element : TestWebComponentInstance } ) => Promise < void > | void ,
6029 ) : Promise < void > => {
61- await withHappyDomEnvironment ( async ( { window } ) => {
62- const { TestWebComponent : TestWebComponentCtor , registerTestWebComponent } = await import (
63- '@components/Test/webComponent'
64- )
65-
66- const renderedHtml : RenderResult = await container . renderToString ( TestWebComponentAstro , {
67- props : {
68- heading : 'Integration test' ,
69- } ,
70- } )
71-
72- window . document . body . innerHTML = renderedHtml
73- window . document . querySelectorAll ( 'script' ) . forEach ( ( script ) => script . remove ( ) )
74-
75- registerTestWebComponent ( )
76-
77- const element = window . document . querySelector ( 'test-web-component' ) as
78- | TestWebComponentInstance
79- | null
80-
81- if ( ! element ) {
82- throw new Error ( 'Unable to locate rendered <test-web-component> instance in document' )
83- }
84-
85- expect ( window . customElements . get ( 'test-web-component' ) ) . toBe ( TestWebComponentCtor )
86-
87- await element . updateComplete
88- await assertion ( { element } )
30+ await renderInHappyDom < TestWebComponentInstance , TestComponentModule > ( {
31+ container,
32+ component : TestWebComponentAstro ,
33+ args : renderArgs ,
34+ selector : componentSelector ,
35+ hydrate : async ( ) => {
36+ const module = await import ( '@components/Test/webComponent' )
37+ module . registerTestWebComponent ( )
38+ return module
39+ } ,
40+ waitForReady : async ( element ) => {
41+ await element . updateComplete
42+ } ,
43+ assert : async ( { element, window, hydrateResult, renderResult } ) => {
44+ expect ( window . customElements . get ( componentSelector ) ) . toBe (
45+ hydrateResult . TestWebComponent ,
46+ )
47+ const hydratedMarkup : RenderResult = renderResult
48+ expect ( hydratedMarkup ) . toContain ( `<${ componentSelector } ` )
49+
50+ await assertion ( { element } )
51+ } ,
8952 } )
9053 }
9154
9255 test ( 'renders default message in light DOM' , async ( ) => {
93- await renderInHappyDom ( async ( { element } ) => {
56+ await executeRender ( async ( { element } ) => {
9457 expect ( element . querySelector ( '#message' ) ?. textContent ) . toBe ( 'Hello from Lit' )
9558 } )
9659 } )
9760
9861 test ( 'updates DOM when message changes' , async ( ) => {
99- await renderInHappyDom ( async ( { element } ) => {
62+ await executeRender ( async ( { element } ) => {
10063 element . message = 'Updated message'
10164 await element . updateComplete
10265
0 commit comments