From e2c7afecfeb26a26ed859d15c7d5353967619667 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Thu, 25 Sep 2025 22:50:15 -0400 Subject: [PATCH 01/19] test(wtr): run all the flavors and see what happens --- .github/workflows/web-test-runner.yml | 39 ++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 12199e8506..72869e9143 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -51,4 +51,41 @@ jobs: # region: us - run: yarn test - - run: yarn test:hydration + - run: yarn test || true + - run: DISABLE_SYNTHETIC=1 yarn test || true + - run: LEGACY_BROWSERS=1 yarn test || true + - run: FORCE_NATIVE_SHADOW_MODE_FOR_TEST=1 yarn test || true + - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test || true + - run: DISABLE_DETACHED_REHYDRATION=1 yarn test || true + - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_SYNTHETIC=1 yarn test || true + - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test || true + - run: API_VERSION=58 yarn test || true + - run: API_VERSION=58 DISABLE_SYNTHETIC=1 yarn test || true + - run: API_VERSION=59 yarn test || true + - run: API_VERSION=59 DISABLE_SYNTHETIC=1 yarn test || true + - run: API_VERSION=60 yarn test || true + - run: API_VERSION=60 DISABLE_SYNTHETIC=1 yarn test || true + - run: API_VERSION=61 yarn test || true + - run: API_VERSION=61 DISABLE_SYNTHETIC=1 yarn test || true + - run: API_VERSION=62 yarn test || true + - run: API_VERSION=62 DISABLE_SYNTHETIC=1 yarn test || true + - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 DISABLE_SYNTHETIC=1 yarn test || true + - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 DISABLE_SYNTHETIC=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test || true + - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 yarn test || true + - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 DISABLE_SYNTHETIC=1 yarn test || true + - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test || true + - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 DISABLE_SYNTHETIC=1 yarn test || true + - run: NODE_ENV_FOR_TEST=production yarn test || true + - run: NODE_ENV_FOR_TEST=production DISABLE_SYNTHETIC=1 yarn test || true + - run: ENGINE_SERVER=1 yarn test:hydration || true + - run: ENGINE_SERVER=1 ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true + - run: ENGINE_SERVER=1 NODE_ENV_FOR_TEST=production yarn test:hydration || true + - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true + - run: ENGINE_SERVER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true + - run: ENGINE_SERVER=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true + - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true + - run: yarn test:hydration || true + - run: ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true + - run: NODE_ENV_FOR_TEST=production yarn test:hydration || true + - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true + - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true From 0c81acb6f9d3e8072a6448c79268925e9c22ebfc Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 11:20:30 -0400 Subject: [PATCH 02/19] test(wtr): prefer DISABLE_SYNTHETIC over duplicate env var name --- .github/workflows/web-test-runner.yml | 6 ++++-- packages/@lwc/integration-not-karma/configs/hydration.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 72869e9143..5537d3dfea 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -78,14 +78,16 @@ jobs: - run: NODE_ENV_FOR_TEST=production yarn test || true - run: NODE_ENV_FOR_TEST=production DISABLE_SYNTHETIC=1 yarn test || true - run: ENGINE_SERVER=1 yarn test:hydration || true - - run: ENGINE_SERVER=1 ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true + # DISABLE_SYNTHETIC is enabled by default in hydration tests, so the inverse is disabling + - run: ENGINE_SERVER=1 DISABLE_SYNTHETIC='' yarn test:hydration || true - run: ENGINE_SERVER=1 NODE_ENV_FOR_TEST=production yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - run: yarn test:hydration || true - - run: ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true + # DISABLE_SYNTHETIC is enabled by default in hydration tests, so the inverse is disabling + - run: DISABLE_SYNTHETIC='' yarn test:hydration || true - run: NODE_ENV_FOR_TEST=production yarn test:hydration || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true diff --git a/packages/@lwc/integration-not-karma/configs/hydration.js b/packages/@lwc/integration-not-karma/configs/hydration.js index 67a2419850..fd04f124fa 100644 --- a/packages/@lwc/integration-not-karma/configs/hydration.js +++ b/packages/@lwc/integration-not-karma/configs/hydration.js @@ -1,5 +1,5 @@ // Use native shadow by default in hydration tests; MUST be set before imports -process.env.DISABLE_SYNTHETIC ??= 'true'; +process.env.DISABLE_SYNTHETIC ??= '1'; import baseConfig from './base.js'; import hydrationTestPlugin from './plugins/serve-hydration.js'; From 716f7b52318ceb1e5e0957976640fb2284cf117a Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 11:50:43 -0400 Subject: [PATCH 03/19] test(wtr): fix hydration tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit they weren't actually running before 😰 --- .../integration-not-karma/configs/plugins/test-hydration.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js b/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js index 3c752910b0..f72df2e500 100644 --- a/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js +++ b/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js @@ -52,8 +52,8 @@ export function runTest(configPath, componentPath, ssrRendered) { let Component; beforeAll(async () => { - testConfig = await import(configPath); - Component = await import(componentPath); + testConfig = (await import(configPath)).default; + Component = (await import(componentPath)).default; setFeatureFlags(testConfig.requiredFeatureFlags, true); }); From f2934f8e10ea4a593f87a0a1c8055693d1f9c496 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 11:51:26 -0400 Subject: [PATCH 04/19] test(wtr): create `process.env` with only string values --- .../@lwc/integration-not-karma/configs/base.js | 17 +++++++++++++++-- .../Node-properties/Node.hasChildNodes.spec.js | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/@lwc/integration-not-karma/configs/base.js b/packages/@lwc/integration-not-karma/configs/base.js index c220593ff3..c9683d09b0 100644 --- a/packages/@lwc/integration-not-karma/configs/base.js +++ b/packages/@lwc/integration-not-karma/configs/base.js @@ -3,11 +3,24 @@ import { LWC_VERSION } from '@lwc/shared'; import * as options from '../helpers/options.js'; import { resolvePathOutsideRoot } from '../helpers/utils.js'; +/** + * We want to convert from parsed options (true/false) to a `process.env` with only strings. + * This drops `false` values and converts everything else to a string. + */ +const envify = (obj) => { + const clone = {}; + for (const [key, val] of Object.entries(obj)) { + if (val !== false) { + clone[key] = String(val); + } + } + return clone; +}; const pluck = (obj, keys) => Object.fromEntries(keys.map((k) => [k, obj[k]])); const maybeImport = (file, condition) => (condition ? `await import('${file}');` : ''); /** `process.env` to inject into test environment. */ -const env = { +const env = envify({ ...pluck(options, [ 'API_VERSION', 'DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE', @@ -21,7 +34,7 @@ const env = { ]), LWC_VERSION, NODE_ENV: options.NODE_ENV_FOR_TEST, -}; +}); /** @type {import("@web/test-runner").TestRunnerConfig} */ export default { diff --git a/packages/@lwc/integration-not-karma/test/shadow-dom/Node-properties/Node.hasChildNodes.spec.js b/packages/@lwc/integration-not-karma/test/shadow-dom/Node-properties/Node.hasChildNodes.spec.js index 083c32fa68..b9bb212eb4 100644 --- a/packages/@lwc/integration-not-karma/test/shadow-dom/Node-properties/Node.hasChildNodes.spec.js +++ b/packages/@lwc/integration-not-karma/test/shadow-dom/Node-properties/Node.hasChildNodes.spec.js @@ -26,7 +26,7 @@ describe('Node.hasChildNodes', () => { expect(container.shadowRoot.querySelector('.container').hasChildNodes()).toBe(true); expect(container.shadowRoot.querySelector('slot').hasChildNodes()).toBe( - process.env.NATIVE_SHADOW + Boolean(process.env.NATIVE_SHADOW) ); }); }); From 95d3b4bce37306ecf17f56ecd31627d552bbfcf1 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 11:53:08 -0400 Subject: [PATCH 05/19] test(wtr): split integration and hydration tests into separate jobs --- .github/workflows/web-test-runner.yml | 32 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 5537d3dfea..079631376f 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -18,11 +18,9 @@ env: NODE_VERSION: '20.19.4' jobs: - # Starting with the basics, just get tests running in CI - # TODO: add env var combos we use for Karma tests # TODO: upload result artifacts # TODO: make it saucy 🥫 - wtr-group-1: + integration-tests: runs-on: ubuntu-22.04 env: SAUCE_TUNNEL_ID: github-action-tunnel-wtr-${{github.run_id}}-group-1 @@ -50,7 +48,6 @@ jobs: # tunnelName: ${{ env.SAUCE_TUNNEL_ID }} # region: us - - run: yarn test - run: yarn test || true - run: DISABLE_SYNTHETIC=1 yarn test || true - run: LEGACY_BROWSERS=1 yarn test || true @@ -77,6 +74,33 @@ jobs: - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 DISABLE_SYNTHETIC=1 yarn test || true - run: NODE_ENV_FOR_TEST=production yarn test || true - run: NODE_ENV_FOR_TEST=production DISABLE_SYNTHETIC=1 yarn test || true + hydration-tests: + runs-on: ubuntu-22.04 + env: + SAUCE_TUNNEL_ID: github-action-tunnel-wtr-${{github.run_id}}-group-1 + defaults: + run: + working-directory: ./packages/@lwc/integration-not-karma + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + working-directory: ./ + + # - uses: saucelabs/sauce-connect-action@v3.0.0 + # with: + # username: ${{ secrets.SAUCE_USERNAME }} + # accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} + # tunnelName: ${{ env.SAUCE_TUNNEL_ID }} + # region: us - run: ENGINE_SERVER=1 yarn test:hydration || true # DISABLE_SYNTHETIC is enabled by default in hydration tests, so the inverse is disabling - run: ENGINE_SERVER=1 DISABLE_SYNTHETIC='' yarn test:hydration || true From 46c3d560c97ccf854977d0071499e53f5d1f0716 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 12:27:11 -0400 Subject: [PATCH 06/19] Revert "test(wtr): prefer DISABLE_SYNTHETIC over duplicate env var name" This reverts commit 0c81acb6f9d3e8072a6448c79268925e9c22ebfc. --- .github/workflows/web-test-runner.yml | 6 ++---- packages/@lwc/integration-not-karma/configs/hydration.js | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 079631376f..cb5294b3da 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -102,16 +102,14 @@ jobs: # tunnelName: ${{ env.SAUCE_TUNNEL_ID }} # region: us - run: ENGINE_SERVER=1 yarn test:hydration || true - # DISABLE_SYNTHETIC is enabled by default in hydration tests, so the inverse is disabling - - run: ENGINE_SERVER=1 DISABLE_SYNTHETIC='' yarn test:hydration || true + - run: ENGINE_SERVER=1 ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 NODE_ENV_FOR_TEST=production yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - run: yarn test:hydration || true - # DISABLE_SYNTHETIC is enabled by default in hydration tests, so the inverse is disabling - - run: DISABLE_SYNTHETIC='' yarn test:hydration || true + - run: ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true - run: NODE_ENV_FOR_TEST=production yarn test:hydration || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true diff --git a/packages/@lwc/integration-not-karma/configs/hydration.js b/packages/@lwc/integration-not-karma/configs/hydration.js index fd04f124fa..67a2419850 100644 --- a/packages/@lwc/integration-not-karma/configs/hydration.js +++ b/packages/@lwc/integration-not-karma/configs/hydration.js @@ -1,5 +1,5 @@ // Use native shadow by default in hydration tests; MUST be set before imports -process.env.DISABLE_SYNTHETIC ??= '1'; +process.env.DISABLE_SYNTHETIC ??= 'true'; import baseConfig from './base.js'; import hydrationTestPlugin from './plugins/serve-hydration.js'; From 8c15e7178d389595ff99fc58cdcffa479eae1f1b Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 12:28:55 -0400 Subject: [PATCH 07/19] test(wtr): revert to using original env var --- .github/workflows/web-test-runner.yml | 2 +- packages/@lwc/integration-not-karma/configs/base.js | 3 ++- packages/@lwc/integration-not-karma/helpers/options.js | 3 +++ .../test-hydration/synthetic-shadow/index.spec.js | 6 +++--- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index cb5294b3da..23a17968ff 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -102,7 +102,7 @@ jobs: # tunnelName: ${{ env.SAUCE_TUNNEL_ID }} # region: us - run: ENGINE_SERVER=1 yarn test:hydration || true - - run: ENGINE_SERVER=1 ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true + - run: ENGINE_SERVER=1 ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 NODE_ENV_FOR_TEST=production yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true diff --git a/packages/@lwc/integration-not-karma/configs/base.js b/packages/@lwc/integration-not-karma/configs/base.js index c9683d09b0..8a69fa278e 100644 --- a/packages/@lwc/integration-not-karma/configs/base.js +++ b/packages/@lwc/integration-not-karma/configs/base.js @@ -23,14 +23,15 @@ const maybeImport = (file, condition) => (condition ? `await import('${file}');` const env = envify({ ...pluck(options, [ 'API_VERSION', + 'DISABLE_DETACHED_REHYDRATION', 'DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE', 'DISABLE_STATIC_CONTENT_OPTIMIZATION', 'DISABLE_SYNTHETIC', 'ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL', + 'ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION', 'ENGINE_SERVER', 'FORCE_NATIVE_SHADOW_MODE_FOR_TEST', 'NATIVE_SHADOW', - 'DISABLE_DETACHED_REHYDRATION', ]), LWC_VERSION, NODE_ENV: options.NODE_ENV_FOR_TEST, diff --git a/packages/@lwc/integration-not-karma/helpers/options.js b/packages/@lwc/integration-not-karma/helpers/options.js index 7124677ef4..1dc2710169 100644 --- a/packages/@lwc/integration-not-karma/helpers/options.js +++ b/packages/@lwc/integration-not-karma/helpers/options.js @@ -9,6 +9,9 @@ import { HIGHEST_API_VERSION } from '@lwc/shared'; export const LEGACY_BROWSERS = Boolean(process.env.LEGACY_BROWSERS); export const DISABLE_SYNTHETIC = Boolean(process.env.DISABLE_SYNTHETIC); +export const ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION = Boolean( + process.env.ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION +); export const FORCE_NATIVE_SHADOW_MODE_FOR_TEST = Boolean( process.env.FORCE_NATIVE_SHADOW_MODE_FOR_TEST diff --git a/packages/@lwc/integration-not-karma/test-hydration/synthetic-shadow/index.spec.js b/packages/@lwc/integration-not-karma/test-hydration/synthetic-shadow/index.spec.js index 7932c8bf9e..24177ac54f 100644 --- a/packages/@lwc/integration-not-karma/test-hydration/synthetic-shadow/index.spec.js +++ b/packages/@lwc/integration-not-karma/test-hydration/synthetic-shadow/index.spec.js @@ -22,10 +22,10 @@ export default { expect(child.shadowRoot.synthetic).toBeUndefined(); // sanity check that the env var is working - if (process.env.DISABLE_SYNTHETIC) { - expect(document.body.attachShadow.toString()).toContain('[native code'); - } else { + if (process.env.ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION) { expect(document.body.attachShadow.toString()).not.toContain('[native code'); + } else { + expect(document.body.attachShadow.toString()).toContain('[native code'); } expect(consoleCalls.warn).toHaveSize(0); From 373bcf1fb5489d3e67697daf93baa8779634c974 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 13:08:49 -0400 Subject: [PATCH 08/19] test(wtr): make integration and hydration tests have different default shadow modes they were supposed to all along, but it was implemented incorrectly --- .github/workflows/web-test-runner.yml | 28 +++--- .../integration-not-karma/configs/base.js | 92 ++++++++++--------- .../configs/hydration.js | 12 ++- .../configs/integration.js | 10 +- .../integration-not-karma/helpers/options.js | 17 ++-- .../synthetic-shadow/index.spec.js | 6 +- 6 files changed, 91 insertions(+), 74 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 23a17968ff..0810a93221 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -49,31 +49,31 @@ jobs: # region: us - run: yarn test || true - - run: DISABLE_SYNTHETIC=1 yarn test || true + - run: SHADOW_MODE_OVERRIDE=native yarn test || true - run: LEGACY_BROWSERS=1 yarn test || true - run: FORCE_NATIVE_SHADOW_MODE_FOR_TEST=1 yarn test || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test || true - run: DISABLE_DETACHED_REHYDRATION=1 yarn test || true - - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_SYNTHETIC=1 yarn test || true + - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 SHADOW_MODE_OVERRIDE=native yarn test || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test || true - run: API_VERSION=58 yarn test || true - - run: API_VERSION=58 DISABLE_SYNTHETIC=1 yarn test || true + - run: API_VERSION=58 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=59 yarn test || true - - run: API_VERSION=59 DISABLE_SYNTHETIC=1 yarn test || true + - run: API_VERSION=59 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=60 yarn test || true - - run: API_VERSION=60 DISABLE_SYNTHETIC=1 yarn test || true + - run: API_VERSION=60 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=61 yarn test || true - - run: API_VERSION=61 DISABLE_SYNTHETIC=1 yarn test || true + - run: API_VERSION=61 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=62 yarn test || true - - run: API_VERSION=62 DISABLE_SYNTHETIC=1 yarn test || true - - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 DISABLE_SYNTHETIC=1 yarn test || true - - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 DISABLE_SYNTHETIC=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test || true + - run: API_VERSION=62 SHADOW_MODE_OVERRIDE=native yarn test || true + - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 SHADOW_MODE_OVERRIDE=native yarn test || true + - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 SHADOW_MODE_OVERRIDE=native DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test || true - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 yarn test || true - - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 DISABLE_SYNTHETIC=1 yarn test || true + - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 SHADOW_MODE_OVERRIDE=native yarn test || true - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test || true - - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 DISABLE_SYNTHETIC=1 yarn test || true + - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 SHADOW_MODE_OVERRIDE=native yarn test || true - run: NODE_ENV_FOR_TEST=production yarn test || true - - run: NODE_ENV_FOR_TEST=production DISABLE_SYNTHETIC=1 yarn test || true + - run: NODE_ENV_FOR_TEST=production SHADOW_MODE_OVERRIDE=native yarn test || true hydration-tests: runs-on: ubuntu-22.04 env: @@ -102,14 +102,14 @@ jobs: # tunnelName: ${{ env.SAUCE_TUNNEL_ID }} # region: us - run: ENGINE_SERVER=1 yarn test:hydration || true - - run: ENGINE_SERVER=1 ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true + - run: ENGINE_SERVER=1 SHADOW_MODE_OVERRIDE=synthetic yarn test:hydration || true - run: ENGINE_SERVER=1 NODE_ENV_FOR_TEST=production yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - run: yarn test:hydration || true - - run: ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION=1 yarn test:hydration || true + - run: SHADOW_MODE_OVERRIDE=synthetic yarn test:hydration || true - run: NODE_ENV_FOR_TEST=production yarn test:hydration || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true diff --git a/packages/@lwc/integration-not-karma/configs/base.js b/packages/@lwc/integration-not-karma/configs/base.js index 8a69fa278e..cae6f5336e 100644 --- a/packages/@lwc/integration-not-karma/configs/base.js +++ b/packages/@lwc/integration-not-karma/configs/base.js @@ -1,6 +1,5 @@ import { join } from 'node:path'; import { LWC_VERSION } from '@lwc/shared'; -import * as options from '../helpers/options.js'; import { resolvePathOutsideRoot } from '../helpers/utils.js'; /** @@ -19,51 +18,53 @@ const envify = (obj) => { const pluck = (obj, keys) => Object.fromEntries(keys.map((k) => [k, obj[k]])); const maybeImport = (file, condition) => (condition ? `await import('${file}');` : ''); -/** `process.env` to inject into test environment. */ -const env = envify({ - ...pluck(options, [ - 'API_VERSION', - 'DISABLE_DETACHED_REHYDRATION', - 'DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE', - 'DISABLE_STATIC_CONTENT_OPTIMIZATION', - 'DISABLE_SYNTHETIC', - 'ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL', - 'ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION', - 'ENGINE_SERVER', - 'FORCE_NATIVE_SHADOW_MODE_FOR_TEST', - 'NATIVE_SHADOW', - ]), - LWC_VERSION, - NODE_ENV: options.NODE_ENV_FOR_TEST, -}); +/** @type {() => import("@web/test-runner").TestRunnerConfig} */ +export default (options) => { + /** `process.env` to inject into test environment. */ + const env = envify({ + ...pluck(options, [ + 'API_VERSION', + 'DISABLE_DETACHED_REHYDRATION', + 'DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE', + 'DISABLE_STATIC_CONTENT_OPTIMIZATION', + 'ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL', + 'ENGINE_SERVER', + 'FORCE_NATIVE_SHADOW_MODE_FOR_TEST', + 'NATIVE_SHADOW', + ]), + LWC_VERSION, + NODE_ENV: options.NODE_ENV_FOR_TEST, + }); -/** @type {import("@web/test-runner").TestRunnerConfig} */ -export default { - // FIXME: Parallelism breaks tests that rely on focus/requestAnimationFrame, because they often - // time out before they receive focus. But it also makes the full suite take 3x longer to run... - // Potential workaround: https://github.com/modernweb-dev/web/issues/2588 - concurrency: 1, - browserLogs: false, - nodeResolve: true, - rootDir: join(import.meta.dirname, '..'), - plugins: [ - { - name: 'lwc-base-plugin', - resolveImport({ source }) { - if (source === 'wire-service') { - return resolvePathOutsideRoot('../wire-service/dist/index.js'); - } + return { + // FIXME: Parallelism breaks tests that rely on focus/requestAnimationFrame, because they often + // time out before they receive focus. But it also makes the full suite take 3x longer to run... + // Potential workaround: https://github.com/modernweb-dev/web/issues/2588 + concurrency: 1, + browserLogs: false, + nodeResolve: true, + rootDir: join(import.meta.dirname, '..'), + plugins: [ + { + name: 'lwc-base-plugin', + resolveImport({ source }) { + if (source === 'wire-service') { + return resolvePathOutsideRoot('../wire-service/dist/index.js'); + } + }, + async transform(ctx) { + if (ctx.type === 'application/javascript') { + // FIXME: copy/paste Nolan's spiel about why we do this ugly thing + return ctx.body.replace( + /process\.env\.NODE_ENV === 'test-karma-lwc'/g, + 'true' + ); + } + }, }, - async transform(ctx) { - if (ctx.type === 'application/javascript') { - // FIXME: copy/paste Nolan's spiel about why we do this ugly thing - return ctx.body.replace(/process\.env\.NODE_ENV === 'test-karma-lwc'/g, 'true'); - } - }, - }, - ], - testRunnerHtml: (testFramework) => - ` + ], + testRunnerHtml: (testFramework) => + ` `, + }; }; diff --git a/packages/@lwc/integration-not-karma/configs/hydration.js b/packages/@lwc/integration-not-karma/configs/hydration.js index 67a2419850..1b085ad761 100644 --- a/packages/@lwc/integration-not-karma/configs/hydration.js +++ b/packages/@lwc/integration-not-karma/configs/hydration.js @@ -1,8 +1,14 @@ -// Use native shadow by default in hydration tests; MUST be set before imports -process.env.DISABLE_SYNTHETIC ??= 'true'; -import baseConfig from './base.js'; +import * as options from '../helpers/options.js'; +import createConfig from './base.js'; import hydrationTestPlugin from './plugins/serve-hydration.js'; +const SHADOW_MODE = options.SHADOW_MODE_OVERRIDE ?? 'native'; + +const baseConfig = createConfig({ + ...options, + NATIVE_SHADOW: SHADOW_MODE === 'native' || options.FORCE_NATIVE_SHADOW_MODE_FOR_TEST, +}); + /** @type {import("@web/test-runner").TestRunnerConfig} */ export default { ...baseConfig, diff --git a/packages/@lwc/integration-not-karma/configs/integration.js b/packages/@lwc/integration-not-karma/configs/integration.js index 60cb5e4424..cc27ff6bea 100644 --- a/packages/@lwc/integration-not-karma/configs/integration.js +++ b/packages/@lwc/integration-not-karma/configs/integration.js @@ -1,7 +1,15 @@ import { importMapsPlugin } from '@web/dev-server-import-maps'; -import baseConfig from './base.js'; +import * as options from '../helpers/options.js'; +import createConfig from './base.js'; import testPlugin from './plugins/serve-integration.js'; +const SHADOW_MODE = options.SHADOW_MODE_OVERRIDE ?? 'synthetic'; + +const baseConfig = createConfig({ + ...options, + NATIVE_SHADOW: SHADOW_MODE === 'native' || options.FORCE_NATIVE_SHADOW_MODE_FOR_TEST, +}); + /** @type {import("@web/test-runner").TestRunnerConfig} */ export default { ...baseConfig, diff --git a/packages/@lwc/integration-not-karma/helpers/options.js b/packages/@lwc/integration-not-karma/helpers/options.js index 1dc2710169..51a9dd99f8 100644 --- a/packages/@lwc/integration-not-karma/helpers/options.js +++ b/packages/@lwc/integration-not-karma/helpers/options.js @@ -8,11 +8,6 @@ import { HIGHEST_API_VERSION } from '@lwc/shared'; export const LEGACY_BROWSERS = Boolean(process.env.LEGACY_BROWSERS); -export const DISABLE_SYNTHETIC = Boolean(process.env.DISABLE_SYNTHETIC); -export const ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION = Boolean( - process.env.ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION -); - export const FORCE_NATIVE_SHADOW_MODE_FOR_TEST = Boolean( process.env.FORCE_NATIVE_SHADOW_MODE_FOR_TEST ); @@ -39,20 +34,26 @@ export const ENGINE_SERVER = Boolean(process.env.ENGINE_SERVER); // --- Test config --- // +/** + * Integration tests default to synthetic shadow mode, while hydration tests default to native. + * This should be set to "native" or "synthetic" to override the default mode. + * @type {'native'|'synthetic'|undefined} + */ +// NOTE: NATIVE_SHADOW is not defined here because integration/hydration have different defaults +export const SHADOW_MODE_OVERRIDE = process.env.SHADOW_MODE_OVERRIDE; + export const API_VERSION = process.env.API_VERSION ? parseInt(process.env.API_VERSION, 10) : HIGHEST_API_VERSION; export const NODE_ENV_FOR_TEST = process.env.NODE_ENV_FOR_TEST || 'development'; -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. */ export const COVERAGE_DIR_FOR_OPTIONS = Object.entries({ API_VERSION, DISABLE_STATIC_CONTENT_OPTIMIZATION, - DISABLE_SYNTHETIC, + SHADOW_MODE_OVERRIDE, DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER, ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL, FORCE_NATIVE_SHADOW_MODE_FOR_TEST, diff --git a/packages/@lwc/integration-not-karma/test-hydration/synthetic-shadow/index.spec.js b/packages/@lwc/integration-not-karma/test-hydration/synthetic-shadow/index.spec.js index 24177ac54f..04e76568a0 100644 --- a/packages/@lwc/integration-not-karma/test-hydration/synthetic-shadow/index.spec.js +++ b/packages/@lwc/integration-not-karma/test-hydration/synthetic-shadow/index.spec.js @@ -22,10 +22,10 @@ export default { expect(child.shadowRoot.synthetic).toBeUndefined(); // sanity check that the env var is working - if (process.env.ENABLE_SYNTHETIC_SHADOW_IN_HYDRATION) { - expect(document.body.attachShadow.toString()).not.toContain('[native code'); - } else { + if (process.env.NATIVE_SHADOW) { expect(document.body.attachShadow.toString()).toContain('[native code'); + } else { + expect(document.body.attachShadow.toString()).not.toContain('[native code'); } expect(consoleCalls.warn).toHaveSize(0); From 0526e491ed2580e1554f782f7d6244bcffcb722e Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 14:32:41 -0400 Subject: [PATCH 09/19] test(wtr): fix tests running in native shadow mode --- .../elementInternals/sanity/index.spec.js | 4 -- .../force-shadow-migrate-mode/index.spec.js | 41 ++++++++----------- .../multiple-templates/index.spec.js | 2 + 3 files changed, 18 insertions(+), 29 deletions(-) diff --git a/packages/@lwc/integration-not-karma/test/component/LightningElement.attachInternals/elementInternals/sanity/index.spec.js b/packages/@lwc/integration-not-karma/test/component/LightningElement.attachInternals/elementInternals/sanity/index.spec.js index 8bf34045ab..71caf8af55 100644 --- a/packages/@lwc/integration-not-karma/test/component/LightningElement.attachInternals/elementInternals/sanity/index.spec.js +++ b/packages/@lwc/integration-not-karma/test/component/LightningElement.attachInternals/elementInternals/sanity/index.spec.js @@ -9,10 +9,6 @@ beforeEach(() => { document.body.appendChild(elm); }); -afterEach(() => { - document.body.removeChild(elm); -}); - describe.runIf( ENABLE_ELEMENT_INTERNALS_AND_FACE && process.env.NATIVE_SHADOW && diff --git a/packages/@lwc/integration-not-karma/test/native-shadow/force-shadow-migrate-mode/index.spec.js b/packages/@lwc/integration-not-karma/test/native-shadow/force-shadow-migrate-mode/index.spec.js index c1afb704e7..f494070a43 100644 --- a/packages/@lwc/integration-not-karma/test/native-shadow/force-shadow-migrate-mode/index.spec.js +++ b/packages/@lwc/integration-not-karma/test/native-shadow/force-shadow-migrate-mode/index.spec.js @@ -30,8 +30,9 @@ describe.runIf(process.env.NATIVE_SHADOW && !process.env.FORCE_NATIVE_SHADOW_MOD }); describe('flag on', () => { - beforeEach(() => { + beforeEach(async () => { setFeatureFlagForTest('ENABLE_FORCE_SHADOW_MIGRATE_MODE', true); + await Promise.resolve(); }); afterEach(() => { @@ -59,9 +60,8 @@ describe.runIf(process.env.NATIVE_SHADOW && !process.env.FORCE_NATIVE_SHADOW_MOD expect(isActuallyNativeShadow(elm.shadowRoot)).toBe(true); expect(isClaimingToBeSyntheticShadow(elm.shadowRoot)).toBe(false); - expect(getComputedStyle(elm.shadowRoot.querySelector('h1')).color).toBe( - 'rgb(0, 0, 0)' - ); + const computed = getComputedStyle(elm.shadowRoot.querySelector('h1')); + expect(computed.color).toBe('rgb(0, 0, 0)'); }); it('does not apply styles from global light DOM components to synthetic components', async () => { @@ -83,9 +83,8 @@ describe.runIf(process.env.NATIVE_SHADOW && !process.env.FORCE_NATIVE_SHADOW_MOD await doubleMicrotask(); - expect(getComputedStyle(elm.shadowRoot.querySelector('h1')).color).toBe( - 'rgb(0, 0, 255)' - ); + let computed = getComputedStyle(elm.shadowRoot.querySelector('h1')); + expect(computed.color).toBe('rgb(0, 0, 255)'); const style = document.createElement('style'); style.textContent = `h1 { color: purple; background-color: crimson }`; @@ -93,12 +92,9 @@ describe.runIf(process.env.NATIVE_SHADOW && !process.env.FORCE_NATIVE_SHADOW_MOD await doubleMicrotask(); - expect(getComputedStyle(elm.shadowRoot.querySelector('h1')).color).toBe( - 'rgb(128, 0, 128)' - ); - expect(getComputedStyle(elm.shadowRoot.querySelector('h1')).backgroundColor).toBe( - 'rgb(220, 20, 60)' - ); + computed = getComputedStyle(elm.shadowRoot.querySelector('h1')); + expect(computed.color).toBe('rgb(128, 0, 128)'); + expect(computed.backgroundColor).toBe('rgb(220, 20, 60)'); }); it('local styles are defined after global styles', async () => { @@ -111,12 +107,9 @@ describe.runIf(process.env.NATIVE_SHADOW && !process.env.FORCE_NATIVE_SHADOW_MOD await doubleMicrotask(); - expect(getComputedStyle(elm.shadowRoot.querySelector('h1')).backgroundColor).toBe( - 'rgb(0, 128, 0)' - ); - expect(getComputedStyle(elm.shadowRoot.querySelector('h1')).fontFamily).toBe( - 'monospace' - ); + const computed = getComputedStyle(elm.shadowRoot.querySelector('h1')); + expect(computed.backgroundColor).toBe('rgb(0, 128, 0)'); + expect(computed.fontFamily).toBe('monospace'); }); }); @@ -128,9 +121,8 @@ describe.runIf(process.env.NATIVE_SHADOW && !process.env.FORCE_NATIVE_SHADOW_MOD await doubleMicrotask(); expect(isActuallyNativeShadow(elm.shadowRoot)).toBe(true); expect(isClaimingToBeSyntheticShadow(elm.shadowRoot)).toBe(false); - expect(getComputedStyle(elm.shadowRoot.querySelector('h1')).color).toBe( - 'rgb(0, 0, 0)' - ); + const computed = getComputedStyle(elm.shadowRoot.querySelector('h1')); + expect(computed.color).toBe('rgb(0, 0, 0)'); }); it('does not use global styles for native components', async () => { @@ -140,9 +132,8 @@ describe.runIf(process.env.NATIVE_SHADOW && !process.env.FORCE_NATIVE_SHADOW_MOD await doubleMicrotask(); expect(isActuallyNativeShadow(elm.shadowRoot)).toBe(true); expect(isClaimingToBeSyntheticShadow(elm.shadowRoot)).toBe(false); - expect(getComputedStyle(elm.shadowRoot.querySelector('h1')).color).toBe( - 'rgb(0, 0, 0)' - ); + const computed = getComputedStyle(elm.shadowRoot.querySelector('h1')); + expect(computed.color).toBe('rgb(0, 0, 0)'); }); }); } diff --git a/packages/@lwc/integration-not-karma/test/shadow-dom/multiple-templates/index.spec.js b/packages/@lwc/integration-not-karma/test/shadow-dom/multiple-templates/index.spec.js index 499eb919df..aea8616097 100644 --- a/packages/@lwc/integration-not-karma/test/shadow-dom/multiple-templates/index.spec.js +++ b/packages/@lwc/integration-not-karma/test/shadow-dom/multiple-templates/index.spec.js @@ -24,11 +24,13 @@ describe.runIf(process.env.NATIVE_SHADOW)( ); expect(getNumStyleSheets()).toEqual(1); element.next(); + await Promise.resolve(); expect(getComputedStyle(element.shadowRoot.querySelector('div')).color).toEqual( 'rgb(255, 0, 0)' ); expect(getNumStyleSheets()).toEqual(2); element.next(); + await Promise.resolve(); expect(getComputedStyle(element.shadowRoot.querySelector('div')).color).toEqual( 'rgb(0, 0, 255)' ); From 5dbe1e91e52fc091b002222d921ae94df49dc2fd Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 14:33:55 -0400 Subject: [PATCH 10/19] test(wtr): make working tests fail for real in CI if they break --- .github/workflows/web-test-runner.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 0810a93221..29d4903bda 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -48,8 +48,8 @@ jobs: # tunnelName: ${{ env.SAUCE_TUNNEL_ID }} # region: us - - run: yarn test || true - - run: SHADOW_MODE_OVERRIDE=native yarn test || true + - run: yarn test + - run: SHADOW_MODE_OVERRIDE=native yarn test - run: LEGACY_BROWSERS=1 yarn test || true - run: FORCE_NATIVE_SHADOW_MODE_FOR_TEST=1 yarn test || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test || true @@ -108,8 +108,8 @@ jobs: - run: ENGINE_SERVER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - - run: yarn test:hydration || true - - run: SHADOW_MODE_OVERRIDE=synthetic yarn test:hydration || true + - run: yarn test:hydration + - run: SHADOW_MODE_OVERRIDE=synthetic yarn test:hydration - run: NODE_ENV_FOR_TEST=production yarn test:hydration || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true From 0734767eb3ab0295a888f233aa9c9c40b7dd54d5 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 14:36:23 -0400 Subject: [PATCH 11/19] test(wtr): avoid accidentally not running tests again --- .../integration-not-karma/configs/plugins/test-hydration.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js b/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js index f72df2e500..3663a72972 100644 --- a/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js +++ b/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js @@ -93,6 +93,8 @@ export function runTest(configPath, componentPath, ssrRendered) { container, selector, }); + } else { + throw new Error(`Missing test or advancedTest function in ${configPath}.`); } }); } From 39920e1938a96bc1ea84529633696db08d551ca2 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 14:52:29 -0400 Subject: [PATCH 12/19] test(wtr): enable FORCE_NATIVE_SHADOW_MODE_FOR_TEST in CI --- .github/workflows/web-test-runner.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 29d4903bda..6cafa77ba9 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -51,7 +51,7 @@ jobs: - run: yarn test - run: SHADOW_MODE_OVERRIDE=native yarn test - run: LEGACY_BROWSERS=1 yarn test || true - - run: FORCE_NATIVE_SHADOW_MODE_FOR_TEST=1 yarn test || true + - run: FORCE_NATIVE_SHADOW_MODE_FOR_TEST=1 yarn test - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test || true - run: DISABLE_DETACHED_REHYDRATION=1 yarn test || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 SHADOW_MODE_OVERRIDE=native yarn test || true From ee9bc59393fc91a833bab67d530f5be725019b7f Mon Sep 17 00:00:00 2001 From: Will Harney Date: Fri, 26 Sep 2025 14:58:59 -0400 Subject: [PATCH 13/19] test(wtr): enable DISABLE_DETACHED_REHYDRATION=1 in CI --- .github/workflows/web-test-runner.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 6cafa77ba9..8f431d0f3a 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -53,8 +53,8 @@ jobs: - run: LEGACY_BROWSERS=1 yarn test || true - run: FORCE_NATIVE_SHADOW_MODE_FOR_TEST=1 yarn test - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test || true - - run: DISABLE_DETACHED_REHYDRATION=1 yarn test || true - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 SHADOW_MODE_OVERRIDE=native yarn test || true + - run: DISABLE_DETACHED_REHYDRATION=1 yarn test - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test || true - run: API_VERSION=58 yarn test || true - run: API_VERSION=58 SHADOW_MODE_OVERRIDE=native yarn test || true From ca7f8eea283a755fd9190b5256f23ca4df07c91c Mon Sep 17 00:00:00 2001 From: Will Harney Date: Mon, 29 Sep 2025 15:18:35 -0400 Subject: [PATCH 14/19] test(wtr): enable more test scenarios in CI --- .github/workflows/web-test-runner.yml | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index 8f431d0f3a..bf27aef0e9 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -66,14 +66,14 @@ jobs: - run: API_VERSION=61 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=62 yarn test || true - run: API_VERSION=62 SHADOW_MODE_OVERRIDE=native yarn test || true - - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 SHADOW_MODE_OVERRIDE=native yarn test || true - - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 SHADOW_MODE_OVERRIDE=native DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test || true - - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 yarn test || true - - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 SHADOW_MODE_OVERRIDE=native yarn test || true - - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test || true - - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 SHADOW_MODE_OVERRIDE=native yarn test || true - - run: NODE_ENV_FOR_TEST=production yarn test || true - - run: NODE_ENV_FOR_TEST=production SHADOW_MODE_OVERRIDE=native yarn test || true + - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 SHADOW_MODE_OVERRIDE=native yarn test + - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 SHADOW_MODE_OVERRIDE=native DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test + - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 yarn test + - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 SHADOW_MODE_OVERRIDE=native yarn test + - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test + - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 SHADOW_MODE_OVERRIDE=native yarn test + - run: NODE_ENV_FOR_TEST=production yarn test + - run: NODE_ENV_FOR_TEST=production SHADOW_MODE_OVERRIDE=native yarn test hydration-tests: runs-on: ubuntu-22.04 env: @@ -101,15 +101,15 @@ jobs: # accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} # tunnelName: ${{ env.SAUCE_TUNNEL_ID }} # region: us - - run: ENGINE_SERVER=1 yarn test:hydration || true - - run: ENGINE_SERVER=1 SHADOW_MODE_OVERRIDE=synthetic yarn test:hydration || true - - run: ENGINE_SERVER=1 NODE_ENV_FOR_TEST=production yarn test:hydration || true - - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - - run: ENGINE_SERVER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true - - run: ENGINE_SERVER=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true - - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration || true + - run: ENGINE_SERVER=1 yarn test:hydration + - run: ENGINE_SERVER=1 SHADOW_MODE_OVERRIDE=synthetic yarn test:hydration + - run: ENGINE_SERVER=1 NODE_ENV_FOR_TEST=production yarn test:hydration + - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration + - run: ENGINE_SERVER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration + - run: ENGINE_SERVER=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration + - run: ENGINE_SERVER=1 DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test:hydration - run: yarn test:hydration - run: SHADOW_MODE_OVERRIDE=synthetic yarn test:hydration - - run: NODE_ENV_FOR_TEST=production yarn test:hydration || true - - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration || true - - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration || true + - run: NODE_ENV_FOR_TEST=production yarn test:hydration + - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test:hydration + - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test:hydration From 5b35d405fd7ff104b9c32026277c05828646a86c Mon Sep 17 00:00:00 2001 From: Will Harney Date: Mon, 29 Sep 2025 15:44:14 -0400 Subject: [PATCH 15/19] test(wtr): split CI job into groups --- .github/workflows/web-test-runner.yml | 70 ++++++++++++++++++++------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index bf27aef0e9..b1a4465c00 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -20,10 +20,16 @@ env: jobs: # TODO: upload result artifacts # TODO: make it saucy 🥫 - integration-tests: + integration-tests-1: + name: Integration tests (${{ matrix.shadow_mode }} shadow) + strategy: + matrix: + shadow_mode: [native, synthetic] + runs-on: ubuntu-22.04 env: SAUCE_TUNNEL_ID: github-action-tunnel-wtr-${{github.run_id}}-group-1 + SHADOW_MODE_OVERRIDE: ${{ matrix.shadow_mode }} defaults: run: working-directory: ./packages/@lwc/integration-not-karma @@ -49,31 +55,61 @@ jobs: # region: us - run: yarn test - - run: SHADOW_MODE_OVERRIDE=native yarn test - - run: LEGACY_BROWSERS=1 yarn test || true - - run: FORCE_NATIVE_SHADOW_MODE_FOR_TEST=1 yarn test - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test || true - - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 SHADOW_MODE_OVERRIDE=native yarn test || true - - run: DISABLE_DETACHED_REHYDRATION=1 yarn test - - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test || true - run: API_VERSION=58 yarn test || true - - run: API_VERSION=58 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=59 yarn test || true - - run: API_VERSION=59 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=60 yarn test || true - - run: API_VERSION=60 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=61 yarn test || true - - run: API_VERSION=61 SHADOW_MODE_OVERRIDE=native yarn test || true - run: API_VERSION=62 yarn test || true - - run: API_VERSION=62 SHADOW_MODE_OVERRIDE=native yarn test || true - - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 SHADOW_MODE_OVERRIDE=native yarn test - - run: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 SHADOW_MODE_OVERRIDE=native DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 yarn test - - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 SHADOW_MODE_OVERRIDE=native yarn test - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test - - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 SHADOW_MODE_OVERRIDE=native yarn test - run: NODE_ENV_FOR_TEST=production yarn test - - run: NODE_ENV_FOR_TEST=production SHADOW_MODE_OVERRIDE=native yarn test + + integration-tests-2: + # Tests that should run in only synthetic or native shadow, not both + name: Integration tests (singleton batch) + strategy: + matrix: + shadow_mode: [native, synthetic] + + runs-on: ubuntu-22.04 + env: + SAUCE_TUNNEL_ID: github-action-tunnel-wtr-${{github.run_id}}-group-1 + SHADOW_MODE_OVERRIDE: ${{ matrix.shadow_mode }} + defaults: + run: + working-directory: ./packages/@lwc/integration-not-karma + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + working-directory: ./ + + # - uses: saucelabs/sauce-connect-action@v3.0.0 + # with: + # username: ${{ secrets.SAUCE_USERNAME }} + # accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} + # tunnelName: ${{ env.SAUCE_TUNNEL_ID }} + # region: us + + # Synthetic shadow only + - run: LEGACY_BROWSERS=1 yarn test || true + - run: FORCE_NATIVE_SHADOW_MODE_FOR_TEST=1 yarn test + - run: DISABLE_DETACHED_REHYDRATION=1 yarn test + - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 DISABLE_DETACHED_REHYDRATION=1 yarn test || true + + # Native shadow only -- don't forget SHADOW_MODE_OVERRIDE! + - run: SHADOW_MODE_OVERRIDE=native DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 yarn test + - run: SHADOW_MODE_OVERRIDE=native DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER=1 DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test + hydration-tests: runs-on: ubuntu-22.04 env: From 71f8185f3d14558253449af69070ffa1d74df97b Mon Sep 17 00:00:00 2001 From: Will Harney Date: Mon, 29 Sep 2025 15:50:25 -0400 Subject: [PATCH 16/19] test(wtr): remove matrix config from job that doesn't need it oops --- .github/workflows/web-test-runner.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index b1a4465c00..df93033ab0 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -68,10 +68,6 @@ jobs: integration-tests-2: # Tests that should run in only synthetic or native shadow, not both name: Integration tests (singleton batch) - strategy: - matrix: - shadow_mode: [native, synthetic] - runs-on: ubuntu-22.04 env: SAUCE_TUNNEL_ID: github-action-tunnel-wtr-${{github.run_id}}-group-1 From e525359864bd9e0b4cf66f185cb4c04b544d7377 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Mon, 29 Sep 2025 16:03:03 -0400 Subject: [PATCH 17/19] test(wtr): enable API_VERSION tests in CI and alphabetize list also make john fix it later --- .github/workflows/web-test-runner.yml | 16 ++++++++-------- .../integration-not-karma/configs/integration.js | 7 ++++++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/web-test-runner.yml b/.github/workflows/web-test-runner.yml index df93033ab0..3db9f7dd3b 100644 --- a/.github/workflows/web-test-runner.yml +++ b/.github/workflows/web-test-runner.yml @@ -20,7 +20,7 @@ env: jobs: # TODO: upload result artifacts # TODO: make it saucy 🥫 - integration-tests-1: + integration-tests: name: Integration tests (${{ matrix.shadow_mode }} shadow) strategy: matrix: @@ -55,17 +55,17 @@ jobs: # region: us - run: yarn test + - run: API_VERSION=58 yarn test + - run: API_VERSION=59 yarn test + - run: API_VERSION=60 yarn test + - run: API_VERSION=61 yarn test + - run: API_VERSION=62 yarn test - run: DISABLE_NATIVE_CUSTOM_ELEMENT_LIFECYCLE=1 yarn test || true - - run: API_VERSION=58 yarn test || true - - run: API_VERSION=59 yarn test || true - - run: API_VERSION=60 yarn test || true - - run: API_VERSION=61 yarn test || true - - run: API_VERSION=62 yarn test || true - - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 yarn test - run: DISABLE_STATIC_CONTENT_OPTIMIZATION=1 yarn test + - run: ENABLE_ARIA_REFLECTION_GLOBAL_POLYFILL=1 yarn test - run: NODE_ENV_FOR_TEST=production yarn test - integration-tests-2: + integration-tests-not-both-modes: # Tests that should run in only synthetic or native shadow, not both name: Integration tests (singleton batch) runs-on: ubuntu-22.04 diff --git a/packages/@lwc/integration-not-karma/configs/integration.js b/packages/@lwc/integration-not-karma/configs/integration.js index cc27ff6bea..f7b2b8618a 100644 --- a/packages/@lwc/integration-not-karma/configs/integration.js +++ b/packages/@lwc/integration-not-karma/configs/integration.js @@ -13,7 +13,12 @@ const baseConfig = createConfig({ /** @type {import("@web/test-runner").TestRunnerConfig} */ export default { ...baseConfig, - files: ['test/**/*.spec.js'], + files: [ + 'test/**/*.spec.js', + // Make John fix this after his PR is merged + '!test/template-expressions/errors/index.spec.js', + '!test/template-expressions/smoke-test/index.spec.js', + ], plugins: [ ...baseConfig.plugins, importMapsPlugin({ inject: { importMap: { imports: { lwc: './mocks/lwc.js' } } } }), From da8a3af2886f0a4f553b7c76ef97902a7b022d86 Mon Sep 17 00:00:00 2001 From: Will Harney Date: Mon, 29 Sep 2025 16:21:29 -0400 Subject: [PATCH 18/19] test(wtr): use vitest spy instead of jasmine spy --- .../test/lwc-on/index.spec.js | 89 ++++++++----------- 1 file changed, 37 insertions(+), 52 deletions(-) diff --git a/packages/@lwc/integration-not-karma/test/lwc-on/index.spec.js b/packages/@lwc/integration-not-karma/test/lwc-on/index.spec.js index 32a1562a25..a054025c85 100644 --- a/packages/@lwc/integration-not-karma/test/lwc-on/index.spec.js +++ b/packages/@lwc/integration-not-karma/test/lwc-on/index.spec.js @@ -11,45 +11,40 @@ import RerenderLoop from 'x/rerenderLoop'; import PublicProp from 'x/publicProp'; import ComputedKey from 'x/computedKey'; import ValueEvaluationThrows from 'x/valueEvaluationThrows'; -import { spyOn } from '@vitest/spy'; -import { jasmine } from '../../helpers/jasmine.js'; +import { spyOn, fn as mockFn } from '@vitest/spy'; import { catchUnhandledRejectionsAndErrors } from '../../helpers/utils.js'; describe('lwc:on', () => { it('adds multiple event listeners', () => { const element = createElement('x-basic', { is: Basic }); - const testFn = jasmine.createSpy('test function'); - element.testFn = testFn; + element.testFn = mockFn(); document.body.appendChild(element); const button = element.shadowRoot.querySelector('button'); button.click(); button.dispatchEvent(new MouseEvent('mouseover')); - expect(testFn).toHaveBeenCalledWith('click handler called'); - expect(testFn).toHaveBeenCalledWith('mouseover handler called'); + expect(element.testFn).toHaveBeenCalledWith('click handler called'); + expect(element.testFn).toHaveBeenCalledWith('mouseover handler called'); }); it('event listeners added by lwc:on are bound to the owner component', () => { const element = createElement('x-execution-context', { is: ExecutionContext }); - const testFn = jasmine.createSpy('test function'); - element.testFn = testFn; + element.testFn = mockFn(); document.body.appendChild(element); const button = element.shadowRoot.querySelector('button'); button.click(); - expect(testFn).toHaveBeenCalledWith("'this' is the component"); + expect(element.testFn).toHaveBeenCalledWith("'this' is the component"); }); describe('ignored properties', () => { let element; let button; - let testFn; function setup(propType) { element = createElement('x-ignored', { is: Ignored }); - testFn = jasmine.createSpy('test function'); - element.testFn = testFn; + element.testFn = mockFn(); element.propType = propType; document.body.appendChild(element); button = element.shadowRoot.querySelector('button'); @@ -60,13 +55,13 @@ describe('lwc:on', () => { it('silently ignores non-enumerable properties', () => { setup('non-enumerable'); button.click(); - expect(testFn).not.toHaveBeenCalled(); + expect(element.testFn).not.toHaveBeenCalled(); }); it('silently ignores inherited properties', () => { setup('inherited'); button.click(); - expect(testFn).not.toHaveBeenCalled(); + expect(element.testFn).not.toHaveBeenCalled(); }); it('silently ignores symbol-keyed properties', () => { @@ -77,12 +72,10 @@ describe('lwc:on', () => { describe('event type case', () => { let element; let button; - let testFn; function setup(propCase) { element = createElement('x-case-variants', { is: CaseVariants }); - testFn = jasmine.createSpy('test function'); - element.testFn = testFn; + element.testFn = mockFn(); element.propCase = propCase; document.body.appendChild(element); button = element.shadowRoot.querySelector('button'); @@ -91,60 +84,58 @@ describe('lwc:on', () => { it('adds event listeners corresponding to lowercase keyed property', () => { setup('lower'); button.dispatchEvent(new CustomEvent('lowercase')); - expect(testFn).toHaveBeenCalledWith('lowercase handler called'); + expect(element.testFn).toHaveBeenCalledWith('lowercase handler called'); }); it('adds event listeners corresponding to kebab-case keyed property', () => { setup('kebab'); button.dispatchEvent(new CustomEvent('kebab-case')); - expect(testFn).toHaveBeenCalledWith('kebab-case handler called'); + expect(element.testFn).toHaveBeenCalledWith('kebab-case handler called'); }); it('adds event listeners corresponding to camelCase keyed property', () => { setup('camel'); button.dispatchEvent(new CustomEvent('camelCase')); - expect(testFn).toHaveBeenCalledWith('camelCase handler called'); + expect(element.testFn).toHaveBeenCalledWith('camelCase handler called'); }); it('adds event listeners corresponding to CAPScase keyed property', () => { setup('caps'); button.dispatchEvent(new CustomEvent('CAPSCASE')); - expect(testFn).toHaveBeenCalledWith('CAPSCASE handler called'); + expect(element.testFn).toHaveBeenCalledWith('CAPSCASE handler called'); }); it('adds event listeners corresponding to PascalCase keyed property', () => { setup('pascal'); button.dispatchEvent(new CustomEvent('PascalCase')); - expect(testFn).toHaveBeenCalledWith('PascalCase handler called'); + expect(element.testFn).toHaveBeenCalledWith('PascalCase handler called'); }); it('adds event listeners corresponding to empty-string keyed property', () => { setup('empty'); button.dispatchEvent(new CustomEvent('')); - expect(testFn).toHaveBeenCalledWith('empty string handler called'); + expect(element.testFn).toHaveBeenCalledWith('empty string handler called'); }); }); it('event listeners are added independently from lwc:on and lwc:spread', () => { const element = createElement('x-spread', { is: Spread }); - const testFn = jasmine.createSpy('test function'); - element.testFn = testFn; + element.testFn = mockFn(); document.body.appendChild(element); const button = element.shadowRoot.querySelector('button'); button.click(); - expect(testFn).toHaveBeenCalledWith('lwc:spread handler called'); - expect(testFn).toHaveBeenCalledWith('lwc:on handler called'); + expect(element.testFn).toHaveBeenCalledWith('lwc:spread handler called'); + expect(element.testFn).toHaveBeenCalledWith('lwc:on handler called'); }); it("event listeners are added before child's connectedCallback", () => { const element = createElement('x-lifecycle', { is: Lifecycle }); - const testFn = jasmine.createSpy('foo handler'); - element.testFn = testFn; + element.testFn = mockFn(); document.body.appendChild(element); - expect(testFn).toHaveBeenCalledWith( + expect(element.testFn).toHaveBeenCalledWith( 'handled events dispatched from child connectedCallback' ); }); @@ -214,13 +205,11 @@ describe('lwc:on', () => { describe('re-render behavior', () => { let element; let button; - let testFn; describe('without for:each loop', () => { beforeEach(() => { element = createElement('x-rerender', { is: Rerender }); - testFn = jasmine.createSpy('test function'); - element.testFn = testFn; + element.testFn = mockFn(); document.body.appendChild(element); button = element.shadowRoot.querySelector('button'); }); @@ -232,8 +221,8 @@ describe('lwc:on', () => { button.click(); button.dispatchEvent(new MouseEvent('mouseover')); - expect(testFn).toHaveBeenCalledWith('click handler called'); - expect(testFn).toHaveBeenCalledWith('mouseover handler called'); + expect(element.testFn).toHaveBeenCalledWith('click handler called'); + expect(element.testFn).toHaveBeenCalledWith('mouseover handler called'); }); it('Event listeners are removed when lwc:on is provided a new object with reduced properties', async () => { @@ -241,7 +230,7 @@ describe('lwc:on', () => { await element.triggerReRender(); button.click(); - expect(testFn).not.toHaveBeenCalledWith('click handler called'); + expect(element.testFn).not.toHaveBeenCalledWith('click handler called'); }); it('Event listeners are modified when lwc:on is provided a new object with modified properties', async () => { @@ -249,8 +238,8 @@ describe('lwc:on', () => { await element.triggerReRender(); button.click(); - expect(testFn).not.toHaveBeenCalledWith('click handler called'); - expect(testFn).toHaveBeenCalledWith('modified click handler called'); + expect(element.testFn).not.toHaveBeenCalledWith('click handler called'); + expect(element.testFn).toHaveBeenCalledWith('modified click handler called'); }); }); @@ -322,8 +311,7 @@ describe('lwc:on', () => { describe('with for:each loop and local variable passed as argument to lwc:on', () => { beforeEach(() => { element = createElement('x-rerender-loop', { is: RerenderLoop }); - testFn = jasmine.createSpy('test function'); - element.testFn = testFn; + element.testFn = mockFn(); document.body.appendChild(element); button = element.shadowRoot.querySelector('button'); }); @@ -335,8 +323,8 @@ describe('lwc:on', () => { button.click(); button.dispatchEvent(new MouseEvent('mouseover')); - expect(testFn).toHaveBeenCalledWith('click handler called'); - expect(testFn).toHaveBeenCalledWith('mouseover handler called'); + expect(element.testFn).toHaveBeenCalledWith('click handler called'); + expect(element.testFn).toHaveBeenCalledWith('mouseover handler called'); }); it('Event listeners are removed when lwc:on is provided a new object with reduced properties', async () => { @@ -344,7 +332,7 @@ describe('lwc:on', () => { await element.triggerReRender(); button.click(); - expect(testFn).not.toHaveBeenCalledWith('click handler called'); + expect(element.testFn).not.toHaveBeenCalledWith('click handler called'); }); it('Event listeners are modified when lwc:on is provided a new object with modified properties', async () => { @@ -352,8 +340,8 @@ describe('lwc:on', () => { await element.triggerReRender(); button.click(); - expect(testFn).not.toHaveBeenCalledWith('click handler called'); - expect(testFn).toHaveBeenCalledWith('modified click handler called'); + expect(element.testFn).not.toHaveBeenCalledWith('click handler called'); + expect(element.testFn).toHaveBeenCalledWith('modified click handler called'); }); }); @@ -426,10 +414,8 @@ describe('lwc:on', () => { it('works when the object is passed as public property to component', () => { // In this test, we are implicitly asserting that no error is thrown if the test passes const element = createElement('x-public-prop', { is: PublicProp }); - const testFn = jasmine.createSpy('test function'); - element.eventHandlers = { - click: testFn, - }; + const testFn = mockFn(); + element.eventHandlers = { click: testFn }; document.body.appendChild(element); const button = element.shadowRoot.querySelector('button'); @@ -439,13 +425,12 @@ describe('lwc:on', () => { it('works properly with objects whose keys are computed', () => { const element = createElement('x-computed-key', { is: ComputedKey }); - const testFn = jasmine.createSpy('test function'); - element.testFn = testFn; + element.testFn = mockFn(); document.body.appendChild(element); const button = element.shadowRoot.querySelector('button'); button.click(); - expect(testFn).toHaveBeenCalled(); + expect(element.testFn).toHaveBeenCalled(); }); describe('object passed to lwc:on has property whose value evaluation throws', () => { From d1af772f8196e6bb0ae42a8ad3fb58fe09ddfc7e Mon Sep 17 00:00:00 2001 From: Will Harney Date: Mon, 29 Sep 2025 16:39:58 -0400 Subject: [PATCH 19/19] test(wtr): fix broken test assertion --- .../lifecycle-remove-disconnected.spec.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/@lwc/integration-not-karma/test/misc/lifecycle-remove-disconnected/lifecycle-remove-disconnected.spec.js b/packages/@lwc/integration-not-karma/test/misc/lifecycle-remove-disconnected/lifecycle-remove-disconnected.spec.js index 2903426bd9..1a07acc4b0 100644 --- a/packages/@lwc/integration-not-karma/test/misc/lifecycle-remove-disconnected/lifecycle-remove-disconnected.spec.js +++ b/packages/@lwc/integration-not-karma/test/misc/lifecycle-remove-disconnected/lifecycle-remove-disconnected.spec.js @@ -17,9 +17,13 @@ describe('vdom removes component while it is already disconnected', () => { expect(spy).not.toHaveBeenCalled(); } else { // expected since the engine calls appendChild to a disconnected DOM node - expect(spy).toHaveBeenCalledTimes(1); - expect(spy.calls.mostRecent().args[0]).toMatch( - /fired a `connectedCallback` and rendered, but was not connected to the DOM/ + expect(spy).toHaveBeenCalledExactlyOnceWith(expect.any(Error)); + expect(spy).toHaveBeenCalledExactlyOnceWith( + expect.objectContaining({ + message: expect.stringMatching( + /fired a `connectedCallback` and rendered, but was not connected to the DOM/ + ), + }) ); } });