Skip to content

Commit

Permalink
More additions to the hacky, but useful integration test
Browse files Browse the repository at this point in the history
Sigh… second-level precision mtimes. Filed
webpack/webpack#2003 in relation to the
difficulties I was having.

Oh, and still need to revisit caching with preventSymlinkResolution on.
  • Loading branch information
timmfin committed Feb 5, 2016
1 parent 8bfe802 commit 708227c
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 15 deletions.
1 change: 1 addition & 0 deletions .node-version
@@ -0,0 +1 @@
4.2.1
31 changes: 21 additions & 10 deletions index.js
Expand Up @@ -11,8 +11,8 @@ var symlinkOrCopySync = require('symlink-or-copy').sync;

var PreventResolveSymlinkPlugin = require('./prevent-resolve-symlink-plugin');

function WebpackFilter(inputNode, options) {
if (!(this instanceof WebpackFilter)) return new WebpackFilter(inputNode, options);
function WebpackFilter(inputNode, options, postBuildDebugCallback) {
if (!(this instanceof WebpackFilter)) return new WebpackFilter(inputNode, options, postBuildDebugCallback);

if (Array.isArray(inputNode)) {
throw new Error("WebpackFilter only accepts a single inputNode");
Expand All @@ -22,6 +22,9 @@ function WebpackFilter(inputNode, options) {
annotation: options.annotation
});
this.options = options;

console.log("postBuildDebugCallback", postBuildDebugCallback);
this.postBuildDebugCallback = postBuildDebugCallback;
}

WebpackFilter.prototype = Object.create(Plugin.prototype);
Expand Down Expand Up @@ -77,7 +80,8 @@ WebpackFilter.prototype.initializeCompiler = function() {

// Let webpack do all the caching (we will call webpack's compile method every
// build and rely on it to only build what is necessary)
this.options.cache = this._webpackCache = {};
// this.options.cache = this._webpackCache = {};
this.options.cache = true;


// Save state between builds do that changes to module ids doesn't bust the cache
Expand All @@ -89,13 +93,16 @@ WebpackFilter.prototype.initializeCompiler = function() {

// By default, log webpack's output to the console
var DEFAULT_LOG_OPTIONS = true;
// {
// var DEFAULT_LOG_OPTIONS = {
// // assets: true,

// colors: true,
// timings: true,
// modules: true,
// cached: true,
// reasons: true,

// hash: true
// // timings: true,
// // reasons: true,
// // chunkOrigins: true
// };

Expand All @@ -105,10 +112,10 @@ WebpackFilter.prototype.initializeCompiler = function() {
// modules (a common problem with Broccoli's highly symlinked output trees).
// This is on by default, but can be disabled.
if (this.options.preventSymlinkResolution === true || this.options.preventSymlinkResolution === undefined) {
this.options.plugins = this.options.plugins || [];
this.options.plugins.push(
new webpack.ResolverPlugin([PreventResolveSymlinkPlugin])
);
// this.options.plugins = this.options.plugins || [];
// this.options.plugins.push(
// new webpack.ResolverPlugin([PreventResolveSymlinkPlugin])
// );
}


Expand Down Expand Up @@ -210,6 +217,10 @@ WebpackFilter.prototype.build = function() {

that.lastWrittenBuffersByFilepath = writtenBuffersByFilepath;

if (that.postBuildDebugCallback) {
that.postBuildDebugCallback(that.compiler);
}

resolve();
}

Expand Down
9 changes: 7 additions & 2 deletions test/fixtures/kitchen-sink/Brocfile.js
Expand Up @@ -43,10 +43,10 @@ var bundleTree = WebpackFilter(tree, {
},

plugins: [
new webpack.optimize.CommonsChunkPlugin("extra.bundle.js"),

// new webpack.optimize.UglifyJsPlugin(),

new webpack.optimize.CommonsChunkPlugin("extra.bundle.js"),

new HtmlWebpackPlugin({
filename: 'one.html',
title: 'Generated by webpack',
Expand Down Expand Up @@ -111,6 +111,11 @@ var bundleTree = WebpackFilter(tree, {
root: []
}

}, function(webpackCompiler) {
// Hacky debugging callback

// Pass through for access to inputFileSystem
module.exports.webpackCompiler = webpackCompiler;
});

bundleTree = log(bundleTree, {output: 'tree'});
Expand Down
191 changes: 188 additions & 3 deletions test/integration-test.js
@@ -1,17 +1,80 @@
var fs = require('fs');
var path = require('path');
var Promise = require('rsvp').Promise;
var expect = require('chai').expect;
var assert = require('chai').assert
var broccoli = require('broccoli');

function fixturePath(fixture, optionalSubpath) {
var args = [__dirname, 'fixtures'].concat(Array.prototype.slice.call(arguments));
return path.join.apply(path, args);
}

function getBuilderForFixture(fixture) {
var fixturePath = path.join(__dirname, 'fixtures', fixture);
process.chdir(fixturePath);
process.chdir(fixturePath(fixture));

var node = broccoli.loadBrocfile();
return new broccoli.Builder(node);
}

var oldContents = Object.create(null);

// Manually set mtime to the future (2secs) to get around second level mtime precision :/
function bumpMtime(filepath) {
// var stat = fs.statSync(filepath);
// console.log('Pre bump mtime for', filepath, fs.statSync(filepath).mtime);
// fs.utimesSync(filepath, stat.atime, Math.floor((new Date).getTime() / 1000) + 2)
// console.log('Bumped mtime for', filepath, fs.statSync(filepath).mtime, '(' + fs.statSync(filepath).mtime.getTime() + ')');
}

function saveContent(filepath) {
var content = fs.readFileSync(filepath);

if (oldContents[filepath] === undefined) {
oldContents[filepath] = content;
}

return content;
}

function appendToFile(filepath, newContent) {
var content = saveContent(filepath);

fs.writeFileSync(filepath, content + newContent);
bumpMtime(filepath);
}

function searchAndReplaceInFile(filepath, regexOrStringToMatch, replacement) {
var content = saveContent(filepath);

fs.writeFileSync(filepath, content.toString().replace(regexOrStringToMatch, replacement));
bumpMtime(filepath);
}

function deleteFile(filepath) {
saveContent(filepath);

fs.unlinkSync(filepath);
// console.log('deleted file', filepath);
}

function restoreFiles() {
Object.keys(oldContents).forEach(function(filepath) {
restoreFile(filepath);
});
}

function restoreFile(filepath) {
fs.writeFileSync(filepath, oldContents[filepath]);
console.log('restored file', filepath, 'to', oldContents[filepath]);
delete oldContents[filepath];
}

function restoreFileAndBumpMtime(filepath) {
restoreFile(filepath);
bumpMtime(filepath);
}

var filesThatShouldExist = [
'extra.bundle.js',
'one-bundle.js',
Expand All @@ -23,12 +86,19 @@ var filesThatShouldExist = [
'two.html'
];

function logFileIfDesired(filepath) {
// Keep the chain intact
return filepath;
}


describe('broccoli-webpack-cached', function() {
this.timeout(5000);

describe('kitchen sink integration test', function() {
var builder,
buildPromise;
buildPromise,
webpackCompiler;

before(function() {
builder = getBuilderForFixture('kitchen-sink');
Expand All @@ -37,10 +107,15 @@ describe('broccoli-webpack-cached', function() {

it('should build the first time and have all necessary files', function() {
return buildPromise = buildPromise.then(function(result) {

// After first build save hackily passed through webpack compiler reference
webpackCompiler = builder.tree.webpackCompiler;

filesThatShouldExist
.map(function(filename) {
return path.join(result.directory, filename);
})
.map(logFileIfDesired)
.map(fs.statSync)
.map(assert.ok);

Expand All @@ -53,6 +128,7 @@ describe('broccoli-webpack-cached', function() {
.map(function(filename) {
return path.join(result.directory, filename);
})
.map(logFileIfDesired)
.map(fs.statSync)
.map(assert.ok);

Expand All @@ -65,13 +141,122 @@ describe('broccoli-webpack-cached', function() {
.map(function(filename) {
return path.join(result.directory, filename);
})
.map(logFileIfDesired)
.map(fs.statSync)
.map(assert.ok);

})
});

it('should build after adding a new line', function() {
appendToFile(fixturePath('kitchen-sink', 'src/js/1.js'), 'console.log(\'new line added by INTEGRATION TEST\');\n');
webpackCompiler.inputFileSystem.purge();

return buildPromise = builder.build().then(function(result) {

['requireTest-bundle.js']
.map(function(filename) {
return path.join(result.directory, filename);
})
.map(logFileIfDesired)
.map(function(filepath) {
expect(fs.readFileSync(filepath).toString()).to.contain('new line added by INTEGRATION TEST');
return filepath;
})
.map(fs.statSync)
.map(assert.ok);
});
});

it('should build after removing that new line', function() {
restoreFileAndBumpMtime(fixturePath('kitchen-sink', 'src/js/1.js'));
webpackCompiler.inputFileSystem.purge();

return buildPromise = builder.build().then(function(result) {

['requireTest-bundle.js']
.map(function(filename) {
return path.join(result.directory, filename);
})
.map(logFileIfDesired)
.map(function(filepath) {
expect(fs.readFileSync(filepath).toString()).to.not.contain('new line added by INTEGRATION TEST');
return filepath;
})
.map(fs.statSync)
.map(assert.ok);
});
});

it('should errro after removing a necessary file', function() {
deleteFile(fixturePath('kitchen-sink', 'src/js/1.js'));
webpackCompiler.inputFileSystem.purge();

expect
return buildPromise = builder.build().then(function(result) {
throw new Error('This build should not succeed!');
}).catch(function(error) {
expect(error.code).to.equal('ENOENT');
expect(error.path).to.equal('./src/js/1.js');
});
})

it('should work again after bringing the file back', function() {
restoreFileAndBumpMtime(fixturePath('kitchen-sink', 'src/js/1.js'));
webpackCompiler.inputFileSystem.purge();

return buildPromise = builder.build().then(function(result) {
['requireTest-bundle.js']
.map(function(filename) {
return path.join(result.directory, filename);
})
.map(logFileIfDesired)
.map(fs.statSync)
.map(assert.ok);
});
})

it('should work after removing a require dep', function() {
searchAndReplaceInFile(fixturePath('kitchen-sink', 'src/js/combiner.js'), "require('./1.js')", "/* HIDDEN IN TEST: require('./1.js') */");
webpackCompiler.inputFileSystem.purge();

return buildPromise = builder.build().then(function(result) {
['requireTest-bundle.js']
.map(function(filename) {
return path.join(result.directory, filename);
})
.map(logFileIfDesired)
.map(function(filepath) {
expect(fs.readFileSync(filepath).toString()).to.not.contain('console.log(1)');
return filepath;
})
.map(fs.statSync)
.map(assert.ok);
});
})

it('should work after bringing require dep back', function() {
restoreFileAndBumpMtime(fixturePath('kitchen-sink', 'src/js/combiner.js'));
webpackCompiler.inputFileSystem.purge();

return buildPromise = builder.build().then(function(result) {
['requireTest-bundle.js']
.map(function(filename) {
return path.join(result.directory, filename);
})
.map(logFileIfDesired)
.map(function(filepath) {
expect(fs.readFileSync(filepath).toString()).to.contain('console.log(1)');
return filepath;
})
.map(fs.statSync)
.map(assert.ok);
});
})

after(function() {
// console.log("Test cleanup");
restoreFiles();
return builder.cleanup()
})
});
Expand Down

0 comments on commit 708227c

Please sign in to comment.