From 394e44c166b1f5fb41f03499b98a3152491d84d7 Mon Sep 17 00:00:00 2001 From: evilebottnawi Date: Tue, 23 Jul 2019 14:48:56 +0300 Subject: [PATCH] test: refactor --- src/processPattern.js | 2 + test/CopyPlugin.test.js | 1177 +++++-------------------- test/cache-option.test.js | 250 ++++++ test/context-option.test.js | 148 ++++ test/flatten-option.test.js | 147 +++ test/force-option.test.js | 237 +++++ test/helpers/watch/.gitkeep | 0 test/helpers/watch/directory/.gitkeep | 0 test/ignore-option.test.js | 186 ++++ test/test-option.test.js | 73 ++ test/toType-option.test.js | 107 +++ test/transform-option.test.js | 184 ++++ test/transformPath-option.test.js | 22 +- test/utils/run.js | 19 +- 14 files changed, 1559 insertions(+), 993 deletions(-) create mode 100644 test/cache-option.test.js create mode 100644 test/context-option.test.js create mode 100644 test/flatten-option.test.js create mode 100644 test/force-option.test.js create mode 100644 test/helpers/watch/.gitkeep create mode 100644 test/helpers/watch/directory/.gitkeep create mode 100644 test/ignore-option.test.js create mode 100644 test/test-option.test.js create mode 100644 test/toType-option.test.js create mode 100644 test/transform-option.test.js diff --git a/src/processPattern.js b/src/processPattern.js index 6cec72ba..b881b624 100644 --- a/src/processPattern.js +++ b/src/processPattern.js @@ -12,6 +12,8 @@ export default function processPattern(globalRef, pattern) { { cwd: pattern.context, follow: true, + // Todo in next major release + // dot: true }, pattern.globOptions || {} ); diff --git a/test/CopyPlugin.test.js b/test/CopyPlugin.test.js index d54b8420..1c1be46a 100644 --- a/test/CopyPlugin.test.js +++ b/test/CopyPlugin.test.js @@ -1,18 +1,14 @@ import fs from 'fs'; import path from 'path'; -import zlib from 'zlib'; -import findCacheDir from 'find-cache-dir'; -import cacache from 'cacache'; -import isGzip from 'is-gzip'; import mkdirp from 'mkdirp'; import CopyPlugin from '../src/index'; import removeIllegalCharacterForWindows from './utils/removeIllegalCharacterForWindows'; -import { MockCompiler, MockCompilerNoStat } from './utils/mocks'; -import { run, runEmit, runForce, runChange } from './utils/run'; +import { MockCompiler } from './utils/mocks'; +import { run, runEmit, runChange } from './utils/run'; const BUILD_DIR = path.join(__dirname, 'build'); const HELPER_DIR = path.join(__dirname, 'helpers'); @@ -38,10 +34,9 @@ describe('apply function', () => { }); }); - // Use then and catch explicitly, so errors - // aren't seen as unhandled exceptions - describe('error handling', () => { - it("doesn't throw an error if no patterns are passed", (done) => { + // Use then and catch explicitly, so errors aren't seen as unhandled exceptions + describe('errors', () => { + it('should not throw an error if no patterns are passed', (done) => { runEmit({ expectedAssetKeys: [], patterns: undefined, // eslint-disable-line no-undefined @@ -50,7 +45,7 @@ describe('apply function', () => { .catch(done); }); - it('throws an error if the patterns are an object', () => { + it('should throw an error if the patterns are an object', () => { const createPluginWithObject = () => { // eslint-disable-next-line no-new new CopyPlugin({}); @@ -59,7 +54,7 @@ describe('apply function', () => { expect(createPluginWithObject).toThrow(Error); }); - it('throws an error if the patterns are null', () => { + it('should throw an error if the patterns are null', () => { const createPluginWithNull = () => { // eslint-disable-next-line no-new new CopyPlugin(null); @@ -68,7 +63,7 @@ describe('apply function', () => { expect(createPluginWithNull).toThrow(Error); }); - it('throws an error if the "from" path is an empty string', () => { + it('should throws an error if the "from" path is an empty string', () => { const createPluginWithNull = () => { // eslint-disable-next-line no-new new CopyPlugin({ @@ -256,26 +251,6 @@ describe('apply function', () => { .catch(done); }); - it('can use a glob to flatten multiple files in a relative context to a non-root directory', (done) => { - runEmit({ - expectedAssetKeys: [ - 'nested/deepnested.txt', - 'nested/directoryfile.txt', - 'nested/nestedfile.txt', - ], - patterns: [ - { - context: 'directory', - flatten: true, - from: '**/*', - to: 'nested', - }, - ], - }) - .then(done) - .catch(done); - }); - it('can use a glob to move multiple files in a different absolute context to a non-root directory', (done) => { runEmit({ expectedAssetKeys: [ @@ -362,46 +337,6 @@ describe('apply function', () => { .catch(done); }); - it('can flatten or normalize glob matches', (done) => { - runEmit({ - expectedAssetKeys: [ - '[!]-hello.txt', - '[special?directory]-(special-*file).txt', - '[special?directory]-directoryfile.txt', - 'dir (86)-file.txt', - 'directory-directoryfile.txt', - ], - patterns: [ - { - from: '*/*.*', - test: `([^\\${path.sep}]+)\\${path.sep}([^\\${path.sep}]+)\\.\\w+$`, - to: '[1]-[2].[ext]', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('adds the context directory to the watch list when using glob', (done) => { - run({ - patterns: [ - { - from: 'directory/**/*', - }, - ], - }) - .then((compilation) => { - expect( - Array.from(compilation.contextDependencies) - .map((contextDependency) => contextDependency) - .sort() - ).toEqual([path.join(HELPER_DIR, 'directory')].sort()); - }) - .then(done) - .catch(done); - }); - it('does not add the directory to the watch list when glob is a file', (done) => { run({ patterns: [ @@ -460,27 +395,6 @@ describe('apply function', () => { .catch(done); }); - it('can transform a file', (done) => { - runEmit({ - expectedAssetKeys: ['file.txt'], - expectedAssetContent: { - 'file.txt': 'newchanged', - }, - patterns: [ - { - from: 'file.txt', - transform(content, absoluteFrom) { - expect(absoluteFrom).toBe(path.join(HELPER_DIR, 'file.txt')); - - return `${content}changed`; - }, - }, - ], - }) - .then(done) - .catch(done); - }); - it('warns when file not found', (done) => { runEmit({ expectedAssetKeys: [], @@ -499,45 +413,6 @@ describe('apply function', () => { .catch(done); }); - it('warns when file not found and stats is undefined', (done) => { - runEmit({ - compiler: new MockCompilerNoStat(), - expectedAssetKeys: [], - expectedWarnings: [ - new Error( - `unable to locate 'nonexistent.txt' at '${HELPER_DIR}${path.sep}nonexistent.txt'` - ), - ], - patterns: [ - { - from: 'nonexistent.txt', - to: '.', - toType: 'dir', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('warns when tranform failed', (done) => { - runEmit({ - expectedAssetKeys: [], - expectedErrors: ['a failure happened'], - patterns: [ - { - from: 'file.txt', - transform() { - // eslint-disable-next-line no-throw-literal - throw 'a failure happened'; - }, - }, - ], - }) - .then(done) - .catch(done); - }); - it('warns when pattern is empty', (done) => { runEmit({ expectedAssetKeys: [ @@ -557,6 +432,8 @@ describe('apply function', () => { 'file.txt', 'file.txt.gz', 'noextension', + 'watch/.gitkeep', + 'watch/directory/.gitkeep', ], expectedErrors: [new Error(`path "from" cannot be empty string`)], patterns: [ @@ -726,21 +603,6 @@ describe('apply function', () => { .catch(done); }); - it('can move a file to a new directory with an extension', (done) => { - runEmit({ - expectedAssetKeys: ['newdirectory.ext/file.txt'], - patterns: [ - { - from: 'file.txt', - to: 'newdirectory.ext', - toType: 'dir', - }, - ], - }) - .then(done) - .catch(done); - }); - it('can move a file to a new directory with an extension and path separator at end', (done) => { runEmit({ expectedAssetKeys: ['newdirectory.ext/file.txt'], @@ -769,21 +631,6 @@ describe('apply function', () => { .catch(done); }); - it('can move a file to a new file with no extension', (done) => { - runEmit({ - expectedAssetKeys: ['newname'], - patterns: [ - { - from: 'file.txt', - to: 'newname', - toType: 'file', - }, - ], - }) - .then(done) - .catch(done); - }); - it('can move a file without an extension to a file using a template', (done) => { runEmit({ expectedAssetKeys: ['noextension.newext'], @@ -878,102 +725,6 @@ describe('apply function', () => { .catch(done); }); - it("won't overwrite a file already in the compilation", (done) => { - runForce({ - existingAsset: 'file.txt', - expectedAssetContent: { - 'file.txt': 'existing', - }, - patterns: [ - { - from: 'file.txt', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('can force overwrite of a file already in the compilation', (done) => { - runForce({ - existingAsset: 'file.txt', - expectedAssetContent: { - 'file.txt': 'new', - }, - patterns: [ - { - force: true, - from: 'file.txt', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('adds the file to the watch list', (done) => { - run({ - patterns: [ - { - from: 'file.txt', - }, - ], - }) - .then((compilation) => { - const absFrom = path.join(HELPER_DIR, 'file.txt'); - - expect(Array.from(compilation.fileDependencies).sort()).toEqual( - [absFrom].sort() - ); - }) - .then(done) - .catch(done); - }); - - it('only include files that have changed', (done) => { - runChange({ - expectedAssetKeys: ['tempfile1.txt'], - newFileLoc1: path.join(HELPER_DIR, 'tempfile1.txt'), - newFileLoc2: path.join(HELPER_DIR, 'tempfile2.txt'), - patterns: [ - { - from: 'tempfile1.txt', - }, - { - from: 'tempfile2.txt', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('ignores files in pattern', (done) => { - runEmit({ - expectedAssetKeys: [ - '[!]/hello.txt', - 'binextension.bin', - 'dir (86)/nesteddir/deepnesteddir/deepnesteddir.txt', - 'dir (86)/nesteddir/nestedfile.txt', - 'directory/directoryfile.txt', - 'directory/nested/deep-nested/deepnested.txt', - 'directory/nested/nestedfile.txt', - '[special?directory]/directoryfile.txt', - '[special?directory]/(special-*file).txt', - '[special?directory]/nested/nestedfile.txt', - 'noextension', - ], - patterns: [ - { - from: '**/*', - ignore: ['file.*', 'file-in-nested-directory.*'], - }, - ], - }) - .then(done) - .catch(done); - }); - it('allows pattern to contain name, hash or ext', (done) => { runEmit({ expectedAssetKeys: ['directory/directoryfile-22af64.txt'], @@ -1030,27 +781,6 @@ describe('apply function', () => { .catch(done); }); - it('transform with promise', (done) => { - runEmit({ - expectedAssetKeys: ['file.txt'], - expectedAssetContent: { - 'file.txt': 'newchanged!', - }, - patterns: [ - { - from: 'file.txt', - transform(content) { - return new Promise((resolve) => { - resolve(`${content}changed!`); - }); - }, - }, - ], - }) - .then(done) - .catch(done); - }); - it('same file to multiple targets', (done) => { runEmit({ expectedAssetKeys: ['first/file.txt', 'second/file.txt'], @@ -1215,26 +945,6 @@ describe('apply function', () => { .catch(done); }); - it("can flatten a directory's contents to a new directory", (done) => { - runEmit({ - expectedAssetKeys: [ - 'newdirectory/.dottedfile', - 'newdirectory/deepnested.txt', - 'newdirectory/directoryfile.txt', - 'newdirectory/nestedfile.txt', - ], - patterns: [ - { - flatten: true, - from: 'directory', - to: 'newdirectory', - }, - ], - }) - .then(done) - .catch(done); - }); - it("can move a directory's contents to a new directory using an absolute to", (done) => { runEmit({ expectedAssetKeys: [ @@ -1303,15 +1013,18 @@ describe('apply function', () => { .catch(done); }); - it("won't overwrite a file already in the compilation", (done) => { - runForce({ - existingAsset: 'directoryfile.txt', - expectedAssetContent: { - 'directoryfile.txt': 'existing', - }, + it('can move multiple files to a non-root directory with name, hash and ext', (done) => { + runEmit({ + expectedAssetKeys: [ + 'nested/.dottedfile-79d39f', + 'nested/directoryfile-22af64.txt', + 'nested/nested/deep-nested/deepnested-d41d8c.txt', + 'nested/nested/nestedfile-d41d8c.txt', + ], patterns: [ { from: 'directory', + to: 'nested/[path][name]-[hash:6].[ext]', }, ], }) @@ -1319,49 +1032,62 @@ describe('apply function', () => { .catch(done); }); - it('can force overwrite of a file already in the compilation', (done) => { - runForce({ - existingAsset: 'directoryfile.txt', - expectedAssetContent: { - 'directoryfile.txt': 'new', - }, + it("can move a directory's contents to the root directory from symbolic link", (done) => { + runEmit({ + // Windows doesn't support symbolic link + symlink: true, + expectedAssetKeys: + process.platform === 'win32' + ? [] + : ['file.txt', 'nested-directory/file-in-nested-directory.txt'], patterns: [ { - force: true, - from: 'directory', + from: 'symlink/directory-ln', }, ], }) .then(done) .catch(done); }); + }); - it('adds the context directory to the watch list', (done) => { - run({ + describe('basic', () => { + it('should works with multiple patterns as String', (done) => { + runEmit({ + expectedAssetKeys: ['binextension.bin', 'file.txt', 'noextension'], + patterns: ['binextension.bin', 'file.txt', 'noextension'], + }) + .then(done) + .catch(done); + }); + + it('should works with multiple patterns as Object', (done) => { + runEmit({ + expectedAssetKeys: ['binextension.bin', 'file.txt', 'noextension'], patterns: [ { - from: 'directory', + from: 'binextension.bin', + }, + { + from: 'file.txt', + }, + { + from: 'noextension', }, ], }) - .then((compilation) => { - const absFrom = path.resolve(HELPER_DIR, 'directory'); - expect(Array.from(compilation.contextDependencies).sort()).toEqual( - [absFrom].sort() - ); - }) .then(done) .catch(done); }); + }); - it('only include files that have changed', (done) => { - runChange({ - expectedAssetKeys: ['tempfile1.txt'], - newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'), - newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt'), + describe('difference path segment separation', () => { + it('should work with linux path segment separation path when "from" is glob', (done) => { + runEmit({ + expectedAssetKeys: ['directory/nested/nestedfile.txt'], patterns: [ { - from: 'directory', + from: 'directory/nested/*', }, ], }) @@ -1369,24 +1095,14 @@ describe('apply function', () => { .catch(done); }); - it('include all files if copyUnmodified is true', (done) => { - runChange({ - expectedAssetKeys: [ - '.dottedfile', - 'directoryfile.txt', - 'nested/deep-nested/deepnested.txt', - 'nested/nestedfile.txt', - 'tempfile1.txt', - 'tempfile2.txt', - ], - newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'), - newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt'), - options: { - copyUnmodified: true, - }, + it('should work with windows path segment separation path when "from" is glob', (done) => { + runEmit({ + expectedAssetKeys: ['directory/nested/nestedfile.txt'], patterns: [ { - from: 'directory', + from: { + glob: 'directory\\nested\\*', + }, }, ], }) @@ -1394,18 +1110,14 @@ describe('apply function', () => { .catch(done); }); - it('can move multiple files to a non-root directory with name, hash and ext', (done) => { + it('should work with mixed path segment separation path when "from" is glob', (done) => { runEmit({ - expectedAssetKeys: [ - 'nested/.dottedfile-79d39f', - 'nested/directoryfile-22af64.txt', - 'nested/nested/deep-nested/deepnested-d41d8c.txt', - 'nested/nested/nestedfile-d41d8c.txt', - ], + expectedAssetKeys: ['directory/nested/nestedfile.txt'], patterns: [ { - from: 'directory', - to: 'nested/[path][name]-[hash:6].[ext]', + from: { + glob: 'directory/nested\\*', + }, }, ], }) @@ -1413,14 +1125,20 @@ describe('apply function', () => { .catch(done); }); - it('can move multiple files to a non-root directory with [1]', (done) => { + it('should exclude path with linux path segment separators', (done) => { runEmit({ - expectedAssetKeys: ['nested/txt'], + expectedAssetKeys: [ + '[!]/hello.txt', + '[special?directory]/(special-*file).txt', + '[special?directory]/directoryfile.txt', + '[special?directory]/nested/nestedfile.txt', + 'dir (86)/file.txt', + 'dir (86)/nesteddir/deepnesteddir/deepnesteddir.txt', + 'dir (86)/nesteddir/nestedfile.txt', + ], patterns: [ { - from: 'directory/nested/deep-nested', - to: 'nested/[1]', - test: /\.([^.]*)$/, + from: '!(directory)/**/*.txt', }, ], }) @@ -1428,17 +1146,20 @@ describe('apply function', () => { .catch(done); }); - it("can move a directory's contents to the root directory from symbolic link", (done) => { + it('should exclude path with windows path segment separators', (done) => { runEmit({ - // Windows doesn't support symbolic link - symlink: true, - expectedAssetKeys: - process.platform === 'win32' - ? [] - : ['file.txt', 'nested-directory/file-in-nested-directory.txt'], + expectedAssetKeys: [ + '[!]/hello.txt', + '[special?directory]/(special-*file).txt', + '[special?directory]/directoryfile.txt', + '[special?directory]/nested/nestedfile.txt', + 'dir (86)/file.txt', + 'dir (86)/nesteddir/deepnesteddir/deepnesteddir.txt', + 'dir (86)/nesteddir/nestedfile.txt', + ], patterns: [ { - from: 'symlink/directory-ln', + from: '!(directory)\\**\\*.txt', }, ], }) @@ -1447,54 +1168,75 @@ describe('apply function', () => { }); }); - describe('with simple string patterns', () => { - it('can move multiple files', (done) => { - runEmit({ - expectedAssetKeys: ['binextension.bin', 'file.txt', 'noextension'], - patterns: ['binextension.bin', 'file.txt', 'noextension'], + describe('watch mode', () => { + it('should add the file to the watch list when "from" is a file', (done) => { + run({ + patterns: [ + { + from: 'file.txt', + }, + ], }) + .then((compilation) => { + const absFrom = path.join(HELPER_DIR, 'file.txt'); + + expect(Array.from(compilation.fileDependencies).sort()).toEqual( + [absFrom].sort() + ); + }) .then(done) .catch(done); }); - }); - describe('with difference path segment separation', () => { - it('can normalize backslash path with glob in from', (done) => { - runEmit({ - expectedAssetKeys: ['directory/nested/nestedfile.txt'], + it('should add a directory to the watch list when "from" is a directory', (done) => { + run({ patterns: [ { - from: { - glob: 'directory\\nested\\*', - }, + from: 'directory', }, ], }) + .then((compilation) => { + const absFrom = path.resolve(HELPER_DIR, 'directory'); + + expect(Array.from(compilation.contextDependencies).sort()).toEqual( + [absFrom].sort() + ); + }) .then(done) .catch(done); }); - it('can normalize backslash path with glob in from (mixed path segment separation)', (done) => { - runEmit({ - expectedAssetKeys: ['directory/nested/nestedfile.txt'], + it('should add a directory to the watch list when "from" is a glob', (done) => { + run({ patterns: [ { - from: { - glob: 'directory/nested\\*', - }, + from: 'directory/**/*', }, ], }) + .then((compilation) => { + expect( + Array.from(compilation.contextDependencies) + .map((contextDependency) => contextDependency) + .sort() + ).toEqual([path.join(HELPER_DIR, 'directory')].sort()); + }) .then(done) .catch(done); }); - it('can normalize backslash path with glob in from (simple)', (done) => { - runEmit({ - expectedAssetKeys: ['directory/nested/nestedfile.txt'], + it('only include files that have changed', (done) => { + runChange({ + expectedAssetKeys: ['tempfile1.txt'], + newFileLoc1: path.join(HELPER_DIR, 'watch', 'tempfile1.txt'), + newFileLoc2: path.join(HELPER_DIR, 'watch', 'tempfile2.txt'), patterns: [ { - from: 'directory\\nested\\*', + from: 'tempfile1.txt', + }, + { + from: 'tempfile2.txt', }, ], }) @@ -1502,20 +1244,24 @@ describe('apply function', () => { .catch(done); }); - it('can exclude path', (done) => { - runEmit({ - expectedAssetKeys: [ - '[!]/hello.txt', - '[special?directory]/(special-*file).txt', - '[special?directory]/directoryfile.txt', - '[special?directory]/nested/nestedfile.txt', - 'dir (86)/file.txt', - 'dir (86)/nesteddir/deepnesteddir/deepnesteddir.txt', - 'dir (86)/nesteddir/nestedfile.txt', - ], + it('only include files that have changed', (done) => { + runChange({ + expectedAssetKeys: ['tempfile1.txt'], + newFileLoc1: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile1.txt' + ), + newFileLoc2: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile2.txt' + ), patterns: [ { - from: '!(directory)/**/*.txt', + from: 'directory', }, ], }) @@ -1523,34 +1269,49 @@ describe('apply function', () => { .catch(done); }); - it('can exclude path with backslash path', (done) => { - runEmit({ - expectedAssetKeys: [ - '[!]/hello.txt', - '[special?directory]/(special-*file).txt', - '[special?directory]/directoryfile.txt', - '[special?directory]/nested/nestedfile.txt', - 'dir (86)/file.txt', - 'dir (86)/nesteddir/deepnesteddir/deepnesteddir.txt', - 'dir (86)/nesteddir/nestedfile.txt', - ], + it('include all files if copyUnmodified is true', (done) => { + runChange({ + expectedAssetKeys: ['tempfile1.txt', 'tempfile2.txt', '.gitkeep'], + newFileLoc1: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile1.txt' + ), + newFileLoc2: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile2.txt' + ), + options: { + copyUnmodified: true, + }, patterns: [ { - from: '!(directory)\\**\\*.txt', + from: 'directory', }, ], }) .then(done) .catch(done); }); - }); - describe('modified files', () => { it('copy only changed files', (done) => { runChange({ expectedAssetKeys: ['dest1/tempfile1.txt'], - newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'), - newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt'), + newFileLoc1: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile1.txt' + ), + newFileLoc2: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile2.txt' + ), patterns: [ { context: 'directory', @@ -1566,8 +1327,18 @@ describe('apply function', () => { it('copy only changed files (multiple patterns)', (done) => { runChange({ expectedAssetKeys: ['dest1/tempfile1.txt', 'dest2/tempfile1.txt'], - newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'), - newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt'), + newFileLoc1: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile1.txt' + ), + newFileLoc2: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile2.txt' + ), patterns: [ { context: 'directory', @@ -1591,8 +1362,13 @@ describe('apply function', () => { 'dest1/tempfile1.txt', 'dest2/directory/tempfile1.txt', ], - newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'), - newFileLoc2: path.join(HELPER_DIR, 'tempfile2.txt'), + newFileLoc1: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile1.txt' + ), + newFileLoc2: path.join(HELPER_DIR, 'watch', 'tempfile2.txt'), patterns: [ { context: 'directory', @@ -1615,8 +1391,13 @@ describe('apply function', () => { 'dest1/directory/tempfile1.txt', 'dest2/tempfile1.txt', ], - newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'), - newFileLoc2: path.join(HELPER_DIR, 'tempfile2.txt'), + newFileLoc1: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile1.txt' + ), + newFileLoc2: path.join(HELPER_DIR, 'watch', 'tempfile2.txt'), patterns: [ { from: '**/*.txt', @@ -1636,8 +1417,13 @@ describe('apply function', () => { it('copy only changed files (multiple patterns with difference context 2)', (done) => { runChange({ expectedAssetKeys: ['dest1/tempfile1.txt'], - newFileLoc1: path.join(HELPER_DIR, 'tempfile1.txt'), - newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt'), + newFileLoc1: path.join(HELPER_DIR, 'watch', 'tempfile1.txt'), + newFileLoc2: path.join( + HELPER_DIR, + 'watch', + 'directory', + 'tempfile2.txt' + ), patterns: [ { from: '**/*.txt', @@ -1654,543 +1440,4 @@ describe('apply function', () => { .catch(done); }); }); - - describe('options', () => { - describe('ignore', () => { - it('ignores files when from is a file', (done) => { - runEmit({ - expectedAssetKeys: ['directoryfile.txt'], - options: { - ignore: ['file.*'], - }, - patterns: [ - { - from: 'file.txt', - }, - { - from: 'directory/directoryfile.txt', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('ignores files when from is a directory', (done) => { - runEmit({ - expectedAssetKeys: [ - '.dottedfile', - 'directoryfile.txt', - 'nested/deep-nested/deepnested.txt', - ], - options: { - ignore: ['*/nestedfile.*'], - }, - patterns: [ - { - from: 'directory', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('ignores files with a certain extension', (done) => { - runEmit({ - expectedAssetKeys: ['.dottedfile'], - options: { - ignore: ['*.txt'], - }, - patterns: [ - { - from: 'directory', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('ignores files that start with a dot', (done) => { - runEmit({ - expectedAssetKeys: [ - '[!]/hello.txt', - 'binextension.bin', - 'dir (86)/file.txt', - 'dir (86)/nesteddir/deepnesteddir/deepnesteddir.txt', - 'dir (86)/nesteddir/nestedfile.txt', - 'file.txt', - 'file.txt.gz', - 'directory/directoryfile.txt', - 'directory/nested/deep-nested/deepnested.txt', - 'directory/nested/nestedfile.txt', - '[special?directory]/directoryfile.txt', - '[special?directory]/(special-*file).txt', - '[special?directory]/nested/nestedfile.txt', - 'noextension', - ], - options: { - ignore: ['.dottedfile', '.file.txt'], - }, - patterns: [ - { - from: '.', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('ignores all files except those with dots', (done) => { - runEmit({ - expectedAssetKeys: ['.file.txt', 'directory/.dottedfile'], - options: { - ignore: [ - { - dot: false, - glob: '**/*', - }, - ], - }, - patterns: [ - { - from: '.', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('ignores all files even if they start with a dot', (done) => { - runEmit({ - expectedAssetKeys: [], - options: { - ignore: ['**/*'], - }, - patterns: [ - { - from: '.', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('ignores nested directory', (done) => { - runEmit({ - expectedAssetKeys: [ - '.file.txt', - '[!]/hello.txt', - 'binextension.bin', - 'dir (86)/file.txt', - 'dir (86)/nesteddir/deepnesteddir/deepnesteddir.txt', - 'dir (86)/nesteddir/nestedfile.txt', - 'file.txt', - 'file.txt.gz', - 'noextension', - ], - options: { - ignore: [ - 'directory/**/*', - `[[]special${ - process.platform === 'win32' ? '' : '[?]' - }directory]/**/*`, - ], - }, - patterns: [ - { - from: '.', - }, - ], - }) - .then(done) - .catch(done); - }); - - if (path.sep === '/') { - it('ignores nested directory(can use "\\" to escape if path.sep is "/")', (done) => { - runEmit({ - expectedAssetKeys: [ - '.file.txt', - '[!]/hello.txt', - 'binextension.bin', - 'dir (86)/file.txt', - 'dir (86)/nesteddir/deepnesteddir/deepnesteddir.txt', - 'dir (86)/nesteddir/nestedfile.txt', - 'file.txt', - 'file.txt.gz', - 'noextension', - ], - options: { - ignore: ['directory/**/*', '\\[special\\?directory\\]/**/*'], - }, - patterns: [ - { - from: '.', - }, - ], - }) - .then(done) - .catch(done); - }); - } - - it('ignores nested directory (glob)', (done) => { - runEmit({ - expectedAssetKeys: ['.dottedfile', 'directoryfile.txt'], - options: { - ignore: ['nested/**/*'], - }, - patterns: [ - { - from: 'directory', - }, - ], - }) - .then(done) - .catch(done); - }); - }); - - describe('context', () => { - it('overrides webpack config context with absolute path', (done) => { - runEmit({ - expectedAssetKeys: [ - 'newdirectory/deep-nested/deepnested.txt', - 'newdirectory/nestedfile.txt', - ], - options: { - context: path.resolve(HELPER_DIR, 'directory'), - }, - patterns: [ - { - from: 'nested', - to: 'newdirectory', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('overrides webpack config context with relative path', (done) => { - runEmit({ - expectedAssetKeys: [ - 'newdirectory/deep-nested/deepnested.txt', - 'newdirectory/nestedfile.txt', - ], - options: { - context: 'directory', - }, - patterns: [ - { - from: 'nested', - to: 'newdirectory', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('is overridden by pattern context', (done) => { - runEmit({ - expectedAssetKeys: [ - 'newdirectory/deep-nested/deepnested.txt', - 'newdirectory/nestedfile.txt', - ], - options: { - context: 'directory', - }, - patterns: [ - { - context: 'nested', - from: '.', - to: 'newdirectory', - }, - ], - }) - .then(done) - .catch(done); - }); - - it('overrides webpack config context with absolute path', (done) => { - runEmit({ - expectedAssetKeys: [ - 'newdirectory/file.txt', - 'newdirectory/nesteddir/deepnesteddir/deepnesteddir.txt', - 'newdirectory/nesteddir/nestedfile.txt', - ], - options: { - context: path.resolve(HELPER_DIR, 'dir (86)'), - }, - patterns: [ - { - from: '**/*', - to: 'newdirectory', - }, - ], - }) - .then(done) - .catch(done); - }); - }); - - describe('cache', () => { - const cacheDir = findCacheDir({ name: 'copy-webpack-plugin' }); - - beforeEach(() => cacache.rm.all(cacheDir)); - - it('file should be cached', (done) => { - const newContent = 'newchanged!'; - const from = 'file.txt'; - - runEmit({ - expectedAssetKeys: ['file.txt'], - expectedAssetContent: { - 'file.txt': newContent, - }, - patterns: [ - { - from, - cache: true, - transform: function transform(content) { - return new Promise((resolve) => { - resolve(`${content}changed!`); - }); - }, - }, - ], - }) - .then(() => - cacache.ls(cacheDir).then((cacheEntries) => { - const cacheKeys = Object.keys(cacheEntries); - - expect(cacheKeys).toHaveLength(1); - - cacheKeys.forEach((cacheKey) => { - // eslint-disable-next-line no-new-func - const cacheEntry = new Function( - `'use strict'\nreturn ${cacheKey}` - )(); - - expect(cacheEntry.pattern.from).toBe(from); - }); - }) - ) - .then(done) - .catch(done); - }); - - it('files in directory should be cached', (done) => { - const from = 'directory'; - - runEmit({ - expectedAssetKeys: [ - '.dottedfile', - 'directoryfile.txt', - 'nested/deep-nested/deepnested.txt', - 'nested/nestedfile.txt', - ], - expectedAssetContent: { - '.dottedfile': 'dottedfile contents\nchanged!', - 'directoryfile.txt': 'newchanged!', - 'nested/nestedfile.txt': 'changed!', - }, - patterns: [ - { - from, - cache: true, - transform: function transform(content) { - return new Promise((resolve) => { - resolve(`${content}changed!`); - }); - }, - }, - ], - }) - .then(() => - cacache.ls(cacheDir).then((cacheEntries) => { - const cacheKeys = Object.keys(cacheEntries); - - expect(cacheKeys).toHaveLength(3); - - cacheKeys.forEach((cacheKey) => { - // eslint-disable-next-line no-new-func - const cacheEntry = new Function( - `'use strict'\nreturn ${cacheKey}` - )(); - - expect(cacheEntry.pattern.from).toBe(from); - }); - }) - ) - .then(done) - .catch(done); - }); - - it('glob should be cached', (done) => { - const from = '*.txt'; - - runEmit({ - expectedAssetKeys: ['file.txt'], - expectedAssetContent: { - 'file.txt': 'newchanged!', - }, - patterns: [ - { - from, - cache: true, - transform: function transform(content) { - return new Promise((resolve) => { - resolve(`${content}changed!`); - }); - }, - }, - ], - }) - .then(() => - cacache.ls(cacheDir).then((cacheEntries) => { - const cacheKeys = Object.keys(cacheEntries); - - expect(cacheKeys).toHaveLength(1); - - cacheKeys.forEach((cacheKey) => { - // eslint-disable-next-line no-new-func - const cacheEntry = new Function( - `'use strict'\nreturn ${cacheKey}` - )(); - - expect(cacheEntry.pattern.from).toBe(from); - }); - }) - ) - .then(done) - .catch(done); - }); - - it('file should be cached with custom cache key', (done) => { - const newContent = 'newchanged!'; - const from = 'file.txt'; - - runEmit({ - expectedAssetKeys: ['file.txt'], - expectedAssetContent: { - 'file.txt': newContent, - }, - patterns: [ - { - from, - cache: { - key: 'foobar', - }, - transform(content) { - return new Promise((resolve) => { - resolve(`${content}changed!`); - }); - }, - }, - ], - }) - .then(() => - cacache.ls(cacheDir).then((cacheEntries) => { - const cacheKeys = Object.keys(cacheEntries); - - expect(cacheKeys).toHaveLength(1); - - cacheKeys.forEach((cacheKey) => { - expect(cacheKey).toBe('foobar'); - }); - }) - ) - .then(done) - .catch(done); - }); - - it('binary file should be cached', (done) => { - const from = 'file.txt.gz'; - const content = fs.readFileSync(path.join(HELPER_DIR, from)); - const expectedNewContent = zlib.gzipSync('newchanged!'); - - expect(isGzip(content)).toBe(true); - expect(isGzip(expectedNewContent)).toBe(true); - - runEmit({ - expectedAssetKeys: ['file.txt.gz'], - expectedAssetContent: { - 'file.txt.gz': expectedNewContent, - }, - patterns: [ - { - from, - cache: true, - // eslint-disable-next-line no-shadow - transform: function transform(content) { - expect(isGzip(content)).toBe(true); - - return new Promise((resolve) => { - // eslint-disable-next-line no-shadow - zlib.unzip(content, (error, content) => { - if (error) { - throw error; - } - - const newContent = Buffer.from(`${content}changed!`); - - // eslint-disable-next-line no-shadow - zlib.gzip(newContent, (error, compressedData) => { - if (error) { - throw error; - } - - expect(isGzip(compressedData)).toBe(true); - - return resolve(compressedData); - }); - }); - }); - }, - }, - ], - }) - .then(() => - cacache.ls(cacheDir).then((cacheEntries) => { - const cacheKeys = Object.keys(cacheEntries); - - expect(cacheKeys).toHaveLength(1); - - cacheKeys.forEach((cacheKey) => { - // eslint-disable-next-line no-new-func - const cacheEntry = new Function( - `'use strict'\nreturn ${cacheKey}` - )(); - - expect(cacheEntry.pattern.from).toBe(from); - }); - }) - ) - .then(done) - .catch(done); - }); - }); - }); - - it('should move a file and use posix separator for emitting assets', (done) => { - runEmit({ - expectedAssetKeys: ['dir/nestedfile.txt'], - patterns: [ - { - context: HELPER_DIR, - from: 'directory/nested/nestedfile.txt', - to: 'dir', - }, - ], - }) - .then(done) - .catch(done); - }); }); diff --git a/test/cache-option.test.js b/test/cache-option.test.js new file mode 100644 index 00000000..f175961a --- /dev/null +++ b/test/cache-option.test.js @@ -0,0 +1,250 @@ +import fs from 'fs'; +import path from 'path'; +import zlib from 'zlib'; + +import cacache from 'cacache'; +import findCacheDir from 'find-cache-dir'; +import isGzip from 'is-gzip'; + +import { runEmit } from './utils/run'; + +const HELPER_DIR = path.join(__dirname, 'helpers'); + +describe('cache option', () => { + const cacheDir = findCacheDir({ name: 'copy-webpack-plugin' }); + + beforeEach(() => cacache.rm.all(cacheDir)); + + it('should cache when "from" is a file', (done) => { + const newContent = 'newchanged!'; + const from = 'file.txt'; + + runEmit({ + expectedAssetKeys: ['file.txt'], + expectedAssetContent: { + 'file.txt': newContent, + }, + patterns: [ + { + from, + cache: true, + transform: function transform(content) { + return new Promise((resolve) => { + resolve(`${content}changed!`); + }); + }, + }, + ], + }) + .then(() => + cacache.ls(cacheDir).then((cacheEntries) => { + const cacheKeys = Object.keys(cacheEntries); + + expect(cacheKeys).toHaveLength(1); + + cacheKeys.forEach((cacheKey) => { + // eslint-disable-next-line no-new-func + const cacheEntry = new Function( + `'use strict'\nreturn ${cacheKey}` + )(); + + expect(cacheEntry.pattern.from).toBe(from); + }); + }) + ) + .then(done) + .catch(done); + }); + + it('should cache files when "from" is a directory', (done) => { + const from = 'directory'; + + runEmit({ + expectedAssetKeys: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + expectedAssetContent: { + '.dottedfile': 'dottedfile contents\nchanged!', + 'directoryfile.txt': 'newchanged!', + 'nested/nestedfile.txt': 'changed!', + }, + patterns: [ + { + from, + cache: true, + transform: function transform(content) { + return new Promise((resolve) => { + resolve(`${content}changed!`); + }); + }, + }, + ], + }) + .then(() => + cacache.ls(cacheDir).then((cacheEntries) => { + const cacheKeys = Object.keys(cacheEntries); + + expect(cacheKeys).toHaveLength(3); + + cacheKeys.forEach((cacheKey) => { + // eslint-disable-next-line no-new-func + const cacheEntry = new Function( + `'use strict'\nreturn ${cacheKey}` + )(); + + expect(cacheEntry.pattern.from).toBe(from); + }); + }) + ) + .then(done) + .catch(done); + }); + + it('should cache when "from" is a glob', (done) => { + const from = 'directory/*.txt'; + + runEmit({ + expectedAssetKeys: ['directory/directoryfile.txt'], + expectedAssetContent: { + 'directory/directoryfile.txt': 'newchanged!', + }, + patterns: [ + { + from, + cache: true, + transform: function transform(content) { + return new Promise((resolve) => { + resolve(`${content}changed!`); + }); + }, + }, + ], + }) + .then(() => + cacache.ls(cacheDir).then((cacheEntries) => { + const cacheKeys = Object.keys(cacheEntries); + + expect(cacheKeys).toHaveLength(1); + + cacheKeys.forEach((cacheKey) => { + // eslint-disable-next-line no-new-func + const cacheEntry = new Function( + `'use strict'\nreturn ${cacheKey}` + )(); + + // Todo need investigate + expect(cacheEntry.pattern.from.replace(/\\/, '/')).toBe(from); + }); + }) + ) + .then(done) + .catch(done); + }); + + it('should cache file with custom cache key', (done) => { + const newContent = 'newchanged!'; + const from = 'file.txt'; + + runEmit({ + expectedAssetKeys: ['file.txt'], + expectedAssetContent: { + 'file.txt': newContent, + }, + patterns: [ + { + from, + cache: { + key: 'foobar', + }, + transform(content) { + return new Promise((resolve) => { + resolve(`${content}changed!`); + }); + }, + }, + ], + }) + .then(() => + cacache.ls(cacheDir).then((cacheEntries) => { + const cacheKeys = Object.keys(cacheEntries); + + expect(cacheKeys).toHaveLength(1); + + cacheKeys.forEach((cacheKey) => { + expect(cacheKey).toBe('foobar'); + }); + }) + ) + .then(done) + .catch(done); + }); + + it('should cache binary file', (done) => { + const from = 'file.txt.gz'; + const content = fs.readFileSync(path.join(HELPER_DIR, from)); + const expectedNewContent = zlib.gzipSync('newchanged!'); + + expect(isGzip(content)).toBe(true); + expect(isGzip(expectedNewContent)).toBe(true); + + runEmit({ + expectedAssetKeys: ['file.txt.gz'], + expectedAssetContent: { + 'file.txt.gz': expectedNewContent, + }, + patterns: [ + { + from, + cache: true, + // eslint-disable-next-line no-shadow + transform: function transform(content) { + expect(isGzip(content)).toBe(true); + + return new Promise((resolve) => { + // eslint-disable-next-line no-shadow + zlib.unzip(content, (error, content) => { + if (error) { + throw error; + } + + const newContent = Buffer.from(`${content}changed!`); + + // eslint-disable-next-line no-shadow + zlib.gzip(newContent, (error, compressedData) => { + if (error) { + throw error; + } + + expect(isGzip(compressedData)).toBe(true); + + return resolve(compressedData); + }); + }); + }); + }, + }, + ], + }) + .then(() => + cacache.ls(cacheDir).then((cacheEntries) => { + const cacheKeys = Object.keys(cacheEntries); + + expect(cacheKeys).toHaveLength(1); + + cacheKeys.forEach((cacheKey) => { + // eslint-disable-next-line no-new-func + const cacheEntry = new Function( + `'use strict'\nreturn ${cacheKey}` + )(); + + expect(cacheEntry.pattern.from).toBe(from); + }); + }) + ) + .then(done) + .catch(done); + }); +}); diff --git a/test/context-option.test.js b/test/context-option.test.js new file mode 100644 index 00000000..4b02872e --- /dev/null +++ b/test/context-option.test.js @@ -0,0 +1,148 @@ +import path from 'path'; + +import { runEmit } from './utils/run'; + +const HELPER_DIR = path.join(__dirname, 'helpers'); + +describe('context option', () => { + it('should work when "from" is a file', (done) => { + runEmit({ + expectedAssetKeys: ['directoryfile.txt'], + patterns: [ + { + from: 'directoryfile.txt', + context: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should work when "from" is a directory', (done) => { + runEmit({ + expectedAssetKeys: ['deep-nested/deepnested.txt', 'nestedfile.txt'], + patterns: [ + { + from: 'nested', + context: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should work when "from" is a glob', (done) => { + runEmit({ + expectedAssetKeys: [ + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + patterns: [ + { + from: 'nested/**/*', + context: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should work when "from" is a file and "context" is an absolute path', (done) => { + runEmit({ + expectedAssetKeys: ['directoryfile.txt'], + patterns: [ + { + from: 'directoryfile.txt', + context: path.resolve(HELPER_DIR, 'directory'), + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should override webpack config context with an absolute path', (done) => { + runEmit({ + expectedAssetKeys: [ + 'newdirectory/deep-nested/deepnested.txt', + 'newdirectory/nestedfile.txt', + ], + options: { + context: path.resolve(HELPER_DIR, 'directory'), + }, + patterns: [ + { + from: 'nested', + to: 'newdirectory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should override webpack config context with a relative path', (done) => { + runEmit({ + expectedAssetKeys: [ + 'newdirectory/deep-nested/deepnested.txt', + 'newdirectory/nestedfile.txt', + ], + options: { + context: 'directory', + }, + patterns: [ + { + from: 'nested', + to: 'newdirectory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should override global context on pattern context with a relative path', (done) => { + runEmit({ + expectedAssetKeys: [ + 'newdirectory/deep-nested/deepnested.txt', + 'newdirectory/nestedfile.txt', + ], + options: { + context: 'directory', + }, + patterns: [ + { + context: 'nested', + from: '.', + to: 'newdirectory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('overrides webpack config context with an absolute path', (done) => { + runEmit({ + expectedAssetKeys: [ + 'newdirectory/file.txt', + 'newdirectory/nesteddir/deepnesteddir/deepnesteddir.txt', + 'newdirectory/nesteddir/nestedfile.txt', + ], + options: { + context: path.resolve(HELPER_DIR, 'dir (86)'), + }, + patterns: [ + { + from: '**/*', + to: 'newdirectory', + }, + ], + }) + .then(done) + .catch(done); + }); +}); diff --git a/test/flatten-option.test.js b/test/flatten-option.test.js new file mode 100644 index 00000000..80766edb --- /dev/null +++ b/test/flatten-option.test.js @@ -0,0 +1,147 @@ +import { runEmit } from './utils/run'; + +describe('flatten option', () => { + it('should flatten a directory\'s files to a root directory when "from" is a file', (done) => { + runEmit({ + expectedAssetKeys: ['directoryfile.txt'], + patterns: [ + { + flatten: true, + from: 'directory/directoryfile.txt', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should flatten a directory\'s files to a new directory when "from" is a file', (done) => { + runEmit({ + expectedAssetKeys: ['nested/directoryfile.txt'], + patterns: [ + { + flatten: true, + from: 'directory/directoryfile.txt', + to: 'nested', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should flatten a directory\'s files to a root directory when "from" is a directory', (done) => { + runEmit({ + expectedAssetKeys: [ + '.dottedfile', + 'deepnested.txt', + 'directoryfile.txt', + 'nestedfile.txt', + ], + patterns: [ + { + flatten: true, + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should flatten a directory\'s files to new directory when "from" is a directory', (done) => { + runEmit({ + expectedAssetKeys: [ + 'newdirectory/.dottedfile', + 'newdirectory/deepnested.txt', + 'newdirectory/directoryfile.txt', + 'newdirectory/nestedfile.txt', + ], + patterns: [ + { + flatten: true, + from: 'directory', + to: 'newdirectory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should flatten a directory\'s files to a root directory when "from" is a glob', (done) => { + runEmit({ + expectedAssetKeys: [ + 'deepnested.txt', + 'directoryfile.txt', + 'nestedfile.txt', + ], + patterns: [ + { + flatten: true, + from: 'directory/**/*', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should flatten a directory\'s files to a new directory when "from" is a glob', (done) => { + runEmit({ + expectedAssetKeys: [ + 'nested/deepnested.txt', + 'nested/directoryfile.txt', + 'nested/nestedfile.txt', + ], + patterns: [ + { + flatten: true, + from: 'directory/**/*', + to: 'nested', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should flatten files in a relative context to a root directory when "from" is a glob', (done) => { + runEmit({ + expectedAssetKeys: [ + 'deepnested.txt', + 'directoryfile.txt', + 'nestedfile.txt', + ], + patterns: [ + { + context: 'directory', + flatten: true, + from: '**/*', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should flatten files in a relative context to a non-root directory when "from" is a glob', (done) => { + runEmit({ + expectedAssetKeys: [ + 'nested/deepnested.txt', + 'nested/directoryfile.txt', + 'nested/nestedfile.txt', + ], + patterns: [ + { + context: 'directory', + flatten: true, + from: '**/*', + to: 'nested', + }, + ], + }) + .then(done) + .catch(done); + }); +}); diff --git a/test/force-option.test.js b/test/force-option.test.js new file mode 100644 index 00000000..1567007d --- /dev/null +++ b/test/force-option.test.js @@ -0,0 +1,237 @@ +import { runForce } from './utils/run'; + +describe('force option', () => { + describe('is not specified', () => { + it('should not overwrite a file already in the compilation by default when "from" is a file', (done) => { + runForce({ + existingAssets: ['file.txt'], + expectedAssetKeys: ['file.txt'], + expectedAssetContent: { + 'file.txt': 'existing', + }, + patterns: [ + { + from: 'file.txt', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should not overwrite files already in the compilation when "from" is a directory', (done) => { + runForce({ + existingAssets: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + expectedAssetKeys: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + expectedAssetContent: { + '.dottedfile': 'existing', + 'nested/deep-nested/deepnested.txt': 'existing', + 'nested/nestedfile.txt': 'existing', + 'directoryfile.txt': 'existing', + }, + patterns: [ + { + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should not overwrite files already in the compilation when "from" is a glob', (done) => { + runForce({ + existingAssets: [ + 'directory/directoryfile.txt', + 'directory/nested/deep-nested/deepnested.txt', + 'directory/nested/nestedfile.txt', + ], + expectedAssetKeys: [ + 'directory/directoryfile.txt', + 'directory/nested/deep-nested/deepnested.txt', + 'directory/nested/nestedfile.txt', + ], + expectedAssetContent: { + 'directory/nested/deep-nested/deepnested.txt': 'existing', + 'directory/nested/nestedfile.txt': 'existing', + 'directory/directoryfile.txt': 'existing', + }, + patterns: [ + { + from: 'directory/**/*', + }, + ], + }) + .then(done) + .catch(done); + }); + }); + + describe('is "false" (Boolean)', () => { + it('should not overwrite a file already in the compilation by default when "from" is a file', (done) => { + runForce({ + existingAssets: ['file.txt'], + expectedAssetKeys: ['file.txt'], + expectedAssetContent: { + 'file.txt': 'existing', + }, + patterns: [ + { + force: false, + from: 'file.txt', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should not overwrite files already in the compilation when "from" is a directory', (done) => { + runForce({ + existingAssets: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + expectedAssetKeys: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + expectedAssetContent: { + '.dottedfile': 'existing', + 'nested/deep-nested/deepnested.txt': 'existing', + 'nested/nestedfile.txt': 'existing', + 'directoryfile.txt': 'existing', + }, + patterns: [ + { + force: false, + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should not overwrite files already in the compilation when "from" is a glob', (done) => { + runForce({ + existingAssets: [ + 'directory/directoryfile.txt', + 'directory/nested/deep-nested/deepnested.txt', + 'directory/nested/nestedfile.txt', + ], + expectedAssetKeys: [ + 'directory/directoryfile.txt', + 'directory/nested/deep-nested/deepnested.txt', + 'directory/nested/nestedfile.txt', + ], + expectedAssetContent: { + 'directory/nested/deep-nested/deepnested.txt': 'existing', + 'directory/nested/nestedfile.txt': 'existing', + 'directory/directoryfile.txt': 'existing', + }, + patterns: [ + { + force: false, + from: 'directory/**/*', + }, + ], + }) + .then(done) + .catch(done); + }); + }); + + describe('is "true" (Boolean)', () => { + it('should force overwrite a file already in the compilation when "from" is a file', (done) => { + runForce({ + existingAssets: ['file.txt'], + expectedAssetKeys: ['file.txt'], + expectedAssetContent: { + 'file.txt': 'new', + }, + patterns: [ + { + force: true, + from: 'file.txt', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should force overwrite files already in the compilation when "from" is a directory', (done) => { + runForce({ + existingAssets: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + expectedAssetKeys: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + expectedAssetContent: { + '.dottedfile': 'dottedfile contents\n', + 'nested/deep-nested/deepnested.txt': '', + 'nested/nestedfile.txt': '', + 'directoryfile.txt': 'new', + }, + patterns: [ + { + force: true, + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should force overwrite files already in the compilation when "from" is a glob', (done) => { + runForce({ + existingAssets: [ + 'directory/directoryfile.txt', + 'directory/nested/deep-nested/deepnested.txt', + 'directory/nested/nestedfile.txt', + ], + expectedAssetKeys: [ + 'directory/directoryfile.txt', + 'directory/nested/deep-nested/deepnested.txt', + 'directory/nested/nestedfile.txt', + ], + expectedAssetContent: { + 'directory/nested/deep-nested/deepnested.txt': '', + 'directory/nested/nestedfile.txt': '', + 'directory/directoryfile.txt': 'new', + }, + patterns: [ + { + force: true, + from: 'directory/**/*', + }, + ], + }) + .then(done) + .catch(done); + }); + }); +}); diff --git a/test/helpers/watch/.gitkeep b/test/helpers/watch/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/helpers/watch/directory/.gitkeep b/test/helpers/watch/directory/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/ignore-option.test.js b/test/ignore-option.test.js new file mode 100644 index 00000000..50db76c8 --- /dev/null +++ b/test/ignore-option.test.js @@ -0,0 +1,186 @@ +import { runEmit } from './utils/run'; + +describe('ignore option', () => { + it('should ignore files when "from" is a file', (done) => { + runEmit({ + expectedAssetKeys: [], + patterns: [ + { + ignore: ['file.*'], + from: 'file.txt', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should files when "from" is a directory', (done) => { + runEmit({ + expectedAssetKeys: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + ], + patterns: [ + { + ignore: ['*/nestedfile.*'], + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should files in nested directory when "from" is a directory', (done) => { + runEmit({ + expectedAssetKeys: ['.dottedfile', 'directoryfile.txt'], + patterns: [ + { + ignore: ['**/nested/**'], + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should files when from is a glob', (done) => { + runEmit({ + expectedAssetKeys: [ + 'directory/directoryfile.txt', + 'directory/nested/deep-nested/deepnested.txt', + ], + patterns: [ + { + ignore: ['*nestedfile.*'], + from: 'directory/**/*', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should files in nested directory when from is a glob', (done) => { + runEmit({ + expectedAssetKeys: ['directory/directoryfile.txt'], + patterns: [ + { + ignore: ['*/nested/**'], + from: 'directory/**/*', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should ignore files with a certain extension', (done) => { + runEmit({ + expectedAssetKeys: ['.dottedfile'], + patterns: [ + { + ignore: ['*.txt'], + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should ignore files with multiple ignore patterns', (done) => { + runEmit({ + expectedAssetKeys: ['directory/nested/nestedfile.txt'], + patterns: [ + { + ignore: ['directoryfile.*', '**/deep-nested/**'], + from: 'directory/**/*', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should ignore files except those with dots', (done) => { + runEmit({ + expectedAssetKeys: ['.dottedfile'], + options: { + ignore: [ + { + dot: false, + glob: '**/*', + }, + ], + }, + patterns: [ + { + from: 'directory', + ignore: [ + { + dot: false, + glob: '**/*', + }, + ], + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should ignore files that start with a dot', (done) => { + runEmit({ + expectedAssetKeys: [ + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + patterns: [ + { + ignore: ['.dottedfile'], + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should ignores all files even if they start with a dot', (done) => { + runEmit({ + expectedAssetKeys: [], + options: { + ignore: ['**/*'], + }, + patterns: [ + { + from: 'directory', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should ignore files when "from" is a file (global ignore)', (done) => { + runEmit({ + expectedAssetKeys: [], + options: { + ignore: ['file.*'], + }, + patterns: [ + { + ignore: ['file.*'], + from: 'file.txt', + }, + ], + }) + .then(done) + .catch(done); + }); +}); diff --git a/test/test-option.test.js b/test/test-option.test.js new file mode 100644 index 00000000..c0503161 --- /dev/null +++ b/test/test-option.test.js @@ -0,0 +1,73 @@ +import path from 'path'; + +import { runEmit } from './utils/run'; + +describe('test option', () => { + it('should move files to a root directory with [1]', (done) => { + runEmit({ + expectedAssetKeys: ['txt'], + patterns: [ + { + from: 'directory/nested/deep-nested', + to: '[1]', + test: /\.([^.]*)$/, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should move files to a non-root directory with [1]', (done) => { + runEmit({ + expectedAssetKeys: ['nested/txt'], + patterns: [ + { + from: 'directory/nested/deep-nested', + to: 'nested/[1]', + test: /\.([^.]*)$/, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should move files and flatten them to a non-root directory with [1]-[2].[ext]', (done) => { + runEmit({ + expectedAssetKeys: [ + 'nested/deep-nested-deepnested.txt', + 'nested/directory-directoryfile.txt', + 'nested/nested-nestedfile.txt', + ], + patterns: [ + { + from: 'directory/**/*', + test: `([^\\${path.sep}]+)\\${path.sep}([^\\${path.sep}]+)\\.\\w+$`, + to: 'nested/[1]-[2].[ext]', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should move files and flatten them to a root directory with [1]-[2].[ext]', (done) => { + runEmit({ + expectedAssetKeys: [ + 'deep-nested-deepnested.txt', + 'directory-directoryfile.txt', + 'nested-nestedfile.txt', + ], + patterns: [ + { + from: 'directory/**/*', + test: `([^\\${path.sep}]+)\\${path.sep}([^\\${path.sep}]+)\\.\\w+$`, + to: '[1]-[2].[ext]', + }, + ], + }) + .then(done) + .catch(done); + }); +}); diff --git a/test/toType-option.test.js b/test/toType-option.test.js new file mode 100644 index 00000000..b0009aac --- /dev/null +++ b/test/toType-option.test.js @@ -0,0 +1,107 @@ +import path from 'path'; + +import { runEmit } from './utils/run'; +import { MockCompilerNoStat } from './utils/mocks'; + +const HELPER_DIR = path.join(__dirname, 'helpers'); + +describe('toType option', () => { + it('should move a file to a new file', (done) => { + runEmit({ + expectedAssetKeys: ['new-file.txt'], + patterns: [ + { + from: 'file.txt', + to: 'new-file.txt', + toType: 'file', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should move a file to a new directory', (done) => { + runEmit({ + expectedAssetKeys: ['new-file.txt/file.txt'], + patterns: [ + { + from: 'file.txt', + to: 'new-file.txt', + toType: 'dir', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should move a file to a new directory', (done) => { + runEmit({ + expectedAssetKeys: [ + 'directory/directorynew-directoryfile.txt.22af645d1859cb5ca6da0c484f1f37ea.22af645d1859cb5ca6da0c484f1f37ea.22af645d.22af645d.txt', + ], + patterns: [ + { + from: 'directory/directoryfile.*', + to: + '[path][folder]new-[name].[ext].[hash].[contenthash].[md5:contenthash:hex:8].[md5:hash:hex:8].txt', + toType: 'template', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should move a file to a new file with no extension', (done) => { + runEmit({ + expectedAssetKeys: ['newname'], + patterns: [ + { + from: 'file.txt', + to: 'newname', + toType: 'file', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should move a file to a new directory with an extension', (done) => { + runEmit({ + expectedAssetKeys: ['newdirectory.ext/file.txt'], + patterns: [ + { + from: 'file.txt', + to: 'newdirectory.ext', + toType: 'dir', + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should warns when file not found and stats is undefined', (done) => { + runEmit({ + compiler: new MockCompilerNoStat(), + expectedAssetKeys: [], + expectedWarnings: [ + new Error( + `unable to locate 'nonexistent.txt' at '${HELPER_DIR}${path.sep}nonexistent.txt'` + ), + ], + patterns: [ + { + from: 'nonexistent.txt', + to: '.', + toType: 'dir', + }, + ], + }) + .then(done) + .catch(done); + }); +}); diff --git a/test/transform-option.test.js b/test/transform-option.test.js new file mode 100644 index 00000000..f1434b91 --- /dev/null +++ b/test/transform-option.test.js @@ -0,0 +1,184 @@ +import path from 'path'; + +import { runEmit } from './utils/run'; + +const HELPER_DIR = path.join(__dirname, 'helpers'); + +describe('transform option', () => { + it('should transform file when "from" is a file', (done) => { + runEmit({ + expectedAssetKeys: ['file.txt'], + expectedAssetContent: { + 'file.txt': 'newchanged', + }, + patterns: [ + { + from: 'file.txt', + transform(content, absoluteFrom) { + expect(absoluteFrom.includes(HELPER_DIR)).toBe(true); + + return `${content}changed`; + }, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should transform target path of every when "from" is a directory', (done) => { + runEmit({ + expectedAssetKeys: [ + '.dottedfile', + 'directoryfile.txt', + 'nested/deep-nested/deepnested.txt', + 'nested/nestedfile.txt', + ], + expectedAssetContent: { + '.dottedfile': 'dottedfile contents\nchanged', + 'directoryfile.txt': 'newchanged', + 'nested/deep-nested/deepnested.txt': 'changed', + 'nested/nestedfile.txt': 'changed', + }, + patterns: [ + { + from: 'directory', + transform(content, absoluteFrom) { + expect(absoluteFrom.includes(HELPER_DIR)).toBe(true); + + return `${content}changed`; + }, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should transform target path of every file when "from" is a glob', (done) => { + runEmit({ + expectedAssetKeys: [ + 'directory/directoryfile.txt', + 'directory/nested/deep-nested/deepnested.txt', + 'directory/nested/nestedfile.txt', + ], + expectedAssetContent: { + 'directory/directoryfile.txt': 'newchanged', + 'directory/nested/deep-nested/deepnested.txt': 'changed', + 'directory/nested/nestedfile.txt': 'changed', + }, + patterns: [ + { + from: 'directory/**/*', + transform(content, absoluteFrom) { + expect(absoluteFrom.includes(HELPER_DIR)).toBe(true); + + return `${content}changed`; + }, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should transform file when function return Promise', (done) => { + runEmit({ + expectedAssetKeys: ['file.txt'], + expectedAssetContent: { + 'file.txt': 'newchanged!', + }, + patterns: [ + { + from: 'file.txt', + transform(content) { + return new Promise((resolve) => { + resolve(`${content}changed!`); + }); + }, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should transform target path when async function used', (done) => { + runEmit({ + expectedAssetKeys: ['file.txt'], + expectedAssetContent: { + 'file.txt': 'newchanged!', + }, + patterns: [ + { + from: 'file.txt', + async transform(content) { + const newPath = await new Promise((resolve) => { + resolve(`${content}changed!`); + }); + + return newPath; + }, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should warns when function throw error', (done) => { + runEmit({ + expectedAssetKeys: [], + expectedErrors: [new Error('a failure happened')], + patterns: [ + { + from: 'file.txt', + transform() { + // eslint-disable-next-line no-throw-literal + throw new Error('a failure happened'); + }, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should warns when Promise was rejected', (done) => { + runEmit({ + expectedAssetKeys: [], + expectedErrors: [new Error('a failure happened')], + patterns: [ + { + from: 'file.txt', + transform() { + return new Promise((resolve, reject) => { + return reject(new Error('a failure happened')); + }); + }, + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should warns when async function throw error', (done) => { + runEmit({ + expectedAssetKeys: [], + expectedErrors: [new Error('a failure happened')], + patterns: [ + { + from: 'file.txt', + async transform() { + await new Promise((resolve, reject) => { + reject(new Error('a failure happened')); + }); + }, + }, + ], + }) + .then(done) + .catch(done); + }); +}); diff --git a/test/transformPath-option.test.js b/test/transformPath-option.test.js index 3ab1b161..ee066238 100644 --- a/test/transformPath-option.test.js +++ b/test/transformPath-option.test.js @@ -51,20 +51,13 @@ describe('transformPath option', () => { it('should transform target path of every file when "from" is a glob', (done) => { runEmit({ expectedAssetKeys: [ - '/some/path/(special-*file).txt.tst', - '/some/path/binextension.bin.tst', '/some/path/deepnested.txt.tst', - '/some/path/deepnesteddir.txt.tst', - '/some/path/file.txt.tst', - '/some/path/file.txt.gz.tst', '/some/path/directoryfile.txt.tst', '/some/path/nestedfile.txt.tst', - '/some/path/noextension.tst', - '/some/path/hello.txt.tst', ], patterns: [ { - from: '**/*', + from: 'directory/**/*', transformPath(targetPath, absoluteFrom) { expect(absoluteFrom.includes(HELPER_DIR)).toBe(true); @@ -177,24 +170,13 @@ describe('transformPath option', () => { it('should transform target path of every file in glob after applying template', (done) => { runEmit({ expectedAssetKeys: [ - 'transformed/[!]/hello-d41d8c.txt', - 'transformed/[special?directory]/directoryfile-22af64.txt', - 'transformed/[special?directory]/(special-*file)-0bd650.txt', - 'transformed/[special?directory]/nested/nestedfile-d41d8c.txt', - 'transformed/binextension-d41d8c.bin', - 'transformed/dir (86)/file-d41d8c.txt', - 'transformed/dir (86)/nesteddir/deepnesteddir/deepnesteddir-d41d8c.txt', - 'transformed/dir (86)/nesteddir/nestedfile-d41d8c.txt', - 'transformed/file-22af64.txt', - 'transformed/file.txt-5b311c.gz', 'transformed/directory/directoryfile-22af64.txt', 'transformed/directory/nested/deep-nested/deepnested-d41d8c.txt', 'transformed/directory/nested/nestedfile-d41d8c.txt', - 'transformed/noextension-d41d8c', ], patterns: [ { - from: '**/*', + from: 'directory/**/*', to: 'nested/[path][name]-[hash:6].[ext]', transformPath(targetPath, absoluteFrom) { expect(absoluteFrom.includes(HELPER_DIR)).toBe(true); diff --git a/test/utils/run.js b/test/utils/run.js index 17cf14f1..7cdc1134 100644 --- a/test/utils/run.js +++ b/test/utils/run.js @@ -136,14 +136,17 @@ function runForce(opts) { opts.compilation = { assets: {}, }; - // eslint-disable-next-line no-param-reassign - opts.compilation.assets[opts.existingAsset] = { - source() { - return 'existing'; - }, - }; - return run(opts).then(() => {}); + opts.existingAssets.forEach((assetName) => { + // eslint-disable-next-line no-param-reassign + opts.compilation.assets[assetName] = { + source() { + return 'existing'; + }, + }; + }); + + return runEmit(opts).then(() => {}); } function runChange(opts) { @@ -163,7 +166,7 @@ function runChange(opts) { return run({ compiler, - options: opts.options, + options: Object.assign({}, opts.options, { context: 'watch' }), patterns: opts.patterns, }) .then(() => {