import cssesc from 'cssesc' import { mockBundleAndRun, genId, normalizeNewline, DEFAULT_VUE_USE, } from './utils' test('scoped style', async () => { const { window, instance, componentModule } = await mockBundleAndRun({ entry: 'scoped-css.vue', }) const shortId = genId('scoped-css.vue') const scopeId = 'data-v-' + shortId expect(componentModule.__scopeId).toBe(scopeId) //
//

hi

//

hi

// //

//
expect(instance.$el.children[0].tagName).toBe('DIV') expect(instance.$el.children[1].tagName).toBe('P') expect(instance.$el.children[1].className).toBe('abc def') expect(instance.$el.children[2].tagName).toBe('P') expect(instance.$el.children[2].className).toBe('test') const style = normalizeNewline( window.document.querySelector('style')!.textContent! ) expect(style).toContain(`.test[${scopeId}] {\n color: yellow;\n}`) expect(style).toContain(`.test[${scopeId}]:after {\n content: \'bye!\';\n}`) expect(style).toContain(`h1[${scopeId}] {\n color: green;\n}`) // scoped keyframes // note: vue 3 uses short ids for keyframes // see https://github.com/vuejs/vue-next/commit/5f271515cf17e541a2a085d23854dac7e45e074e expect(style).toContain( `.anim[${scopeId}] {\n animation: color-${shortId} 5s infinite, other 5s;` ) expect(style).toContain( `.anim-2[${scopeId}] {\n animation-name: color-${shortId}` ) expect(style).toContain( `.anim-3[${scopeId}] {\n animation: 5s color-${shortId} infinite, 5s other;` ) expect(style).toContain(`@keyframes color-${shortId} {`) expect(style).toContain(`@-webkit-keyframes color-${shortId} {`) expect(style).toContain( `.anim-multiple[${scopeId}] {\n animation: color-${shortId} 5s infinite,opacity-${shortId} 2s;` ) expect(style).toContain( `.anim-multiple-2[${scopeId}] {\n animation-name: color-${shortId},opacity-${shortId};` ) expect(style).toContain(`@keyframes opacity-${shortId} {`) expect(style).toContain(`@-webkit-keyframes opacity-${shortId} {`) // >>> combinator expect(style).toContain(`.foo p[${scopeId}] .bar {\n color: red;\n}`) }) test('postcss', async () => { const { window } = await mockBundleAndRun({ entry: 'postcss.vue', module: { rules: [ { test: /\.postcss$/, use: [ 'style-loader', 'css-loader', { loader: 'postcss-loader', options: { postcssOptions: { parser: require('sugarss'), }, }, }, ], }, ], }, }) const id = 'data-v-' + genId('postcss.vue') const style = normalizeNewline( window.document.querySelector('style')!.textContent! ) expect(style).toContain(`h1[${id}] {\n color: red;\n font-size: 14px\n}`) }) test('CSS Modules', async () => { const testWithIdent = async ( localIdentName: string | undefined, regexToMatch: RegExp ) => { const baseLoaders = [ 'style-loader', { loader: 'css-loader', options: { modules: { localIdentName, }, }, }, ] const { window, instance } = await mockBundleAndRun({ entry: 'css-modules.vue', modify: (config: any) => { config!.module!.rules = [ { test: /\.vue$/, use: [DEFAULT_VUE_USE], }, { test: /\.css$/, use: baseLoaders, }, { test: /\.stylus$/, use: [...baseLoaders, 'stylus-loader'], }, ] }, }) // get local class name const className = instance.$style.red const escapedClassName = cssesc(instance.$style.red, { isIdentifier: true }) expect(className).toMatch(regexToMatch) // class name in style let style = [].slice .call(window.document.querySelectorAll('style')) .map((style: any) => { return style!.textContent }) .join('\n') style = normalizeNewline(style) expect(style).toContain('.' + escapedClassName + ' {\n color: red;\n}') // animation name const match = style.match(/@keyframes\s+(\S+)\s+{/) expect(match).toHaveLength(2) const animationName = match[1] expect(animationName).not.toBe('fade') expect(style).toContain('animation: ' + animationName + ' 1s;') // default module + pre-processor + scoped const anotherClassName = instance.$style.red const escapedAnotherClassName = cssesc(instance.$style.red, { isIdentifier: true, }) expect(anotherClassName).toMatch(regexToMatch) const id = 'data-v-' + genId('css-modules.vue') expect(style).toContain('.' + escapedAnotherClassName + '[' + id + ']') } // default ident await testWithIdent(undefined, /^\w{21,}/) // custom ident await testWithIdent( '[path][name]---[local]---[hash:base64:5]', /css-modules---red---\w{5}/ ) }) test('CSS Modules Extend', async () => { const baseLoaders = [ 'style-loader', { loader: 'css-loader', options: { modules: true, }, }, ] const { window, instance } = await mockBundleAndRun({ entry: 'css-modules-extend.vue', modify: (config: any) => { config!.module!.rules = [ { test: /\.vue$/, use: [DEFAULT_VUE_USE], }, { test: /\.css$/, use: baseLoaders, }, ] }, }) expect(instance.$el.className).toBe(instance.$style.red) const escapedClassName = cssesc(instance.$style.red, { isIdentifier: true }) const style = window.document.querySelectorAll('style')![1]!.textContent expect(style).toContain(`.${escapedClassName} {\n color: #FF0000;\n}`) }) test('v-bind() in CSS', async () => { const { window, instance } = await mockBundleAndRun({ entry: 'style-v-bind.vue', }) const shortId = genId('style-v-bind.vue') const style = normalizeNewline( window.document.querySelector('style')!.textContent! ) expect(style).toMatch(`color: var(--${shortId}-color);`) expect(style).toMatch(`font-size: var(--${shortId}-font\\.size);`) const computedStyle = window.getComputedStyle(instance.$el) // Because the tests run in JSDOM, we can't directly get the computed `color` value. // To get around this, we test the corresponding CSS variable instead. expect(computedStyle.getPropertyValue(`--${shortId}-color`)).toBe('red') expect(computedStyle.getPropertyValue(`--${shortId}-font.size`)).toBe('2em') })