@@ -4,21 +4,20 @@ import fs from 'node:fs/promises';
44import { rollup } from 'rollup' ;
55import lwcRollupPlugin from '@lwc/rollup-plugin' ;
66import { DISABLE_STATIC_CONTENT_OPTIMIZATION , ENGINE_SERVER } from '../../helpers/options.js' ;
7- const lwcSsr = await ( ENGINE_SERVER ? import ( '@lwc/engine-server' ) : import ( '@lwc/ssr-runtime' ) ) ;
8-
9- const ROOT_DIR = path . join ( import . meta. dirname , '../..' ) ;
10-
11- const context = {
12- LWC : lwcSsr ,
13- moduleOutput : null ,
14- } ;
7+ /** LWC SSR module to use when server-side rendering components. */
8+ const lwcSsr = await ( ENGINE_SERVER
9+ ? // Using import('literal') rather than import(variable) so static analysis tools work
10+ import ( '@lwc/engine-server' )
11+ : import ( '@lwc/ssr-runtime' ) ) ;
1512
1613lwcSsr . setHooks ( {
1714 sanitizeHtmlContent ( content ) {
1815 return content ;
1916 } ,
2017} ) ;
2118
19+ const ROOT_DIR = path . join ( import . meta. dirname , '../..' ) ;
20+
2221let guid = 0 ;
2322const COMPONENT_UNDER_TEST = 'main' ;
2423
@@ -100,7 +99,7 @@ function throwOnUnexpectedConsoleCalls(runnable, expectedConsoleCalls = {}) {
10099 } ;
101100 }
102101 try {
103- runnable ( ) ;
102+ return runnable ( ) ;
104103 } finally {
105104 Object . assign ( console , originals ) ;
106105 }
@@ -117,31 +116,26 @@ function throwOnUnexpectedConsoleCalls(runnable, expectedConsoleCalls = {}) {
117116 * So, script runs, generates markup, & we get that markup out and return it for use
118117 * in client-side tests.
119118 */
120- async function getSsrCode ( moduleCode , testConfig , filename , expectedSSRConsoleCalls ) {
119+ async function getSsrCode ( moduleCode , testConfig , filePath , expectedSSRConsoleCalls ) {
121120 const script = new vm . Script (
122- // FIXME: Can these IIFEs be converted to ESM imports?
123- // No, vm.Script doesn't support that. But might be doable with experimental vm.Module
124- `
125- ${ testConfig } ;
126- config = config || {};
127- ${ moduleCode } ;
128- moduleOutput = LWC.renderComponent(
121+ `(() => {
122+ ${ testConfig }
123+ ${ moduleCode }
124+ return LWC.renderComponent(
129125 'x-${ COMPONENT_UNDER_TEST } -${ guid ++ } ',
130126 Main,
131127 config.props || {},
132128 false,
133129 'sync'
134130 );
135- ` ,
136- { filename }
131+ })() ` ,
132+ { filename : `[SSR] ${ filePath } ` }
137133 ) ;
138134
139- throwOnUnexpectedConsoleCalls ( ( ) => {
140- vm . createContext ( context ) ;
141- script . runInContext ( context ) ;
142- } , expectedSSRConsoleCalls ) ;
143-
144- return await context . moduleOutput ;
135+ return throwOnUnexpectedConsoleCalls (
136+ ( ) => script . runInContext ( vm . createContext ( { LWC : lwcSsr } ) ) ,
137+ expectedSSRConsoleCalls
138+ ) ;
145139}
146140
147141async function getTestConfig ( input ) {
@@ -178,52 +172,41 @@ async function existsUp(dir, file) {
178172 * This function wraps those configs in the test code to be executed.
179173 */
180174async function wrapHydrationTest ( filePath ) {
181- const suiteDir = path . dirname ( filePath ) ;
182-
183- // Wrap all the tests into a describe block with the file stricture name
184- const describeTitle = path . relative ( ROOT_DIR , suiteDir ) . split ( path . sep ) . join ( ' ' ) ;
185-
186- const testCode = await getTestConfig ( filePath ) ;
187-
188- // Create a temporary module to evaluate the bundled code and extract config properties for test configuration
189- const configModule = new vm . Script ( testCode ) ;
190- const configContext = { config : { } } ;
191- vm . createContext ( configContext ) ;
192- configModule . runInContext ( configContext ) ;
193- const { expectedSSRConsoleCalls, requiredFeatureFlags } = configContext . config ;
194-
195- requiredFeatureFlags ?. forEach ( ( featureFlag ) => {
196- lwcSsr . setFeatureFlagForTest ( featureFlag , true ) ;
197- } ) ;
175+ const {
176+ default : { expectedSSRConsoleCalls, requiredFeatureFlags } ,
177+ } = await import ( path . join ( ROOT_DIR , filePath ) ) ;
198178
199179 try {
180+ requiredFeatureFlags ?. forEach ( ( featureFlag ) => {
181+ lwcSsr . setFeatureFlagForTest ( featureFlag , true ) ;
182+ } ) ;
183+
184+ const suiteDir = path . dirname ( filePath ) ;
200185 // You can add an `.only` file alongside an `index.spec.js` file to make it `fdescribe()`
201186 const onlyFileExists = await existsUp ( suiteDir , '.only' ) ;
202187
203- const describeFn = onlyFileExists ? 'describe.only' : 'describe' ;
204188 const componentDefCSR = await getCompiledModule ( suiteDir , false ) ;
205189 const componentDefSSR = ENGINE_SERVER
206190 ? componentDefCSR
207191 : await getCompiledModule ( suiteDir , true ) ;
208192 const ssrOutput = await getSsrCode (
209193 componentDefSSR ,
210- testCode ,
211- path . join ( suiteDir , 'ssr.js' ) ,
194+ await getTestConfig ( filePath ) ,
195+ filePath ,
212196 expectedSSRConsoleCalls
213197 ) ;
214198
215199 // FIXME: can we turn these IIFEs into ESM imports?
216200 return `
217201 import { runTest } from '/helpers/test-hydrate.js';
218202 import config from '/${ filePath } ?original=1';
219- ${ describeFn } ("${ describeTitle } ", () => {
220- it('test', async () => {
221- const ssrRendered = ${ JSON . stringify ( ssrOutput ) /* escape quotes */ } ;
222- // Component code, IIFE set as Main
223- ${ componentDefCSR } ;
224- return await runTest(ssrRendered, Main, config);
225- })
226- });` ;
203+ ${ onlyFileExists ? 'it.only' : 'it' } ('${ filePath } ', async () => {
204+ const ssrRendered = ${ JSON . stringify ( ssrOutput ) /* escape quotes */ } ;
205+ // Component code, IIFE set as Main
206+ ${ componentDefCSR } ;
207+ return await runTest(ssrRendered, Main, config);
208+ });
209+ ` ;
227210 } finally {
228211 requiredFeatureFlags ?. forEach ( ( featureFlag ) => {
229212 lwcSsr . setFeatureFlagForTest ( featureFlag , false ) ;
0 commit comments