Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a82076d
wip: wip
divmain Feb 17, 2025
f381308
build(deps-dev): bump prettier from 3.4.2 to 3.5.0 in the prettier gr…
dependabot[bot] Feb 10, 2025
342ae8d
build(deps): bump the theoretically-non-breaking group with 20 update…
dependabot[bot] Feb 10, 2025
de4f777
chore: bump nx (#5207)
wjhsf Feb 10, 2025
0ef4402
fix(ssr): make internals non-configurable (#5208)
wjhsf Feb 10, 2025
4923180
fix: provide template prop on LightningElement in SSRv2 (#5212)
divmain Feb 11, 2025
c9f4c66
chore: release v8.13.2 (#5213)
jhefferman-sfdc Feb 11, 2025
f8c1670
fix(rollup-plugin): remove default modules (#5203)
cardoso Feb 12, 2025
b5d0414
build(deps): bump koa from 2.15.3 to 2.15.4 (#5220)
dependabot[bot] Feb 12, 2025
807f4d6
feat(ssr): add LWC version comment to end of compiled template (#5218)
wjhsf Feb 13, 2025
50516ed
chore: rename ephemeral placeholder variable (#5210)
wjhsf Feb 13, 2025
3e31a4d
chore: don't use custom test timeout for fixtures (#5221)
wjhsf Feb 13, 2025
7ba157f
test(engine-server): clean up fixture tests (#5219)
wjhsf Feb 13, 2025
fd5ebda
chore: reintroduce locker logic (#5217)
jhefferman-sfdc Feb 14, 2025
ec93224
test(fixtures): eradicate `index.js` (#5222)
wjhsf Feb 14, 2025
6f39aa5
chore: release v8.13.3 (#5223)
jhefferman-sfdc Feb 14, 2025
76e905c
fix: corrections, v1/2 pathing, expected failures
jhefferman-sfdc Feb 18, 2025
fe96c28
Merge branch 'master' into mob/karma-with-ssrv2
jhefferman-sfdc Feb 18, 2025
4af38ce
chore: variable rename
jhefferman-sfdc Feb 18, 2025
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
5 changes: 5 additions & 0 deletions .github/workflows/karma.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ jobs:
- run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 DISABLE_SYNTHETIC=1 yarn sauce:ci
- run: NODE_ENV_FOR_TEST=production yarn sauce:ci
- run: NODE_ENV_FOR_TEST=production DISABLE_SYNTHETIC=1 yarn sauce:ci
- run: yarn hydration:sauce:ci:engine-server
- run: ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn hydration:sauce:ci:engine-server
- run: NODE_ENV_FOR_TEST=production yarn hydration:sauce:ci:engine-server
- run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn hydration:sauce:ci:engine-server
- run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn hydration:sauce:ci:engine-server
Comment on lines +181 to +185
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these added to the shortest-running group of tests?

- run: yarn hydration:sauce:ci
- run: ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn hydration:sauce:ci
- run: NODE_ENV_FOR_TEST=production yarn hydration:sauce:ci
Expand Down
4 changes: 4 additions & 0 deletions packages/@lwc/integration-karma/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
"scripts": {
"start": "KARMA_MODE=watch karma start ./scripts/karma-configs/test/local.js",
"test": "karma start ./scripts/karma-configs/test/local.js --single-run --browsers ChromeHeadless",
"hydration:start:engine-server": "ENGINE_SERVER=true KARMA_MODE=watch karma start ./scripts/karma-configs/hydration/local.js",
"hydration:test:engine-server": "ENGINE_SERVER=true karma start ./scripts/karma-configs/hydration/local.js --single-run --browsers ChromeHeadless",
"hydration:sauce:engine-server": "ENGINE_SERVER=true karma start ./scripts/karma-configs/hydration/sauce.js --single-run",
"hydration:sauce:ci:engine-server": "ENGINE_SERVER=true ../../../scripts/ci/retry.sh karma start ./scripts/karma-configs/hydration/sauce.js --single-run",
"hydration:start": "KARMA_MODE=watch karma start ./scripts/karma-configs/hydration/local.js",
"hydration:test": "karma start ./scripts/karma-configs/hydration/local.js --single-run --browsers ChromeHeadless",
"hydration:sauce": "karma start ./scripts/karma-configs/hydration/sauce.js --single-run",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,29 @@
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
const ENGINE_SERVER = process.env.ENGINE_SERVER;
const path = require('node:path');
const vm = require('node:vm');
const fs = require('node:fs/promises');
const { format } = require('node:util');
const { rollup } = require('rollup');
const lwcRollupPlugin = require('@lwc/rollup-plugin');
const ssr = require('@lwc/engine-server');
const ssr = ENGINE_SERVER ? require('@lwc/engine-server') : require('@lwc/ssr-runtime');
const { DISABLE_STATIC_CONTENT_OPTIMIZATION } = require('../shared/options');
const Watcher = require('./Watcher');

/*
* These are hydration tests that currently fail in ssr-compiler (V2)
* They need to be removed from this list and then fixed. There are only 4
* so kept here for now.
*/
const EXPECTED_V2_FAILURES = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These failures only occur in V2, due to engine differences. I didn't want to solve them in this PR but there are only 4 and they should be quick to resolve so I kept them in this file. Could also import them if someone feels strongly.

I can open bugs for them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can create one WI for all of them, if you think they'll be quick fixes. If they'll take individual effort, multiple WIs is fine.

Also, please add corresponding tests to the SSR test suite so we can get easier signal about future breakage without having to run the integration/hydration tests.

'mismatches/host-mutation-in-connected-callback/class-mutated-attr-mismatch',
'mismatches/host-mutation-in-connected-callback/class',
'directives/lwc-dynamic',
'inner-outer-html',
];

const context = {
LWC: ssr,
moduleOutput: null,
Expand Down Expand Up @@ -54,13 +67,12 @@ async function exists(path) {
}
}

let cache;

async function getCompiledModule(dirName) {
async function getCompiledModule(dirName, compileForSSR) {
const bundle = await rollup({
input: path.join(dirName, 'x', COMPONENT_UNDER_TEST, `${COMPONENT_UNDER_TEST}.js`),
plugins: [
lwcRollupPlugin({
targetSSR: !!compileForSSR,
modules: [
{
dir: dirName,
Expand All @@ -74,9 +86,8 @@ async function getCompiledModule(dirName) {
enableStaticContentOptimization: !DISABLE_STATIC_CONTENT_OPTIMIZATION,
}),
],
cache,

external: ['lwc', 'test-utils', '@test/loader'], // @todo: add ssr modules for test-utils and @test/loader
external: ['lwc', '@lwc/ssr-runtime', 'test-utils', '@test/loader'], // @todo: add ssr modules for test-utils and @test/loader

onwarn(warning, warn) {
// Ignore warnings from our own Rollup plugin
Expand All @@ -87,13 +98,13 @@ async function getCompiledModule(dirName) {
});

const { watchFiles } = bundle;
cache = bundle.cache;

const { output } = await bundle.generate({
format: 'iife',
name: 'Main',
globals: {
lwc: 'LWC',
'@lwc/ssr-runtime': 'LWC',
'test-utils': 'TestUtils',
},
});
Expand Down Expand Up @@ -131,25 +142,30 @@ function throwOnUnexpectedConsoleCalls(runnable) {
}

/**
* @param {string} moduleCode
* @param {string} testConfig
* @param {string} filename
* This is the function that takes SSR bundle code and test config, constructs a script that will
* run in a separate JS runtime environment with its own global scope. The `context` object
* (defined at the top of this file) is passed in as the global scope for that script. The script
* runs, utilizing the `LWC` object that we've attached to the global scope, it sets a
* new value (the rendered markup) to the globalThis.moduleOutput, which correspond to
* context.moduleOutput in the hydration-test.js module scope.
*
* So, script runs, generates markup, & we get that markup out and return it to Karma for use
* in client-side tests.
*/
function getSsrCode(moduleCode, testConfig, filename) {
async function getSsrCode(moduleCode, testConfig, filename) {
const script = new vm.Script(
`
${testConfig};
config = config || {};
${moduleCode};
moduleOutput = LWC.renderComponent('x-${COMPONENT_UNDER_TEST}-${guid++}', Main, config.props || {});`,
moduleOutput = LWC.renderComponent('x-${COMPONENT_UNDER_TEST}-${guid++}', Main, config.props || {}, 'sync');`,
{ filename }
);

throwOnUnexpectedConsoleCalls(() => {
vm.createContext(context);
script.runInContext(context);
});

return context.moduleOutput;
}

Expand All @@ -160,7 +176,6 @@ async function getTestModuleCode(input) {
});

const { watchFiles } = bundle;
cache = bundle.cache;

const { output } = await bundle.generate({
format: 'iife',
Expand Down Expand Up @@ -194,33 +209,66 @@ function createHCONFIG2JSPreprocessor(config, logger, emitter) {
return async (_content, file, done) => {
const filePath = file.path;
const suiteDir = path.dirname(filePath);

// Wrap all the tests into a describe block with the file stricture name
const describeTitle = path.relative(basePath, suiteDir).split(path.sep).join(' ');

try {
const { code: testCode, watchFiles: testWatchFiles } =
await getTestModuleCode(filePath);
const { code: componentDef, watchFiles: componentWatchFiles } =
await getCompiledModule(suiteDir);

// You can add an `.only` file alongside an `index.spec.js` file to make it `fdescribe()`
const onlyFileExists = await existsUp(suiteDir, '.only');
// If V2 failure is expected, skip it.
const v2FailureExpected =
!ENGINE_SERVER && EXPECTED_V2_FAILURES.some((dir) => suiteDir.includes(dir));

let describe = onlyFileExists ? 'fdescribe' : 'describe';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this variable name. describe should be the test runner function, not a string!


const { code: componentDefCSR, watchFiles: componentWatchFilesCSR } =
await getCompiledModule(suiteDir, false);

let ssrOutput;
/* eslint-disable vitest/no-conditional-tests */
if (ENGINE_SERVER) {
// engine-server uses the same def as the client
watcher.watchSuite(filePath, testWatchFiles.concat(componentWatchFilesCSR));
ssrOutput = await getSsrCode(
componentDefCSR,
testCode,
path.join(suiteDir, 'ssr.js')
);
} else if (!v2FailureExpected) {
// ssr-compiler has it's own def
const { code: componentDefSSR, watchFiles: componentWatchFilesSSR } =
await getCompiledModule(suiteDir, true);
watcher.watchSuite(
filePath,
testWatchFiles.concat(componentWatchFilesCSR).concat(componentWatchFilesSSR)
);
ssrOutput = await getSsrCode(
componentDefSSR.replace(`process.env.NODE_ENV === 'test-karma-lwc'`, 'true'),
testCode,
path.join(suiteDir, 'ssr.js')
);
} else {
console.log(`Expected failure for ${suiteDir}, skipping`);
describe = 'xdescribe';
}
/* eslint-enable vitest/no-conditional-tests */

const ssrOutput = getSsrCode(componentDef, testCode, path.join(suiteDir, 'ssr.js'));

watcher.watchSuite(filePath, testWatchFiles.concat(componentWatchFiles));
const newContent = format(
TEMPLATE,
JSON.stringify(ssrOutput),
componentDef,
componentDefCSR,
testCode,
onlyFileExists ? 'fdescribe' : 'describe',
describe,
JSON.stringify(describeTitle)
);
done(null, newContent);
} catch (error) {
const location = path.relative(basePath, filePath);
log.error('Error processing “%s”\n\n%s\n', location, error.stack || error.message);

done(error, null);
}
};
Expand Down