diff --git a/packages/@lwc/integration-not-karma/configs/plugins/serve-integration.js b/packages/@lwc/integration-not-karma/configs/plugins/serve-integration.js index 3864925f65..0f0a0cd1c7 100644 --- a/packages/@lwc/integration-not-karma/configs/plugins/serve-integration.js +++ b/packages/@lwc/integration-not-karma/configs/plugins/serve-integration.js @@ -32,12 +32,8 @@ const createRollupPlugin = (input, options) => { enableStaticContentOptimization: !DISABLE_STATIC_CONTENT_OPTIMIZATION, disableSyntheticShadowSupport: DISABLE_SYNTHETIC_SHADOW_SUPPORT_IN_COMPILER, apiVersion: API_VERSION, - modules: [ - { - // Assume `ctx.path` is a component file, e.g. modules/x/foo/foo.js - dir: path.resolve(input, '../../..'), - }, - ], + // Assume `ctx.path` is a component file, e.g. modules/x/foo/foo.js + modules: [{ dir: path.resolve(input, '../../..') }], ...options, }); }; @@ -85,6 +81,7 @@ const transform = async (ctx) => { plugins: [customLwcRollupPlugin], external: [ + '@vitest/spy', 'lwc', 'wire-service', // Some helper files export functions that mutate a global state. The setup file calls 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 96d0371034..01e8a6fa6d 100644 --- a/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js +++ b/packages/@lwc/integration-not-karma/configs/plugins/test-hydration.js @@ -1,5 +1,5 @@ +import { spyOn } from '@vitest/spy'; import * as LWC from 'lwc'; -import { spyConsole } from '../../helpers/console'; import { setHooks } from '../../helpers/hooks'; setHooks({ sanitizeHtmlContent: (content) => content }); @@ -8,6 +8,28 @@ function parseStringToDom(html) { return Document.parseHTMLUnsafe(html).body.firstChild; } +/** + * A much simplified version of the spies originally used for Karma. + * Should probably be eventually replaced with individual spies. + */ +export function spyConsole() { + const log = spyOn(console, 'log'); + const warn = spyOn(console, 'warn'); + const error = spyOn(console, 'error'); + return { + calls: { + log: log.mock.calls, + warn: warn.mock.calls, + error: error.mock.calls, + }, + reset() { + log.mockRestore(); + warn.mockRestore(); + error.mockRestore(); + }, + }; +} + function appendTestTarget(ssrText) { const div = document.createElement('div'); const testTarget = parseStringToDom(ssrText); diff --git a/packages/@lwc/integration-not-karma/helpers/console.js b/packages/@lwc/integration-not-karma/helpers/console.js deleted file mode 100644 index 9a52018dcb..0000000000 --- a/packages/@lwc/integration-not-karma/helpers/console.js +++ /dev/null @@ -1,23 +0,0 @@ -import { spyOn } from '@vitest/spy'; - -/** - * A much simplified version of the spies originally used for Karma. - * Should probably be eventually replaced with individual spies. - */ -export function spyConsole() { - const log = spyOn(console, 'log'); - const warn = spyOn(console, 'warn'); - const error = spyOn(console, 'error'); - return { - calls: { - log: log.mock.calls, - warn: warn.mock.calls, - error: error.mock.calls, - }, - reset() { - log.mockRestore(); - warn.mockRestore(); - error.mockRestore(); - }, - }; -} diff --git a/packages/@lwc/integration-not-karma/test/api/CustomElementConstructor-getter/index.spec.js b/packages/@lwc/integration-not-karma/test/api/CustomElementConstructor-getter/index.spec.js index 5d224c512c..6850011af9 100644 --- a/packages/@lwc/integration-not-karma/test/api/CustomElementConstructor-getter/index.spec.js +++ b/packages/@lwc/integration-not-karma/test/api/CustomElementConstructor-getter/index.spec.js @@ -9,7 +9,7 @@ import AttrChanged from 'x/attrChanged'; import ReflectCamel from 'x/reflectCamel'; import WithChildElmsHasSlot from 'x/withChildElmsHasSlot'; import WithChildElmsHasSlotLight from 'x/withChildElmsHasSlotLight'; -import { spyConsole } from '../../../helpers/console.js'; +import { spyOn } from '@vitest/spy'; import { USE_COMMENTS_FOR_FRAGMENT_BOOKENDS } from '../../../helpers/constants.js'; const vFragBookend = USE_COMMENTS_FOR_FRAGMENT_BOOKENDS ? '' : ''; @@ -69,19 +69,9 @@ it('should create custom element if it exists before customElements.define', () describe('non-empty custom element', () => { let consoleSpy; beforeEach(() => { - consoleSpy = spyConsole(); + consoleSpy = spyOn(console, 'warn').mockImplementation(() => {}); }); - afterEach(() => { - consoleSpy.reset(); - }); - - function expectWarnings(expectedWarnings) { - const observedWarnings = consoleSpy.calls.warn - .flat() - .map((err) => (err instanceof Error ? err.message : err)); - - expect(observedWarnings).toEqual(expectedWarnings); - } + afterEach(() => consoleSpy.mockRestore()); it('should log error if non-native-shadow custom element has children', () => { const elm = document.createElement('test-custom-element-preexisting2'); @@ -93,11 +83,11 @@ describe('non-empty custom element', () => { class extends WithChildElms.CustomElementConstructor {} ); if (process.env.NODE_ENV !== 'production' && !process.env.NATIVE_SHADOW) { - expectWarnings([ - 'Light DOM and synthetic shadow custom elements cannot have child nodes. Ensure the element is empty, including whitespace.', - ]); + expect(consoleSpy).toHaveBeenCalledExactlyOnceWith( + 'Light DOM and synthetic shadow custom elements cannot have child nodes. Ensure the element is empty, including whitespace.' + ); } else { - expectWarnings([]); + expect(consoleSpy).not.toHaveBeenCalled(); } expect(elm.shadowRoot.childNodes.length).toBe(1); @@ -123,11 +113,11 @@ describe('non-empty custom element', () => { ); if (process.env.NODE_ENV !== 'production') { - expectWarnings([ - 'Light DOM and synthetic shadow custom elements cannot have child nodes. Ensure the element is empty, including whitespace.', - ]); + expect(consoleSpy).toHaveBeenCalledExactlyOnceWith( + 'Light DOM and synthetic shadow custom elements cannot have child nodes. Ensure the element is empty, including whitespace.' + ); } else { - expectWarnings([]); + expect(consoleSpy).not.toHaveBeenCalled(); } expect(elm.innerHTML).toBe(`${vFragBookend}${vFragBookend}`); @@ -143,11 +133,11 @@ describe('non-empty custom element', () => { class extends WithChildElmsHasSlot.CustomElementConstructor {} ); if (process.env.NODE_ENV !== 'production' && !process.env.NATIVE_SHADOW) { - expectWarnings([ - 'Light DOM and synthetic shadow custom elements cannot have child nodes. Ensure the element is empty, including whitespace.', - ]); + expect(consoleSpy).toHaveBeenCalledExactlyOnceWith( + 'Light DOM and synthetic shadow custom elements cannot have child nodes. Ensure the element is empty, including whitespace.' + ); } else { - expectWarnings([]); + expect(consoleSpy).not.toHaveBeenCalled(); } expect(elm.shadowRoot.childNodes.length).toBe(1); @@ -174,11 +164,11 @@ describe('non-empty custom element', () => { ); if (process.env.NODE_ENV === 'production') { - expectWarnings([]); + expect(consoleSpy).not.toHaveBeenCalled(); } else { - expectWarnings([ - 'Found an existing shadow root for the custom element "Child". Call `hydrateComponent` instead.', - ]); + expect(consoleSpy).toHaveBeenCalledExactlyOnceWith( + 'Found an existing shadow root for the custom element "Child". Call `hydrateComponent` instead.' + ); } expect(elm.shadowRoot.innerHTML).toBe('
'); }); @@ -194,11 +184,11 @@ describe('non-empty custom element', () => { document.body.appendChild(elm); if (process.env.NODE_ENV !== 'production' && !process.env.NATIVE_SHADOW) { - expectWarnings([ - 'Light DOM and synthetic shadow custom elements cannot have child nodes. Ensure the element is empty, including whitespace.', - ]); + expect(consoleSpy).toHaveBeenCalledExactlyOnceWith( + 'Light DOM and synthetic shadow custom elements cannot have child nodes. Ensure the element is empty, including whitespace.' + ); } else { - expectWarnings([]); + expect(consoleSpy).not.toHaveBeenCalled(); } expect(elm.childNodes.length).toBe(1); diff --git a/packages/@lwc/integration-not-karma/test/component/LightningElement.addEventListener/index.spec.js b/packages/@lwc/integration-not-karma/test/component/LightningElement.addEventListener/index.spec.js index 2b10f0152f..2f5c7c9cdf 100644 --- a/packages/@lwc/integration-not-karma/test/component/LightningElement.addEventListener/index.spec.js +++ b/packages/@lwc/integration-not-karma/test/component/LightningElement.addEventListener/index.spec.js @@ -3,7 +3,7 @@ import { createElement } from 'lwc'; import EventHandler from 'x/eventHandler'; import EventHandlerOptions from 'x/eventHandlerOptions'; import AdditionWhileDispatch from 'x/additionWhileDispatch'; -import { spyConsole } from '../../../helpers/console.js'; +import { spyOn } from '@vitest/spy'; it('should be able to attach an event listener on the host element', () => { let thisValue; @@ -39,12 +39,11 @@ it('should warn when passing a 3rd parameter to the event handler', () => { describe('event handler is not a function', () => { let consoleSpy; - beforeEach(() => { - consoleSpy = spyConsole(); - }); - afterEach(() => { - consoleSpy.reset(); + beforeAll(() => { + consoleSpy = spyOn(console, 'error'); }); + afterEach(() => consoleSpy.mockReset()); + afterAll(() => consoleSpy.mockRestore()); it('should log an error if event handler is not a function', () => { const elm = createElement('x-event-handler', { is: EventHandler }); @@ -54,11 +53,10 @@ describe('event handler is not a function', () => { }).toThrowCallbackReactionError(/Expected an EventListener but received undefined/); if (process.env.NODE_ENV === 'production') { - expect(consoleSpy.calls.error.length).toEqual(0); + expect(consoleSpy).not.toHaveBeenCalled(); } else { - expect(consoleSpy.calls.error.length).toEqual(1); - expect(consoleSpy.calls.error[0][0].message).toContain( - 'Invalid second argument for this.addEventListener()' + expect(consoleSpy).toHaveBeenCalledExactlyOnceWith( + expect.stringContaining('Invalid second argument for this.addEventListener()') ); } }); diff --git a/packages/@lwc/integration-not-karma/test/component/dynamic-imports/index.spec.js b/packages/@lwc/integration-not-karma/test/component/dynamic-imports/index.spec.js index 640bd51eea..23d79a92b8 100644 --- a/packages/@lwc/integration-not-karma/test/component/dynamic-imports/index.spec.js +++ b/packages/@lwc/integration-not-karma/test/component/dynamic-imports/index.spec.js @@ -10,7 +10,7 @@ import LwcDynamicSlotted from 'x/lwcDynamicSlotted'; import ContainerFoo from 'x/containerFoo'; import ContainerBar from 'x/containerBar'; -import { spyConsole } from '../../../helpers/console.js'; +import { spyOn } from '@vitest/spy'; import { registerForLoad, clearRegister } from '../../../helpers/dynamic-loader.js'; beforeEach(() => { @@ -116,12 +116,10 @@ it('should not cache DOM elements using lwc:dynamic', async () => { describe('slotted content using lwc:dynamic', () => { let consoleSpy; - beforeEach(() => { - consoleSpy = spyConsole(); - }); - afterEach(() => { - consoleSpy.reset(); + beforeAll(() => { + consoleSpy = spyOn(console, 'error'); }); + afterAll(() => consoleSpy.mockRestore()); it('reallocate slotted content after changing constructor', async () => { const elm = createElement('x-dynamic-slotted', { is: LwcDynamicSlotted }); @@ -136,7 +134,7 @@ describe('slotted content using lwc:dynamic', () => { // `slot-bar` is not rendered in synthetic shadow expect(elm.shadowRoot.querySelector('[data-id="slot-bar"]').assignedSlot).toBe(null); } - expect(consoleSpy.calls.error.length).toEqual(0); + expect(consoleSpy).not.toHaveBeenCalled(); // Swap construstor and check if nodes have been reallocated. elm.ctor = ContainerBar; @@ -150,7 +148,7 @@ describe('slotted content using lwc:dynamic', () => { // `slot-foo` is not rendered in synthetic shadow expect(elm.shadowRoot.querySelector('[data-id="slot-foo"]').assignedSlot).toBe(null); } - expect(consoleSpy.calls.error.length).toEqual(0); + expect(consoleSpy).not.toHaveBeenCalled(); }); }); @@ -242,12 +240,10 @@ it('should not cache DOM elements', async () => { describe('slotted content', () => { let consoleSpy; - beforeEach(() => { - consoleSpy = spyConsole(); - }); - afterEach(() => { - consoleSpy.reset(); + beforeAll(() => { + consoleSpy = spyOn(console, 'error'); }); + afterAll(() => consoleSpy.mockRestore()); it('reallocate slotted content after changing constructor', async () => { const elm = createElement('x-dynamic-slotted', { is: DynamicSlotted }); @@ -262,7 +258,7 @@ describe('slotted content', () => { // `slot-bar` is not rendered in synthetic shadow expect(elm.shadowRoot.querySelector('[data-id="slot-bar"]').assignedSlot).toBe(null); } - expect(consoleSpy.calls.error.length).toEqual(0); + expect(consoleSpy).not.toHaveBeenCalled(); // Swap constructor and check if nodes have been reallocated. elm.ctor = ContainerBar; @@ -276,6 +272,6 @@ describe('slotted content', () => { // `slot-foo` is not rendered in synthetic shadow expect(elm.shadowRoot.querySelector('[data-id="slot-foo"]').assignedSlot).toBe(null); } - expect(consoleSpy.calls.error.length).toEqual(0); + expect(consoleSpy).not.toHaveBeenCalled(); }); }); 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 d62d43e081..32a1562a25 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,8 +11,8 @@ 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 { spyConsole } from '../../helpers/console.js'; import { catchUnhandledRejectionsAndErrors } from '../../helpers/utils.js'; describe('lwc:on', () => { @@ -257,10 +257,10 @@ describe('lwc:on', () => { describe('with same object modified', () => { let consoleSpy; beforeEach(() => { - consoleSpy = spyConsole(); + consoleSpy = spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { - consoleSpy.reset(); + consoleSpy.mockRestore(); }); it('throws when a new property is added to object passed to lwc:on', async () => { @@ -268,12 +268,16 @@ describe('lwc:on', () => { await element.triggerReRender(); if (process.env.NODE_ENV !== 'production') { - expect(consoleSpy.calls.error.length).toEqual(1); - expect(consoleSpy.calls.error[0][0].message).toContain( - "Detected mutation of property 'mouseover' in the object passed to lwc:on for