diff options
Diffstat (limited to 'packages/astro/test/0-css.test.js')
-rw-r--r-- | packages/astro/test/0-css.test.js | 568 |
1 files changed, 284 insertions, 284 deletions
diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js index 4387f2774..5acf83ad6 100644 --- a/packages/astro/test/0-css.test.js +++ b/packages/astro/test/0-css.test.js @@ -11,310 +11,310 @@ import { loadFixture } from './test-utils.js'; let fixture; describe('CSS', function () { - before(async () => { - fixture = await loadFixture({ - projectRoot: './fixtures/0-css/', - renderers: ['@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'], - }); - }); - - // test HTML and CSS contents for accuracy - describe('build', () => { - this.timeout(30000); // test needs a little more time in CI - - let $; - let bundledCSS; - - before(async () => { - await fixture.build(); - - // 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(/^\/?/, '/')); - }); - - 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}')); - }); - }); - - describe('Styles in src/', () => { - it('.css', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-css[^{]*{color:gold')); - }); - - it('.sass', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-sass[^{]*{color:#789')); - }); - - it('.scss', async () => { - expect(bundledCSS).to.match(new RegExp('.linked-scss[^{]*{color:#6b8e23')); - }); - }); - - describe('JSX', () => { - it('.css', async () => { - const el = $('#react-css'); - - // 1. check HTML - expect(el.attr('class')).to.include('react-title'); - - // 2. check CSS - expect(bundledCSS).to.include('.react-title{'); - }); - - 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)); + before(async () => { + fixture = await loadFixture({ + projectRoot: './fixtures/0-css/', + renderers: ['@astrojs/renderer-react', '@astrojs/renderer-svelte', '@astrojs/renderer-vue'], + }); + }); + + // test HTML and CSS contents for accuracy + describe('build', () => { + this.timeout(30000); // test needs a little more time in CI + + let $; + let bundledCSS; + + before(async () => { + await fixture.build(); + + // 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(/^\/?/, '/')); + }); + + 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}')); + }); + }); + + describe('Styles in src/', () => { + it('.css', async () => { + expect(bundledCSS).to.match(new RegExp('.linked-css[^{]*{color:gold')); + }); + + it('.sass', async () => { + expect(bundledCSS).to.match(new RegExp('.linked-sass[^{]*{color:#789')); + }); + + it('.scss', async () => { + expect(bundledCSS).to.match(new RegExp('.linked-scss[^{]*{color:#6b8e23')); + }); + }); + + describe('JSX', () => { + it('.css', async () => { + const el = $('#react-css'); + + // 1. check HTML + expect(el.attr('class')).to.include('react-title'); + + // 2. check CSS + expect(bundledCSS).to.include('.react-title{'); + }); + + 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)); - // 1. check HTML - expect(el.attr('class')).to.include(moduleClass); + // 1. check HTML + expect(el.attr('class')).to.include(moduleClass); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); - }); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); + }); - it('.sass', async () => { - const el = $('#react-sass'); - - // 1. check HTML - expect(el.attr('class')).to.include('react-sass-title'); + it('.sass', async () => { + const el = $('#react-sass'); + + // 1. check HTML + expect(el.attr('class')).to.include('react-sass-title'); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy}`)); - }); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.react-sass-title[^{]*{font-family:fantasy}`)); + }); - it('.scss', async () => { - const el = $('#react-scss'); + it('.scss', async () => { + const el = $('#react-scss'); - // 1. check HTML - expect(el.attr('class')).to.include('react-scss-title'); + // 1. check HTML + expect(el.attr('class')).to.include('react-scss-title'); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.react-scss-title[^{]*{font-family:fantasy}`)); - }); - - 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-scss-title[^{]*{font-family:fantasy}`)); + }); + + 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)); - // 1. check HTML - expect(el.attr('class')).to.include(moduleClass); + // 1. check HTML + expect(el.attr('class')).to.include(moduleClass); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); - }); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); + }); - 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)); + 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(moduleClass); + // 1. check HTML + expect(el.attr('class')).to.include(moduleClass); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); - }); - }); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.${moduleClass}[^{]*{font-family:fantasy}`)); + }); + }); - describe('Vue', () => { - it('<style>', async () => { - const el = $('#vue-css'); + describe('Vue', () => { + it('<style>', async () => { + const el = $('#vue-css'); - // 1. check HTML - expect(el.attr('class')).to.include('vue-css'); + // 1. check HTML + expect(el.attr('class')).to.include('vue-css'); - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-css[^{]*{font-family:cursive`)); - }); + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.vue-css[^{]*{font-family:cursive`)); + }); - it('<style scoped>', async () => { - const el = $('#vue-scoped'); + it('<style scoped>', async () => { + const el = $('#vue-scoped'); - // 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; + // 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; - // 1. check HTML - expect(el.attr('class')).to.include('vue-scoped'); + // 1. check HTML + expect(el.attr('class')).to.include('vue-scoped'); - // 2. check CSS - expect(bundledCSS).to.include(`.vue-scoped[${scopeId}]`); - }); + // 2. check CSS + expect(bundledCSS).to.include(`.vue-scoped[${scopeId}]`); + }); - 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)); + 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)); - // 1. check HTML - expect(el.attr('class')).to.include(moduleClass); + // 1. check HTML + expect(el.attr('class')).to.include(moduleClass); - // 2. check CSS - expect(bundledCSS).to.include(`${moduleClass}{`); - }); - - it('<style lang="sass">', async () => { - const el = $('#vue-sass'); - - // 1. check HTML - expect(el.attr('class')).to.include('vue-sass'); - - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-sass[^{]*{font-family:cursive`)); - }); - - it('<style lang="scss">', async () => { - const el = $('#vue-scss'); - - // 1. check HTML - expect(el.attr('class')).to.include('vue-scss'); - - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.vue-scss[^{]*{font-family:cursive`)); - }); - }); - - 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('svelte-css'); - - // 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)); - - // 1. check HTML - expect(el.attr('class')).to.include('svelte-sass'); - - // 2. check CSS - expect(bundledCSS).to.match(new RegExp(`.svelte-sass.${scopedClass}[^{]*{font-family:"Comic Sans MS"`)); - }); - - 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"`)); - }); - }); - }); - - // with "build" handling CSS checking, the dev tests are mostly testing the paths resolve in dev - describe('dev', () => { - let devServer; - let $; - - before(async () => { - devServer = await fixture.startDevServer(); - const html = await fixture.fetch('/').then((res) => res.text()); - $ = cheerio.load(html); - }); - - after(async () => { - devServer && (await devServer.stop()); - }); - - it('resolves CSS in public/', async () => { - const href = $('link[href="/global.css"]').attr('href'); - expect((await fixture.fetch(href)).status).to.equal(200); - }); - - it('resolves CSS in src/', async () => { - const href = $('link[href$="linked.css"]').attr('href'); - expect((await fixture.fetch(href)).status).to.equal(200); - }); - - it('resolved imported CSS with ?url', async () => { - const href = $('link[href$="imported-url.css"]').attr('href'); - expect((await fixture.fetch(href)).status).to.equal(200); - }); - - it('resolves Astro styles', async () => { - const style = $('style[astro-style]'); - expect(style.length).to.not.equal(0); - }); - - 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); - } - }); - - 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); - } - }); - - 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); - } - }); - }); + // 2. check CSS + expect(bundledCSS).to.include(`${moduleClass}{`); + }); + + it('<style lang="sass">', async () => { + const el = $('#vue-sass'); + + // 1. check HTML + expect(el.attr('class')).to.include('vue-sass'); + + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.vue-sass[^{]*{font-family:cursive`)); + }); + + it('<style lang="scss">', async () => { + const el = $('#vue-scss'); + + // 1. check HTML + expect(el.attr('class')).to.include('vue-scss'); + + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.vue-scss[^{]*{font-family:cursive`)); + }); + }); + + 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('svelte-css'); + + // 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)); + + // 1. check HTML + expect(el.attr('class')).to.include('svelte-sass'); + + // 2. check CSS + expect(bundledCSS).to.match(new RegExp(`.svelte-sass.${scopedClass}[^{]*{font-family:"Comic Sans MS"`)); + }); + + 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"`)); + }); + }); + }); + + // with "build" handling CSS checking, the dev tests are mostly testing the paths resolve in dev + describe('dev', () => { + let devServer; + let $; + + before(async () => { + devServer = await fixture.startDevServer(); + const html = await fixture.fetch('/').then((res) => res.text()); + $ = cheerio.load(html); + }); + + after(async () => { + devServer && (await devServer.stop()); + }); + + it('resolves CSS in public/', async () => { + const href = $('link[href="/global.css"]').attr('href'); + expect((await fixture.fetch(href)).status).to.equal(200); + }); + + it('resolves CSS in src/', async () => { + const href = $('link[href$="linked.css"]').attr('href'); + expect((await fixture.fetch(href)).status).to.equal(200); + }); + + it('resolved imported CSS with ?url', async () => { + const href = $('link[href$="imported-url.css"]').attr('href'); + expect((await fixture.fetch(href)).status).to.equal(200); + }); + + it('resolves Astro styles', async () => { + const style = $('style[astro-style]'); + expect(style.length).to.not.equal(0); + }); + + 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); + } + }); + + 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); + } + }); + + 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); + } + }); + }); }); |