405 lines
12 KiB
TypeScript
405 lines
12 KiB
TypeScript
import { serializeJson } from '@nrwl/workspace';
|
|
import {
|
|
checkFilesDoNotExist,
|
|
checkFilesExist,
|
|
newProject,
|
|
readFile,
|
|
readJson,
|
|
renameFile,
|
|
runCLI,
|
|
runCLIAsync,
|
|
uniq,
|
|
updateFile,
|
|
workspaceConfigName,
|
|
} from '@nrwl/e2e/utils';
|
|
|
|
describe('React Applications', () => {
|
|
it('should be able to generate a react app + lib', async () => {
|
|
newProject();
|
|
const appName = uniq('app');
|
|
const libName = uniq('lib');
|
|
|
|
runCLI(`generate @nrwl/react:app ${appName} --style=css --no-interactive`);
|
|
runCLI(`generate @nrwl/react:lib ${libName} --style=css --no-interactive`);
|
|
|
|
// Libs should not include package.json by default
|
|
checkFilesDoNotExist(`libs/${libName}/package.json`);
|
|
|
|
const mainPath = `apps/${appName}/src/main.tsx`;
|
|
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));
|
|
|
|
const libTestResults = await runCLIAsync(`test ${libName}`);
|
|
expect(libTestResults.combinedOutput).toContain(
|
|
'Test Suites: 1 passed, 1 total'
|
|
);
|
|
|
|
await testGeneratedApp(appName, {
|
|
checkStyles: true,
|
|
checkProdBuild: true,
|
|
checkLinter: true,
|
|
checkE2E: true,
|
|
});
|
|
}, 120000);
|
|
|
|
it('should support vendor sourcemaps', () => {
|
|
newProject();
|
|
const appName = uniq('app');
|
|
|
|
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
|
|
|
|
// By default, vendor sourcemaps are off
|
|
runCLI(`build ${appName}`);
|
|
|
|
let vendorContent = readFile(`dist/apps/${appName}/vendor.js`);
|
|
|
|
expect(vendorContent).not.toMatch(/sourceMappingURL/);
|
|
|
|
// Turn vendor sourcemaps on
|
|
updateFile(`workspace.json`, (content) => {
|
|
const json = JSON.parse(content);
|
|
json.projects[appName].architect.build.options.sourceMap = {
|
|
scripts: true,
|
|
vendor: true,
|
|
};
|
|
return JSON.stringify(json, null, 2);
|
|
});
|
|
|
|
runCLI(`build ${appName}`);
|
|
vendorContent = readFile(`dist/apps/${appName}/vendor.js`);
|
|
|
|
expect(vendorContent).toMatch(/sourceMappingURL/);
|
|
}, 120000);
|
|
|
|
it('should be able to generate a publishable react lib', async () => {
|
|
newProject();
|
|
const libName = uniq('lib');
|
|
|
|
runCLI(
|
|
`generate @nrwl/react:lib ${libName} --publishable --importPath=@proj/${libName} --no-interactive`
|
|
);
|
|
|
|
const libTestResults = await runCLIAsync(
|
|
`build ${libName} --no-extract-css`
|
|
);
|
|
expect(libTestResults.stdout).toContain('Bundle complete.');
|
|
|
|
checkFilesExist(
|
|
`dist/libs/${libName}/package.json`,
|
|
`dist/libs/${libName}/index.d.ts`,
|
|
`dist/libs/${libName}/${libName}.esm.js`,
|
|
`dist/libs/${libName}/${libName}.umd.js`
|
|
);
|
|
|
|
checkFilesDoNotExist(
|
|
`dist/libs/${libName}/${libName}.esm.css`,
|
|
`dist/libs/${libName}/${libName}.umd.css`
|
|
);
|
|
|
|
await runCLIAsync(`build ${libName} --extract-css`);
|
|
|
|
checkFilesExist(
|
|
`dist/libs/${libName}/package.json`,
|
|
`dist/libs/${libName}/index.d.ts`,
|
|
`dist/libs/${libName}/${libName}.esm.css`,
|
|
`dist/libs/${libName}/${libName}.esm.js`,
|
|
`dist/libs/${libName}/${libName}.umd.css`,
|
|
`dist/libs/${libName}/${libName}.umd.js`
|
|
);
|
|
}, 120000);
|
|
|
|
it('should be able to generate a react lib with no components', async () => {
|
|
newProject();
|
|
const appName = uniq('app');
|
|
const libName = uniq('lib');
|
|
|
|
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
|
|
runCLI(
|
|
`generate @nrwl/react:lib ${libName} --no-interactive --no-component`
|
|
);
|
|
|
|
const mainPath = `apps/${appName}/src/main.tsx`;
|
|
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));
|
|
|
|
const libTestResults = await runCLIAsync(`test ${libName}`);
|
|
expect(libTestResults.stderr).toBe('');
|
|
|
|
await testGeneratedApp(appName, {
|
|
checkStyles: true,
|
|
checkProdBuild: false,
|
|
checkLinter: false,
|
|
checkE2E: false,
|
|
});
|
|
}, 120000);
|
|
|
|
it('should not create a dist folder if there is an error', async () => {
|
|
newProject();
|
|
const libName = uniq('lib');
|
|
|
|
runCLI(
|
|
`generate @nrwl/react:lib ${libName} --publishable --importPath=@proj/${libName} --no-interactive`
|
|
);
|
|
|
|
const mainPath = `libs/${libName}/src/lib/${libName}.tsx`;
|
|
updateFile(mainPath, readFile(mainPath) + `\n console.log(a);`); // should error - "a" will be undefined
|
|
|
|
await expect(runCLIAsync(`build ${libName}`)).rejects.toThrow(
|
|
/Bundle failed/
|
|
);
|
|
expect(() => {
|
|
checkFilesExist(`dist/libs/${libName}/package.json`);
|
|
}).toThrow();
|
|
}, 120000);
|
|
|
|
it('should generate app with routing', async () => {
|
|
newProject();
|
|
const appName = uniq('app');
|
|
|
|
runCLI(`generate @nrwl/react:app ${appName} --routing --no-interactive`);
|
|
|
|
await testGeneratedApp(appName, {
|
|
checkStyles: true,
|
|
checkProdBuild: false,
|
|
checkLinter: false,
|
|
checkE2E: false,
|
|
});
|
|
}, 120000);
|
|
|
|
it('should generate app with different style options', async () => {
|
|
newProject();
|
|
|
|
const styledComponentsApp = uniq('app');
|
|
|
|
runCLI(
|
|
`generate @nrwl/react:app ${styledComponentsApp} --style styled-components --no-interactive`
|
|
);
|
|
|
|
// Checking that we can enable displayName just for development.
|
|
updateFile(
|
|
`apps/${styledComponentsApp}/.babelrc`,
|
|
JSON.stringify(
|
|
{
|
|
presets: ['@nrwl/react/babel'],
|
|
plugins: [
|
|
['styled-components', { pure: true, ssr: true, displayName: true }],
|
|
],
|
|
env: {
|
|
production: {
|
|
plugins: [
|
|
[
|
|
'styled-components',
|
|
{ pure: true, ssr: true, displayName: false },
|
|
],
|
|
],
|
|
},
|
|
},
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
);
|
|
|
|
await testGeneratedApp(styledComponentsApp, {
|
|
checkStyles: false,
|
|
checkProdBuild: true,
|
|
checkLinter: false,
|
|
checkE2E: false,
|
|
});
|
|
|
|
expect(readFile(`dist/apps/${styledComponentsApp}/main.js`)).toContain(
|
|
'app__StyledApp'
|
|
);
|
|
expect(
|
|
readFile(`dist/apps/${styledComponentsApp}/prod/main.esm.js`)
|
|
).not.toContain('app__StyledApp');
|
|
|
|
const styledJsxApp = uniq('app');
|
|
|
|
runCLI(
|
|
`generate @nrwl/react:app ${styledJsxApp} --style styled-jsx --no-interactive`
|
|
);
|
|
|
|
await testGeneratedApp(styledJsxApp, {
|
|
checkStyles: false,
|
|
checkProdBuild: false,
|
|
checkLinter: false,
|
|
checkE2E: false,
|
|
});
|
|
|
|
const noStylesApp = uniq('app');
|
|
|
|
runCLI(
|
|
`generate @nrwl/react:app ${noStylesApp} --style none --no-interactive`
|
|
);
|
|
|
|
await testGeneratedApp(noStylesApp, {
|
|
checkStyles: false,
|
|
checkProdBuild: false,
|
|
checkLinter: false,
|
|
checkE2E: false,
|
|
});
|
|
|
|
expect(() =>
|
|
checkFilesExist(`dist/apps/${noStylesApp}/styles.css`)
|
|
).toThrow(/does not exist/);
|
|
expect(readFile(`dist/apps/${noStylesApp}/index.html`)).not.toContain(
|
|
`<link rel="stylesheet" href="styles.css">`
|
|
);
|
|
}, 120000);
|
|
|
|
it('should generate app with legacy-ie support', async () => {
|
|
newProject();
|
|
const appName = uniq('app');
|
|
|
|
runCLI(`generate @nrwl/react:app ${appName} --style=css --no-interactive`);
|
|
|
|
// changing browser suporrt of this application
|
|
updateFile(`apps/${appName}/.browserslistrc`, `IE 11`);
|
|
|
|
await testGeneratedApp(appName, {
|
|
checkStyles: false,
|
|
checkProdBuild: true,
|
|
checkLinter: false,
|
|
checkE2E: false,
|
|
});
|
|
|
|
const filesToCheck = [
|
|
`dist/apps/${appName}/prod/polyfills.es5.js`,
|
|
`dist/apps/${appName}/prod/main.es5.js`,
|
|
];
|
|
|
|
checkFilesExist(...filesToCheck);
|
|
|
|
expect(readFile(`dist/apps/${appName}/prod/index.html`)).toContain(
|
|
`<script src="main.esm.js" type="module"></script><script src="main.es5.js" nomodule defer></script>`
|
|
);
|
|
}, 120000);
|
|
|
|
it('should be able to add a redux slice', async () => {
|
|
newProject();
|
|
const appName = uniq('app');
|
|
const libName = uniq('lib');
|
|
|
|
runCLI(`g @nrwl/react:app ${appName} --no-interactive`);
|
|
runCLI(`g @nrwl/react:redux lemon --project=${appName}`);
|
|
runCLI(`g @nrwl/react:lib ${libName} --no-interactive`);
|
|
runCLI(`g @nrwl/react:redux orange --project=${libName}`);
|
|
|
|
const appTestResults = await runCLIAsync(`test ${appName}`);
|
|
expect(appTestResults.combinedOutput).toContain(
|
|
'Test Suites: 2 passed, 2 total'
|
|
);
|
|
|
|
const libTestResults = await runCLIAsync(`test ${libName}`);
|
|
expect(libTestResults.combinedOutput).toContain(
|
|
'Test Suites: 2 passed, 2 total'
|
|
);
|
|
}, 120000);
|
|
|
|
it('should be able to use JSX', async () => {
|
|
newProject();
|
|
const appName = uniq('app');
|
|
const libName = uniq('lib');
|
|
|
|
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
|
|
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive`);
|
|
|
|
renameFile(`apps/${appName}/src/main.tsx`, `apps/${appName}/src/main.jsx`);
|
|
renameFile(
|
|
`apps/${appName}/src/app/app.tsx`,
|
|
`apps/${appName}/src/app/app.jsx`
|
|
);
|
|
renameFile(
|
|
`apps/${appName}/src/app/app.spec.tsx`,
|
|
`apps/${appName}/src/app/app.spec.jsx`
|
|
);
|
|
renameFile(
|
|
`apps/${appName}/src/polyfills.ts`,
|
|
`apps/${appName}/src/polyfills.js`
|
|
);
|
|
const angularJson = readJson(workspaceConfigName());
|
|
|
|
angularJson.projects[
|
|
appName
|
|
].architect.build.options.main = `apps/${appName}/src/main.jsx`;
|
|
angularJson.projects[
|
|
appName
|
|
].architect.build.options.polyfills = `apps/${appName}/src/polyfills.js`;
|
|
updateFile(workspaceConfigName(), serializeJson(angularJson));
|
|
|
|
const mainPath = `apps/${appName}/src/main.jsx`;
|
|
updateFile(mainPath, `import '@proj/${libName}';\n` + readFile(mainPath));
|
|
|
|
await testGeneratedApp(appName, {
|
|
checkStyles: true,
|
|
checkProdBuild: false,
|
|
checkLinter: false,
|
|
checkE2E: false,
|
|
});
|
|
}, 30000);
|
|
|
|
async function testGeneratedApp(
|
|
appName,
|
|
opts: {
|
|
checkProdBuild: boolean;
|
|
checkStyles: boolean;
|
|
checkLinter: boolean;
|
|
checkE2E: boolean;
|
|
}
|
|
) {
|
|
if (opts.checkLinter) {
|
|
const lintResults = runCLI(`lint ${appName}`);
|
|
expect(lintResults).toContain('All files pass linting.');
|
|
}
|
|
|
|
runCLI(`build ${appName}`);
|
|
let filesToCheck = [
|
|
`dist/apps/${appName}/index.html`,
|
|
`dist/apps/${appName}/polyfills.js`,
|
|
`dist/apps/${appName}/runtime.js`,
|
|
`dist/apps/${appName}/vendor.js`,
|
|
`dist/apps/${appName}/main.js`,
|
|
];
|
|
if (opts.checkStyles) {
|
|
filesToCheck.push(`dist/apps/${appName}/styles.js`);
|
|
}
|
|
checkFilesExist(...filesToCheck);
|
|
|
|
expect(readFile(`dist/apps/${appName}/main.js`)).toContain(
|
|
'function App() {'
|
|
);
|
|
|
|
if (opts.checkProdBuild) {
|
|
const prodOutputPath = `dist/apps/${appName}/prod`;
|
|
runCLI(
|
|
`build ${appName} --prod --output-hashing none --outputPath ${prodOutputPath}`
|
|
);
|
|
filesToCheck = [
|
|
`${prodOutputPath}/index.html`,
|
|
`${prodOutputPath}/runtime.js`,
|
|
`${prodOutputPath}/polyfills.esm.js`,
|
|
`${prodOutputPath}/main.esm.js`,
|
|
];
|
|
if (opts.checkStyles) {
|
|
filesToCheck.push(`${prodOutputPath}/styles.css`);
|
|
}
|
|
checkFilesExist(...filesToCheck);
|
|
|
|
if (opts.checkStyles) {
|
|
expect(readFile(`${prodOutputPath}/index.html`)).toContain(
|
|
`<link rel="stylesheet" href="styles.css">`
|
|
);
|
|
}
|
|
}
|
|
|
|
const testResults = await runCLIAsync(`test ${appName}`);
|
|
expect(testResults.combinedOutput).toContain(
|
|
'Test Suites: 1 passed, 1 total'
|
|
);
|
|
|
|
if (opts.checkE2E) {
|
|
const e2eResults = runCLI(`e2e ${appName}-e2e`);
|
|
expect(e2eResults).toContain('All specs passed!');
|
|
}
|
|
}
|
|
});
|