Skip to content

Instantly share code, notes, and snippets.

@davidpfahler
Created July 27, 2015 08:40
Show Gist options
  • Save davidpfahler/3316d4824242c1d9873a to your computer and use it in GitHub Desktop.
Save davidpfahler/3316d4824242c1d9873a to your computer and use it in GitHub Desktop.
Experiment in running unit tests without temporary files but still compile using webpack
import vm from 'vm'
import realFs from 'fs'
import path from 'path'
import react from 'react/addons'
import test from 'tape'
import mock from 'mock-fs'
import MemoryFileSystem from 'memory-fs'
import webpack from 'webpack'
import config from './webpack.config.js'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
// To avoid temporary files which are ugly and require cleanup
// we instead mock those files to the node files system.
let mockFiles = {}
// There are two reason why we need separate testing files
// from the source or distribution build. We cannot use the
// distribution build under ./dist/redbox.js, because the
// error-stack-parser module only works in the browser with
// a window object. So we alias it to a mock. It seems there
// is no way around that.
// We cannot require the source under ./src/index.js because
// we need css-loader to parse the css for local scoped classes.
config.resolve.alias = config.resolve.alias || {}
config.resolve.alias['error-stack-parser'] = path.join(__dirname, './tests/errorStackParserMock')
// Remove style-loader (development)
config.module.loaders.pop()
config.plugins.push(new ExtractTextPlugin('redbox.css', {allChunks: true}))
config.module.loaders.push({
test: /\.css?$/,
loader: ExtractTextPlugin.extract('css-loader?modules&localIdentName=[hash:base64:5]')
})
// But such a different build for testing only would result
// in a temporary file. To avoid this, we compile to
// an in-memory file system. We need to tweak the webpack
// config a bit to make this work. Let's start with the output
// and entry.
config.output = {
path: path.join(__dirname, './dist'),
filename: 'redbox.js'
}
config.entry = './src/index.js'
// Now we can create a compiler for this configuration.
let compiler = webpack(config)
// To the compiler, we can assign an output file system.
// Of course, we use a newly created memory file system.
let fs = compiler.outputFileSystem = new MemoryFileSystem()
// Before running the compiler, we register a plugin that
// receives the compilation after the compiler is done.
compiler.plugin('after-emit', (compilation, callback) => {
Object.keys(compilation.assets).forEach(key => {
const location = fs.join(compiler.outputPath, key)
const contents = fs.readFileSync(location)
// Add the string of the compiled bundle to mockFiles
realFs.writeFileSync('./tmp/'+key, contents)
mockFiles[key] = contents
})
callback()
})
// Run the compiler and handle eventual errors.
compiler.run(function(err, stats) {
if (err) throw err
const jsonStats = stats.toJson()
if (jsonStats.errors.length > 0)
return console.log(jsonStats.errors)
if (jsonStats.warnings.length > 0)
console.log(jsonStats.warnings)
startTests()
})
function startTests () {
let sandbox = vm.createContext({react})
const RedBox = vm.runInContext(mockFiles['redbox.js'], sandbox)
test('RedBox static name', t => {
t.equal(
RedBox.name,
'RedBox',
'correct static ES6 name property on class'
)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment