UNPKG

copy-webpack-plugin

Version:
606 lines (547 loc) 17.3 kB
/* globals describe, it, afterEach, __dirname */ var expect = require('chai').expect; var CopyWebpackPlugin = require('../index'); var path = require('path'); var _ = require('lodash'); var Promise = require('bluebird'); var fs = Promise.promisifyAll(require('fs-extra')); var HELPER_DIR = path.join(__dirname, 'helpers'); var TEMP_DIR = path.join(__dirname, 'tempdir'); function MockCompiler() { this.options = { context: HELPER_DIR }; } MockCompiler.prototype.plugin = function(type, fn) { switch(type) { case 'emit': this.emitFn = fn; break; case 'after-emit': this.afterEmitFn = fn; break; } }; describe('apply function', function() { // Ideally we pass in patterns and confirm the resulting assets function run(opts) { return new Promise(function(resolve, reject) { var plugin = new CopyWebpackPlugin(opts.patterns, opts.options); // Get a mock compiler to pass to plugin.apply var compiler = opts.compiler || new MockCompiler(); plugin.apply(compiler); // Call the registered function with a mock compilation and callback var compilation = _.extend({ errors: [], assets: {}, fileDependencies: [], contextDependencies: [] }, opts.compilation); // Execute the functions in series Promise.each([compiler.emitFn, compiler.afterEmitFn], function(fn) { return new Promise(function(res, rej) { try { fn(compilation, res); } catch(e) { rej(e); } }); }) .then(function() { if (compilation.errors.length > 0) { throw compilation.errors[0]; } resolve(compilation); }) .catch(reject); }); } function runEmit(opts) { return run(opts) .then(function(compilation) { if (opts.expectedAssetKeys && opts.expectedAssetKeys.length > 0) { // Replace all paths with platform-specific paths var expectedAssetKeys = _.map(opts.expectedAssetKeys, path.normalize); expect(compilation.assets).to.have.all.keys(expectedAssetKeys); } else { expect(compilation.assets).to.be.empty; } }); } function runForce(opts) { opts.compilation = { assets: {} }; opts.compilation.assets[opts.existingAsset] = { source: function() { return 'existing'; } }; return run(opts) .then(function(compilation) { var assetContent = compilation.assets[opts.existingAsset].source().toString(); expect(assetContent).to.equal(opts.expectedAssetContent); }); } function runChange(opts) { // Create two test files fs.writeFileSync(opts.newFileLoc1); fs.writeFileSync(opts.newFileLoc2); // Create a reference to the compiler var compiler = new MockCompiler(); var compilation = { errors: [], assets: {}, fileDependencies: [], contextDependencies: [] }; return run({ compiler: compiler, patterns: opts.patterns }) // mtime is only measured in whole seconds .delay(1000) .then(function() { // Change a file fs.appendFileSync(opts.newFileLoc1, 'extra'); // Trigger another compile return new Promise(function(res, rej) { compiler.emitFn(compilation, res); }); }) .then(function() { return compilation; }) .finally(function() { fs.unlinkSync(opts.newFileLoc1); fs.unlinkSync(opts.newFileLoc2); }); } // Use then and catch explicitly, so errors // aren't seen as unhandled exceptions describe('error handling', function() { it('doesn\'t throw an error if no patterns are passed', function(done) { runEmit({ patterns: undefined, expectedAssetKeys: [] }) .then(done) .catch(done); }); it('throws an error if the patterns are an object', function() { var createPluginWithObject = function() { new CopyWebpackPlugin({}); }; expect(createPluginWithObject).to.throw(Error); }); it('throws an error if the patterns are null', function() { var createPluginWithNull = function() { new CopyWebpackPlugin(null); }; expect(createPluginWithNull).to.throw(Error); }); }); describe('with file in from', function() { it('can move a file to the root directory', function(done) { runEmit({ patterns: [{ from: 'file.txt' }], expectedAssetKeys: ['file.txt'] }) .then(done) .catch(done); }); it('can use an absolute path to move a file to the root directory', function(done) { var absolutePath = path.resolve(HELPER_DIR, 'file.txt'); runEmit({ patterns: [{ from: absolutePath }], expectedAssetKeys: ['file.txt'] }) .then(done) .catch(done); }); it('can use a glob to move a file to the root directory', function(done) { runEmit({ patterns: [{ from: '*.txt' }], expectedAssetKeys: ['file.txt'] }) .then(done) .catch(done); }); it('can use a glob to move multiple files to the root directory', function(done) { runEmit({ patterns: [{ from: '**/*.txt' }], expectedAssetKeys: ['file.txt', 'directory/directoryfile.txt', 'directory/nested/nestedfile.txt'] }) .then(done) .catch(done); }); it('can use a glob with a full path to move a file to the root directory', function(done) { runEmit({ patterns: [{ from: path.join(HELPER_DIR, '*.txt') }], expectedAssetKeys: ['file.txt'] }) .then(done) .catch(done); }); it('can use a glob with a full path to move multiple files to the root directory', function(done) { runEmit({ patterns: [{ from: path.join(HELPER_DIR, '**/*.txt') }], expectedAssetKeys: ['file.txt', 'directory/directoryfile.txt', 'directory/nested/nestedfile.txt'] }) .then(done) .catch(done); }); it('can move a file to a new directory without a forward slash', function(done) { runEmit({ patterns: [{ from: 'file.txt', to: 'newdirectory' }], expectedAssetKeys: ['newdirectory/file.txt'] }) .then(done) .catch(done); }); it('can move a file to the root directory using an absolute to', function(done) { runEmit({ patterns: [{ from: 'file.txt', to: HELPER_DIR }], expectedAssetKeys: ['file.txt'] }) .then(done) .catch(done); }); it('can move a file to a new directory using an absolute to', function(done) { runEmit({ patterns: [{ from: 'file.txt', to: TEMP_DIR }], expectedAssetKeys: ['../tempdir/file.txt'] }) .then(done) .catch(done); }); it('can move a file to a new file using an absolute to', function(done) { var absolutePath = path.resolve(TEMP_DIR, 'newfile.txt'); runEmit({ patterns: [{ from: 'file.txt', to: absolutePath }], expectedAssetKeys: ['../tempdir/newfile.txt'] }) .then(done) .catch(done); }); it('can move a file to a new directory with a forward slash', function(done) { runEmit({ patterns: [{ from: 'file.txt', to: 'newdirectory/' }], expectedAssetKeys: ['newdirectory/file.txt'] }) .then(done) .catch(done); }); it('can move a file to a new directory with an extension', function(done) { runEmit({ patterns: [{ from: 'file.txt', to: 'newdirectory.ext', toType: 'dir' }], expectedAssetKeys: ['newdirectory.ext/file.txt'] }) .then(done) .catch(done); }); it('can move a file to a new directory with an extension and forward slash', function(done) { runEmit({ patterns: [{ from: 'file.txt', to: 'newdirectory.ext/' }], expectedAssetKeys: ['newdirectory.ext/file.txt'] }) .then(done) .catch(done); }); it('can move a file to a new file with a different name', function(done) { runEmit({ patterns: [{ from: 'file.txt', to: 'newname.txt' }], expectedAssetKeys: ['newname.txt'] }) .then(done) .catch(done); }); it('can move a file to a new file with no extension', function(done) { runEmit({ patterns: [{ from: 'file.txt', to: 'newname', toType: 'file' }], expectedAssetKeys: ['newname'] }) .then(done) .catch(done); }); it('can move a nested file to the root directory', function(done) { runEmit({ patterns: [{ from: 'directory/directoryfile.txt' }], expectedAssetKeys: ['directoryfile.txt'] }) .then(done) .catch(done); }); it('can use an absolute path to move a nested file to the root directory', function(done) { var absolutePath = path.resolve(HELPER_DIR, 'directory', 'directoryfile.txt'); runEmit({ patterns: [{ from: absolutePath }], expectedAssetKeys: ['directoryfile.txt'] }) .then(done) .catch(done); }); it('can move a nested file to a new directory', function(done) { runEmit({ patterns: [{ from: 'directory/directoryfile.txt', to: 'newdirectory' }], expectedAssetKeys: ['newdirectory/directoryfile.txt'] }) .then(done) .catch(done); }); it('can use an absolute path to move a nested file to a new directory', function(done) { var absolutePath = path.resolve(HELPER_DIR, 'directory', 'directoryfile.txt'); runEmit({ patterns: [{ from: absolutePath, to: 'newdirectory' }], expectedAssetKeys: ['newdirectory/directoryfile.txt'] }) .then(done) .catch(done); }); it('won\'t overwrite a file already in the compilation', function(done) { runForce({ patterns: [{ from: 'file.txt' }], existingAsset: 'file.txt', expectedAssetContent: 'existing' }) .then(done) .catch(done); }); it('can force overwrite of a file already in the compilation', function(done) { runForce({ patterns: [{ from: 'file.txt', force: true }], existingAsset: 'file.txt', expectedAssetContent: 'new' }) .then(done) .catch(done); }); it('adds the file to the watch list', function(done) { run({ patterns: [{ from: 'file.txt' }] }) .then(function(compilation) { var absFrom = path.join(HELPER_DIR, 'file.txt'); expect(compilation.fileDependencies).to.have.members([absFrom]); }) .then(done) .catch(done); }); it('only include files that have changed', function(done) { runChange({ patterns: [{ from: 'tempfile1.txt' }, { from: 'tempfile2.txt' }], newFileLoc1: path.join(HELPER_DIR, 'tempfile1.txt'), newFileLoc2: path.join(HELPER_DIR, 'tempfile2.txt') }) .then(function(compilation) { expect(compilation.assets).to.have.key('tempfile1.txt'); expect(compilation.assets).not.to.have.key('tempfile2.txt'); }) .then(done) .catch(done); }); }); describe('with directory in from', function() { it('can move a directory\'s contents to the root directory', function(done) { runEmit({ patterns: [{ from: 'directory' }], expectedAssetKeys: ['directoryfile.txt', 'nested/nestedfile.txt'] }) .then(done) .catch(done); }); it('can use an absolute path to move a directory\'s contents to the root directory', function(done) { var absolutePath = path.resolve(HELPER_DIR, 'directory'); runEmit({ patterns: [{ from: absolutePath }], expectedAssetKeys: ['directoryfile.txt', 'nested/nestedfile.txt'] }) .then(done) .catch(done); }); it('can move a directory\'s contents to a new directory', function(done) { runEmit({ patterns: [{ from: 'directory', to: 'newdirectory' }], expectedAssetKeys: ['newdirectory/directoryfile.txt','newdirectory/nested/nestedfile.txt'] }) .then(done) .catch(done); }); it('can move a directory\'s contents to a new directory using an absolute to', function(done) { runEmit({ patterns: [{ from: 'directory', to: TEMP_DIR }], expectedAssetKeys: [ '../tempdir/directoryfile.txt', '../tempdir/nested/nestedfile.txt' ] }) .then(done) .catch(done); }); it('can move a nested directory\'s contents to the root directory', function(done) { runEmit({ patterns: [{ from: 'directory/nested' }], expectedAssetKeys: ['nestedfile.txt'] }) .then(done) .catch(done); }); it('can move a nested directory\'s contents to a new directory', function(done) { runEmit({ patterns: [{ from: 'directory/nested', to: 'newdirectory' }], expectedAssetKeys: ['newdirectory/nestedfile.txt'] }) .then(done) .catch(done); }); it('can use an absolute path to move a nested directory\'s contents to a new directory', function(done) { var absolutePath = path.resolve(HELPER_DIR, 'directory', 'nested'); runEmit({ patterns: [{ from: absolutePath, to: 'newdirectory' }], expectedAssetKeys: ['newdirectory/nestedfile.txt'] }) .then(done) .catch(done); }); it('won\'t overwrite a file already in the compilation', function(done) { runForce({ patterns: [{ from: 'directory' }], existingAsset: 'directoryfile.txt', expectedAssetContent: 'existing' }) .then(done) .catch(done); }); it('can force overwrite of a file already in the compilation', function(done) { runForce({ patterns: [{ from: 'directory', force: true }], existingAsset: 'directoryfile.txt', expectedAssetContent: 'new' }) .then(done) .catch(done); }); it('adds the directory to the watch list', function(done) { run({ patterns: [{ from: 'directory' }] }) .then(function(compilation) { var absFrom = path.join(HELPER_DIR, 'directory'); expect(compilation.contextDependencies).to.have.members([absFrom]); }) .then(done) .catch(done); }); it('only include files that have changed', function(done) { runChange({ patterns: [{ from: 'directory' }], newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'), newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt') }) .then(function(compilation) { expect(compilation.assets).to.have.key('tempfile1.txt'); expect(compilation.assets).not.to.have.key('tempfile2.txt'); }) .then(done) .catch(done); }); }); describe('options', function() { describe('ignore', function() { it('ignores files when from is a file', function(done) { runEmit({ patterns: [ { from: 'file.txt' }, { from: 'directory/directoryfile.txt' } ], options: { ignore: [ 'file.*' ] }, expectedAssetKeys: ['directoryfile.txt'] }) .then(done) .catch(done); }); it('ignores files when from is a directory', function(done) { runEmit({ patterns: [{ from: 'directory' }], options: { ignore: [ '*/nestedfile.*' ] }, expectedAssetKeys: ['directoryfile.txt'] }) .then(done) .catch(done); }); it('ignores files with a certain extension', function(done) { runEmit({ patterns: [{ from: 'directory' }], options: { ignore: [ '*.txt' ] }, expectedAssetKeys: [] }) .then(done) .catch(done); }); it('ignores files that start with a dot', function(done) { runEmit({ patterns: [{ from: '.' }], options: { ignore: [ '.dotted_file' ] }, expectedAssetKeys: [ 'file.txt', 'directory/directoryfile.txt', 'directory/nested/nestedfile.txt' ] }) .then(done) .catch(done); }); it('ignores all files except those with dots', function(done) { runEmit({ patterns: [{ from: '.' }], options: { ignore: [ '**/*' ] }, expectedAssetKeys: ['.dotted_file'] }) .then(done) .catch(done); }); it('ignores all files even if they start with a dot', function(done) { runEmit({ patterns: [{ from: '.' }], options: { ignore: [ { glob: '**/*', dot: true } ] }, expectedAssetKeys: [] }) .then(done) .catch(done); }); }); }); });