Skip to content

Commit

Permalink
feat(testing): add preprocessor for react native jest
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Jun 24, 2022
1 parent fc45883 commit 89ae996
Show file tree
Hide file tree
Showing 8 changed files with 229 additions and 12 deletions.
15 changes: 6 additions & 9 deletions packages/react-native/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
"cli": "nx",
"description": "Rename blacklistRE to blockList in metro.config.js",
"factory": "./src/migrations/update-14-2-1/rename-blockList-metro-config"
},
"rename-jest-preprocessor-14-4-0": {
"version": "14.4.0-beta.0",
"cli": "nx",
"description": "Rename preprocessor in jest.config",
"factory": "./src/migrations/update-14-4-0/rename-jest-preprocessor"
}
},
"packageJsonUpdates": {
Expand Down Expand Up @@ -675,15 +681,6 @@
"alwaysAddToPackageJson": false
}
}
},
"14.4.0": {
"version": "14.4.0-beta.0",
"packages": {
"jest-jasmine2": {
"version": "~28.1.1",
"alwaysAddToPackageJson": false
}
}
}
}
}
89 changes: 89 additions & 0 deletions packages/react-native/plugins/jest/preprocessor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* From https://github.com/facebook/react-native/blob/main/jest/private/preprocessor.js
* Need to change it to be compatible with jest 28
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/

/* eslint-env node */

'use strict';

const babelRegisterOnly = require('metro-babel-register');
const createCacheKeyFunction =
require('@jest/create-cache-key-function').default;

const { transformSync: babelTransformSync } = require('@babel/core');
const generate = require('@babel/generator').default;

const nodeFiles = new RegExp(
[
'/metro(?:-[^/]*)?/', // metro, metro-core, metro-source-map, metro-etc.
].join('|')
);
const nodeOptions = babelRegisterOnly.config([nodeFiles]);

babelRegisterOnly([]);

const transformer = require('metro-react-native-babel-transformer');
module.exports = {
process(src /*: string */, file /*: string */) /*: {code: string, ...} */ {
if (nodeFiles.test(file)) {
// node specific transforms only
return {
code: babelTransformSync(src, {
filename: file,
sourceType: 'script',
...nodeOptions,
ast: false,
}),
};
}

const { ast } = transformer.transform({
filename: file,
options: {
ast: true, // needed for open source (?) https://github.com/facebook/react-native/commit/f8d6b97140cffe8d18b2558f94570c8d1b410d5c#r28647044
dev: true,
enableBabelRuntime: false,
experimentalImportSupport: false,
globalPrefix: '',
hot: false,
inlineRequires: true,
minify: false,
platform: '',
projectRoot: '',
publicPath: '/assets',
retainLines: true,
sourceType: 'unambiguous', // b7 required. detects module vs script mode
},
src,
});

return generate(
ast,
// $FlowFixMe[prop-missing] Error found when improving flow typing for libs
{
code: true,
comments: false,
compact: false,
filename: file,
retainLines: true,
sourceFileName: file,
sourceMaps: true,
},
src
);
},

getCacheKey: (createCacheKeyFunction([
__filename,
require.resolve('metro-react-native-babel-transformer'),
require.resolve('@babel/core/package.json'),
]) /*: any */),
};
5 changes: 5 additions & 0 deletions packages/react-native/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
"glob": "**/files/**/.babelrc.template",
"output": "/"
},
{
"input": "packages/react-native",
"glob": "**/preprocessor.js",
"output": "/"
},
{
"input": "packages/react-native/src",
"glob": "**/*.!(ts)",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { addProjectConfiguration, readJson, Tree } from '@nrwl/devkit';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import update from './rename-jest-preprocessor';

describe('Rename jest preprocessor', () => {
let tree: Tree;

beforeEach(async () => {
tree = createTreeWithEmptyWorkspace();
addProjectConfiguration(tree, 'products', {
root: 'apps/products',
sourceRoot: 'apps/products/src',
targets: {
start: {
executor: '@nrwl/react-native:start',
options: {
port: 8081,
},
},
test: {
executor: '@nrwl/jest:jest',
options: {
jestConfig: 'apps/products/jest.config.js',
passWithNoTests: true,
},
},
},
});
});

it(`should not update if the code does not contain existing preprocessor`, async () => {
tree.write(
'apps/products/jest.config.js',
`module.exports = {
preset: 'react-native',
};`
);
await update(tree);

const jestConfig = tree.read('apps/products/jest.config.js', 'utf-8');
expect(jestConfig).not.toContain(
`@nrwl/react-native/plugins/jest/preprocessor.js`
);
});

it(`should update if the code contains existing preprocessor`, async () => {
tree.write(
'apps/products/jest.config.js',
`module.exports = {
preset: 'react-native',
testRunner: 'jest-jasmine2',
transform: {
'\\.(js|ts|tsx)$': require.resolve('react-native/jest/preprocessor.js'),
'^.+\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp)$': require.resolve(
'react-native/jest/assetFileTransformer.js'
),
}
};`
);
await update(tree);

const jestConfig = tree.read('apps/products/jest.config.js', 'utf-8');
expect(jestConfig).toContain(
`@nrwl/react-native/plugins/jest/preprocessor.js`
);
expect(jestConfig).not.toContain(`testRunner: 'jest-jasmine2',`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
Tree,
formatFiles,
getProjects,
ProjectConfiguration,
logger,
stripIndents,
} from '@nrwl/devkit';

/**
* Rename reprocessor in jest to be from @nrwl/react-native
* and remove the testRunner
*/
export default async function update(tree: Tree) {
const projects = getProjects(tree);

projects.forEach((project) => {
if (project.targets?.start?.executor !== '@nrwl/react-native:start') return;

mockSvgInJestConfig(tree, project);
});

await formatFiles(tree);
}

function mockSvgInJestConfig(host: Tree, project: ProjectConfiguration) {
const jestConfigPath = project.targets?.test?.options?.jestConfig;
if (!jestConfigPath || !host.exists(jestConfigPath)) return;
try {
const contents = host.read(jestConfigPath, 'utf-8');
if (contents.includes('@nrwl/react-native/plugins/jest/preprocessor.js'))
return;
host.write(
jestConfigPath,
contents
.replace(
'react-native/jest/preprocessor.js',
'@nrwl/react-native/plugins/jest/preprocessor.js'
)
.replace(`testRunner: 'jest-jasmine2',\n`, '')
);
} catch {
logger.error(
stripIndents`Unable to update ${jestConfigPath} for project ${project.root}.`
);
}
}
3 changes: 1 addition & 2 deletions packages/react-native/src/utils/add-jest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,14 @@ export async function addJest(
const content = `module.exports = {
displayName: '${projectName}',
preset: 'react-native',
testRunner: 'jest-jasmine2',
resolver: '@nrwl/jest/plugins/resolver',
moduleFileExtensions: ['ts', 'js', 'html', 'tsx', 'jsx'],
setupFilesAfterEnv: ['<rootDir>/test-setup.${js ? 'js' : 'ts'}'],
moduleNameMapper: {
'\\.svg': '@nrwl/react-native/plugins/jest/svg-mock'
},
transform: {
'\\\\.(js|ts|tsx)$': require.resolve('react-native/jest/preprocessor.js'),
'\\\\.(js|ts|tsx)$': require.resolve('@nrwl/react-native/plugins/jest/preprocessor.js'),
'^.+\\\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp)$': require.resolve(
'react-native/jest/assetFileTransformer.js',
),
Expand Down
7 changes: 6 additions & 1 deletion packages/react-native/tsconfig.lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"declaration": true,
"types": ["node"]
},
"exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"],
"exclude": [
"**/*.spec.ts",
"**/*.test.ts",
"jest.config.ts",
"plugin/jest/preprocessor.js"
],
"include": ["**/*.ts"]
}
7 changes: 7 additions & 0 deletions scripts/depcheck/missing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ const IGNORE_MATCHES = {
],
nest: ['semver'],
'make-angular-cli-faster': ['@angular/core'],
'react-native': [
'@babel/core',
'@babel/generator',
'@jest/create-cache-key-function',
'metro-babel-register',
'metro-react-native-babel-transformer',
],
};

export default async function getMissingDependencies(
Expand Down

0 comments on commit 89ae996

Please sign in to comment.