Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8c7ed79
test(wtr): update tests to use relative path to utils
wjhsf Aug 28, 2025
e2f88d5
test(wtr): revert removing TestUtils
wjhsf Sep 2, 2025
81275d0
test(wtr): remove useless describe
wjhsf Sep 2, 2025
39533d3
test(wtr): replace IIFE script with module import
wjhsf Sep 2, 2025
33e58c8
test(wtr): clean up SSR execution script
wjhsf Sep 2, 2025
d0b8003
test(wtr): always use DISABLE_SYNTHETIC
wjhsf Sep 3, 2025
976c002
test(wtr): enable all hydration tests
wjhsf Sep 3, 2025
8d39505
Merge branch 'master' into wjh/wtr-hyd
wjhsf Sep 4, 2025
c66afad
test(wtr): remove side effect from signals helper
wjhsf Sep 4, 2025
f1c50b0
test(wtr): import directly from file, not from barrel exporter
wjhsf Sep 4, 2025
7a73bf0
test(wtr): change bulk export statement to individual exports
wjhsf Sep 4, 2025
9b7b21b
test(wtr): remove unnecessary aria re-export
wjhsf Sep 4, 2025
9deaca2
test(wtr): fix a few more ARIA util imports
wjhsf Sep 4, 2025
1cf1275
chore: move comment for nicer aesthetic
wjhsf Sep 4, 2025
7fc137c
test(wtr): remove unused option
wjhsf Sep 4, 2025
ae937cc
test(wtr): fix another ARIA util import
wjhsf Sep 4, 2025
9250dd5
test(wtr): import directly from hooks file rather than utils
wjhsf Sep 4, 2025
d4280e8
test(wtr): import directly from signals file rather than utils
wjhsf Sep 4, 2025
bbffbf3
test(wtr): import directly from console helper rather than utils
wjhsf Sep 4, 2025
1ef88ed
test(wtr): import directly from constants helper rather than utils
wjhsf Sep 4, 2025
02f8ed1
test(wtr): only import what is needed from LWC
wjhsf Sep 4, 2025
7a5260a
test(wtr): split lwc:dynamic load helpers into separate file
wjhsf Sep 4, 2025
1a08a62
test(wtr): avoid relying on global LWC
wjhsf Sep 4, 2025
f14c829
Merge branch 'master' into wjh/wtr-smaller-utils
wjhsf Sep 9, 2025
289deeb
Merge branch 'master' into wjh/wtr-smaller-utils
wjhsf Sep 9, 2025
b447a90
Merge branch 'master' into wjh/wtr-smaller-utils
wjhsf Sep 11, 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
20 changes: 19 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,25 @@ export default tseslint.config(
},
},
{
files: ['packages/@lwc/integration-karma/**', 'packages/@lwc/integration-not-karma/**'],
files: ['packages/@lwc/integration-not-karma/**'],

languageOptions: {
globals: {
lwcRuntimeFlags: true,
process: true,
TestUtils: true,
...globals.browser,
...globals.jasmine,
},
},

rules: {
'no-var': 'off',
'prefer-rest-params': 'off',
},
},
{
files: ['packages/@lwc/integration-karma/**'],

languageOptions: {
globals: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'node:path';
import vm from 'node:vm';
import fs from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { rollup } from 'rollup';
import lwcRollupPlugin from '@lwc/rollup-plugin';
import { DISABLE_STATIC_CONTENT_OPTIMIZATION, ENGINE_SERVER } from '../../helpers/options.js';
Expand Down Expand Up @@ -39,7 +40,7 @@ async function getCompiledModule(dir, compileForSSR) {
targetSSR: !!compileForSSR,
modules: [{ dir: path.join(ROOT_DIR, dir) }],
experimentalDynamicComponent: {
loader: 'test-utils',
loader: fileURLToPath(new URL('../../helpers/loader.js', import.meta.url)),
strict: true,
},
enableDynamicComponents: true,
Expand Down Expand Up @@ -198,6 +199,7 @@ async function wrapHydrationTest(filePath) {

// FIXME: can we turn these IIFEs into ESM imports?
return `
import * as LWC from 'lwc';
import { runTest } from '/helpers/test-hydrate.js';
import config from '/${filePath}?original=1';
${onlyFileExists ? 'it.only' : 'it'}('${filePath}', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import {
DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER,
} from '../../helpers/options.js';

const UTILS = fileURLToPath(new URL('../../helpers/utils.js', import.meta.url));

/** Cache reused between each compilation to speed up the compilation time. */
let cache;

Expand All @@ -25,7 +23,7 @@ const createRollupPlugin = (input, options) => {
// Sourcemaps don't work with Istanbul coverage
sourcemap: !process.env.COVERAGE,
experimentalDynamicComponent: {
loader: UTILS,
loader: fileURLToPath(new URL('../../helpers/dynamic-loader', import.meta.url)),
strict: true,
},
enableDynamicComponents: true,
Expand Down Expand Up @@ -88,7 +86,16 @@ const transform = async (ctx) => {

// Rollup should not attempt to resolve the engine and the test utils, Karma takes care of injecting it
// globally in the page before running the tests.
external: ['lwc', 'wire-service', '@test/loader', UTILS],
external: [
'lwc',
'wire-service',
'@test/loader',
// Some helper files export functions that mutate a global state. The setup file calls
// some of those functions and does not get bundled. Including the helper files in the
// bundle would create a separate global state, causing tests to fail. We don't need to
// mark _all_ helpers as external, but we do anyway for ease of maintenance.
/\/helpers\/\w+\.js$/,
],

onwarn(warning, warn) {
// Ignore warnings from our own Rollup plugin
Expand Down
4 changes: 2 additions & 2 deletions packages/@lwc/integration-not-karma/helpers/constants.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { API_VERSION } from './options.js';

// These values are based on the API versions in @lwc/shared/api-version
export const LOWERCASE_SCOPE_TOKENS = API_VERSION >= 59,
export const // These values are based on the API versions in @lwc/shared/api-version
LOWERCASE_SCOPE_TOKENS = API_VERSION >= 59,
USE_COMMENTS_FOR_FRAGMENT_BOOKENDS = API_VERSION >= 60,
USE_FRAGMENTS_FOR_LIGHT_DOM_SLOTS = API_VERSION >= 60,
DISABLE_OBJECT_REST_SPREAD_TRANSFORMATION = API_VERSION >= 60,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Helpers for testing lwc:dynamic
const register = new Map();
/**
* Called by compiled components to, well, load another component. The path to this file is
* specified by the `experimentalDynamicComponent.loader` rollup plugin option.
*/
export const load = async (id) => await Promise.resolve(register.get(id));
export const registerForLoad = (name, Ctor) => register.set(name, Ctor);
export const clearRegister = () => register.clear();
2 changes: 0 additions & 2 deletions packages/@lwc/integration-not-karma/helpers/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ export const API_VERSION = process.env.API_VERSION

export const NODE_ENV_FOR_TEST = process.env.NODE_ENV_FOR_TEST || 'development';

export const GREP = process.env.GREP;

export const NATIVE_SHADOW = DISABLE_SYNTHETIC || FORCE_NATIVE_SHADOW_MODE_FOR_TEST;

/** Unique directory name that encodes the flags that the tests were executed with. */
Expand Down
6 changes: 3 additions & 3 deletions packages/@lwc/integration-not-karma/helpers/setup.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// This import ensures that the global `Mocha` object is present for mutation.
import { JestAsymmetricMatchers, JestChaiExpect, JestExtend } from '@vitest/expect';
import * as chai from 'chai';
import * as LWC from 'lwc';
import { spyOn, fn } from '@vitest/spy';
import { registerCustomMatchers } from './matchers/index.js';
import * as TestUtils from './utils.js';
import { initSignals } from './signals.js';

initSignals();

// FIXME: As a relic of the Karma tests, some test files rely on the global object,
// rather than importing from `test-utils`.
Expand Down Expand Up @@ -47,8 +49,6 @@ function jasmineSpyAdapter(spy) {

// expose so we don't need to import `expect` in every test file
globalThis.expect = chai.expect;
// Expose globals for karma compat
globalThis.LWC = LWC;
globalThis.spyOn = (object, prop) => jasmineSpyAdapter(spyOn(object, prop));
globalThis.jasmine = {
any: expect.any,
Expand Down
5 changes: 4 additions & 1 deletion packages/@lwc/integration-not-karma/helpers/signals.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { setTrustedSignalSet } from 'lwc';

const signalValidator = new WeakSet();
setTrustedSignalSet(signalValidator);

export function initSignals() {
setTrustedSignalSet(signalValidator);
}

export function addTrustedSignal(signal) {
signalValidator.add(signal);
Expand Down
104 changes: 16 additions & 88 deletions packages/@lwc/integration-not-karma/helpers/utils.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,7 @@
/*
* An as yet uncategorized mishmash of helpers, relics of Karma
*/
import * as LWC from 'lwc';
import {
ariaAttributes,
ariaProperties,
ariaPropertiesMapping,
nonPolyfilledAriaProperties,
nonStandardAriaProperties,
} from './aria.js';
import { setHooks, getHooks } from './hooks.js';
import { spyConsole } from './console.js';
import {
DISABLE_OBJECT_REST_SPREAD_TRANSFORMATION,
ENABLE_ELEMENT_INTERNALS_AND_FACE,
ENABLE_THIS_DOT_HOST_ELEMENT,
ENABLE_THIS_DOT_STYLE,
IS_SYNTHETIC_SHADOW_LOADED,
LOWERCASE_SCOPE_TOKENS,
TEMPLATE_CLASS_NAME_OBJECT_BINDING,
USE_COMMENTS_FOR_FRAGMENT_BOOKENDS,
USE_FRAGMENTS_FOR_LIGHT_DOM_SLOTS,
USE_LIGHT_DOM_SLOT_FORWARDING,
} from './constants.js';
import { addTrustedSignal } from './signals.js';
import { __unstable__ReportingControl } from 'lwc';

// Listen for errors thrown directly by the callback
function directErrorListener(callback) {
Expand Down Expand Up @@ -63,7 +41,7 @@ function windowErrorListener(callback) {
// 2) We're using native lifecycle callbacks, so the error is thrown asynchronously and can
// only be caught with window.addEventListener('error')
// - Note native lifecycle callbacks are all thrown asynchronously.
function customElementCallbackReactionErrorListener(callback) {
export function customElementCallbackReactionErrorListener(callback) {
return lwcRuntimeFlags.DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE
? directErrorListener(callback)
: windowErrorListener(callback);
Expand All @@ -74,19 +52,19 @@ function customElementCallbackReactionErrorListener(callback) {
* @param dispatcher
* @param runtimeEvents List of runtime events to filter by. If no list is provided, all events will be dispatched.
*/
function attachReportingControlDispatcher(dispatcher, runtimeEvents) {
LWC.__unstable__ReportingControl.attachDispatcher((eventName, payload) => {
export function attachReportingControlDispatcher(dispatcher, runtimeEvents) {
__unstable__ReportingControl.attachDispatcher((eventName, payload) => {
if (!runtimeEvents || runtimeEvents.includes(eventName)) {
dispatcher(eventName, payload);
}
});
}

function detachReportingControlDispatcher() {
LWC.__unstable__ReportingControl.detachDispatcher();
export function detachReportingControlDispatcher() {
__unstable__ReportingControl.detachDispatcher();
}

function extractDataIds(root) {
export function extractDataIds(root) {
const nodes = {};

function processElement(elm) {
Expand Down Expand Up @@ -120,7 +98,7 @@ function extractDataIds(root) {
return nodes;
}

function extractShadowDataIds(shadowRoot) {
export function extractShadowDataIds(shadowRoot) {
const nodes = {};

// Add the shadow root here even if they don't have [data-id] attributes. This reference is
Expand All @@ -140,36 +118,24 @@ function extractShadowDataIds(shadowRoot) {
return nodes;
}

let register = {};
function load(id) {
return Promise.resolve(register[id]);
}

function registerForLoad(name, Ctor) {
register[name] = Ctor;
}
function clearRegister() {
register = {};
}

// #986 - childNodes on the host element returns a fake shadow comment node on IE11 for debugging purposes. This method
// filters this node.
function getHostChildNodes(host) {
export function getHostChildNodes(host) {
return Array.prototype.slice.call(host.childNodes).filter(function (n) {
return !(n.nodeType === Node.COMMENT_NODE && n.tagName.startsWith('#shadow-root'));
});
}

function isSyntheticShadowRootInstance(sr) {
export function isSyntheticShadowRootInstance(sr) {
return Boolean(sr && sr.synthetic);
}

function isNativeShadowRootInstance(sr) {
export function isNativeShadowRootInstance(sr) {
return Boolean(sr && !sr.synthetic);
}

// Keep traversing up the prototype chain until a property descriptor is found
function getPropertyDescriptor(object, prop) {
export function getPropertyDescriptor(object, prop) {
do {
const descriptor = Object.getOwnPropertyDescriptor(object, prop);
if (descriptor) {
Expand Down Expand Up @@ -216,13 +182,13 @@ function stringifyArg(arg) {
}
}

const expectConsoleCalls = createExpectConsoleCallsFunc(false);
const expectConsoleCallsDev = createExpectConsoleCallsFunc(true);
export const expectConsoleCalls = createExpectConsoleCallsFunc(false);
export const expectConsoleCallsDev = createExpectConsoleCallsFunc(true);

// Utility to handle unhandled rejections or errors without allowing Jasmine to handle them first.
// Captures both onunhandledrejection and onerror events, since you might want both depending on
// native vs synthetic lifecycle timing differences.
function catchUnhandledRejectionsAndErrors(onUnhandledRejectionOrError) {
export function catchUnhandledRejectionsAndErrors(onUnhandledRejectionOrError) {
let originalOnError;

const onError = (e) => {
Expand Down Expand Up @@ -257,7 +223,7 @@ function catchUnhandledRejectionsAndErrors(onUnhandledRejectionOrError) {

// Succeeds if the given DOM element is equivalent to the given HTML in terms of nodes and elements. This is
// basically the same as `expect(element.outerHTML).toBe(html)` except that it works despite bugs in synthetic shadow.
function expectEquivalentDOM(element, html) {
export function expectEquivalentDOM(element, html) {
const fragment = Document.parseHTMLUnsafe(html);

// When the fragment is parsed, the string "abc" is considered one text node. Whereas the engine
Expand Down Expand Up @@ -314,41 +280,3 @@ function expectEquivalentDOM(element, html) {

expectEquivalent(element, fragment.body.firstChild);
}

export {
clearRegister,
extractDataIds,
extractShadowDataIds,
getHostChildNodes,
isNativeShadowRootInstance,
isSyntheticShadowRootInstance,
load,
registerForLoad,
getHooks,
setHooks,
spyConsole,
customElementCallbackReactionErrorListener,
ariaPropertiesMapping,
ariaProperties,
ariaAttributes,
nonStandardAriaProperties,
nonPolyfilledAriaProperties,
getPropertyDescriptor,
attachReportingControlDispatcher,
detachReportingControlDispatcher,
IS_SYNTHETIC_SHADOW_LOADED,
expectConsoleCalls,
expectConsoleCallsDev,
catchUnhandledRejectionsAndErrors,
addTrustedSignal,
expectEquivalentDOM,
LOWERCASE_SCOPE_TOKENS,
USE_COMMENTS_FOR_FRAGMENT_BOOKENDS,
USE_FRAGMENTS_FOR_LIGHT_DOM_SLOTS,
DISABLE_OBJECT_REST_SPREAD_TRANSFORMATION,
ENABLE_ELEMENT_INTERNALS_AND_FACE,
USE_LIGHT_DOM_SLOT_FORWARDING,
ENABLE_THIS_DOT_HOST_ELEMENT,
ENABLE_THIS_DOT_STYLE,
TEMPLATE_CLASS_NAME_OBJECT_BINDING,
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { createElement } from 'lwc';
import Light from 'x/light';
import Shadow from 'x/shadow';
import { nonStandardAriaProperties } from '../../../helpers/aria.js';
import {
attachReportingControlDispatcher,
detachReportingControlDispatcher,
nonStandardAriaProperties,
} from '../../../helpers/utils.js';

// This test only works if the ARIA reflection polyfill is loaded
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import AttrChanged from 'x/attrChanged';
import ReflectCamel from 'x/reflectCamel';
import WithChildElmsHasSlot from 'x/withChildElmsHasSlot';
import WithChildElmsHasSlotLight from 'x/withChildElmsHasSlotLight';
import { spyConsole } from '../../../helpers/utils.js';
import { USE_COMMENTS_FOR_FRAGMENT_BOOKENDS } from '../../../helpers/utils.js';
import { spyConsole } from '../../../helpers/console.js';
import { USE_COMMENTS_FOR_FRAGMENT_BOOKENDS } from '../../../helpers/constants.js';

const vFragBookend = USE_COMMENTS_FOR_FRAGMENT_BOOKENDS ? '<!---->' : '';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import PublicPropertiesInheritance from 'x/publicPropertiesInheritance';
import PublicMethodsInheritance from 'x/publicMethodsInheritance';
import PrivateAccessors from 'x/privateAccessors';
import HtmlElementProps from 'x/htmlElementProps';
import { ariaProperties } from '../../../helpers/utils.js';
import { ariaProperties } from '../../../helpers/aria.js';

function testInvalidComponentConstructor(name, ctor) {
it(`should throw for ${name}`, () => {
Expand Down
Loading