diff --git a/test/blocks/hero/hero.test.js b/test/blocks/hero/hero.test.js new file mode 100644 index 00000000..7e59287c --- /dev/null +++ b/test/blocks/hero/hero.test.js @@ -0,0 +1,14 @@ +/* eslint-disable no-unused-expressions */ +/* global describe it */ + +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; + +document.body.innerHTML = await readFile({ path: '../../scripts/body.html' }); + +describe('Hero block', () => { + it('Builds hero block from picture and h1', async () => { + await import('../../../scripts/scripts.js'); + expect(document.querySelector('.hero')).to.exist; + }); +}); diff --git a/test/scripts/body.html b/test/scripts/body.html new file mode 100644 index 00000000..cdc79c95 --- /dev/null +++ b/test/scripts/body.html @@ -0,0 +1,59 @@ +
+
+
+

+ + + + +

+

This is a Heading 1

+

This is a Heading 2

+

This is a Heading 3

+

This is a Heading 4

+
This is a Heading 5
+
This is a Heading 6
+ +
    +
  1. First
  2. +
  3. Second
  4. +
  5. Third
  6. +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut + labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum.

+

+ + + + +

+

Button

+

Button Bold

+

Button Italic

+

Button Italic Bold

+

Testing a code block

+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
+<script src="/scripts.js" type="module"></script>
+<link rel="stylesheet" href="/styles.css"/>
+        
+
< +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut + labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+
+
+ diff --git a/test/scripts/config.html b/test/scripts/config.html new file mode 100644 index 00000000..8a3d5ae6 --- /dev/null +++ b/test/scripts/config.html @@ -0,0 +1,25 @@ +
+
+
Prop 0
+
Plain text
+
+
+
Prop 1
+

Paragraph

+
+
+
Prop 2
+

First paragraph

Second paragraph

+
+
+
Prop 3
+
Link
+
+
+
Prop 4
+
+ First link + Second link +
+
+
diff --git a/test/scripts/dummy.html b/test/scripts/dummy.html new file mode 100644 index 00000000..fbe89155 --- /dev/null +++ b/test/scripts/dummy.html @@ -0,0 +1,4 @@ + +
+
+ diff --git a/test/scripts/head.html b/test/scripts/head.html new file mode 100644 index 00000000..7068482f --- /dev/null +++ b/test/scripts/head.html @@ -0,0 +1,12 @@ +Foo + + + + + + + + + + + diff --git a/test/scripts/media_mock.png b/test/scripts/media_mock.png new file mode 100644 index 00000000..4faad891 Binary files /dev/null and b/test/scripts/media_mock.png differ diff --git a/test/scripts/scripts.test.js b/test/scripts/scripts.test.js new file mode 100644 index 00000000..88404ba6 --- /dev/null +++ b/test/scripts/scripts.test.js @@ -0,0 +1,171 @@ +/* eslint-disable no-unused-expressions */ +/* global describe before it */ + +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; +import sinon from 'sinon'; + +const scripts = {}; + +document.body.innerHTML = await readFile({ path: './dummy.html' }); +document.head.innerHTML = await readFile({ path: './head.html' }); + +describe('Core Helix features', () => { + before(async () => { + const mod = await import('../../scripts/scripts.js'); + Object + .keys(mod) + .forEach((func) => { + scripts[func] = mod[func]; + }); + document.body.innerHTML = await readFile({ path: './body.html' }); + }); + + it('Initializes window.hlx', async () => { + // simulate code base path and turn on lighthouse + document.head.appendChild(document.createElement('script')).src = '/foo/scripts/scripts.js'; + window.history.pushState({}, '', `${window.location.href}&lighthouse=on`); + + scripts.initHlx(); + expect(window.hlx.codeBasePath).to.equal('/foo'); + expect(window.hlx.lighthouse).to.equal(true); + + // test error handling + const url = sinon.stub(window, 'URL'); + scripts.initHlx(); + + // cleanup + url.restore(); + window.hlx.codeBasePath = ''; + window.hlx.lighthouse = false; + Array.from(document.querySelectorAll('script')).pop().remove(); + }); + + it('Sanitizes class name', async () => { + expect(scripts.toClassName('Hello world')).to.equal('hello-world'); + expect(scripts.toClassName(null)).to.equal(''); + }); + + it('Extracts metadata', async () => { + expect(scripts.getMetadata('description')).to.equal('Lorem ipsum dolor sit amet.'); + expect(scripts.getMetadata('og:title')).to.equal('Foo'); + }); + + it('Adds favicon', async () => { + scripts.addFavIcon('/foo.svg'); + const $favIcon = document.querySelector('link[rel="icon"]'); + expect($favIcon.getAttribute('href')).to.equal('/foo.svg'); + }); + + it('Loads CSS', async () => { + // loads a css file and calls callback + const load = await new Promise((resolve) => { + scripts.loadCSS('/test/scripts/test.css', (e) => resolve(e)); + }); + expect(load).to.equal('load'); + expect(getComputedStyle(document.body).color).to.equal('rgb(255, 0, 0)'); + + // does nothing if css already loaded + const noop = await new Promise((resolve) => { + scripts.loadCSS('/test/scripts/test.css', (e) => resolve(e)); + }); + expect(noop).to.equal('noop'); + + // calls callback in case of error + const error = await new Promise((resolve) => { + scripts.loadCSS('/test/scripts/nope.css', (e) => resolve(e)); + }); + expect(error).to.equal('error'); + }); + + it('Collects RUM data', async () => { + const sendBeacon = sinon.stub(navigator, 'sendBeacon'); + // turn on RUM + window.history.pushState({}, '', `${window.location.href}&rum=on`); + delete window.hlx; + + // sends checkpoint beacon + await scripts.sampleRUM('test', { foo: 'bar' }); + expect(sendBeacon.called).to.be.true; + sendBeacon.resetHistory(); + + // sends cwv beacon + await scripts.sampleRUM('cwv', { foo: 'bar' }); + expect(sendBeacon.called).to.be.true; + + // test error handling + sendBeacon.throws(); + await scripts.sampleRUM('error', { foo: 'bar' }); + + sendBeacon.restore(); + }); + + it('Adds publish dependencies', async () => { + // adds single dependency + scripts.addPublishDependencies('/foo'); + expect(window.hlx.dependencies).to.include('/foo'); + + // adds multiple dependencies + scripts.addPublishDependencies(['/bar', '/baz']); + expect(window.hlx.dependencies).to.deep.equal(['/foo', '/bar', '/baz']); + }); + + it('Creates optimized picture', async () => { + const $picture = scripts.createOptimizedPicture('/test/scripts/mock.png'); + expect($picture.querySelector(':scope source[type="image/webp"]')).to.exist; // webp + expect($picture.querySelector(':scope source:not([type="image/webp"])')).to.exist; // fallback + expect($picture.querySelector(':scope img').src).to.include('format=png&optimize=medium'); // default + }); + + it('Normalizes headings', async () => { + const numHeadings = document.querySelectorAll('h1, h2, h3, h4, h5, h6').length; + scripts.normalizeHeadings(document.querySelector('main'), ['h1', 'h2', 'h3']); + expect(document.querySelectorAll('h1, h2, h3, h4, h5, h6').length).to.equal(numHeadings); + expect(document.querySelectorAll('h4, h5, h6').length).to.equal(0); + }); +}); + +describe('Sections and blocks', () => { + it('Decorates sections', async () => { + scripts.decorateSections(document.querySelector('main')); + expect(document.querySelectorAll('main .section').length).to.equal(2); + }); + + it('Decorates blocks', async () => { + scripts.decorateBlocks(document.querySelector('main')); + expect(document.querySelectorAll('main .block').length).to.equal(1); + }); + + it('Loads blocks', async () => { + await scripts.loadBlocks(document.querySelector('main')); + document.querySelectorAll('main .block').forEach(($block) => { + expect($block.dataset.blockStatus).to.equal('loaded'); + }); + }); + + it('Updates section status', async () => { + scripts.updateSectionsStatus(document.querySelector('main')); + document.querySelectorAll('main .section').forEach(($section) => { + expect($section.dataset.sectionStatus).to.equal('loaded'); + }); + + // test section with block still loading + const $section = document.querySelector('main .section'); + delete $section.dataset.sectionStatus; + $section.querySelector(':scope .block').dataset.blockStatus = 'loading'; + scripts.updateSectionsStatus(document.querySelector('main')); + expect($section.dataset.sectionStatus).to.equal('loading'); + }); + + it('Reads block config', async () => { + document.querySelector('main .section > div').innerHTML += await readFile({ path: './config.html' }); + const cfg = scripts.readBlockConfig(document.querySelector('main .config')); + expect(cfg).to.deep.include({ + 'prop-0': 'Plain text', + 'prop-1': 'Paragraph', + 'prop-2': ['First paragraph', 'Second paragraph'], + 'prop-3': 'https://www.adobe.com/', + 'prop-4': ['https://www.adobe.com/', 'https://www.hlx.live/'], + }); + }); +}); diff --git a/test/scripts/test.css b/test/scripts/test.css new file mode 100644 index 00000000..b05faf8e --- /dev/null +++ b/test/scripts/test.css @@ -0,0 +1,3 @@ +body { + color: red; +}