From a90e6f665ff3ac39adacac54387e00be1889a48e Mon Sep 17 00:00:00 2001 From: evilebottnawi Date: Wed, 25 Dec 2019 17:36:40 +0300 Subject: [PATCH] fix: schema validation --- package-lock.json | 40 +++++++--- package.json | 2 +- src/index.js | 6 ++ src/{options.json => loader-options.json} | 10 +-- src/loader.js | 2 +- src/plugin-options.json | 18 +++++ test/TestMemoryFS.test.js | 14 +++- .../validate-loader-options.test.js.snap | 24 ++++++ .../validate-plugin-options.test.js.snap | 29 ++++++++ test/cases/chunkFilename/async.css | 3 + test/cases/chunkFilename/expected/1.async.css | 4 + test/cases/chunkFilename/expected/main.css | 4 + test/cases/chunkFilename/index.js | 4 + test/cases/chunkFilename/style.css | 3 + test/cases/chunkFilename/webpack.config.js | 19 +++++ test/cases/filename-with-template/async.css | 3 + .../filename-with-template/expected/async.css | 4 + .../filename-with-template/expected/main.css | 4 + test/cases/filename-with-template/index.js | 4 + test/cases/filename-with-template/style.css | 3 + .../filename-with-template/webpack.config.js | 18 +++++ .../cases/filename-without-template/async.css | 3 + .../expected/1.main.css | 4 + .../expected/main.css | 4 + test/cases/filename-without-template/index.js | 4 + .../cases/filename-without-template/style.css | 3 + .../webpack.config.js | 18 +++++ test/fixtures/simple.css | 3 + test/fixtures/simple.js | 3 + test/helpers/compile.js | 11 +++ test/helpers/getCompiler.js | 57 +++++++++++++++ test/helpers/index.js | 4 + test/validate-loader-options.test.js | 69 ++++++++++++++++++ test/validate-plugin-options.test.js | 73 +++++++++++++++++++ 34 files changed, 456 insertions(+), 18 deletions(-) rename src/{options.json => loader-options.json} (59%) create mode 100644 src/plugin-options.json create mode 100644 test/__snapshots__/validate-loader-options.test.js.snap create mode 100644 test/__snapshots__/validate-plugin-options.test.js.snap create mode 100644 test/cases/chunkFilename/async.css create mode 100644 test/cases/chunkFilename/expected/1.async.css create mode 100644 test/cases/chunkFilename/expected/main.css create mode 100644 test/cases/chunkFilename/index.js create mode 100644 test/cases/chunkFilename/style.css create mode 100644 test/cases/chunkFilename/webpack.config.js create mode 100644 test/cases/filename-with-template/async.css create mode 100644 test/cases/filename-with-template/expected/async.css create mode 100644 test/cases/filename-with-template/expected/main.css create mode 100644 test/cases/filename-with-template/index.js create mode 100644 test/cases/filename-with-template/style.css create mode 100644 test/cases/filename-with-template/webpack.config.js create mode 100644 test/cases/filename-without-template/async.css create mode 100644 test/cases/filename-without-template/expected/1.main.css create mode 100644 test/cases/filename-without-template/expected/main.css create mode 100644 test/cases/filename-without-template/index.js create mode 100644 test/cases/filename-without-template/style.css create mode 100644 test/cases/filename-without-template/webpack.config.js create mode 100644 test/fixtures/simple.css create mode 100644 test/fixtures/simple.js create mode 100644 test/helpers/compile.js create mode 100644 test/helpers/getCompiler.js create mode 100644 test/helpers/index.js create mode 100644 test/validate-loader-options.test.js create mode 100644 test/validate-plugin-options.test.js diff --git a/package-lock.json b/package-lock.json index fd12c741..35117107 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3857,9 +3857,9 @@ "dev": true }, "core-js-compat": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.0.tgz", - "integrity": "sha512-Z3eCNjGgoYluH89Jt4wVkfYsc/VdLrA2/woX5lm0isO/pCT+P+Y+o65bOuEnjDJLthdwTBxbCVzptTXtc18fJg==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.1.tgz", + "integrity": "sha512-2Tl1EuxZo94QS2VeH28Ebf5g3xbPZG/hj/N5HDDy4XMP/ImR0JIer/nggQRiMN91Q54JVkGbytf42wO29oXVHg==", "dev": true, "requires": { "browserslist": "^4.8.2", @@ -5179,9 +5179,9 @@ } }, "eslint-config-prettier": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.7.0.tgz", - "integrity": "sha512-FamQVKM3jjUVwhG4hEMnbtsq7xOIDm+SY5iBPfR8gKsJoAB2IQnNF+bk1+8Fy44Nq7PPJaLvkRxILYdJWoguKQ==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.8.0.tgz", + "integrity": "sha512-aq4M7mjjVregZ2l45O9qz6Mv6f5zVMl/IqfmUL8hNOoDAzVKYMhYPJytbqE/lPIVO1iMDXIFqjiEE59BfJZpZw==", "dev": true, "requires": { "get-stdin": "^6.0.0" @@ -5684,6 +5684,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, + "fast-extend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fast-extend/-/fast-extend-1.0.2.tgz", + "integrity": "sha512-XXA9RmlPatkFKUzqVZAFth18R4Wo+Xug/S+C7YlYA3xrXwfPlW3dqNwOb4hvQo7wZJ2cNDYhrYuPzVOfHy5/uQ==", + "dev": true + }, "fast-glob": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz", @@ -6111,6 +6117,12 @@ "universalify": "^0.1.0" } }, + "fs-monkey": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-0.3.3.tgz", + "integrity": "sha512-FNUvuTAJ3CqCQb5ELn+qCbGR/Zllhf2HtwsdAtBi59s1WeCjKMT81fHcSu7dwIskqGVK+MmOrb7VOBlq3/SItw==", + "dev": true + }, "fs-readdir-recursive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", @@ -9756,6 +9768,16 @@ "p-is-promise": "^2.0.0" } }, + "memfs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.0.2.tgz", + "integrity": "sha512-ZuIKa+cuDadcSnwp5ci9KWXW/c904mrzbvxfWUYx3boT/NL55RATjiQVczeIjqwceFWAG/iTIk9NPg0CRaxGlQ==", + "dev": true, + "requires": { + "fast-extend": "1.0.2", + "fs-monkey": "0.3.3" + } + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -10212,9 +10234,9 @@ } }, "node-releases": { - "version": "1.1.43", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.43.tgz", - "integrity": "sha512-Rmfnj52WNhvr83MvuAWHEqXVoZXCcDQssSOffU4n4XOL9sPrP61mSZ88g25NqmABDvH7PiAlFCzoSCSdzA293w==", + "version": "1.1.44", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.44.tgz", + "integrity": "sha512-NwbdvJyR7nrcGrXvKAvzc5raj/NkoJudkarh2yIpJ4t0NH4aqjUDz/486P+ynIW5eokKOfzGNRdYoLfBlomruw==", "dev": true, "requires": { "semver": "^6.3.0" diff --git a/package.json b/package.json index 61f56fa4..01a2cd4e 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "jest": "^24.8.0", "jest-junit": "^10.0.0", "lint-staged": "^9.5.0", - "memory-fs": "^0.4.1", + "memfs": "^3.0.2", "npm-run-all": "^4.1.5", "prettier": "^1.19.1", "standard-version": "^7.0.1", diff --git a/src/index.js b/src/index.js index a21eab13..d9f6bf2d 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,10 @@ import webpack from 'webpack'; import sources from 'webpack-sources'; +import validateOptions from 'schema-utils'; + import CssDependency from './CssDependency'; +import schema from './plugin-options.json'; const { ConcatSource, SourceMapSource, OriginalSource } = sources; const { @@ -97,6 +100,8 @@ class CssModuleFactory { class MiniCssExtractPlugin { constructor(options = {}) { + validateOptions(schema, options, 'Mini CSS Extract Plugin'); + this.options = Object.assign( { filename: DEFAULT_FILENAME, @@ -491,6 +496,7 @@ class MiniCssExtractPlugin { // use list with fewest failed deps // and emit a warning const fallbackModule = bestMatch.pop(); + if (!this.options.ignoreOrder) { const reasons = moduleDependenciesReasons.get(fallbackModule); compilation.warnings.push( diff --git a/src/options.json b/src/loader-options.json similarity index 59% rename from src/options.json rename to src/loader-options.json index 4bcdddec..87423b01 100644 --- a/src/options.json +++ b/src/loader-options.json @@ -1,4 +1,5 @@ { + "type": "object", "additionalProperties": true, "properties": { "publicPath": { @@ -13,10 +14,9 @@ }, "esModule": { "type": "boolean" + }, + "hmr": { + "type": "boolean" } - }, - "errorMessages": { - "publicPath": "should be {String} or {Function} (https://github.com/webpack-contrib/mini-css-extract-plugin#publicpath)" - }, - "type": "object" + } } diff --git a/src/loader.js b/src/loader.js index 2562f705..619c48d3 100644 --- a/src/loader.js +++ b/src/loader.js @@ -12,7 +12,7 @@ import validateOptions from 'schema-utils'; import CssDependency from './CssDependency'; -import schema from './options.json'; +import schema from './loader-options.json'; const pluginName = 'mini-css-extract-plugin'; diff --git a/src/plugin-options.json b/src/plugin-options.json new file mode 100644 index 00000000..391cbf9a --- /dev/null +++ b/src/plugin-options.json @@ -0,0 +1,18 @@ +{ + "type": "object", + "additionalProperties": true, + "properties": { + "filename": { + "type": "string" + }, + "chunkFilename": { + "type": "string" + }, + "moduleFilename": { + "instanceof": "Function" + }, + "ignoreOrder": { + "type": "boolean" + } + } +} diff --git a/test/TestMemoryFS.test.js b/test/TestMemoryFS.test.js index d7bede40..b5fcdc19 100644 --- a/test/TestMemoryFS.test.js +++ b/test/TestMemoryFS.test.js @@ -1,6 +1,6 @@ import path from 'path'; -import MemoryFS from 'memory-fs'; +import { createFsFromVolume, Volume } from 'memfs'; import webpack from 'webpack'; const assetsNames = (json) => json.assets.map((asset) => asset.name); @@ -20,20 +20,30 @@ describe('TestMemoryFS', () => { context: directoryForCase, cache: false, }); - compiler.outputFileSystem = new MemoryFS(); + const outputFileSystem = createFsFromVolume(new Volume()); + // Todo remove when we drop webpack@4 support + outputFileSystem.join = path.join.bind(path); + + compiler.outputFileSystem = outputFileSystem; + compiler.run((err1, stats1) => { if (err1) { done(err1); + return; } + compiler.run((err2, stats2) => { if (err2) { done(err2); + return; } + expect(assetsNames(stats1.toJson())).toEqual( assetsNames(stats2.toJson()) ); + done(); }); }); diff --git a/test/__snapshots__/validate-loader-options.test.js.snap b/test/__snapshots__/validate-loader-options.test.js.snap new file mode 100644 index 00000000..a78b53e1 --- /dev/null +++ b/test/__snapshots__/validate-loader-options.test.js.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validate options should throw an error on the "esModule" option with "1" value 1`] = ` +"Mini CSS Extract Plugin Loader Invalid Options + +options.esModule should be boolean +" +`; + +exports[`validate options should throw an error on the "hmr" option with "1" value 1`] = ` +"Mini CSS Extract Plugin Loader Invalid Options + +options.hmr should be boolean +" +`; + +exports[`validate options should throw an error on the "publicPath" option with "true" value 1`] = ` +"Mini CSS Extract Plugin Loader Invalid Options + +options.publicPath should be string +options.publicPath should pass \\"instanceof\\" keyword validation +options.publicPath should match some schema in anyOf +" +`; diff --git a/test/__snapshots__/validate-plugin-options.test.js.snap b/test/__snapshots__/validate-plugin-options.test.js.snap new file mode 100644 index 00000000..fe506ff6 --- /dev/null +++ b/test/__snapshots__/validate-plugin-options.test.js.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validate options should throw an error on the "chunkFilename" option with "true" value 1`] = ` +"Mini CSS Extract Plugin Invalid Options + +options.chunkFilename should be string +" +`; + +exports[`validate options should throw an error on the "filename" option with "true" value 1`] = ` +"Mini CSS Extract Plugin Invalid Options + +options.filename should be string +" +`; + +exports[`validate options should throw an error on the "ignoreOrder" option with "1" value 1`] = ` +"Mini CSS Extract Plugin Invalid Options + +options.ignoreOrder should be boolean +" +`; + +exports[`validate options should throw an error on the "moduleFilename" option with "true" value 1`] = ` +"Mini CSS Extract Plugin Invalid Options + +options.moduleFilename should pass \\"instanceof\\" keyword validation +" +`; diff --git a/test/cases/chunkFilename/async.css b/test/cases/chunkFilename/async.css new file mode 100644 index 00000000..b1645094 --- /dev/null +++ b/test/cases/chunkFilename/async.css @@ -0,0 +1,3 @@ +.async { + color: red; +} diff --git a/test/cases/chunkFilename/expected/1.async.css b/test/cases/chunkFilename/expected/1.async.css new file mode 100644 index 00000000..e665100a --- /dev/null +++ b/test/cases/chunkFilename/expected/1.async.css @@ -0,0 +1,4 @@ +.async { + color: red; +} + diff --git a/test/cases/chunkFilename/expected/main.css b/test/cases/chunkFilename/expected/main.css new file mode 100644 index 00000000..cebc5c1c --- /dev/null +++ b/test/cases/chunkFilename/expected/main.css @@ -0,0 +1,4 @@ +body { + background: red; +} + diff --git a/test/cases/chunkFilename/index.js b/test/cases/chunkFilename/index.js new file mode 100644 index 00000000..361d71f3 --- /dev/null +++ b/test/cases/chunkFilename/index.js @@ -0,0 +1,4 @@ +import './style.css'; + +/* eslint-disable-next-line no-unused-expressions */ +import(/* webpackChunkName: "async" */ './async.css'); diff --git a/test/cases/chunkFilename/style.css b/test/cases/chunkFilename/style.css new file mode 100644 index 00000000..67ce83e4 --- /dev/null +++ b/test/cases/chunkFilename/style.css @@ -0,0 +1,3 @@ +body { + background: red; +} diff --git a/test/cases/chunkFilename/webpack.config.js b/test/cases/chunkFilename/webpack.config.js new file mode 100644 index 00000000..74275c66 --- /dev/null +++ b/test/cases/chunkFilename/webpack.config.js @@ -0,0 +1,19 @@ +import Self from '../../../src'; + +module.exports = { + entry: './index.js', + module: { + rules: [ + { + test: /\.css$/, + use: [Self.loader, 'css-loader'], + }, + ], + }, + plugins: [ + new Self({ + filename: '[name].css', + chunkFilename: '[id].[name].css', + }), + ], +}; diff --git a/test/cases/filename-with-template/async.css b/test/cases/filename-with-template/async.css new file mode 100644 index 00000000..b1645094 --- /dev/null +++ b/test/cases/filename-with-template/async.css @@ -0,0 +1,3 @@ +.async { + color: red; +} diff --git a/test/cases/filename-with-template/expected/async.css b/test/cases/filename-with-template/expected/async.css new file mode 100644 index 00000000..e665100a --- /dev/null +++ b/test/cases/filename-with-template/expected/async.css @@ -0,0 +1,4 @@ +.async { + color: red; +} + diff --git a/test/cases/filename-with-template/expected/main.css b/test/cases/filename-with-template/expected/main.css new file mode 100644 index 00000000..cebc5c1c --- /dev/null +++ b/test/cases/filename-with-template/expected/main.css @@ -0,0 +1,4 @@ +body { + background: red; +} + diff --git a/test/cases/filename-with-template/index.js b/test/cases/filename-with-template/index.js new file mode 100644 index 00000000..361d71f3 --- /dev/null +++ b/test/cases/filename-with-template/index.js @@ -0,0 +1,4 @@ +import './style.css'; + +/* eslint-disable-next-line no-unused-expressions */ +import(/* webpackChunkName: "async" */ './async.css'); diff --git a/test/cases/filename-with-template/style.css b/test/cases/filename-with-template/style.css new file mode 100644 index 00000000..67ce83e4 --- /dev/null +++ b/test/cases/filename-with-template/style.css @@ -0,0 +1,3 @@ +body { + background: red; +} diff --git a/test/cases/filename-with-template/webpack.config.js b/test/cases/filename-with-template/webpack.config.js new file mode 100644 index 00000000..4e160f26 --- /dev/null +++ b/test/cases/filename-with-template/webpack.config.js @@ -0,0 +1,18 @@ +import Self from '../../../src'; + +module.exports = { + entry: './index.js', + module: { + rules: [ + { + test: /\.css$/, + use: [Self.loader, 'css-loader'], + }, + ], + }, + plugins: [ + new Self({ + filename: '[name].css', + }), + ], +}; diff --git a/test/cases/filename-without-template/async.css b/test/cases/filename-without-template/async.css new file mode 100644 index 00000000..b1645094 --- /dev/null +++ b/test/cases/filename-without-template/async.css @@ -0,0 +1,3 @@ +.async { + color: red; +} diff --git a/test/cases/filename-without-template/expected/1.main.css b/test/cases/filename-without-template/expected/1.main.css new file mode 100644 index 00000000..e665100a --- /dev/null +++ b/test/cases/filename-without-template/expected/1.main.css @@ -0,0 +1,4 @@ +.async { + color: red; +} + diff --git a/test/cases/filename-without-template/expected/main.css b/test/cases/filename-without-template/expected/main.css new file mode 100644 index 00000000..cebc5c1c --- /dev/null +++ b/test/cases/filename-without-template/expected/main.css @@ -0,0 +1,4 @@ +body { + background: red; +} + diff --git a/test/cases/filename-without-template/index.js b/test/cases/filename-without-template/index.js new file mode 100644 index 00000000..361d71f3 --- /dev/null +++ b/test/cases/filename-without-template/index.js @@ -0,0 +1,4 @@ +import './style.css'; + +/* eslint-disable-next-line no-unused-expressions */ +import(/* webpackChunkName: "async" */ './async.css'); diff --git a/test/cases/filename-without-template/style.css b/test/cases/filename-without-template/style.css new file mode 100644 index 00000000..67ce83e4 --- /dev/null +++ b/test/cases/filename-without-template/style.css @@ -0,0 +1,3 @@ +body { + background: red; +} diff --git a/test/cases/filename-without-template/webpack.config.js b/test/cases/filename-without-template/webpack.config.js new file mode 100644 index 00000000..6814befd --- /dev/null +++ b/test/cases/filename-without-template/webpack.config.js @@ -0,0 +1,18 @@ +import Self from '../../../src'; + +module.exports = { + entry: './index.js', + module: { + rules: [ + { + test: /\.css$/, + use: [Self.loader, 'css-loader'], + }, + ], + }, + plugins: [ + new Self({ + filename: 'main.css', + }), + ], +}; diff --git a/test/fixtures/simple.css b/test/fixtures/simple.css new file mode 100644 index 00000000..a15c877a --- /dev/null +++ b/test/fixtures/simple.css @@ -0,0 +1,3 @@ +.foo { + color: red; +} diff --git a/test/fixtures/simple.js b/test/fixtures/simple.js new file mode 100644 index 00000000..f3ccc56d --- /dev/null +++ b/test/fixtures/simple.js @@ -0,0 +1,3 @@ +import './simple.css'; + +console.log('HERE'); diff --git a/test/helpers/compile.js b/test/helpers/compile.js new file mode 100644 index 00000000..066873ab --- /dev/null +++ b/test/helpers/compile.js @@ -0,0 +1,11 @@ +export default (compiler) => { + return new Promise((resolve, reject) => { + compiler.run((error, stats) => { + if (error) { + return reject(error); + } + + return resolve(stats); + }); + }); +}; diff --git a/test/helpers/getCompiler.js b/test/helpers/getCompiler.js new file mode 100644 index 00000000..ef29941e --- /dev/null +++ b/test/helpers/getCompiler.js @@ -0,0 +1,57 @@ +import path from 'path'; + +import webpack from 'webpack'; +import { createFsFromVolume, Volume } from 'memfs'; + +import MiniCssExtractPlugin from '../../src'; + +export default (fixture, loaderOptions = {}, config = {}) => { + const fullConfig = { + mode: 'development', + devtool: config.devtool || false, + context: path.resolve(__dirname, '../fixtures'), + entry: path.resolve(__dirname, '../fixtures', fixture), + output: { + path: path.resolve(__dirname, '../outputs'), + filename: '[name].bundle.js', + chunkFilename: '[name].chunk.js', + }, + module: { + rules: [ + { + test: /\.css$/i, + rules: [ + { + loader: MiniCssExtractPlugin.loader, + options: loaderOptions || {}, + }, + { + loader: 'css-loader', + }, + ], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + // Options similar to the same options in webpackOptions.output + // both options are optional + filename: '[name].css', + chunkFilename: '[id].css', + }), + ], + ...config, + }; + + const compiler = webpack(fullConfig); + + if (!config.outputFileSystem) { + const outputFileSystem = createFsFromVolume(new Volume()); + // Todo remove when we drop webpack@4 support + outputFileSystem.join = path.join.bind(path); + + compiler.outputFileSystem = outputFileSystem; + } + + return compiler; +}; diff --git a/test/helpers/index.js b/test/helpers/index.js new file mode 100644 index 00000000..f0ad1bae --- /dev/null +++ b/test/helpers/index.js @@ -0,0 +1,4 @@ +import compile from './compile'; +import getCompiler from './getCompiler'; + +export { compile, getCompiler }; diff --git a/test/validate-loader-options.test.js b/test/validate-loader-options.test.js new file mode 100644 index 00000000..fbb980b2 --- /dev/null +++ b/test/validate-loader-options.test.js @@ -0,0 +1,69 @@ +import { getCompiler, compile } from './helpers'; + +describe('validate options', () => { + const tests = { + publicPath: { + success: ['/public/path/to/'], + failure: [true], + }, + esModule: { + success: [true, false], + failure: [1], + }, + hmr: { + success: [true, false], + failure: [1], + }, + unknown: { + success: [], + // TODO failed in next release + // failure: [1, true, false, 'test', /test/, [], {}, { foo: 'bar' }], + }, + }; + + function stringifyValue(value) { + if ( + Array.isArray(value) || + (value && typeof value === 'object' && value.constructor === Object) + ) { + return JSON.stringify(value); + } + + return value; + } + + async function createTestCase(key, value, type) { + it(`should ${ + type === 'success' ? 'successfully validate' : 'throw an error on' + } the "${key}" option with "${stringifyValue(value)}" value`, async () => { + const compiler = getCompiler('simple.js', { [key]: value }); + + let stats; + + try { + stats = await compile(compiler); + } finally { + if (type === 'success') { + expect(stats.hasErrors()).toBe(false); + } else if (type === 'failure') { + const { + compilation: { errors }, + } = stats; + + expect(errors).toHaveLength(1); + expect(() => { + throw new Error(errors[0].error.message); + }).toThrowErrorMatchingSnapshot(); + } + } + }); + } + + for (const [key, values] of Object.entries(tests)) { + for (const type of Object.keys(values)) { + for (const value of values[type]) { + createTestCase(key, value, type); + } + } + } +}); diff --git a/test/validate-plugin-options.test.js b/test/validate-plugin-options.test.js new file mode 100644 index 00000000..dbd51106 --- /dev/null +++ b/test/validate-plugin-options.test.js @@ -0,0 +1,73 @@ +import MiniCssExtractPlugin from '../src'; + +describe('validate options', () => { + const tests = { + filename: { + success: ['[name].css'], + failure: [true], + }, + chunkFilename: { + success: ['[id].css'], + failure: [true], + }, + moduleFilename: { + success: [({ name }) => `${name.replace('/js/', '/css/')}.css`], + failure: [true], + }, + ignoreOrder: { + success: [true, false], + failure: [1], + }, + unknown: { + success: [], + // TODO failed in next release + // failure: [1, true, false, 'test', /test/, [], {}, { foo: 'bar' }], + }, + }; + + function stringifyValue(value) { + if ( + Array.isArray(value) || + (value && typeof value === 'object' && value.constructor === Object) + ) { + return JSON.stringify(value); + } + + return value; + } + + async function createTestCase(key, value, type) { + it(`should ${ + type === 'success' ? 'successfully validate' : 'throw an error on' + } the "${key}" option with "${stringifyValue(value)}" value`, async () => { + let error; + + try { + // eslint-disable-next-line no-new + new MiniCssExtractPlugin({ [key]: value }); + } catch (errorFromPlugin) { + if (errorFromPlugin.name !== 'ValidationError') { + throw errorFromPlugin; + } + + error = errorFromPlugin; + } finally { + if (type === 'success') { + expect(error).toBeUndefined(); + } else if (type === 'failure') { + expect(() => { + throw error; + }).toThrowErrorMatchingSnapshot(); + } + } + }); + } + + for (const [key, values] of Object.entries(tests)) { + for (const type of Object.keys(values)) { + for (const value of values[type]) { + createTestCase(key, value, type); + } + } + } +});