Skip to content

Commit

Permalink
test: remove remaining vestiges of jest types (#4435)
Browse files Browse the repository at this point in the history
Co-authored-by: Nolan Lawson <[email protected]>
  • Loading branch information
cardoso and nolanlawson authored Aug 7, 2024
1 parent cdb169a commit 64ee414
Show file tree
Hide file tree
Showing 15 changed files with 63 additions and 67 deletions.
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ yarn dev

### Unit Testing LWC

When developing LWC, utilize [jest](https://jestjs.io/en/) unit testing to provide test coverage for new functionality. To run the jest tests use the following command from the root directory:
When developing LWC, utilize [vitest](https://vitest.dev/) unit testing to provide test coverage for new functionality. To run the vitest tests use the following command from the root directory:

```bash
yarn test
Expand Down Expand Up @@ -84,11 +84,11 @@ If you want to debug these tests, you can do as follow:
3. Click on "Open dedicated DevTools for Node"
4. In your terminal, type the following command: `yarn test:debug <path_to_test>`

Your test should now be running in the Chrome debugger which you can use to poke around and explore. Now simply hit Enter in the terminal running your Jest process anytime you want to re-run your currently selected specs. You'll be dropped right back into the Chrome debugger.
Your test should now be running in the Chrome debugger which you can use to poke around and explore. Now simply hit Enter in the terminal running your Vitest process anytime you want to re-run your currently selected specs. You'll be dropped right back into the Chrome debugger.

### Debugging Test Fixtures LWC

Test fixtures are file-based tests that are executed using a helper called [`testFixtureDir`](./scripts/jest/utils/test-fixture-dir.ts). Because this helper does not list tests individually, jest's [`test.only`](https://jestjs.io/docs/api#testonlyname-fn-timeout) and [`test.skip`](https://jestjs.io/docs/api#testskipname-fn) cannot be used. Instead, to achieve the same behavior, "directive" files can be added to individual test fixtures. If a file called `.only` is found in a test fixture, that test will use `test.only`. Similarly, if a file called `.skip` is found, `test.skip` will be used.
Test fixtures are file-based tests that are executed using a helper called [`testFixtureDir`](./scripts/test-utils/test-fixture-dir.ts). Because this helper does not list tests individually, vitest's [`test.only`](https://vitest.dev/api/#test-only) and [`test.skip`](https://vitest.dev/api/#test-skip) cannot be used. Instead, to achieve the same behavior, "directive" files can be added to individual test fixtures. If a file called `.only` is found in a test fixture, that test will use `test.only`. Similarly, if a file called `.skip` is found, `test.skip` will be used.

### Integration Testing LWC

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"release:version": "./scripts/release/version.js"
},
"//": {
"prettier": "v3 requires ESM, and we use prettier in our Jest tests. Jest does not support ESM yet."
"prettier": "Outdated since Jest has been replaced with vitest: v3 requires ESM, and we use prettier in our Jest tests. Jest does not support ESM yet."
},
"devDependencies": {
"@commitlint/cli": "^19.3.0",
Expand All @@ -47,7 +47,9 @@
"@types/babel__core": "^7.20.5",
"@types/node": "^22.1.0",
"@vitest/coverage-v8": "^2.0.5",
"@vitest/expect": "^2.0.5",
"@vitest/ui": "^2.0.5",
"@vitest/utils": "^2.0.5",
"bytes": "^3.1.2",
"es-module-lexer": "^1.5.4",
"eslint": "^9.8.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/@lwc/engine-core/src/shared/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function log(method: 'warn' | 'error', message: string, vm: VM | undefined, once
alreadyLoggedMessages.add(msg);
}

// In Jest tests, reduce the warning and error verbosity by not printing the callstack
// In Vitest tests, reduce the warning and error verbosity by not printing the callstack
if (process.env.NODE_ENV === 'test') {
/* eslint-disable-next-line no-console */
console[method](msg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

import { renderComponent, LightningElement } from '../index';

class Test extends LightningElement {}
Expand Down
25 changes: 10 additions & 15 deletions packages/@lwc/errors/src/__tests__/errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,18 @@ const ERROR_CODE_RANGES = {
},
};

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace jest {
interface Matchers<R> {
__type: R; // unused, but makes TypeScript happy
toBeUniqueCode: (key: string, seenErrorCodes: Set<number>) => object;
toBeInRange: (min: number, max: number, key: string) => object;
}
}
interface CustomMatchers<R = unknown> {
toBeUniqueCode: (key: string, seenErrorCodes: Set<number>) => R;
toBeInRange: (range: { min: number; max: number }, key: string) => R;
}

declare module 'vitest' {
interface Assertion<T = any> extends CustomMatchers<T> {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}

expect.extend({
toBeInRange(code, min, max, key) {
toBeInRange(code, { min, max }, key) {
const pass = Number.isInteger(code) && code >= min && code <= max;
const message = () =>
`expected ${key}'s error code '${code}'${
Expand Down Expand Up @@ -68,11 +67,7 @@ function traverseErrorInfo(
describe('error validation', () => {
it('compiler error codes are in the correct range', () => {
function validate(errorInfo: LWCErrorInfo, key: string) {
expect(errorInfo.code).toBeInRange(
ERROR_CODE_RANGES.compiler.min,
ERROR_CODE_RANGES.compiler.max,
key
);
expect(errorInfo.code).toBeInRange(ERROR_CODE_RANGES.compiler, key);
}

traverseErrorInfo(CompilerErrors, validate, 'compiler');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import * as errorInfo from '../error-info';

// All exported objects are maps of label/error info, except for GENERIC_COMPILER_ERROR,
// which is a top-level error info object
const { GENERIC_COMPILER_ERROR, ...errors } = errorInfo;
Expand All @@ -14,7 +13,7 @@ const errorInfoMatcher = {
code: expect.any(Number),
message: expect.any(String),
url: expect.any(String),
// Technically not *any* number, but jest doesn't have oneOf
// Technically not *any* number, but vitest doesn't have oneOf
level: expect.any(Number),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

import diff from 'jest-diff';
import MatcherUtils = jest.MatcherUtils;
import diff from '@vitest/utils/diff';
import type { MatcherState } from '@vitest/expect';

export function toThrowErrorWithCode(
this: MatcherUtils,
this: MatcherState,
received: any,
code: string,
message?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/

import diff from 'jest-diff';
import MatcherUtils = jest.MatcherUtils;
import diff from '@vitest/utils/diff';
import type { MatcherState } from '@vitest/expect';

export function toThrowErrorWithType(
this: MatcherUtils,
this: MatcherState,
received: any,
ctor: any,
message?: string
Expand Down
20 changes: 10 additions & 10 deletions packages/@lwc/module-resolver/scripts/test/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
export {}; // required to have a module with just `declare global` in it
/// <reference types="vitest/globals" />
import 'vitest';

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace jest {
interface Matchers<R> {
__type: R; // unused, but makes TypeScript happy
toThrowErrorWithCode(received: any, ctor: any, message?: string): CustomMatcherResult;
toThrowErrorWithType(received: any, ctor: any, message?: string): CustomMatcherResult;
}
}
interface CustomMatchers<R = unknown> {
toThrowErrorWithCode: (received: any, ctor: any, message?: string) => R;
toThrowErrorWithType: (received: any, ctor: any, message?: string) => R;
}

declare module 'vitest' {
interface Assertion<T = any> extends CustomMatchers<T> {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('customRendererConfig normalization', () => {
},
});
expect(normalizedConfig.apiVersion).toBe(HIGHEST_API_VERSION);
normalizedConfig.apiVersion = -1; // avoid testing in inline snapshot so that Jest can easily update it
normalizedConfig.apiVersion = -1; // avoid testing in inline snapshot so that vitest can easily update it

expect(normalizedConfig).toMatchInlineSnapshot(`
{
Expand Down
2 changes: 2 additions & 0 deletions packages/lwc/__tests__/default-exports.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const expectExportDefaultFromPackageInFile = (pkgName: string, ext: string) => {
};

/*
* This comment needs to be updated:
* Jest uses CommonJS, which means that packages with no explicit export statements actually export
* the default `module.exports` empty object. That export is an empty object with the prototype set
* to an empty object with null prototype.
Expand Down Expand Up @@ -59,6 +60,7 @@ describe('default exports are not forgotten', () => {
'dist/index.js'
);
const realModule = await import(pathToEsmDistFile);
// The commend below needs to be updated:
// When jest properly supports ESM, this will be a lot simpler
// const aliasedModule = await import(`lwc/${pkg}`);
// expect(aliasedModule.default).toBe(realModule.default);
Expand Down
2 changes: 1 addition & 1 deletion scripts/test-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
* SPDX-License-Identifier: MIT
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
*/
import './matchers'; // File has no exports, but registers jest matchers
import './matchers'; // File has no exports, but registers vitest matchers
export * from './test-fixture-dir';
export * from './format-html';
43 changes: 21 additions & 22 deletions scripts/test-utils/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,16 @@
*/

import fs from 'fs';
import MatcherUtils = jest.MatcherUtils;
import CustomMatcherResult = jest.CustomMatcherResult;
import type { MatcherState } from '@vitest/expect';

/**
* Jest matcher to assert that the content received matches the content in fixture file.
* Vitest matcher to assert that the content received matches the content in fixture file.
* @param receivedContent the fixture content
* @param filename the fixture absolute path
* @returns matcher result
* @example expect(content).toMatchFile(outputPath)
*/
function toMatchFile(
this: MatcherUtils,
receivedContent: string,
filename: string
): CustomMatcherResult {
function toMatchFile(this: MatcherState, receivedContent: string, filename: string) {
const { snapshotState, expand, utils } = this;

const fileExists = fs.existsSync(filename);
Expand All @@ -29,10 +24,10 @@ function toMatchFile(
const expectedContent = fs.readFileSync(filename, 'utf-8');

if (receivedContent === null || receivedContent === undefined) {
// If the file exists but the expected content is undefined or null. If the Jest is
// If the file exists but the expected content is undefined or null. If Vitest is
// running with the update snapshot flag the file should be deleted. Otherwise fails
// the assertion stating that the file is not expected to be there.
if (snapshotState._updateSnapshot === 'all') {
if (snapshotState['_updateSnapshot'] === 'all') {
fs.unlinkSync(filename);

snapshotState.updated++;
Expand All @@ -57,10 +52,10 @@ function toMatchFile(
// content everything is fine.
return { pass: true, message: () => '' };
} else {
// If the expected file is present but the content is not matching. if Jest is running
// If the expected file is present but the content is not matching. if Vitest is running
// with the update snapshot flag override the expected content. Otherwise fails the
// assertion with a diff.
if (snapshotState._updateSnapshot === 'all') {
if (snapshotState['_updateSnapshot'] === 'all') {
fs.writeFileSync(filename, receivedContent);

snapshotState.updated++;
Expand Down Expand Up @@ -93,7 +88,10 @@ function toMatchFile(

// If expected file doesn't exists but got a received content and if the snapshots
// should be updated, create the new snapshot. Otherwise fails the assertion.
if (snapshotState._updateSnapshot === 'new' || snapshotState._updateSnapshot === 'all') {
if (
snapshotState['_updateSnapshot'] === 'new' ||
snapshotState['_updateSnapshot'] === 'all'
) {
fs.writeFileSync(filename, receivedContent);

snapshotState.added++;
Expand All @@ -113,15 +111,16 @@ function toMatchFile(
}
}

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace jest {
interface Matchers<R> {
__type: R; // unused, but makes TypeScript happy
toMatchFile(receivedContent: string, filename?: string): CustomMatcherResult;
}
}
import 'vitest';

interface CustomMatchers<R = unknown> {
toMatchFile: (receivedContent: string, filename?: string) => R;
}

declare module 'vitest' {
interface Assertion<T = any> extends CustomMatchers<T> {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}

// Register jest matcher.
// Register vitest matcher.
expect.extend({ toMatchFile });
4 changes: 2 additions & 2 deletions scripts/test-utils/test-fixture-dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ const { globSync } = glob;
type TestFixtureOutput = { [filename: string]: unknown };

/**
* Facilitates the use of jest's `test.only`/`test.skip` in fixture files.
* Facilitates the use of vitest's `test.only`/`test.skip` in fixture files.
* @param dirname fixture directory to check for "directive" files
* @returns `test.only` if `.only` exists, `test.skip` if `.skip` exists, otherwise `test`
* @throws if you have both `.only` and `.skip` in the directory
* @example getTestFunc('/fixtures/some-test')
*/
function getTestFunc(dirname: string): jest.It {
function getTestFunc(dirname: string) {
const isOnly = fs.existsSync(path.join(dirname, '.only'));
const isSkip = fs.existsSync(path.join(dirname, '.skip'));
if (isOnly && isSkip) {
Expand Down
4 changes: 2 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1958,7 +1958,7 @@
test-exclude "^7.0.1"
tinyrainbow "^1.2.0"

"@vitest/[email protected]":
"@vitest/[email protected]", "@vitest/expect@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-2.0.5.tgz#f3745a6a2c18acbea4d39f5935e913f40d26fa86"
integrity sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==
Expand Down Expand Up @@ -2021,7 +2021,7 @@
sirv "^2.0.4"
tinyrainbow "^1.2.0"

"@vitest/[email protected]":
"@vitest/[email protected]", "@vitest/utils@^2.0.5":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-2.0.5.tgz#6f8307a4b6bc6ceb9270007f73c67c915944e926"
integrity sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==
Expand Down

0 comments on commit 64ee414

Please sign in to comment.