//
@@ -326,7 +437,35 @@ describe('vue-loader', function () {
})
})
- it('postcss options', function (done) {
+ it('transformToRequire option', done => {
+ test({
+ entry: './test/fixtures/transform.vue',
+ module: {
+ rules: [
+ { test: /\.vue$/, loader: loaderPath },
+ {
+ test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+ loader: 'url-loader'
+ }
+ ]
+ }
+ }, (window, module) => {
+ function includeDataURL (s) {
+ return !!s.match(/\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*/i)
+ }
+ var vnode = mockRender(module)
+ // img tag
+ expect(includeDataURL(vnode.children[0].data.attrs.src)).to.equal(true)
+ // image tag (SVG)
+ expect(includeDataURL(vnode.children[2].children[0].data.attrs['xlink:href'])).to.equal(true)
+ var style = window.document.querySelector('style').textContent
+ // style
+ expect(includeDataURL(style)).to.equal(true)
+ done()
+ })
+ })
+
+ it('postcss options', done => {
test({
entry: './test/fixtures/postcss.vue',
vue: {
@@ -336,18 +475,31 @@ describe('vue-loader', function () {
}
}
}
- }, function (window) {
+ }, (window) => {
+ var style = window.document.querySelector('style').textContent
+ style = normalizeNewline(style)
+ expect(style).to.contain('h1 {\n color: red;\n font-size: 14px\n}')
+ done()
+ })
+ })
+
+ it('load postcss config file', done => {
+ fs.writeFileSync('.postcssrc', JSON.stringify({ parser: 'sugarss' }))
+ test({
+ entry: './test/fixtures/postcss.vue'
+ }, (window) => {
var style = window.document.querySelector('style').textContent
style = normalizeNewline(style)
expect(style).to.contain('h1 {\n color: red;\n font-size: 14px\n}')
+ fs.unlinkSync('.postcssrc')
done()
})
})
- it('transpile ES2015 features in template', function (done) {
+ it('transpile ES2015 features in template', done => {
test({
entry: './test/fixtures/es2015.vue'
- }, function (window, module) {
+ }, (window, module) => {
var vnode = mockRender(module, {
a: 'hello',
b: true
@@ -360,10 +512,10 @@ describe('vue-loader', function () {
})
})
- it('allows to export extended constructor', function (done) {
+ it('allows to export extended constructor', done => {
test({
entry: './test/fixtures/extend.vue'
- }, function (window, Module) {
+ }, (window, Module) => {
// extend.vue should export Vue constructor
var vnode = mockRender(Module.options, {
msg: 'success'
@@ -375,13 +527,13 @@ describe('vue-loader', function () {
})
})
- it('support es compatible modules', function (done) {
+ it('support es compatible modules', done => {
test({
entry: './test/fixtures/basic.vue',
vue: {
esModule: true
}
- }, function (window, module, rawModule) {
+ }, (window, module, rawModule) => {
expect(rawModule.__esModule).to.equal(true)
var vnode = mockRender(rawModule.default, {
msg: 'hi'
@@ -395,7 +547,7 @@ describe('vue-loader', function () {
})
})
- it('css-modules', function (done) {
+ it('css-modules', done => {
function testWithIdent (localIdentName, regexToMatch, cb) {
test({
entry: './test/fixtures/css-modules.vue',
@@ -404,15 +556,13 @@ describe('vue-loader', function () {
localIdentName: localIdentName
}
}
- }, function (window) {
- var module = window.vueModule
-
+ }, (window, module, raw, instance) => {
// get local class name
- var className = module.computed.style().red
+ var className = instance.style.red
expect(className).to.match(regexToMatch)
// class name in style
- var style = [].slice.call(window.document.querySelectorAll('style')).map(function (style) {
+ var style = [].slice.call(window.document.querySelectorAll('style')).map((style) => {
return style.textContent
}).join('\n')
style = normalizeNewline(style)
@@ -426,16 +576,16 @@ describe('vue-loader', function () {
expect(style).to.contain('animation: ' + animationName + ' 1s;')
// default module + pre-processor + scoped
- var anotherClassName = module.computed.$style().red
+ var anotherClassName = instance.$style.red
expect(anotherClassName).to.match(regexToMatch).and.not.equal(className)
- var id = 'data-v-' + genId(require.resolve('./fixtures/css-modules.vue'))
+ var id = 'data-v-' + hash('vue-loader/test/fixtures/css-modules.vue')
expect(style).to.contain('.' + anotherClassName + '[' + id + ']')
cb()
})
}
// default localIdentName
- testWithIdent(undefined, /^\w{23}/, function () {
+ testWithIdent(undefined, /^\w{23}/, () => {
// specified localIdentName
var ident = '[path][name]---[local]---[hash:base64:5]'
var regex = /^test-fixtures-css-modules---red---\w{5}/
@@ -443,25 +593,343 @@ describe('vue-loader', function () {
})
})
- it('css-modules in SSR', function (done) {
+ it('css-modules in SSR', done => {
bundle({
entry: './test/fixtures/css-modules.vue',
target: 'node',
output: Object.assign({}, globalConfig.output, {
libraryTarget: 'commonjs2'
})
- }, function (code) {
+ }, (code, warnings) => {
// http://stackoverflow.com/questions/17581830/load-node-js-module-from-string-in-memory
- function requireFromString(src, filename) {
- var Module = module.constructor;
- var m = new Module();
- m._compile(src, filename);
- return m.exports;
+ function requireFromString (src, filename) {
+ var Module = module.constructor
+ var m = new Module()
+ m._compile(src, filename)
+ return m.exports
}
- var output = requireFromString(code, './test.build.js')
- expect(output.computed.style().red).to.exist
+ var output = interopDefault(requireFromString(code, './test.build.js'))
+ var mockInstance = {}
+ output.beforeCreate.forEach(hook => hook.call(mockInstance))
+ expect(mockInstance.style.red).to.exist
+
+ done()
+ })
+ })
+
+ it('should allow adding custom html loaders', done => {
+ test({
+ entry: './test/fixtures/markdown.vue',
+ vue: {
+ loaders: {
+ html: 'marked'
+ }
+ }
+ }, (window, module) => {
+ var vnode = mockRender(module, {
+ msg: 'hi'
+ })
+ //
{{msg}}
+ expect(vnode.tag).to.equal('h2')
+ expect(vnode.data.attrs.id).to.equal('-msg-')
+ expect(vnode.children[0]).to.equal('hi')
+ done()
+ })
+ })
+
+ it('support chaining with other loaders', done => {
+ const mockLoaderPath = require.resolve('./mock-loaders/js')
+ test({
+ entry: './test/fixtures/basic.vue',
+ module: {
+ rules: [
+ { test: /\.vue$/, loader: loaderPath + '!' + mockLoaderPath }
+ ]
+ }
+ }, (window, module) => {
+ expect(module.data().msg).to.equal('Changed!')
+ done()
+ })
+ })
+
+ it('SSR style and moduleId extraction', done => {
+ bundle({
+ target: 'node',
+ entry: './test/fixtures/ssr-style.js',
+ output: {
+ path: '/',
+ filename: 'test.build.js',
+ libraryTarget: 'commonjs2'
+ },
+ externals: ['vue'],
+ module: {
+ rules: [{ test: /\.vue$/, loader: rawLoaderPath }]
+ }
+ }, code => {
+ const renderer = SSR.createBundleRenderer(code, {
+ basedir: __dirname,
+ runInNewContext: 'once'
+ })
+ const context = {
+ _registeredComponents: new Set()
+ }
+ renderer.renderToString(context, (err, res) => {
+ if (err) return done(err)
+ expect(res).to.contain('data-server-rendered')
+ expect(res).to.contain('
Hello
')
+ expect(res).to.contain('Hello from Component A!')
+ expect(res).to.contain('
functional
')
+ // from main component
+ expect(context.styles).to.contain('h1 { color: green;')
+ // from imported child component
+ expect(context.styles).to.contain('comp-a h2 {\n color: #f00;')
+ // from imported css file
+ expect(context.styles).to.contain('h1 { color: red;')
+ // from imported functional component
+ expect(context.styles).to.contain('.foo { color: red;')
+ // collect component identifiers during render
+ expect(Array.from(context._registeredComponents).length).to.equal(3)
+ done()
+ })
+ })
+ })
+
+ it('extract custom blocks to a separate file', done => {
+ bundle({
+ entry: './test/fixtures/custom-language.vue',
+ vue: {
+ loaders: {
+ 'documentation': ExtractTextPlugin.extract('raw-loader')
+ }
+ },
+ plugins: [
+ new ExtractTextPlugin('doc.md')
+ ]
+ }, (code, warnings) => {
+ var unitTest = mfs.readFileSync('/doc.md').toString()
+ unitTest = normalizeNewline(unitTest)
+ expect(unitTest).to.contain('This is example documentation for a component.')
+ done()
+ })
+ })
+
+ it('add custom blocks to the webpack output', done => {
+ bundle({
+ entry: './test/fixtures/custom-language.vue',
+ vue: {
+ loaders: {
+ 'unit-test': 'babel-loader'
+ }
+ }
+ }, (code, warnings) => {
+ expect(code).to.contain('describe(\'example\', function () {\n it(\'basic\', function (done) {\n done();\n });\n})')
+ done()
+ })
+ })
+
+ it('custom blocks work with src imports', done => {
+ bundle({
+ entry: './test/fixtures/custom-import.vue',
+ vue: {
+ loaders: {
+ 'unit-test': 'babel-loader'
+ }
+ }
+ }, (code, warnings) => {
+ expect(code).to.contain('describe(\'example\', function () {\n it(\'basic\', function (done) {\n done();\n });\n})')
+ done()
+ })
+ })
+
+ it('passes Component to custom block loaders', done => {
+ const mockLoaderPath = require.resolve('./mock-loaders/docs')
+ test({
+ entry: './test/fixtures/custom-language.vue',
+ vue: {
+ loaders: {
+ 'documentation': mockLoaderPath
+ }
+ }
+ }, (window, module) => {
+ expect(module.__docs).to.contain('This is example documentation for a component.')
+ done()
+ })
+ })
+
+ it('custom blocks can be ignored', done => {
+ bundle({
+ entry: './test/fixtures/custom-language.vue'
+ }, (code, warnings) => {
+ expect(code).not.to.contain('describe(\'example\', function () {\n it(\'basic\', function (done) {\n done();\n });\n})')
+ done()
+ })
+ })
+
+ it('passes attributes as options to the loader', done => {
+ bundle({
+ entry: './test/fixtures/custom-options.vue',
+ vue: {
+ loaders: {
+ 'unit-test': 'babel-loader!skeleton-loader'
+ }
+ },
+ plugins: [
+ new webpack.LoaderOptionsPlugin({
+ options: {
+ skeletonLoader: {
+ procedure: (content, sourceMap, callback, options) => {
+ expect(options.foo).to.equal('bar')
+ expect(options.opt2).to.equal('value2')
+
+ // Return the content to output.
+ return content
+ }
+ }
+ }
+ })
+ ]
+ }, (code, warnings) => {
+ expect(code).to.contain('describe(\'example\', function () {\n it(\'basic\', function (done) {\n done();\n });\n})')
+ done()
+ })
+ })
+
+ it('pre/post loaders', done => {
+ test({
+ entry: './test/fixtures/basic.vue',
+ vue: {
+ preLoaders: {
+ js: require.resolve('./mock-loaders/js'),
+ css: require.resolve('./mock-loaders/css')
+ },
+ postLoaders: {
+ html: require.resolve('./mock-loaders/html')
+ }
+ }
+ }, (window, module) => {
+ var vnode = mockRender(module, {
+ msg: 'hi'
+ })
+ //
{{msg}}
+ expect(vnode.tag).to.equal('h2')
+ expect(vnode.data.staticClass).to.equal('green')
+ expect(vnode.children[0]).to.equal('hi')
+
+ expect(module.data().msg).to.contain('Changed!')
+ var style = window.document.querySelector('style').textContent
+ style = normalizeNewline(style)
+ expect(style).to.contain('comp-a h2 {\n color: #00f;\n}')
+ done()
+ })
+ })
+
+ it('pre/post loaders for custom blocks', done => {
+ test({
+ entry: './test/fixtures/custom-blocks.vue',
+ vue: {
+ preLoaders: {
+ i18n: require.resolve('./mock-loaders/yaml')
+ },
+ loaders: {
+ i18n: require.resolve('./mock-loaders/i18n'),
+ blog: 'marked'
+ },
+ postLoaders: {
+ blog: require.resolve('./mock-loaders/blog')
+ }
+ }
+ }, (window, module) => {
+ var vnode = mockRender(module, {
+ msg: JSON.parse(module.__i18n).en.hello,
+ blog: module.__blog
+ })
+ expect(vnode.children[0].children[0]).to.equal('hello world')
+ expect(vnode.children[2].data.domProps.innerHTML).to.equal('
foo
')
+ done()
+ })
+ })
+
+ it('custom compiler modules', done => {
+ test({
+ entry: './test/fixtures/custom-module.vue',
+ vue: {
+ compilerModules: [
+ {
+ postTransformNode: el => {
+ if (el.staticStyle) {
+ el.staticStyle = `$processStyle(${el.staticStyle})`
+ }
+ if (el.styleBinding) {
+ el.styleBinding = `$processStyle(${el.styleBinding})`
+ }
+ }
+ }
+ ]
+ }
+ }, (window, module) => {
+ var results = []
+ // var vnode =
+ mockRender(module, {
+ $processStyle: style => results.push(style),
+ transform: 'translateX(10px)'
+ })
+ expect(results).to.deep.equal([
+ { 'flex-direction': 'row' },
+ { 'transform': 'translateX(10px)' }
+ ])
+ done()
+ })
+ })
+
+ it('custom compiler modules (string)', done => {
+ test({
+ entry: './test/fixtures/basic.vue',
+ vue: {
+ compilerModules: require.resolve('./fixtures/custom-module')
+ }
+ }, (window, module) => {
+ var vnode = mockRender(module, {
+ msg: 'hi'
+ })
+ expect(vnode.data.staticClass).to.equal('red blue')
+ done()
+ })
+ })
+
+ it('functional component with styles', done => {
+ test({
+ entry: './test/fixtures/functional-style.vue'
+ }, (window, module, rawModule) => {
+ expect(module.functional).to.equal(true)
+ var vnode = mockRender(module)
+ //
hi
+ expect(vnode.tag).to.equal('div')
+ expect(vnode.data.class).to.equal('foo')
+ expect(vnode.children[0]).to.equal('functional')
+
+ var style = window.document.querySelector('style').textContent
+ style = normalizeNewline(style)
+ expect(style).to.contain('.foo { color: red;\n}')
+ done()
+ })
+ })
+
+ it('template with comments', done => {
+ test({
+ entry: './test/fixtures/template-comment.vue'
+ }, (window, module, rawModule) => {
+ expect(module.comments).to.equal(true)
+ var vnode = mockRender(module, {
+ msg: 'hi'
+ })
+ expect(vnode.tag).to.equal('div')
+ expect(vnode.children.length).to.equal(2)
+ expect(vnode.children[0].data.staticClass).to.equal('red')
+ expect(vnode.children[0].children[0]).to.equal('hi')
+ expect(vnode.children[1].isComment).to.true
+ expect(vnode.children[1].text).to.equal(' comment here ')
done()
})
})