fix(nx-plugin): run jest 29 migrations for @nrwl/nx-plugin:e2e (#15551)

This commit is contained in:
Caleb Ukle 2023-03-09 15:02:33 -08:00 committed by GitHub
parent d7a008ff5e
commit a67baaa66e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1636 additions and 0 deletions

View File

@ -17,6 +17,18 @@
"version": "15.0.0-beta.0",
"description": "Migrates executor schema files to v2",
"factory": "./src/migrations/update-15-0-0/specify-output-capture"
},
"update-configs-jest-29": {
"version": "15.9.0-beta.0",
"cli": "nx",
"description": "Update nx plugin jest configs to support jest 29 changes (https://jestjs.io/docs/upgrading-to-jest29)",
"factory": "./src/migrations/update-15-9-0/jest-29-configs"
},
"update-tests-jest-29": {
"version": "15.9.0-beta.0",
"cli": "nx",
"description": "Update nx plugin jest test files to support jest 29 changes (https://jestjs.io/docs/upgrading-to-jest29)",
"factory": "./src/migrations/update-15-9-0/jest-29-tests"
}
}
}

View File

@ -32,6 +32,7 @@
"@nrwl/jest": "file:../jest",
"@nrwl/js": "file:../js",
"@nrwl/linter": "file:../linter",
"@phenomnomnominal/tsquery": "4.1.1",
"dotenv": "~10.0.0",
"fs-extra": "^11.1.0",
"tslib": "^2.3.0"

View File

@ -12,6 +12,7 @@ import { JestExecutorOptions } from '@nrwl/jest/src/executors/jest/schema';
import { jestExecutor } from '@nrwl/jest/src/executors/jest/jest.impl';
import type { NxPluginE2EExecutorOptions } from './schema';
// TODO(Caleb & Craigory): can we get rid of this and just use @nrwl/jest directly?
export async function* nxPluginE2EExecutor(
options: NxPluginE2EExecutorOptions,
context: ExecutorContext

View File

@ -0,0 +1,457 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Nx Plugin Migration - jest 29 update configs should NOT update ts-jest with no globals are preset 1`] = `
"const nxPreset = require('@nrwl/jest/preset').default;
module.exports = {
...nxPreset,
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
transform: {
'^.+\\\\.(ts|js|html)$': 'ts-jest',
},
resolver: '@nrwl/jest/plugins/resolver',
moduleFileExtensions: ['ts', 'js', 'html'],
coverageReporters: ['html'],
/* TODO: Update to latest Jest snapshotFormat
* By default Nx has kept the older style of Jest Snapshot formats
* to prevent breaking of any existing tests with snapshots.
* It's recommend you update to the latest format.
* You can do this by removing snapshotFormat property
* and running tests with --update-snapshot flag.
* Example: \\"nx affected --targets=test --update-snapshot\\"
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
*/
snapshotFormat: { escapeString: true, printBasicPrototype: true }
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should add snapshot config with no root preset 1`] = `
"/* eslint-disable */
export default {
displayName: 'my-lib',
preset: '../../jest.preset.js',
globals: { },
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json',
}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/my-lib',
/* TODO: Update to latest Jest snapshotFormat
* By default Nx has kept the older style of Jest Snapshot formats
* to prevent breaking of any existing tests with snapshots.
* It's recommend you update to the latest format.
* You can do this by removing snapshotFormat property
* and running tests with --update-snapshot flag.
* Example: From within the project directory, run \\"nx test --update-snapshot\\"
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
*/
snapshotFormat: { escapeString: true, printBasicPrototype: true }
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should add snapshot config with no root preset 2`] = `
"module.exports = {
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json'
}]
},
// I am a comment and shouldn't be removed
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
/**
* Multi-line comment shouldn't be removed
*/
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
/* TODO: Update to latest Jest snapshotFormat
* By default Nx has kept the older style of Jest Snapshot formats
* to prevent breaking of any existing tests with snapshots.
* It's recommend you update to the latest format.
* You can do this by removing snapshotFormat property
* and running tests with --update-snapshot flag.
* Example: From within the project directory, run \\"nx test --update-snapshot\\"
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
*/
snapshotFormat: { escapeString: true, printBasicPrototype: true }
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should be idempotent 1`] = `
"/* eslint-disable */
export default {
displayName: 'my-lib',
preset: '../../jest.preset.js',
globals: { },
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json',
}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/my-lib'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should be idempotent 2`] = `
"module.exports = {
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json'
}]
},
// I am a comment and shouldn't be removed
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
/**
* Multi-line comment shouldn't be removed
*/
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should update globalThis.ngJest.teardown to testEnvironmentOptions 1`] = `
"globalThis.ngJest = {
}
export default {
globals: { },
transform: {
'^.+.(ts|mjs|js|html)$': ['jest-preset-angular', {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\\\.(html|svg)$',
}],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
testEnvironmentOptions: { teardown: true },
};"
`;
exports[`Nx Plugin Migration - jest 29 update configs should update globalThis.ngJest.teardown to testEnvironmentOptions 2`] = `
"
globalThis.ngJest = {
ngcc: true,
}
module.exports = {
globals: { },
transform: {
'^.+.(ts|mjs|js|html)$': ['jest-preset-angular', {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\\\.(html|svg)$',
}],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
testEnvironmentOptions: {
blah: 123,
teardown: false
},
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};"
`;
exports[`Nx Plugin Migration - jest 29 update configs should update jest.config.ts 1`] = `
"/* eslint-disable */
export default {
displayName: 'my-lib',
preset: '../../jest.preset.js',
globals: { },
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json',
}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/my-lib'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should update jest.config.ts 2`] = `
"module.exports = {
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json'
}]
},
// I am a comment and shouldn't be removed
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
/**
* Multi-line comment shouldn't be removed
*/
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should update root preset 1`] = `
"
const nxPreset = require('@nrwl/jest/preset').default;
module.exports = {
...nxPreset,
/* TODO: Update to latest Jest snapshotFormat
* By default Nx has kept the older style of Jest Snapshot formats
* to prevent breaking of any existing tests with snapshots.
* It's recommend you update to the latest format.
* You can do this by removing snapshotFormat property
* and running tests with --update-snapshot flag.
* Example: \\"nx affected --targets=test --update-snapshot\\"
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
*/
snapshotFormat: { escapeString: true, printBasicPrototype: true }
}"
`;
exports[`Nx Plugin Migration - jest 29 update configs should update root preset 2`] = `
"/* eslint-disable */
export default {
displayName: 'my-lib',
preset: '../../jest.preset.js',
globals: { },
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json',
}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/my-lib'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should update root preset 3`] = `
"module.exports = {
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json'
}]
},
// I am a comment and shouldn't be removed
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
/**
* Multi-line comment shouldn't be removed
*/
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should update root preset if ts-jest is preset 1`] = `
"const nxPreset = require('@nrwl/jest/preset').default;
module.exports = {
...nxPreset,
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
transform: {
'^.+\\\\.(ts|js|html)$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json'
}],
},
resolver: '@nrwl/jest/plugins/resolver',
moduleFileExtensions: ['ts', 'js', 'html'],
coverageReporters: ['html'],
/* TODO: Update to latest Jest snapshotFormat
* By default Nx has kept the older style of Jest Snapshot formats
* to prevent breaking of any existing tests with snapshots.
* It's recommend you update to the latest format.
* You can do this by removing snapshotFormat property
* and running tests with --update-snapshot flag.
* Example: \\"nx affected --targets=test --update-snapshot\\"
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
*/
snapshotFormat: { escapeString: true, printBasicPrototype: true }
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work if not using ts-jest transformer 1`] = `
"export default {
transform: {
'^.+\\\\\\\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work if not using ts-jest transformer 2`] = `
"module.exports = {
transform: {
'^.+\\\\\\\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work snapshotFormat is defined 1`] = `
"export default {
transform: {
'^.+\\\\\\\\.[tj]sx?$': 'babel-jest',
},
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
snapshotFormat: {escapeString: false, printBasicPrototype: true}
};"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work snapshotFormat is defined 2`] = `
"module.exports = {
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json'
}],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
snapshotFormat: {escapeString: false, printBasicPrototype: true}
};"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work with jest-preset-angular 1`] = `
"export default {
globals: { },
transform: {
'^.+.(ts|mjs|js|html)$': ['jest-preset-angular', {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\\\.(html|svg)$',
}],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work with jest-preset-angular 2`] = `
"module.exports = {
globals: { },
transform: {
'^.+.(ts|mjs|js|html)$': ['jest-preset-angular', {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\\\.(html|svg)$',
}],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work with multiple projects + configs 1`] = `
"/* eslint-disable */
export default {
displayName: 'my-lib',
preset: '../../jest.preset.js',
globals: { },
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json',
}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/my-lib'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work with multiple projects + configs 2`] = `
"module.exports = {
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json'
}]
},
// I am a comment and shouldn't be removed
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
/**
* Multi-line comment shouldn't be removed
*/
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work with multiple projects + configs 3`] = `
"/* eslint-disable */
export default {
displayName: 'another-lib',
preset: '../../jest.preset.js',
globals: { },
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json',
}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/another-lib'
};
"
`;
exports[`Nx Plugin Migration - jest 29 update configs should work with multiple projects + configs 4`] = `
"module.exports = {
transform: {
'^.+\\\\\\\\.[tj]sx?$': ['ts-jest', {
tsconfig: '<rootDir>/tsconfig.spec.json'
}]
},
// I am a comment and shouldn't be removed
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: { something: 'else',
abc: [1234, true, {abc: 'yes'}] },
/**
* Multi-line comment shouldn't be removed
*/
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js'
};
"
`;

View File

@ -0,0 +1,193 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Nx Plugin Migration - jest 29 mocked usage in tests should be idempotent 1`] = `
"
import{ MaybeMockedDeep, MaybeMocked } from 'jest-mock';
import {expect, jest, test} from '@jest/globals';
import {song} from './song';
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song, true);
// or through \`jest.Mocked<Source>\`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
});
"
`;
exports[`Nx Plugin Migration - jest 29 mocked usage in tests should be idempotent 2`] = `
"
const { MaybeMockedDeep, MaybeMocked } = require('jest-mock');
const {expect, jest, test} = require('@jest/globals');
const {song} = require('./song');
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song, true);
// or through \`jest.Mocked<Source>\`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
});
"
`;
exports[`Nx Plugin Migration - jest 29 mocked usage in tests should be idempotent 3`] = `
"
import{ MaybeMockedDeep, MaybeMocked } from 'jest-mock';
import {expect, jest, test} from '@jest/globals';
import {song} from './song';
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song, true);
// or through \`jest.Mocked<Source>\`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
});
"
`;
exports[`Nx Plugin Migration - jest 29 mocked usage in tests should be idempotent 4`] = `
"
const { MaybeMockedDeep, MaybeMocked } = require('jest-mock');
const {expect, jest, test} = require('@jest/globals');
const {song} = require('./song');
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song, true);
// or through \`jest.Mocked<Source>\`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
});
"
`;
exports[`Nx Plugin Migration - jest 29 mocked usage in tests should not update anything if there are no tests 1`] = `
"
import{ MaybeMockedDeep, MaybeMocked } from 'jest-mock';
import {expect, jest, test} from '@jest/globals';
import {song} from './song';
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song, true);
// or through \`jest.Mocked<Source>\`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
});
"
`;
exports[`Nx Plugin Migration - jest 29 mocked usage in tests should not update anything if there are no tests 2`] = `
"
const { MaybeMockedDeep, MaybeMocked } = require('jest-mock');
const {expect, jest, test} = require('@jest/globals');
const {song} = require('./song');
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song, true);
// or through \`jest.Mocked<Source>\`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
});
"
`;

View File

@ -0,0 +1,230 @@
import {
formatFiles,
logger,
stripIndents,
Tree,
createProjectGraphAsync,
} from '@nrwl/devkit';
import { tsquery } from '@phenomnomnominal/tsquery';
import * as ts from 'typescript';
import { forEachExecutorOptionsInGraph } from '@nrwl/workspace/src/utilities/executor-options-utils';
import { TS_QUERY_JEST_CONFIG_PREFIX } from '@nrwl/jest/src/utils/ast-utils';
import { findRootJestPreset } from '@nrwl/jest/src/utils/config/find-root-jest-files';
import { JestExecutorOptions } from '@nrwl/jest/src/executors/jest/schema';
// NOTE: this is a copy of the @nrwl/jest v15.8.0 migrations
export async function updateConfigsJest29(tree: Tree) {
const rootPreset = findRootJestPreset(tree);
const targetsWithJest = new Set<string>();
// have to use graph so the negative configuration targets are expanded
const graph = await createProjectGraphAsync();
forEachExecutorOptionsInGraph<JestExecutorOptions>(
graph,
'@nrwl/nx-plugin:e2e',
(options, projectName, targetName) => {
if (options.jestConfig && tree.exists(options.jestConfig)) {
targetsWithJest.add(targetName);
// if the default root preset exists or if the project doesn't have a 'preset' configured
// -> update snapshot config
if (!rootPreset || !hasPresetConfigured(tree, options.jestConfig)) {
addSnapshotOptionsToConfig(
tree,
options.jestConfig,
`From within the project directory, run "nx test --update-snapshot"`
);
}
updateTsJestOptions(tree, options.jestConfig);
updateNgJestOptions(tree, options.jestConfig);
}
}
);
if (rootPreset && tree.exists(rootPreset)) {
const cmd = `"nx affected --targets=${Array.from(targetsWithJest).join(
','
)} --update-snapshot"`;
addSnapshotOptionsToConfig(tree, rootPreset, cmd);
updateTsJestOptions(tree, rootPreset);
updateNgJestOptions(tree, rootPreset);
}
await formatFiles(tree);
logger.info(stripIndents`NX Jest Snapshot format changed in v29.
By default Nx kept the older style to prevent breaking of existing tests with snapshots.
It's recommend you update to the latest format.
You can do this in your project's jest config file.
Remove the snapshotFormat property and re-run tests with the --update-snapshot flag.
More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format`);
}
function addSnapshotOptionsToConfig(
tree: Tree,
configPath: string,
updateSnapshotExample: string
) {
const config = tree.read(configPath, 'utf-8');
const hasSnapshotOptions = tsquery.query(
config,
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression PropertyAssignment:has(Identifier[name="snapshotFormat"])`
);
if (hasSnapshotOptions.length > 0) {
return;
}
const updatedConfig = tsquery.replace(
config,
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression`,
(node: ts.ObjectLiteralExpression) => {
return `{
${node.properties.map((p) => getNodeWithComments(config, p)).join(',\n')},
/* TODO: Update to latest Jest snapshotFormat
* By default Nx has kept the older style of Jest Snapshot formats
* to prevent breaking of any existing tests with snapshots.
* It's recommend you update to the latest format.
* You can do this by removing snapshotFormat property
* and running tests with --update-snapshot flag.
* Example: ${updateSnapshotExample}
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
*/
snapshotFormat: { escapeString: true, printBasicPrototype: true }
}`;
},
{ visitAllChildren: false }
);
tree.write(configPath, updatedConfig);
}
function hasPresetConfigured(tree: Tree, configPath: string): boolean {
const contents = tree.read(configPath, 'utf-8');
return (
tsquery.query(
contents,
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression PropertyAssignment:has(Identifier[name="preset"])`
)?.length > 0
);
}
function updateTsJestOptions(tree: Tree, configPath: string) {
// query for the globals property, if they don't have one then there's nothing to modify.
const contents = tree.read(configPath, 'utf-8');
let tsJestGlobalsConfig: string;
const noTsJestGlobals = tsquery.replace(
contents,
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression PropertyAssignment:has(Identifier[name="globals"])`,
(node: ts.PropertyAssignment) => {
if (tsJestGlobalsConfig) {
logger.warn(
stripIndents`Found more than one "globals" object in the jest config, ${configPath}
Will use the first one`
);
return;
}
tsJestGlobalsConfig = getGlobalTsJestConfig(node);
return getGlobalConfigWithoutTsJest(node);
}
);
if (!tsJestGlobalsConfig) {
return;
}
const updatedTsJestTransformer = tsquery.replace(
noTsJestGlobals,
`${TS_QUERY_JEST_CONFIG_PREFIX}> ObjectLiteralExpression PropertyAssignment:has(Identifier[name="transform"]) PropertyAssignment > :has(StringLiteral[value="ts-jest"], StringLiteral[value="jest-preset-angular"])`,
(node: ts.StringLiteral) => {
return `[${node.getText()}, ${tsJestGlobalsConfig}]`;
}
);
tree.write(configPath, updatedTsJestTransformer);
}
function updateNgJestOptions(tree: Tree, configPath: string) {
const contents = tree.read(configPath, 'utf-8');
let ngJestTeardownConfig: string;
const noTeardownConfig = tsquery.replace(
contents,
'BinaryExpression:has(PropertyAccessExpression:has(Identifier[name=ngJest])) PropertyAssignment:has(Identifier[name=teardown])',
(node: ts.PropertyAssignment) => {
ngJestTeardownConfig = node.initializer.getText();
return ' ';
}
);
if (!ngJestTeardownConfig) {
return;
}
let maybeUpdatedTestEnvOpts = tsquery.replace(
noTeardownConfig,
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression PropertyAssignment:has(Identifier[name="testEnvironmentOptions"]) ObjectLiteralExpression`,
(node: ts.ObjectLiteralExpression) => {
return `{
${node.properties
.map((p) => getNodeWithComments(noTeardownConfig, p))
.join(',\n')},
teardown: ${ngJestTeardownConfig}
}`;
}
);
if (maybeUpdatedTestEnvOpts !== noTeardownConfig) {
tree.write(configPath, maybeUpdatedTestEnvOpts);
return;
}
// didn't find existing testEnvironmentOptions, so add the new property
const updatedConfig = tsquery.replace(
maybeUpdatedTestEnvOpts,
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression`,
(node: ts.ObjectLiteralExpression) => {
return `{
${node.properties
.map((p) => getNodeWithComments(maybeUpdatedTestEnvOpts, p))
.join(',\n')},
testEnvironmentOptions: { teardown: ${ngJestTeardownConfig} },
}`;
},
{ visitAllChildren: false }
);
tree.write(configPath, updatedConfig);
}
function getGlobalTsJestConfig(node: ts.PropertyAssignment): string {
const globalObject = node.initializer as ts.ObjectLiteralExpression;
const foundConfig = globalObject.properties.find(
(p) => ts.isPropertyAssignment(p) && p.name.getText().includes('ts-jest')
) as ts.PropertyAssignment;
return foundConfig?.initializer?.getText() || '';
}
function getGlobalConfigWithoutTsJest(node: ts.PropertyAssignment): string {
const globalObject = node?.initializer as ts.ObjectLiteralExpression;
const withoutTsJest = globalObject?.properties?.filter((p) => {
return !(
ts.isPropertyAssignment(p) && p.name.getText().includes('ts-jest')
);
});
const globalConfigs = withoutTsJest.map((c) => c.getText()).join(',\n');
return `globals: { ${globalConfigs} }`;
}
function getNodeWithComments(fullText: string, node: ts.Node) {
const commentRanges = ts.getLeadingCommentRanges(
fullText,
node.getFullStart()
);
if (commentRanges?.length > 0) {
const withComments = `${commentRanges
.map((r) => fullText.slice(r.pos, r.end))
.join('\n')}\n${node.getText()}`;
return withComments;
}
return node.getText();
}
export default updateConfigsJest29;

View File

@ -0,0 +1,97 @@
import {
createProjectGraphAsync,
formatFiles,
readProjectConfiguration,
Tree,
visitNotIgnoredFiles,
} from '@nrwl/devkit';
import { JestExecutorOptions } from '@nrwl/jest/src/executors/jest/schema';
import { TEST_FILE_PATTERN } from '@nrwl/jest/src/utils/ast-utils';
import { forEachExecutorOptionsInGraph } from '@nrwl/workspace/src/utilities/executor-options-utils';
import { tsquery } from '@phenomnomnominal/tsquery';
import {
CallExpression,
ImportDeclaration,
VariableStatement,
} from 'typescript';
// NOTE: this is a copy of the @nrwl/jest v15.8.0 migrations
export async function updateTestsJest29(tree: Tree) {
const graph = await createProjectGraphAsync();
forEachExecutorOptionsInGraph<JestExecutorOptions>(
graph,
'@nrwl/nx-plugin:e2e',
(options, projectName) => {
const projectConfig = readProjectConfiguration(tree, projectName);
visitNotIgnoredFiles(
tree,
projectConfig.sourceRoot || projectConfig.root,
(file) => {
if (!TEST_FILE_PATTERN.test(file)) {
return;
}
updateJestMockTypes(tree, file);
updateJestMocked(tree, file);
}
);
}
);
await formatFiles(tree);
}
export function updateJestMockTypes(tree: Tree, filePath: string) {
const contents = tree.read(filePath, 'utf-8');
const updatedContent = tsquery.replace(
contents,
':matches(ImportDeclaration, VariableStatement):has(Identifier[name="MaybeMockedDeep"], Identifier[name="MaybeMocked"]):has(StringLiteral[value="jest-mock"])',
(node: ImportDeclaration | VariableStatement) => {
const text = node.getText();
return (
text
// MaybeMockedDeep and MaybeMocked now are exported as Mocked and MockedShallow
.replace('MaybeMockedDeep', 'Mocked')
.replace('MaybeMocked', 'MockedShallow')
);
}
);
tree.write(filePath, updatedContent);
}
export function updateJestMocked(tree: Tree, filePath: string) {
const contents = tree.read(filePath, 'utf-8');
const jestGlobalNodes = tsquery.query(
contents,
':matches(ImportDeclaration, VariableStatement):has(Identifier[name="jest"]):has(StringLiteral[value="@jest/globals"])'
);
// this only applies if using jest from @jest/globals
if (jestGlobalNodes.length === 0) {
return;
}
const updatedJestMockTypes = tsquery.replace(
contents,
'CallExpression:has(Identifier[name="jest"]):has(Identifier[name="mocked"])',
(node: CallExpression) => {
if (
node.arguments.length === 2 &&
node.getText().startsWith('jest.mocked(')
) {
const text = node.getText();
// jest.mocked(someObject, true); => jest.mocked(someObject);
if (node.arguments[1].getText() === 'true') {
return text.replace(/,\s*true/g, '');
}
// jest.mocked(someObject, false); => jest.mocked(someObject, {shallow: true});
// opt into the new behavior unless explicitly opting out
if (node.arguments[1].getText() === 'false') {
return text.replace('false', '{shallow: true}');
}
}
}
);
tree.write(filePath, updatedJestMockTypes);
}
export default updateTestsJest29;

View File

@ -0,0 +1,464 @@
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import {
ProjectGraph,
readProjectConfiguration,
Tree,
updateProjectConfiguration,
} from '@nrwl/devkit';
import { updateConfigsJest29 } from './jest-29-configs';
import { libraryGenerator } from '@nrwl/workspace';
let projectGraph: ProjectGraph;
jest.mock('@nrwl/devkit', () => ({
...jest.requireActual<any>('@nrwl/devkit'),
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
return projectGraph;
}),
}));
describe('Nx Plugin Migration - jest 29 update configs', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
});
afterAll(() => {
jest.resetAllMocks();
});
it('should update jest.config.ts', async () => {
await setup(tree, 'my-lib');
await updateConfigsJest29(tree);
const actualJestConfigTs = tree.read('libs/my-lib/jest.config.ts', 'utf-8');
expect(actualJestConfigTs).toMatchSnapshot();
const actualJestConfigJs = tree.read('libs/my-lib/jest.config.js', 'utf-8');
expect(actualJestConfigJs).toMatchSnapshot();
});
it('should update root preset', async () => {
await setup(tree, 'my-lib');
await updateConfigsJest29(tree);
const actualPreset = tree.read('jest.preset.js', 'utf-8');
expect(actualPreset).toMatchSnapshot();
const actualJestConfigTs = tree.read('libs/my-lib/jest.config.ts', 'utf-8');
expect(actualJestConfigTs).toMatchSnapshot();
const actualJestConfigJs = tree.read('libs/my-lib/jest.config.js', 'utf-8');
expect(actualJestConfigJs).toMatchSnapshot();
});
it('should update root preset if ts-jest is preset', async () => {
await setup(tree, 'my-lib');
tree.write(
'jest.preset.js',
`const nxPreset = require('@nrwl/jest/preset').default;
module.exports = {
...nxPreset,
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json'
},
something: 'else',
abc: [1234, true, {abc: 'yes'}]
},
transform: {
'^.+\\.(ts|js|html)$': 'ts-jest',
},
resolver: '@nrwl/jest/plugins/resolver',
moduleFileExtensions: ['ts', 'js', 'html'],
coverageReporters: ['html'],
};
`
);
await updateConfigsJest29(tree);
const actualPreset = tree.read('jest.preset.js', 'utf-8');
expect(actualPreset).toMatchSnapshot();
});
it('should NOT update ts-jest with no globals are preset', async () => {
await setup(tree, 'my-lib');
tree.write(
'jest.preset.js',
`const nxPreset = require('@nrwl/jest/preset').default;
module.exports = {
...nxPreset,
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
transform: {
'^.+\\.(ts|js|html)$': 'ts-jest',
},
resolver: '@nrwl/jest/plugins/resolver',
moduleFileExtensions: ['ts', 'js', 'html'],
coverageReporters: ['html'],
};
`
);
await updateConfigsJest29(tree);
const actualPreset = tree.read('jest.preset.js', 'utf-8');
expect(actualPreset).toMatchSnapshot();
});
it('should add snapshot config with no root preset', async () => {
await setup(tree, 'my-lib');
tree.delete('jest.preset.js');
await updateConfigsJest29(tree);
const actualJestConfigTs = tree.read('libs/my-lib/jest.config.ts', 'utf-8');
expect(actualJestConfigTs).toMatchSnapshot();
const actualJestConfigJs = tree.read('libs/my-lib/jest.config.js', 'utf-8');
expect(actualJestConfigJs).toMatchSnapshot();
});
it('should work with multiple projects + configs', async () => {
await setup(tree, 'my-lib');
await setup(tree, 'another-lib', projectGraph);
await updateConfigsJest29(tree);
const actualJestConfigTs1 = tree.read(
'libs/my-lib/jest.config.ts',
'utf-8'
);
expect(actualJestConfigTs1).toMatchSnapshot();
const actualJestConfigJs1 = tree.read(
'libs/my-lib/jest.config.js',
'utf-8'
);
expect(actualJestConfigJs1).toMatchSnapshot();
const actualJestConfigTs2 = tree.read(
'libs/another-lib/jest.config.ts',
'utf-8'
);
expect(actualJestConfigTs2).toMatchSnapshot();
const actualJestConfigJs2 = tree.read(
'libs/another-lib/jest.config.js',
'utf-8'
);
expect(actualJestConfigJs2).toMatchSnapshot();
});
it('should update globalThis.ngJest.teardown to testEnvironmentOptions ', async () => {
await setup(tree, 'jest-preset-angular');
tree.write(
`libs/jest-preset-angular/jest.config.ts`,
`globalThis.ngJest = {
teardown: true
}
export default {
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
}
},
transform: {
'^.+.(ts|mjs|js|html)$': 'jest-preset-angular',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};`
);
tree.write(
`libs/jest-preset-angular/jest.config.js`,
`
globalThis.ngJest = {
ngcc: true,
teardown: false
}
module.exports = {
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
}
},
transform: {
'^.+.(ts|mjs|js|html)$': 'jest-preset-angular',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
testEnvironmentOptions: {
blah: 123,
},
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};`
);
await updateConfigsJest29(tree);
const jpaJestConfigTs = tree.read(
`libs/jest-preset-angular/jest.config.ts`,
'utf-8'
);
expect(jpaJestConfigTs).toMatchSnapshot();
const jpaJestConfigJs = tree.read(
`libs/jest-preset-angular/jest.config.js`,
'utf-8'
);
expect(jpaJestConfigJs).toMatchSnapshot();
});
it('should work with jest-preset-angular', async () => {
await setup(tree, 'jest-preset-angular');
tree.write(
`libs/jest-preset-angular/jest.config.ts`,
`export default {
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
}
},
transform: {
'^.+.(ts|mjs|js|html)$': 'jest-preset-angular',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};`
);
tree.write(
`libs/jest-preset-angular/jest.config.js`,
`module.exports = {
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
}
},
transform: {
'^.+.(ts|mjs|js|html)$': 'jest-preset-angular',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};`
);
await updateConfigsJest29(tree);
const jpaJestConfigTs = tree.read(
`libs/jest-preset-angular/jest.config.ts`,
'utf-8'
);
expect(jpaJestConfigTs).toMatchSnapshot();
const jpaJestConfigJs = tree.read(
`libs/jest-preset-angular/jest.config.js`,
'utf-8'
);
expect(jpaJestConfigJs).toMatchSnapshot();
});
it('should work if not using ts-jest transformer', async () => {
await setup(tree, 'no-ts-jest');
tree.write(
`libs/no-ts-jest/jest.config.ts`,
`export default {
transform: {
'^.+\\\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};`
);
tree.write(
`libs/no-ts-jest/jest.config.js`,
`module.exports = {
transform: {
'^.+\\\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
};`
);
await updateConfigsJest29(tree);
const noTsJestConfigTs = tree.read(
`libs/no-ts-jest/jest.config.ts`,
'utf-8'
);
expect(noTsJestConfigTs).toMatchSnapshot();
const noTsJestConfigJs = tree.read(
`libs/no-ts-jest/jest.config.js`,
'utf-8'
);
expect(noTsJestConfigJs).toMatchSnapshot();
});
it('should work snapshotFormat is defined', async () => {
await setup(tree, 'no-ts-jest');
tree.write(
`libs/no-ts-jest/jest.config.ts`,
`export default {
transform: {
'^.+\\\\.[tj]sx?$': 'babel-jest',
},
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json'
},
something: 'else',
abc: [1234, true, {abc: 'yes'}]
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
snapshotFormat: {escapeString: false, printBasicPrototype: true}
};`
);
tree.write(
`libs/no-ts-jest/jest.config.js`,
`module.exports = {
transform: {
'^.+\\\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json'
},
something: 'else',
abc: [1234, true, {abc: 'yes'}]
},
snapshotFormat: {escapeString: false, printBasicPrototype: true}
};`
);
await updateConfigsJest29(tree);
const snapshotJestConfigTs = tree.read(
'libs/no-ts-jest/jest.config.ts',
'utf-8'
);
expect(snapshotJestConfigTs).toMatchSnapshot();
const snapshotJestConfigJs = tree.read(
`libs/no-ts-jest/jest.config.js`,
'utf-8'
);
expect(snapshotJestConfigJs).toMatchSnapshot();
});
it('should be idempotent', async () => {
await setup(tree, 'my-lib');
await updateConfigsJest29(tree);
const actualJestConfigTs1 = tree.read(
'libs/my-lib/jest.config.ts',
'utf-8'
);
expect(actualJestConfigTs1).toMatchSnapshot();
const actualJestConfigJs1 = tree.read(
'libs/my-lib/jest.config.js',
'utf-8'
);
expect(actualJestConfigJs1).toMatchSnapshot();
await updateConfigsJest29(tree);
const actualJestConfigTs2 = tree.read(
'libs/my-lib/jest.config.ts',
'utf-8'
);
expect(actualJestConfigTs2).toEqual(actualJestConfigTs1);
const actualJestConfigJs2 = tree.read(
'libs/my-lib/jest.config.js',
'utf-8'
);
expect(actualJestConfigJs2).toEqual(actualJestConfigJs1);
});
});
async function setup(tree: Tree, name: string, existingGraph?: ProjectGraph) {
await libraryGenerator(tree, {
name,
});
const projectConfig = readProjectConfiguration(tree, name);
projectConfig.targets['test'] = {
...projectConfig.targets['test'],
executor: '@nrwl/nx-plugin:e2e',
configurations: {
ci: {
ci: true,
},
other: {
jestConfig: `libs/${name}/jest.config.js`,
},
},
};
updateProjectConfiguration(tree, name, projectConfig);
tree.write(
`libs/${name}/jest.config.ts`,
`/* eslint-disable */
export default {
displayName: '${name}',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
}
},
transform: {
'^.+\\\\.[tj]sx?$': 'ts-jest'
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/${name}'
};
`
);
tree.write(
`libs/${name}/jest.config.js`,
`module.exports = {
transform: {
'^.+\\\\.[tj]sx?$': 'ts-jest'
},
// I am a comment and shouldn't be removed
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json'
},
something: 'else',
abc: [1234, true, {abc: 'yes'}]
},
/**
* Multi-line comment shouldn't be removed
*/
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js'
};
`
);
projectGraph = {
dependencies: {
...existingGraph?.dependencies,
},
nodes: {
...existingGraph?.nodes,
[name]: {
name,
type: 'lib',
data: projectConfig,
} as any,
},
};
}

View File

@ -0,0 +1,181 @@
import {
ProjectGraph,
readProjectConfiguration,
Tree,
updateProjectConfiguration,
} from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { libraryGenerator } from '@nrwl/workspace';
import { updateTestsJest29 } from './jest-29-tests';
let projectGraph: ProjectGraph;
jest.mock('@nrwl/devkit', () => ({
...jest.requireActual<any>('@nrwl/devkit'),
createProjectGraphAsync: jest
.fn()
.mockImplementation(async () => projectGraph),
}));
describe('Nx Plugin Migration - jest 29 mocked usage in tests', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
});
it('should not update anything if there are no tests', async () => {
await setup(tree, 'my-lib');
const expected = tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8');
await updateTestsJest29(tree);
expect(
tree.read('libs/my-lib/src/file-one.spec.ts', 'utf-8')
).toMatchSnapshot();
expect(
tree.read('libs/my-lib/src/file-two.spec.ts', 'utf-8')
).toMatchSnapshot();
expect(tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8')).toEqual(
expected
);
});
it('should be idempotent', async () => {
await setup(tree, 'my-lib');
const expected = tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8');
await updateTestsJest29(tree);
expect(
tree.read('libs/my-lib/src/file-one.spec.ts', 'utf-8')
).toMatchSnapshot();
expect(
tree.read('libs/my-lib/src/file-two.spec.ts', 'utf-8')
).toMatchSnapshot();
expect(tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8')).toEqual(
expected
);
await updateTestsJest29(tree);
expect(
tree.read('libs/my-lib/src/file-one.spec.ts', 'utf-8')
).toMatchSnapshot();
expect(
tree.read('libs/my-lib/src/file-two.spec.ts', 'utf-8')
).toMatchSnapshot();
expect(tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8')).toEqual(
expected
);
});
});
async function setup(tree: Tree, name: string) {
await libraryGenerator(tree, {
name,
});
const projectConfig = readProjectConfiguration(tree, name);
projectConfig.targets['test'] = {
...projectConfig.targets['test'],
configurations: {
ci: {
ci: true,
},
other: {
jestConfig: `libs/${name}/jest.config.js`,
},
},
};
updateProjectConfiguration(tree, name, projectConfig);
tree.write(
`libs/${name}/jest.config.js`,
`module.exports = {
transform: {
'^.+\\\\.[tj]sx?$': 'ts-jest'
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json'
}
},
displayName: 'jest',
testEnvironment: 'node',
preset: '../../jest.preset.js'
};
`
);
tree.write(
`libs/${name}/src/file-one.spec.ts`,
`
import{ MaybeMockedDeep, MaybeMocked } from 'jest-mock';
import {expect, jest, test} from '@jest/globals';
import {song} from './song';
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song, true);
// or through \`jest.Mocked<Source>\`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
});
`
);
tree.write(
`libs/${name}/src/file-two.spec.ts`,
`
const { MaybeMockedDeep, MaybeMocked } = require('jest-mock');
const {expect, jest, test} = require('@jest/globals');
const {song} = require('./song');
jest.mock('./song');
jest.spyOn(console, 'log');
const mockedSong = jest.mocked(song, true);
// or through \`jest.Mocked<Source>\`
// const mockedSong = song as jest.Mocked<typeof song>;
test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);
expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});
test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});
console.log('one more time');
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
});
`
);
projectGraph = {
dependencies: {},
nodes: {
[name]: {
name,
type: 'lib',
data: projectConfig,
} as any,
},
};
}