diff options
Diffstat (limited to 'packages/astro/test/0-css.test.js')
-rw-r--r-- | packages/astro/test/0-css.test.js | 412 |
1 files changed, 230 insertions, 182 deletions
diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 06b9c0d14..6e58dbe64 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -8,260 +8,308 @@ import { expect } from 'chai'; import cheerio from 'cheerio'; import { loadFixture } from './test-utils.js'; -describe('Styles SSR', function () { - this.timeout(30000); // test needs a little more time in CI - - let fixture; - let index$; - let bundledCSS; +let fixture; +describe('CSS', function () { before(async () => { fixture = await loadFixture({ projectRoot: './fixtures/0-css/', renderers: ['@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'], }); - await fixture.build(); - - // get bundled CSS (will be hashed, hence DOM query) - const html = await fixture.readFile('/index.html'); - index$ = cheerio.load(html); - const bundledCSSHREF = index$('link[rel=stylesheet][href^=assets/]').attr('href'); - bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); }); - describe('Astro styles', () => { - it('HTML and CSS scoped correctly', async () => { - const $ = index$; + // test HTML and CSS contents for accuracy + describe('build', () => { + this.timeout(30000); // test needs a little more time in CI - const el1 = $('#dynamic-class'); - const el2 = $('#dynamic-vis'); - const classes = $('#class').attr('class').split(' '); - const scopedClass = classes.find((name) => /^astro-[A-Za-z0-9-]+/.test(name)); + let $; + let bundledCSS; - // 1. check HTML - expect(el1.attr('class')).to.equal(`blue ${scopedClass}`); - expect(el2.attr('class')).to.equal(`visible ${scopedClass}`); + before(async () => { + await fixture.build(); - // 2. check CSS - expect(bundledCSS).to.include(`.blue.${scopedClass}{color:#b0e0e6}.color\\:blue.${scopedClass}{color:#b0e0e6}.visible.${scopedClass}{display:block}`); + // get bundled CSS (will be hashed, hence DOM query) + const html = await fixture.readFile('/index.html'); + $ = cheerio.load(html); + const bundledCSSHREF = $('link[rel=stylesheet][href^=assets/]').attr('href'); + bundledCSS = await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')); }); - it('No <style> skips scoping', async () => { - const $ = index$; - - // Astro component without <style> should not include scoped class - expect($('#no-scope').attr('class')).to.equal(undefined); + describe('Astro Styles', () => { + it('HTML and CSS scoped correctly', async () => { + const el1 = $('#dynamic-class'); + const el2 = $('#dynamic-vis'); + const classes = $('#class').attr('class').split(' '); + const scopedClass = classes.find((name) => /^astro-[A-Za-z0-9-]+/.test(name)); + + // 1. check HTML + expect(el1.attr('class')).to.equal(`blue ${scopedClass}`); + expect(el2.attr('class')).to.equal(`visible ${scopedClass}`); + + // 2. check CSS + expect(bundledCSS).to.include(`.blue.${scopedClass}{color:#b0e0e6}.color\\:blue.${scopedClass}{color:#b0e0e6}.visible.${scopedClass}{display:block}`); + }); + + it('No <style> skips scoping', async () => { + // Astro component without <style> should not include scoped class + expect($('#no-scope').attr('class')).to.equal(undefined); + }); + + it('Child inheritance', async () => { + expect($('#passed-in').attr('class')).to.match(/outer astro-[A-Z0-9]+ astro-[A-Z0-9]+/); + }); + + it('Using hydrated components adds astro-root styles', async () => { + expect(bundledCSS).to.include('display:contents'); + }); + + it('<style lang="sass">', async () => { + expect(bundledCSS).to.match(new RegExp('h1.astro-[^{]*{color:#90ee90}')); + }); + + it('<style lang="scss">', async () => { + expect(bundledCSS).to.match(new RegExp('h1.astro-[^{]*{color:#ff69b4}')); + }); }); - it('Child inheritance', async () => { - const $ = index$; + describe('Styles in src/', () => { + it('.css', async () => { + expect(bundledCSS).to.match(new RegExp('.linked-css[^{]*{color:gold')); + }); - expect($('#passed-in').attr('class')).to.match(/outer astro-[A-Z0-9]+ astro-[A-Z0-9]+/); - }); + it('.sass', async () => { + expect(bundledCSS).to.match(new RegExp('.linked-sass[^{]*{color:#789')); + }); - it('Using hydrated components adds astro-root styles', async () => { - expect(bundledCSS).to.include('display:contents'); + it('.scss', async () => { + expect(bundledCSS).to.match(new RegExp('.linked-scss[^{]*{color:#6b8e23')); + }); }); - it('<style lang="sass">', async () => { - expect(bundledCSS).to.match(new RegExp('h1.astro-[^{]*{color:#90ee90}')); - }); + describe('JSX', () => { + it('.css', async () => { + const el = $('#react-css'); - it('<style lang="scss">', async () => { - expect(bundledCSS).to.match(new RegExp('h1.astro-[^{]*{color:#ff69b4}')); - }); - }); + // 1. check HTML + expect(el.attr('class')).to.include('react-title'); - describe('Styles in src/', () => { - it('.css', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-css[^{]*{color:gold')); - }); + // 2. check CSS + expect(bundledCSS).to.include('.react-title{'); + }); - it('.sass', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-sass[^{]*{color:#789')); - }); + it('.module.css', async () => { + const el = $('#react-module-css'); + const classes = el.attr('class').split(' '); + const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); - it('.scss', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-scss[^{]*{color:#6b8e23')); - }); - }); + // 1. check HTML + expect(el.attr('class')).to.include(moduleClass); - describe('JSX', () => { - it('.css', async () => { - const $ = index$; - const el = $('#react-css'); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); + }); - // 1. check HTML - expect(el.attr('class')).to.include('react-title'); + it('.sass', async () => { + const el = $('#react-sass'); - // 2. check CSS - expect(bundledCSS).to.include('.react-title{'); - }); + // 1. check HTML + expect(el.attr('class')).to.include('react-sass-title'); - it('.module.css', async () => { - const $ = index$; - const el = $('#react-module-css'); - const classes = el.attr('class').split(' '); - const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy}`)); + }); - // 1. check HTML - expect(el.attr('class')).to.include(moduleClass); + it('.scss', async () => { + const el = $('#react-scss'); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); - }); + // 1. check HTML + expect(el.attr('class')).to.include('react-scss-title'); - it('.sass', async () => { - const $ = index$; - const el = $('#react-sass'); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.react-scss-title[^{]*{font-family:fantasy}`)); + }); - // 1. check HTML - expect(el.attr('class')).to.include('react-sass-title'); + it('.module.sass', async () => { + const el = $('#react-module-sass'); + const classes = el.attr('class').split(' '); + const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy}`)); - }); + // 1. check HTML + expect(el.attr('class')).to.include(moduleClass); + + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); + }); - it('.scss', async () => { - const $ = index$; - const el = $('#react-scss'); + it('.module.scss', async () => { + const el = $('#react-module-scss'); + const classes = el.attr('class').split(' '); + const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); - // 1. check HTML - expect(el.attr('class')).to.include('react-scss-title'); + // 1. check HTML + expect(el.attr('class')).to.include(moduleClass); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.react-scss-title[^{]*{font-family:fantasy}`)); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); + }); }); - it('.module.sass', async () => { - const $ = index$; - const el = $('#react-module-sass'); - const classes = el.attr('class').split(' '); - const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); + describe('Vue', () => { + it('<style>', async () => { + const el = $('#vue-css'); - // 1. check HTML - expect(el.attr('class')).to.include(moduleClass); + // 1. check HTML + expect(el.attr('class')).to.include('vue-css'); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); - }); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.vue-css[^{]*{font-family:cursive`)); + }); - it('.module.scss', async () => { - const $ = index$; - const el = $('#react-module-scss'); - const classes = el.attr('class').split(' '); - const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); + it('<style scoped>', async () => { + const el = $('#vue-scoped'); - // 1. check HTML - expect(el.attr('class')).to.include(moduleClass); + // find data-v-* attribute (how Vue CSS scoping works) + const { attribs } = el.get(0); + const scopeId = Object.keys(attribs).find((k) => k.startsWith('data-v-')); + expect(scopeId).to.be.ok; - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); - }); - }); + // 1. check HTML + expect(el.attr('class')).to.include('vue-scoped'); - describe('Vue', () => { - it('<style>', async () => { - const $ = index$; - const el = $('#vue-css'); + // 2. check CSS + expect(bundledCSS).to.include(`.vue-scoped[${scopeId}]`); + }); - // 1. check HTML - expect(el.attr('class')).to.include('vue-css'); + it('<style module>', async () => { + const el = $('#vue-modules'); + const classes = el.attr('class').split(' '); + const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-css[^{]*{font-family:cursive`)); - }); + // 1. check HTML + expect(el.attr('class')).to.include(moduleClass); - it('<style scoped>', async () => { - const $ = index$; - const el = $('#vue-scoped'); + // 2. check CSS + expect(bundledCSS).to.include(`${moduleClass}{`); + }); - // find data-v-* attribute (how Vue CSS scoping works) - const { attribs } = el.get(0); - const scopeId = Object.keys(attribs).find((k) => k.startsWith('data-v-')); - expect(scopeId).to.be.ok; + it('<style lang="sass">', async () => { + const el = $('#vue-sass'); - // 1. check HTML - expect(el.attr('class')).to.include('vue-scoped'); + // 1. check HTML + expect(el.attr('class')).to.include('vue-sass'); - // 2. check CSS - expect(bundledCSS).to.include(`.vue-scoped[${scopeId}]`); - }); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.vue-sass[^{]*{font-family:cursive`)); + }); - it('<style module>', async () => { - const $ = index$; - const el = $('#vue-modules'); - const classes = el.attr('class').split(' '); - const moduleClass = classes.find((name) => /^_title_[A-Za-z0-9-_]+/.test(name)); + it('<style lang="scss">', async () => { + const el = $('#vue-scss'); - // 1. check HTML - expect(el.attr('class')).to.include(moduleClass); + // 1. check HTML + expect(el.attr('class')).to.include('vue-scss'); - // 2. check CSS - expect(bundledCSS).to.include(`${moduleClass}{`); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.vue-scss[^{]*{font-family:cursive`)); + }); }); - it('<style lang="sass">', async () => { - const $ = index$; - const el = $('#vue-sass'); + describe('Svelte', () => { + it('<style>', async () => { + const el = $('#svelte-css'); + const classes = el.attr('class').split(' '); + const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name)); - // 1. check HTML - expect(el.attr('class')).to.include('vue-sass'); + // 1. check HTML + expect(el.attr('class')).to.include('svelte-css'); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-sass[^{]*{font-family:cursive`)); - }); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.svelte-css.${scopedClass}[^{]*{font-family:"Comic Sans MS"`)); + }); + + it('<style lang="sass">', async () => { + const el = $('#svelte-sass'); + const classes = el.attr('class').split(' '); + const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name)); - it('<style lang="scss">', async () => { - const $ = index$; - const el = $('#vue-scss'); + // 1. check HTML + expect(el.attr('class')).to.include('svelte-sass'); - // 1. check HTML - expect(el.attr('class')).to.include('vue-scss'); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.svelte-sass.${scopedClass}[^{]*{font-family:"Comic Sans MS"`)); + }); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-scss[^{]*{font-family:cursive`)); + it('<style lang="scss">', async () => { + const el = $('#svelte-scss'); + const classes = el.attr('class').split(' '); + const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name)); + + // 1. check HTML + expect(el.attr('class')).to.include('svelte-scss'); + + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.svelte-scss.${scopedClass}[^{]*{font-family:"Comic Sans MS"`)); + }); }); }); - describe('Svelte', () => { - it('<style>', async () => { - const $ = index$; - const el = $('#svelte-css'); - const classes = el.attr('class').split(' '); - const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name)); + // with "build" handling CSS checking, the dev tests are mostly testing the paths resolve in dev + describe('dev', () => { + let devServer; + let $; - // 1. check HTML - expect(el.attr('class')).to.include('svelte-css'); + before(async () => { + devServer = await fixture.startDevServer(); + const html = await fixture.fetch('/').then((res) => res.text()); + $ = cheerio.load(html); + }); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.svelte-css.${scopedClass}[^{]*{font-family:"Comic Sans MS"`)); + after(async () => { + devServer && (await devServer.stop()); }); - it('<style lang="sass">', async () => { - const $ = index$; - const el = $('#svelte-sass'); - const classes = el.attr('class').split(' '); - const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name)); + it('resolves CSS in public/', async () => { + const href = $('link[href="/global.css"]').attr('href'); + expect((await fixture.fetch(href)).status).to.equal(200); + }); - // 1. check HTML - expect(el.attr('class')).to.include('svelte-sass'); + it('resolves CSS in src/', async () => { + const href = $('link[href$="linked.css"]').attr('href'); + expect((await fixture.fetch(href)).status).to.equal(200); + }); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.svelte-sass.${scopedClass}[^{]*{font-family:"Comic Sans MS"`)); + it('resolves Astro styles', async () => { + const style = $('style[astro-style]'); + expect(style.length).to.not.equal(0); }); - it('<style lang="scss">', async () => { - const $ = index$; - const el = $('#svelte-scss'); - const classes = el.attr('class').split(' '); - const scopedClass = classes.find((name) => /^s-[A-Za-z0-9-]+/.test(name)); + it('resolves Styles from React', async () => { + const styles = ['ReactCSS.css', 'ReactModules.module.css', 'ReactModules.module.scss', 'ReactModules.module.sass', 'ReactSass.sass', 'ReactScss.scss']; + for (const style of styles) { + const href = $(`link[href$="${style}"]`).attr('href'); + expect((await fixture.fetch(href)).status, style).to.equal(200); + } + }); - // 1. check HTML - expect(el.attr('class')).to.include('svelte-scss'); + it('resolves CSS from Svelte', async () => { + const scripts = ['SvelteCSS.svelte?svelte&type=style&lang.css', 'SvelteSass.svelte?svelte&type=style&lang.css', 'SvelteScss.svelte?svelte&type=style&lang.css']; + for (const script of scripts) { + const src = $(`script[src$="${script}"]`).attr('src'); + expect((await fixture.fetch(src)).status, script).to.equal(200); + } + }); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.svelte-scss.${scopedClass}[^{]*{font-family:"Comic Sans MS"`)); + it('resolves CSS from Vue', async () => { + const styles = [ + 'VueCSS.vue?vue&type=style&index=0&lang.css', + 'VueModules.vue?vue&type=style&index=0&lang.module.scss', + 'VueSass.vue?vue&type=style&index=0&lang.sass', + 'VueScoped.vue?vue&type=style&index=0&scoped=true&lang.css', + 'VueScss.vue?vue&type=style&index=0&lang.scss', + ]; + for (const style of styles) { + const href = $(`link[href$="${style}"]`).attr('href'); + expect((await fixture.fetch(href)).status, style).to.equal(200); + } }); }); }); |