Skip to content

Commit

Permalink
feat(cli): change react to create-react-app
Browse files Browse the repository at this point in the history
As reported (stryker-mutator#1435) the react preset is only applicable to a react project set up by create-react-app.
In addition, the error (unable to locate package react-scripts) isn't that useful either.
Therefore, I propose we change the projectType to react-create-app as it resembles the applicability of this preset.
  • Loading branch information
Peter Wessels authored and Peter Wessels committed Jan 21, 2020
1 parent 97eed08 commit dbf961e
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 10 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/initializer/presets/ReactPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/ma
* https://github.com/stryker-mutator/stryker-handbook/blob/master/stryker/guides/react.md#react
*/
export class ReactPreset implements Preset {
public readonly name = 'react';
public readonly name = 'create-react-app';
private readonly generalDependencies = ['@stryker-mutator/core', '@stryker-mutator/jest-runner', '@stryker-mutator/html-reporter'];

private readonly sharedConfig = `testRunner: 'jest',
reporters: ['progress', 'clear-text', 'html'],
coverageAnalysis: 'off',
jest: {
projectType: 'react'
projectType: 'create-react-app'
}
`;

Expand Down
4 changes: 2 additions & 2 deletions packages/core/test/unit/initializer/Presets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ describe('Presets', () => {
reactPreset = new ReactPreset();
});

it('should have the name "react"', () => {
expect(reactPreset.name).to.eq('react');
it('should have the name "create-react-app"', () => {
expect(reactPreset.name).to.eq('create-react-app');
});

it('should mutate typescript when TSX is chosen', async () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/jest-runner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ The @stryker-mutator/jest-runner also provides a couple of configurable options
| option | description | default value | alternative values |
|----|----|----|---|
| projectType (optional) | The type of project you are working on. | `custom` | `custom` uses the `config` option (see below)|
| | | | `react` when you are using [create-react-app](https://github.com/facebook/create-react-app) |
| | | | `react-ts` when you are using [create-react-app-typescript](https://github.com/wmonk/create-react-app-typescript) |
| | | | `create-react-app` when you are using [create-react-app](https://github.com/facebook/create-react-app) |
| | | | `create-react-app-ts` when you are using [create-react-app-typescript](https://github.com/wmonk/create-react-app-typescript) |
| config (optional) | A custom Jest configuration object. You could also use `require` to load it here) | undefined | |
| enableFindRelatedTests (optional) | Whether to run jest with the `--findRelatedTests` flag. When `true`, Jest will only run tests related to the mutated file per test. (See [_--findRelatedTests_](https://jestjs.io/docs/en/cli.html#findrelatedtests-spaceseparatedlistofsourcefiles)) | true | false |

Expand Down
16 changes: 16 additions & 0 deletions packages/jest-runner/src/JestConfigEditor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Config, ConfigEditor } from '@stryker-mutator/api/config';
import { Logger } from '@stryker-mutator/api/logging';
import jest from 'jest';
import { commonTokens, tokens } from '@stryker-mutator/api/plugin';

import CustomJestConfigLoader from './configLoaders/CustomJestConfigLoader';
import JestConfigLoader from './configLoaders/JestConfigLoader';
Expand All @@ -10,6 +12,10 @@ import JEST_OVERRIDE_OPTIONS from './jestOverrideOptions';
const DEFAULT_PROJECT_NAME = 'custom';

export default class JestConfigEditor implements ConfigEditor {
public static inject = tokens(commonTokens.logger);

constructor(private readonly log: Logger) {}

public edit(strykerConfig: Config): void {
// If there is no Jest property on the Stryker config create it
strykerConfig.jest = strykerConfig.jest || {};
Expand All @@ -28,9 +34,19 @@ export default class JestConfigEditor implements ConfigEditor {
switch (projectType.toLowerCase()) {
case DEFAULT_PROJECT_NAME:
return new CustomJestConfigLoader(process.cwd());
case 'create-react-app':
return new ReactScriptsJestConfigLoader(process.cwd());
case 'create-react-app-ts':
return new ReactScriptsTSJestConfigLoader(process.cwd());
case 'react':
this.log.warn(
'DEPRECATED: The projectType "react" is deprecated. Use projectType "create-react-app" for react projects created by "create-react-app" or use "custom" for other react projects.'
);
return new ReactScriptsJestConfigLoader(process.cwd());
case 'react-ts':
this.log.warn(
'DEPRECATED: The projectType "react-ts" is deprecated. Use projectType "create-react-app-ts" for react projects created by "create-react-app" or use "custom" for other react projects.'
);
return new ReactScriptsTSJestConfigLoader(process.cwd());
default:
throw new Error(`No configLoader available for ${projectType}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class ReactScriptsJestConfigLoader implements JestConfigLoader {
return jestConfiguration;
} catch (e) {
if (this.isNodeErrnoException(e) && e.code === 'MODULE_NOT_FOUND') {
throw Error('Unable to locate package react-scripts. This package is required when projectType is set to "react".');
throw Error('Unable to locate package react-scripts. This package is required when projectType is set to "create-react-app".');
}
throw e;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class ReactScriptsTSJestConfigLoader implements JestConfigLoader
return jestConfiguration;
} catch (e) {
if (this.isNodeErrnoException(e) && e.code === 'MODULE_NOT_FOUND') {
throw Error('Unable to locate package react-scripts-ts. ' + 'This package is required when projectType is set to "react-ts".');
throw Error('Unable to locate package react-scripts-ts. ' + 'This package is required when projectType is set to "create-react-app-ts".');
}
throw e;
}
Expand Down
92 changes: 92 additions & 0 deletions packages/jest-runner/test/integration/JestConfigEditor.it.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,39 @@ describe('Integration test for Jest ConfigEditor', () => {
config = new Config();
});

it('should create a Jest configuration for a create-react-app project', () => {
config.set({ jest: { projectType: 'create-react-app' } });

jestConfigEditor.edit(config);

const expectedResult = {
bail: false,
collectCoverage: false,
collectCoverageFrom: ['!src/**/*.d.ts', 'src/**/*.{js,jsx,ts,tsx}'],
moduleFileExtensions: ['js', 'json', 'jsx', 'node', 'ts', 'tsx', 'web.js', 'web.jsx', 'web.ts', 'web.tsx'],
moduleNameMapper: {
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
'^react-native$': 'react-native-web'
},
notify: false,
rootDir: projectRoot,
setupFiles: [path.join(projectRoot, 'node_modules', 'react-app-polyfill', 'jsdom.js')],
setupTestFrameworkScriptFile: undefined,
testEnvironment: 'jsdom',
testMatch: ['<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}', '<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}'],
testResultsProcessor: undefined,
transform: {
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': path.join(projectRoot, 'node_modules', 'react-scripts', 'config', 'jest', 'fileTransform.js'),
'^.+\\.(js|jsx|ts|tsx)$': path.join(projectRoot, 'node_modules', 'react-scripts', 'config', 'jest', 'babelTransform.js'),
'^.+\\.css$': path.join(projectRoot, 'node_modules', 'react-scripts', 'config', 'jest', 'cssTransform.js')
},
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$', '^.+\\.module\\.(css|sass|scss)$'],
verbose: false
};

assertJestConfig(expectedResult, config.jest.config);
});

it('should create a Jest configuration for a React project', () => {
config.set({ jest: { projectType: 'react' } });

Expand Down Expand Up @@ -56,6 +89,55 @@ describe('Integration test for Jest ConfigEditor', () => {
assertJestConfig(expectedResult, config.jest.config);
});

it('should log a deprecation warning when projectType is "react"', () => {
config.set({ jest: { projectType: 'react' } });

jestConfigEditor.edit(config);

expect(testInjector.logger.warn).calledWith(
'DEPRECATED: The projectType "react" is deprecated. Use projectType "create-react-app" for react projects created by "create-react-app" or use "custom" for other react projects.'
);
});

it('should create a Jest configuration for a create-react-app + TypeScript project', () => {
config.set({ jest: { projectType: 'create-react-app-ts' } });

jestConfigEditor.edit(config);

const expectedResult = {
bail: false,
collectCoverage: false,
collectCoverageFrom: ['!**/*.d.ts', 'src/**/*.{js,jsx,ts,tsx}'],
globals: {
'ts-jest': {
tsConfigFile: path.join(projectRoot, 'testResources', 'reactTsProject', 'tsconfig.test.json')
}
},
moduleFileExtensions: ['web.ts', 'ts', 'web.tsx', 'tsx', 'web.js', 'js', 'web.jsx', 'jsx', 'json', 'node', 'mjs'],
moduleNameMapper: {
'^react-native$': 'react-native-web'
},
notify: false,
rootDir: projectRoot,
setupFiles: [path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'polyfills.js')],
setupTestFrameworkScriptFile: undefined,
testEnvironment: 'jsdom',
testMatch: ['<rootDir>/src/**/__tests__/**/*.(j|t)s?(x)', '<rootDir>/src/**/?(*.)(spec|test).(j|t)s?(x)'],
testResultsProcessor: undefined,
testURL: 'http://localhost',
transform: {
'^(?!.*\\.(js|jsx|mjs|css|json)$)': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'fileTransform.js'),
'^.+\\.(js|jsx|mjs)$': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'babelTransform.js'),
'^.+\\.css$': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'cssTransform.js'),
'^.+\\.tsx?$': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'typescriptTransform.js')
},
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|ts|tsx)$'],
verbose: false
};

assertJestConfig(expectedResult, config.jest.config);
});

it('should create a Jest configuration for a React + TypeScript project', () => {
config.set({ jest: { projectType: 'react-ts' } });

Expand Down Expand Up @@ -95,6 +177,16 @@ describe('Integration test for Jest ConfigEditor', () => {
assertJestConfig(expectedResult, config.jest.config);
});

it('should log a deprecation warning when projectType is "react-ts"', () => {
config.set({ jest: { projectType: 'react-ts' } });

jestConfigEditor.edit(config);

expect(testInjector.logger.warn).calledWith(
'DEPRECATED: The projectType "react-ts" is deprecated. Use projectType "create-react-app-ts" for react projects created by "create-react-app" or use "custom" for other react projects.'
);
});

it('should load the Jest configuration from the jest.config.js', () => {
getProjectRootStub.returns(path.join(process.cwd(), 'testResources', 'exampleProjectWithExplicitJestConfig'));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ describe(ReactScriptsJestConfigLoader.name, () => {
requireResolveStub.throws(error);

// Act & Assert
expect(() => sut.loadConfig()).throws('Unable to locate package react-scripts. This package is required when projectType is set to "react".');
expect(() => sut.loadConfig()).throws(
'Unable to locate package react-scripts. This package is required when projectType is set to "create-react-app".'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe(ReactScriptsTSJestConfigLoader.name, () => {

// Act & Assert
expect(() => sut.loadConfig()).throws(
'Unable to locate package react-scripts-ts. This package is required when projectType is set to "react-ts".'
'Unable to locate package react-scripts-ts. This package is required when projectType is set to "create-react-app-ts".'
);
});
});

0 comments on commit dbf961e

Please sign in to comment.