Skip to content

Commit

Permalink
tests(generators): add many init tests
Browse files Browse the repository at this point in the history
  • Loading branch information
knagaitsev committed Mar 30, 2020
1 parent 5b051de commit fc17a6f
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 60 deletions.
148 changes: 143 additions & 5 deletions packages/generators/__tests__/init-generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,168 @@ import * as assert from 'yeoman-assert';
import { run } from 'yeoman-test';
import { join } from 'path';

jest.setTimeout(10000);

describe('init generator', () => {
it('generates a webpack project config', async () => {
it('generates a webpack config with default options', async () => {
const outputDir = await run(join(__dirname, '../src/init-generator')).withPrompts({
multiEntries: false,
singularEntry: 'src/index',
outputDir: 'dist',
langType: 'No',
stylingType: 'No',
});

// Check that all the project files are generated with the correct name
const filePaths = ['package.json', 'README.md', 'src/index.js', 'sw.js'];
assert.file(filePaths.map(file => join(outputDir, file)));

// Check generated file contents
assert.fileContent(join(outputDir, 'package.json'), '"name": "my-webpack-project"');
assert.fileContent(join(outputDir, 'README.md'), 'Welcome to your new awesome project!');
assert.fileContent(join(outputDir, 'src', 'index.js'), 'console.log("Hello World from your main file!");');
assert.fileContent(join(outputDir, 'sw.js'), 'self.addEventListener(\'install\'');

const output = require(join(outputDir, '.yo-rc.json'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config = (Object.entries(output)[0][1] as any).configuration.config.webpackOptions;
// since default options were used, entry and output are not specified
// in the generated config file
expect(config.entry).toEqual(undefined);
expect(config.output).toEqual(undefined);
// there are no special loaders, so rules should be empty
expect(config.module.rules).toEqual([]);
});

it('generates a webpack config with custom entry and output', async () => {
const outputDir = await run(join(__dirname, '../src/init-generator')).withPrompts({
multiEntries: false,
singularEntry: 'src/index2',
outputDir: 'dist2',
langType: 'No',
stylingType: 'No',
useExtractPlugin: 'main',
});

// Check that all the project files are generated with the correct name
const filePaths = ['package.json', 'README.md', 'src/index2.js'];
assert.file(filePaths.map(file => join(outputDir, file)));

// this file is only added if the default options are used
assert.noFile(join(outputDir, 'sw.js'));

// Check generated file contents
assert.fileContent(join(outputDir, 'package.json'), '"name": "my-webpack-project"');
assert.fileContent(join(outputDir, 'README.md'), 'Welcome to your new awesome project!');
assert.fileContent(join(outputDir, 'src', 'index2.js'), 'console.log("Hello World from your main file!");');

const output = require(join(outputDir, '.yo-rc.json'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config = (Object.entries(output)[0][1] as any).configuration.config.webpackOptions;
expect(config.entry).toEqual("'./src/index2.js'");
expect(config.output.path).toEqual("path.resolve(__dirname, 'dist2')");
}, 10000);
// there are no special loaders, so rules should be empty
expect(config.module.rules).toEqual([]);
});

it('generates a webpack config using CSS without mini-css-extract-plugin', async () => {
const outputDir = await run(join(__dirname, '../src/init-generator')).withPrompts({
multiEntries: false,
singularEntry: 'src/index',
outputDir: 'dist',
langType: 'No',
stylingType: 'CSS',
useExtractPlugin: false,
});

// Check that all the project files are generated with the correct name
const filePaths = ['package.json', 'README.md', 'src/index.js'];
assert.file(filePaths.map(file => join(outputDir, file)));

const output = require(join(outputDir, '.yo-rc.json'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config = (Object.entries(output)[0][1] as any).configuration.config.webpackOptions;
expect(config.module.rules[0].test).toEqual('/.css$/');
expect(config.module.rules[0].use.length).toEqual(2);
expect(config.module.rules[0].use[0].loader).toEqual('"style-loader"');
expect(config.module.rules[0].use[1].loader).toEqual('"css-loader"');
});

it('generates a webpack config using CSS with mini-css-extract-plugin', async () => {
const outputDir = await run(join(__dirname, '../src/init-generator')).withPrompts({
multiEntries: false,
singularEntry: 'src/index',
outputDir: 'dist',
langType: 'No',
stylingType: 'CSS',
useExtractPlugin: true,
cssBundleName: 'main',
});

// Check that all the project files are generated with the correct name
const filePaths = ['package.json', 'README.md', 'src/index.js'];
assert.file(filePaths.map(file => join(outputDir, file)));

const output = require(join(outputDir, '.yo-rc.json'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config = (Object.entries(output)[0][1] as any).configuration.config.webpackOptions;
expect(config.module.rules[0].test).toEqual('/.css$/');
expect(config.module.rules[0].use.length).toEqual(3);
expect(config.module.rules[0].use[0].loader).toEqual('MiniCssExtractPlugin.loader');
expect(config.module.rules[0].use[1].loader).toEqual('"style-loader"');
expect(config.module.rules[0].use[2].loader).toEqual('"css-loader"');
});

it('generates a webpack config with multiple entries', async () => {
const outputDir = await run(join(__dirname, '../src/init-generator')).withPrompts({
multiEntries: true,
multipleEntries: 'test1, test2',
test1: 'dir1/test1',
test2: 'dir2/test2',
outputDir: 'dist',
langType: 'No',
stylingType: 'No',
});

// Check that all the project files are generated with the correct name
const filePaths = ['package.json', 'README.md', 'dir1/test1.js', 'dir2/test2.js'];
assert.file(filePaths.map(file => join(outputDir, file)));

// Check generated file contents
assert.fileContent(join(outputDir, 'dir1', 'test1.js'), 'console.log("Hello World from test1 main file!");');
assert.fileContent(join(outputDir, 'dir2', 'test2.js'), 'console.log("Hello World from test2 main file!");');

const output = require(join(outputDir, '.yo-rc.json'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config = (Object.entries(output)[0][1] as any).configuration.config.webpackOptions;
expect(config.entry).toEqual({
test1: "'./dir1/test1.js'",
test2: "'./dir2/test2.js'",
});
});

it('generates a webpack config that uses ES6', async () => {
const outputDir = await run(join(__dirname, '../src/init-generator')).withPrompts({
multiEntries: false,
singularEntry: 'src/index',
outputDir: 'dist',
langType: 'ES6',
stylingType: 'No',
});

// Check that all the project files are generated with the correct name
const filePaths = ['package.json', 'README.md', 'src/index.js', '.babelrc'];
assert.file(filePaths.map(file => join(outputDir, file)));
});

it('generates a webpack config that uses Typescript', async () => {
const outputDir = await run(join(__dirname, '../src/init-generator')).withPrompts({
multiEntries: false,
singularEntry: 'src/index',
outputDir: 'dist',
langType: 'Typescript',
stylingType: 'No',
});

// Check that all the project files are generated with the correct name
const filePaths = ['package.json', 'README.md', 'src/index.ts', 'tsconfig.json'];
assert.file(filePaths.map(file => join(outputDir, file)));
});
});
118 changes: 65 additions & 53 deletions packages/generators/src/init-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default class InitGenerator extends Generator {
usingDefaults?: boolean;
};
private langType: string;
private entryOption: void | {};

public constructor(args, opts) {
super(args, opts);
Expand All @@ -65,6 +66,8 @@ export default class InitGenerator extends Generator {
}
};

this.entryOption = "./src/index.js";

// add splitChunks options for transparency
// defaults coming from: https://webpack.js.org/plugins/split-chunks-plugin/#optimization-splitchunks
this.configuration.config.topScope.push(
Expand All @@ -80,6 +83,8 @@ export default class InitGenerator extends Generator {
public async prompting(): Promise<void | {}> {
const self: this = this;

this.usingDefaults = true;

process.stdout.write(
`\n${logSymbols.info}${chalk.blue(" INFO ")} ` +
`For more information and a detailed description of each question, have a look at: ` +
Expand All @@ -102,18 +107,19 @@ export default class InitGenerator extends Generator {
const entryOption: void | {} = await entryQuestions(self, multiEntries, this.autoGenerateConfig);

if (typeof entryOption === "string") {
if (entryOption.length === 0) {
this.usingDefaults = true;
} else if (entryOption.length > 0) {
this.usingDefaults = entryOption && entryOption === "'./src/index.js'";
if (!this.usingDefaults) {
this.configuration.config.webpackOptions.entry = `${entryOption}`;
}
// single entry
if (entryOption.length > 0 && entryOption !== "'./src/index.js'") {
this.usingDefaults = false;
this.configuration.config.webpackOptions.entry = entryOption;
}
} else if (typeof entryOption === "object") {
// multiple entries
this.usingDefaults = false;
this.configuration.config.webpackOptions.entry = entryOption;
}

this.entryOption = entryOption;

const { outputDir } = await Input(
self,
"outputDir",
Expand Down Expand Up @@ -145,6 +151,7 @@ export default class InitGenerator extends Generator {
if (this.langType !== "No") {
this.usingDefaults = false;
}

const { stylingType } = await List(
self,
"stylingType",
Expand All @@ -153,45 +160,51 @@ export default class InitGenerator extends Generator {
"No",
this.autoGenerateConfig
);
if (this.langType !== "No") {
this.usingDefaults = false;
}
const { ExtractUseProps, regExpForStyles } = styleQuestionHandler(self, stylingType);
if (stylingType !== "No") {
this.usingDefaults = false;
}
// Ask if the user wants to use extractPlugin
const { useExtractPlugin } = await Input(
self,
"useExtractPlugin",
"If you want to bundle your CSS files, what will you name the bundle? (press enter to skip)",
"main",
this.autoGenerateConfig
);

if (regExpForStyles) {
const cssBundleName: string = useExtractPlugin;
this.dependencies.push("mini-css-extract-plugin");
this.configuration.config.topScope.push(
tooltip.cssPlugin(),
"const MiniCssExtractPlugin = require('mini-css-extract-plugin');",
"\n"
// Ask if the user wants to use extractPlugin
const { useExtractPlugin } = await Confirm(
self,
"useExtractPlugin",
"Will you bundle your CSS files with MiniCssExtractPlugin?",
false,
this.autoGenerateConfig
);
if (cssBundleName.length !== 0) {
(this.configuration.config.webpackOptions.plugins as string[]).push(
// TODO: use [contenthash] after it is supported
`new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`
if (useExtractPlugin) {
const { cssBundleName } = await Input(
self,
"cssBundleName",
"What will you name the CSS bundle?",
"main",
this.autoGenerateConfig
);
} else {
(this.configuration.config.webpackOptions.plugins as string[]).push(
"new MiniCssExtractPlugin({ filename:'style.css' })"
this.dependencies.push("mini-css-extract-plugin");
this.configuration.config.topScope.push(
tooltip.cssPlugin(),
"const MiniCssExtractPlugin = require('mini-css-extract-plugin');",
"\n"
);
if (cssBundleName.length !== 0) {
(this.configuration.config.webpackOptions.plugins as string[]).push(
// TODO: use [contenthash] after it is supported
`new MiniCssExtractPlugin({ filename:'${cssBundleName}.[chunkhash].css' })`
);
} else {
(this.configuration.config.webpackOptions.plugins as string[]).push(
"new MiniCssExtractPlugin({ filename:'style.css' })"
);
}

ExtractUseProps.unshift({
loader: "MiniCssExtractPlugin.loader"
});
}

ExtractUseProps.unshift({
loader: "MiniCssExtractPlugin.loader"
});

// load CSS assets, with or without mini-css-extract-plugin
this.configuration.config.webpackOptions.module.rules.push({
test: regExpForStyles,
use: ExtractUseProps
Expand All @@ -216,6 +229,15 @@ export default class InitGenerator extends Generator {
this.configuration.config.webpackOptions.devServer = {
open: true
};

// PWA + offline support
this.configuration.config.topScope.push("const workboxPlugin = require('workbox-webpack-plugin');", "\n");
this.dependencies.push("workbox-webpack-plugin");
(this.configuration.config.webpackOptions.plugins as string[]).push(`new workboxPlugin.GenerateSW({
swDest: 'sw.js',
clientsClaim: true,
skipWaiting: false,
})`);
}

// TerserPlugin
Expand All @@ -226,15 +248,6 @@ export default class InitGenerator extends Generator {
"\n"
);

// PWA + offline support
this.configuration.config.topScope.push("const workboxPlugin = require('workbox-webpack-plugin');", "\n");
this.dependencies.push("workbox-webpack-plugin");
(this.configuration.config.webpackOptions.plugins as string[]).push(`new workboxPlugin.GenerateSW({
swDest: 'sw.js',
clientsClaim: true,
skipWaiting: false,
})`);

// Chunksplitting
this.configuration.config.webpackOptions.optimization = getDefaultOptimization(this.usingDefaults);
this.configuration.config.webpackOptions.mode = this.usingDefaults ? "'production'" : "'development'";
Expand All @@ -254,13 +267,6 @@ export default class InitGenerator extends Generator {
this.configuration.usingDefaults = this.usingDefaults;
this.config.set("configuration", this.configuration);

if (this.langType === "ES6") {
this.fs.copyTpl(
path.resolve(__dirname, "../templates/.babelrc"),
this.destinationPath(".babelrc"),
{}
);
}
const packageJsonTemplatePath = "../templates/package.json.js";
this.fs.extendJSON(this.destinationPath("package.json"), require(packageJsonTemplatePath)(this.usingDefaults));

Expand Down Expand Up @@ -290,8 +296,14 @@ export default class InitGenerator extends Generator {
this.fs.copyTpl(path.resolve(__dirname, '../templates/sw.js'), this.destinationPath('sw.js'), {});
}

// Generate tsconfig
if (this.langType === LangType.Typescript) {
if (this.langType === LangType.ES6) {
this.fs.copyTpl(
path.resolve(__dirname, "../templates/.babelrc"),
this.destinationPath(".babelrc"),
{}
);
} else if (this.langType === LangType.Typescript) {
// Generate tsconfig
const tsConfigTemplatePath = "../templates/tsconfig.json.js";
this.fs.extendJSON(this.destinationPath("tsconfig.json"), require(tsConfigTemplatePath));
}
Expand Down
4 changes: 2 additions & 2 deletions packages/generators/src/utils/languageSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export enum LangType {
const replaceExt = (path: string, ext: string): string => path.substr(0, path.lastIndexOf(".")) + `${ext}'`;

function updateEntryExt(self, newExt: string): void {
const jsEntryOption = self.configuration.config.webpackOptions.entry;
const jsEntryOption = self.entryOption;
let tsEntryOption = {};
if (typeof jsEntryOption === "string") {
tsEntryOption = replaceExt(jsEntryOption, newExt);
Expand All @@ -28,7 +28,7 @@ const getFolder = (path: string): string =>
.join("/");

function getEntryFolders(self): string[] {
const entryOption = self.configuration.config.webpackOptions.entry;
const entryOption = self.entryOption;
const entryFolders = {};
if (typeof entryOption === "string") {
const folder = getFolder(entryOption);
Expand Down

0 comments on commit fc17a6f

Please sign in to comment.