test: add visual regression testing with playwright

This commit is contained in:
Mathieu Puech 2020-07-30 11:18:55 -04:00
parent bfd9e99638
commit 64336f4211
32 changed files with 655 additions and 16 deletions

2
.gitignore vendored
View file

@ -28,6 +28,8 @@ yarn-error.log
/.tmp/ /.tmp/
/coverage/ /coverage/
/storybook-static/ /storybook-static/
/screenshots/.current/
/screenshots/.diff/
## temp files ## temp files
local.log local.log

View file

@ -30,7 +30,9 @@
"test:browser:all": "wtr \"packages/**/*/test/**/*.test.js\" --playwright --browsers webkit chromium firefox --coverage", "test:browser:all": "wtr \"packages/**/*/test/**/*.test.js\" --playwright --browsers webkit chromium firefox --coverage",
"test:browser:watch": "wtr \"packages/**/*/test/**/*.test.js\" --watch", "test:browser:watch": "wtr \"packages/**/*/test/**/*.test.js\" --watch",
"test:browserstack": "wtr --config ./web-test-runner-browserstack.config.js \"packages/form-core/test/**/*.test.js\"", "test:browserstack": "wtr --config ./web-test-runner-browserstack.config.js \"packages/form-core/test/**/*.test.js\"",
"test:node": "node scripts/workspaces-scripts.mjs run test:node" "test:node": "node scripts/workspaces-scripts.mjs run test:node",
"test:screenshots": "rimraf screenshots/.diff/ && rimraf screenshots/.current/ && mocha --require scripts/screenshots/bootstrap.js --exit --timeout 10000 \"packages/**/test/*.screenshots-test.js\"",
"test:screenshots:update": "cross-env UPDATE_SCREENSHOTS=true npm run test:screenshots"
}, },
"devDependencies": { "devDependencies": {
"@changesets/cli": "^2.9.2", "@changesets/cli": "^2.9.2",
@ -55,14 +57,21 @@
"chai": "^4.2.0", "chai": "^4.2.0",
"chalk": "^4.1.0", "chalk": "^4.1.0",
"concurrently": "^5.2.0", "concurrently": "^5.2.0",
"cross-env": "^7.0.2",
"eclint": "^2.8.1", "eclint": "^2.8.1",
"es-dev-server": "^1.57.1",
"es6-promisify": "^6.1.1",
"eslint": "^6.1.0", "eslint": "^6.1.0",
"eslint-config-prettier": "^6.11.0", "eslint-config-prettier": "^6.11.0",
"husky": "^1.0.0", "husky": "^1.0.0",
"lint-staged": "^10.0.0", "lint-staged": "^10.0.0",
"looks-same": "^7.2.3",
"markdownlint-cli": "^0.17.0", "markdownlint-cli": "^0.17.0",
"minimist": "^1.2.5",
"mkdirp-promise": "^5.0.1",
"mocha": "^7.1.1", "mocha": "^7.1.1",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"playwright": "^1.2.1",
"prettier": "^2.0.5", "prettier": "^2.0.5",
"prettier-package-json": "^2.1.3", "prettier-package-json": "^2.1.3",
"rimraf": "^2.6.3", "rimraf": "^2.6.3",

View file

@ -0,0 +1,60 @@
/* globals capture getStoryPage */
const selector = 'lion-button';
describe('buttons-button', () => {
it('main', async () => {
const id = 'buttons-button--main';
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('main-hovered', async () => {
const id = 'buttons-button--main';
const page = await getStoryPage(id);
await page.hover(selector);
await capture({ selector, id: `${id}-hovered`, page });
});
it('main-focused', async () => {
const id = 'buttons-button--main';
const page = await getStoryPage(id);
await page.focus(selector);
await capture({ selector, id: `${id}-focused`, page });
});
it('handler', async () => {
const id = 'buttons-button--handler';
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('icon-button', async () => {
const id = 'buttons-button--icon-button';
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('icon-only', async () => {
const id = 'buttons-button--icon-only';
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('icon-only-hovered', async () => {
const id = 'buttons-button--icon-only';
const page = await getStoryPage(id);
await page.hover(selector);
await capture({ selector, id: `${id}-hovered`, page });
});
it('icon-only-focused', async () => {
const id = 'buttons-button--icon-only';
const page = await getStoryPage(id);
await page.focus(selector);
await capture({ selector, id: `${id}-focused`, page });
});
it('disabled', async () => {
const id = 'buttons-button--disabled';
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('within-form', async () => {
const id = 'buttons-button--within-form';
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
});

View file

@ -39,7 +39,7 @@
"chalk": "^4.1.0", "chalk": "^4.1.0",
"commander": "^2.20.0", "commander": "^2.20.0",
"deepmerge": "^4.0.0", "deepmerge": "^4.0.0",
"es-dev-server": "^1.18.1", "es-dev-server": "^1.57.1",
"es-module-lexer": "^0.3.6", "es-module-lexer": "^0.3.6",
"glob": "^7.1.6", "glob": "^7.1.6",
"htm": "^3.0.3", "htm": "^3.0.3",

View file

@ -0,0 +1,131 @@
/* globals capture getStoryPage */
const selector = 'lion-select-rich';
describe('forms-select-rich', () => {
it('main', async () => {
const id = `forms-select-rich--main`;
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('main-opened', async () => {
const id = `forms-select-rich--main`;
const page = await getStoryPage(id);
await page.evaluate(() => {
const el = document.querySelector('lion-select-rich');
el.opened = true;
});
await capture({
selector,
id: `${id}-opened`,
page,
endClipSelector: 'lion-options',
});
});
it('options-with-html', async () => {
const id = `forms-select-rich--options-with-html`;
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('options-with-html-opened', async () => {
const id = `forms-select-rich--options-with-html`;
const page = await getStoryPage(id);
await page.evaluate(() => {
const el = document.querySelector('lion-select-rich');
el.opened = true;
});
await capture({
selector,
id: `${id}-opened`,
page,
endClipSelector: 'lion-options',
});
});
it('many-options-with-scrolling-opened', async () => {
const id = `forms-select-rich--many-options-with-scrolling`;
const page = await getStoryPage(id);
await page.evaluate(() => {
const el = document.querySelector('lion-select-rich');
el.opened = true;
});
await capture({
selector,
id: `${id}-opened`,
page,
endClipSelector: 'lion-options',
});
});
it('read-only-prefilled', async () => {
const id = `forms-select-rich--read-only-prefilled`;
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('disabled-select', async () => {
const id = `forms-select-rich--disabled-select`;
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('disabled-option-opened', async () => {
const id = `forms-select-rich--disabled-option`;
const page = await getStoryPage(id);
await page.evaluate(() => {
const el = document.querySelector('lion-select-rich');
el.opened = true;
});
await capture({
selector,
id: `${id}-opened`,
page,
endClipSelector: 'lion-options',
});
});
it('validation', async () => {
const id = `forms-select-rich--validation`;
const page = await getStoryPage(id);
await page.evaluate(() => {
const el = document.querySelector('lion-select-rich');
el.updateComplete.then(() => {
el.touched = true;
el.dirty = true;
});
});
await capture({ selector, id, page });
});
it('render-options', async () => {
const id = `forms-select-rich--render-options`;
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('interaction-mode-mac', async () => {
const id = `forms-select-rich--interaction-mode`;
const page = await getStoryPage(id);
await page.click('lion-select-rich');
await page.keyboard.press('ArrowDown');
await capture({
selector,
id: `${id}-mac`,
page,
});
});
it('interaction-mode-windows-linux', async () => {
const id = `forms-select-rich--interaction-mode`;
const page = await getStoryPage(id);
await page.click('lion-select-rich:last-of-type');
await page.keyboard.press('ArrowDown');
await capture({
selector: 'lion-select-rich:last-of-type',
id: `${id}-windows-linux`,
page,
});
});
it('no-default-selection', async () => {
const id = `forms-select-rich--no-default-selection`;
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
it('single-option', async () => {
const id = `forms-select-rich--single-option`;
const page = await getStoryPage(id);
await capture({ selector, id, page });
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

5
scripts/screenshots/bootstrap.js vendored Normal file
View file

@ -0,0 +1,5 @@
const { createCapture, getPage, getStoryPage } = require('./index.js');
global.capture = createCapture();
global.getPage = getPage;
global.getStoryPage = getStoryPage;

View file

@ -0,0 +1,282 @@
/* eslint-disable import/no-extraneous-dependencies */
const { chromium } = require('playwright');
const looksSame = require('looks-same');
const { join } = require('path');
const mkdirp = require('mkdirp-promise');
const fs = require('fs');
const { promisify } = require('es6-promisify');
const minimist = require('minimist');
const { mdjsTransformer } = require('@mdjs/core');
const { createConfig, startServer } = require('es-dev-server');
const nodePath = require('path');
const access = promisify(fs.access);
const compareScreenshots = promisify(looksSame);
const createScreenshotsDiff = promisify(looksSame.createDiff);
const args = minimist(process.argv);
const DIFF_FOLDER_PREFIX = '.diff';
const CURRENT_FOLDER_PREFIX = '.current';
const ROOT = 'screenshots';
// eslint-disable-next-line no-unused-vars
function log(line) {
// console.info(line);
}
/**
* @param {string} root
* @param {string} id
* @param {string} selector
* @return {{file: string, folder: string, path: string}}
*/
async function buildPath({ root, id, selector }) {
log(`Building path for ${id} - ${selector}`);
const [, category, component, file] = /(\w*)-(.*)--(.*)$/g.exec(id);
const extension = '.png';
const paths = {
file,
folder: join(root, category, component),
path: join(root, category, component, file + extension),
};
log(`Path is ${paths.path}`);
return paths;
}
const PATHS = {
root: ROOT,
diffRoot: join(ROOT, DIFF_FOLDER_PREFIX),
currentRoot: join(ROOT, CURRENT_FOLDER_PREFIX),
};
async function start() {
const config = createConfig({
nodeResolve: true,
preserveSymlinks: true,
rootDir: nodePath.join(__dirname, '../../storybook-static/'),
responseTransformers: [mdjsTransformer],
});
console.log('⚠️ Screenshots tests are made with builded storybook');
try {
return await startServer(config);
} catch (e) {
return start();
}
}
const serverPromise = start();
const browserPromise = chromium.launch({
ignoreDefaultArgs: ['--hide-scrollbars'],
});
process.on('beforeExit', () => {
browserPromise.then(browser => browser.close());
serverPromise.then(server => server.close());
});
async function getPage(path) {
const browser = await browserPromise;
const server = await serverPromise;
const url = `http://127.0.0.1:${server.server.address().port}${path}`;
log(`Creating a page for ${url}`);
const page = await browser.newPage();
// eslint-disable-next-line no-unused-vars
page.on('console', msg => {
if (msg._type !== 'debug') {
if (msg._type === 'warning') {
console.warn(`PAGE console.warn:`, msg._text);
} else {
console[msg._type](`PAGE console.${msg._type}:`, msg._text);
}
}
});
await page.goto(url);
await page.waitForTimeout(500);
log(`Page has been created`);
return page;
}
async function getStoryPage(id) {
return getPage(`/iframe.html?id=${id}&viewMode=story`);
}
async function getClip({ page, selector, endClipSelector }) {
log(`Getting clip for ${selector}`);
const clip = await page.evaluate(
([sel, endSel]) => {
const el = document.querySelector(sel);
if (!el) {
throw new Error(`An el matching selector \`${sel}\` wasn't found`);
}
const range = document.createRange();
range.setStartBefore(el);
range.setEndAfter(endSel ? document.querySelector(endSel) : el);
const rect = range.getBoundingClientRect();
const margin = 8;
const x = rect.left - margin < 0 ? 0 : rect.left - margin;
const y = rect.top - margin < 0 ? 0 : rect.top - margin;
return {
x,
y,
width: rect.width + (rect.left - x) * 2,
height: rect.height + (rect.top - y) * 2,
};
},
[selector, endClipSelector],
);
log(`Clip has been retrieved: ${JSON.stringify(clip)}`);
return clip;
}
async function getScreenshot({ root, id, selector, page, clip }) {
log(`Making a screenshot`);
const { path, folder } = await buildPath({ root, id, selector });
mkdirp(folder);
// Remove caret from screenshots to avoid caret diff
await page.evaluate(() => {
document.body.style.caretColor = 'transparent';
});
await page.screenshot({ path, clip });
log(`Screenshot was saved in ${path}`);
return {
path,
};
}
async function getReference({ root, id, selector }) {
log(`Searching for the reference`);
const { path } = await buildPath({ root, id, selector });
try {
await access(path);
log(`Reference is in ${path}`);
return {
path,
};
} catch (e) {
log(e);
log(`Reference was not found`);
return {};
}
}
const screenshotsCompareOptions = {
highlightColor: '#ff00ff',
tolerance: 0,
};
async function invalidateScreenshots({ diffRoot: root, id, selector, reference, current }) {
const { path, folder } = await buildPath({ root, id, selector });
mkdirp(folder);
await createScreenshotsDiff({
...screenshotsCompareOptions,
reference: reference.path,
current: current.path,
diff: path,
});
log(`Screenshot is invalid. Diff saved in ${path}`);
return {
path,
};
}
async function validateScreenshot(suite) {
const { current, reference } = suite;
log(`Validating screenshot`);
if (!reference.path) {
throw new Error('No reference screenshot was found.');
}
const { equal } = await compareScreenshots(
current.path,
reference.path,
screenshotsCompareOptions,
);
if (!equal) {
const { path } = await invalidateScreenshots(suite);
throw new Error(`Screenshots mismatch. Diff saved in ${path}`);
} else {
log(`Screenshot is valid`);
}
}
let updateScreenshots = args['update-screenshots'] || process.env.UPDATE_SCREENSHOTS;
try {
const avaConfig = JSON.parse(args._[2]);
updateScreenshots = avaConfig.updateScreenshots;
} catch (e) {
log('Could not parse config');
}
/**
* @return {capture}
*/
function createCapture() {
return async function capture({ url = '', selector = 'body', endClipSelector, page, id }) {
const suite = {
...PATHS,
url,
id,
selector,
page,
endClipSelector,
};
if (!suite.page) {
suite.page = await getPage(url);
}
await page.waitForTimeout(500);
suite.clip = await getClip(suite);
if (updateScreenshots) {
await getScreenshot(suite);
} else {
suite.current = await getScreenshot({ ...suite, root: suite.currentRoot });
suite.reference = await getReference(suite);
if (suite.reference) {
await validateScreenshot(suite);
} else {
throw new Error('No reference screenshot was found.');
}
}
};
}
exports.createCapture = createCapture;
exports.getPage = getPage;
exports.getStoryPage = getStoryPage;

178
yarn.lock
View file

@ -1651,9 +1651,9 @@
integrity sha512-1HpblP5edeENi0SKms7B+PKYdxHMBIQpaf0nAgTVsZeYgM9OJ3r9nrK/0MOUBZODAOZ1quvO3wlpuljq2hZPWA== integrity sha512-1HpblP5edeENi0SKms7B+PKYdxHMBIQpaf0nAgTVsZeYgM9OJ3r9nrK/0MOUBZODAOZ1quvO3wlpuljq2hZPWA==
"@open-wc/demoing-storybook@^2.0.2": "@open-wc/demoing-storybook@^2.0.2":
version "2.3.13" version "2.3.14"
resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-2.3.13.tgz#d258c3d09c4fb4fd3f587d72cf40428792c72066" resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-2.3.14.tgz#a99c4f1709bacd14a69bbd3669d4ea1ad4e22920"
integrity sha512-zQ8HJs4DmyStRA3rOlTYyvxvJnDCi41+xRGLVwEyRZ+/bN+rChUwUz+A+2i9qcuxmc8Fkw0cvPJtak0IwDAmkg== integrity sha512-UzgJXuSSywfEJ3lmBmldm9MEiP3XGZ8m5hRfa7dymGqOAMXMHzQaS5PAmC5RdPIcLtUmd7CeFW3feDBadTr0wA==
dependencies: dependencies:
"@babel/core" "^7.9.0" "@babel/core" "^7.9.0"
"@babel/generator" "^7.9.6" "@babel/generator" "^7.9.6"
@ -1671,7 +1671,7 @@
command-line-args "^5.0.2" command-line-args "^5.0.2"
command-line-usage "^6.1.0" command-line-usage "^6.1.0"
deepmerge "^4.2.2" deepmerge "^4.2.2"
es-dev-server "^1.57.0" es-dev-server "^1.57.1"
es-module-lexer "^0.3.13" es-module-lexer "^0.3.13"
fs-extra "^8.1.0" fs-extra "^8.1.0"
glob "^7.1.3" glob "^7.1.3"
@ -3812,6 +3812,16 @@ color-convert@^2.0.1:
dependencies: dependencies:
color-name "~1.1.4" color-name "~1.1.4"
color-convert@~0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
integrity sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=
color-diff@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/color-diff/-/color-diff-1.2.0.tgz#01a7c806de47dbd5ae571da49e65489666c999a7"
integrity sha512-FN7iLBCfb97ElJU2AQXbBAFXPbKmu0XJjPU9GWWmUkIbXka+Im8Q5w1geiL9GB+AktJ4pIA6nRZD1+TlEG6/rA==
color-name@1.1.3: color-name@1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
@ -3904,6 +3914,16 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
concat-stream@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
concurrently@^5.2.0: concurrently@^5.2.0:
version "5.2.0" version "5.2.0"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-5.2.0.tgz#ead55121d08a0fc817085584c123cedec2e08975" resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-5.2.0.tgz#ead55121d08a0fc817085584c123cedec2e08975"
@ -4047,6 +4067,13 @@ create-react-context@0.3.0, create-react-context@^0.3.0:
gud "^1.0.0" gud "^1.0.0"
warning "^4.0.3" warning "^4.0.3"
cross-env@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9"
integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==
dependencies:
cross-spawn "^7.0.1"
cross-spawn@^4.0.0: cross-spawn@^4.0.0:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41"
@ -4075,7 +4102,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
shebang-command "^1.2.0" shebang-command "^1.2.0"
which "^1.2.9" which "^1.2.9"
cross-spawn@^7.0.0: cross-spawn@^7.0.0, cross-spawn@^7.0.1:
version "7.0.3" version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@ -4866,7 +4893,7 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5:
string.prototype.trimend "^1.0.1" string.prototype.trimend "^1.0.1"
string.prototype.trimstart "^1.0.1" string.prototype.trimstart "^1.0.1"
es-dev-server@^1.18.1, es-dev-server@^1.57.0: es-dev-server@^1.18.1:
version "1.57.0" version "1.57.0"
resolved "https://registry.yarnpkg.com/es-dev-server/-/es-dev-server-1.57.0.tgz#79a30dcaec7a2cd0aa998baa572551794c21ef45" resolved "https://registry.yarnpkg.com/es-dev-server/-/es-dev-server-1.57.0.tgz#79a30dcaec7a2cd0aa998baa572551794c21ef45"
integrity sha512-vCQuXNir9L7HAxIStt2JpWHCKmudpSilhdLngWDbmkLDT+fAgy9YFLYRbs/ppU0VlrhUpjftpVvmEjRsFpib7Q== integrity sha512-vCQuXNir9L7HAxIStt2JpWHCKmudpSilhdLngWDbmkLDT+fAgy9YFLYRbs/ppU0VlrhUpjftpVvmEjRsFpib7Q==
@ -4936,6 +4963,76 @@ es-dev-server@^1.18.1, es-dev-server@^1.57.0:
useragent "^2.3.0" useragent "^2.3.0"
whatwg-url "^7.0.0" whatwg-url "^7.0.0"
es-dev-server@^1.57.1:
version "1.57.1"
resolved "https://registry.yarnpkg.com/es-dev-server/-/es-dev-server-1.57.1.tgz#c6ed4d8f4961215ce9c50f3c58604f047737eebe"
integrity sha512-wdYZCVNdCJxM9Z8V+lAqw6XfUTQZRhjbkU4Fdk/UcZqB6gJ2VaaafdBBwue9L3EWu6zVJz63btpA4Xe+kVqGUw==
dependencies:
"@babel/core" "^7.9.0"
"@babel/plugin-proposal-dynamic-import" "^7.8.3"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3"
"@babel/plugin-proposal-optional-chaining" "^7.9.0"
"@babel/plugin-syntax-class-properties" "^7.8.3"
"@babel/plugin-syntax-import-meta" "^7.8.3"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
"@babel/plugin-syntax-numeric-separator" "^7.8.3"
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
"@babel/plugin-transform-template-literals" "^7.8.3"
"@babel/preset-env" "^7.9.0"
"@koa/cors" "^3.1.0"
"@open-wc/building-utils" "^2.18.0"
"@rollup/plugin-node-resolve" "^7.1.1"
"@rollup/pluginutils" "^3.0.0"
"@types/babel__core" "^7.1.3"
"@types/browserslist" "^4.8.0"
"@types/browserslist-useragent" "^3.0.0"
"@types/caniuse-api" "^3.0.0"
"@types/command-line-args" "^5.0.0"
"@types/command-line-usage" "^5.0.1"
"@types/debounce" "^1.2.0"
"@types/koa" "^2.0.48"
"@types/koa-compress" "^2.0.9"
"@types/koa-etag" "^3.0.0"
"@types/koa-static" "^4.0.1"
"@types/koa__cors" "^3.0.1"
"@types/lru-cache" "^5.1.0"
"@types/minimatch" "^3.0.3"
"@types/path-is-inside" "^1.0.0"
"@types/whatwg-url" "^6.4.0"
browserslist "^4.9.1"
browserslist-useragent "^3.0.2"
builtin-modules "^3.1.0"
camelcase "^5.3.1"
caniuse-api "^3.0.0"
caniuse-lite "^1.0.30001033"
chokidar "^3.0.0"
command-line-args "^5.0.2"
command-line-usage "^6.1.0"
debounce "^1.2.0"
deepmerge "^4.2.2"
es-module-lexer "^0.3.13"
get-stream "^5.1.0"
is-stream "^2.0.0"
isbinaryfile "^4.0.2"
koa "^2.7.0"
koa-compress "^3.0.0"
koa-etag "^3.0.0"
koa-static "^5.0.0"
lru-cache "^5.1.1"
mime-types "^2.1.27"
minimatch "^3.0.4"
open "^7.0.3"
parse5 "^5.1.1"
path-is-inside "^1.0.2"
polyfills-loader "^1.6.1"
portfinder "^1.0.21"
rollup "^2.7.2"
strip-ansi "^5.2.0"
systemjs "^6.3.1"
tslib "^1.11.1"
useragent "^2.3.0"
whatwg-url "^7.0.0"
es-module-lexer@^0.3.13, es-module-lexer@^0.3.24, es-module-lexer@^0.3.6: es-module-lexer@^0.3.13, es-module-lexer@^0.3.24, es-module-lexer@^0.3.6:
version "0.3.24" version "0.3.24"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.3.24.tgz#e6b2900758e9e210d23aec2092efc13ca235adea" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.3.24.tgz#e6b2900758e9e210d23aec2092efc13ca235adea"
@ -4960,6 +5057,11 @@ es6-error@^4.0.1:
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
es6-promisify@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.1.1.tgz#46837651b7b06bf6fff893d03f29393668d01621"
integrity sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg==
escalade@^3.0.1: escalade@^3.0.1:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4"
@ -6888,6 +6990,11 @@ js-beautify@^1.8.9:
mkdirp "~1.0.3" mkdirp "~1.0.3"
nopt "^4.0.3" nopt "^4.0.3"
js-graph-algorithms@1.0.18:
version "1.0.18"
resolved "https://registry.yarnpkg.com/js-graph-algorithms/-/js-graph-algorithms-1.0.18.tgz#f96ec87bf194f5c0a31365fa0e1d07b7b962d891"
integrity sha1-+W7Ie/GU9cCjE2X6Dh0Ht7li2JE=
js-levenshtein-esm@^1.2.0: js-levenshtein-esm@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz#96532c34e0c90df198c9419963c64ca3cf43ae92" resolved "https://registry.yarnpkg.com/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz#96532c34e0c90df198c9419963c64ca3cf43ae92"
@ -7378,12 +7485,12 @@ lodash@4.17.11:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.2.0, lodash@^4.2.1: lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15:
version "4.17.15" version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
lodash@^4.17.19: lodash@^4.17.19, lodash@^4.17.3, lodash@^4.2.0, lodash@^4.2.1:
version "4.17.19" version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
@ -7443,6 +7550,20 @@ longest-streak@^2.0.1:
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4"
integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==
looks-same@^7.2.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/looks-same/-/looks-same-7.2.3.tgz#ded12be01b7848591a32694a8fba48b41baba55c"
integrity sha512-e67WnUOWLpLC0+4iOsbtMx1eUBgyx7NDUOq8esSxaSO63YTwrmoppkia+9w9oR1W+QLTw4eTKHv2pVLkGdkVrw==
dependencies:
color-diff "^1.1.0"
concat-stream "^1.6.2"
fs-extra "^8.1.0"
js-graph-algorithms "1.0.18"
lodash "^4.17.3"
nested-error-stacks "^2.1.0"
parse-color "^1.0.0"
pngjs "^3.3.3"
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
@ -7882,6 +8003,18 @@ mkdirp-classic@^0.5.2:
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp-promise@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1"
integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=
dependencies:
mkdirp "*"
mkdirp@*, mkdirp@~1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
mkdirp@0.5.4: mkdirp@0.5.4:
version "0.5.4" version "0.5.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512"
@ -7896,11 +8029,6 @@ mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3:
dependencies: dependencies:
minimist "^1.2.5" minimist "^1.2.5"
mkdirp@~1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
mocha@^6.2.2: mocha@^6.2.2:
version "6.2.3" version "6.2.3"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.3.tgz#e648432181d8b99393410212664450a4c1e31912" resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.3.tgz#e648432181d8b99393410212664450a4c1e31912"
@ -8029,6 +8157,11 @@ negotiator@0.6.2:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
nested-error-stacks@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61"
integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==
nice-try@^1.0.4: nice-try@^1.0.4:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@ -8543,6 +8676,13 @@ parse-author@^2.0.0:
dependencies: dependencies:
author-regex "^1.0.0" author-regex "^1.0.0"
parse-color@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/parse-color/-/parse-color-1.0.0.tgz#7b748b95a83f03f16a94f535e52d7f3d94658619"
integrity sha1-e3SLlag/A/FqlPU15S1/PZRlhhk=
dependencies:
color-convert "~0.5.0"
parse-entities@^1.0.2, parse-entities@^1.1.0, parse-entities@^1.1.2: parse-entities@^1.0.2, parse-entities@^1.1.0, parse-entities@^1.1.2:
version "1.2.2" version "1.2.2"
resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50"
@ -8829,6 +8969,11 @@ plugin-error@^1.0.1:
arr-union "^3.1.0" arr-union "^3.1.0"
extend-shallow "^3.0.2" extend-shallow "^3.0.2"
pngjs@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==
pngjs@^5.0.0: pngjs@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
@ -9327,7 +9472,7 @@ read-yaml-file@^1.1.0:
string_decoder "^1.1.1" string_decoder "^1.1.1"
util-deprecate "^1.0.1" util-deprecate "^1.0.1"
readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7" version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@ -10998,6 +11143,11 @@ typedarray-to-buffer@^3.1.5:
dependencies: dependencies:
is-typedarray "^1.0.0" is-typedarray "^1.0.0"
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^3.8.3: typescript@^3.8.3:
version "3.9.6" version "3.9.6"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"