diff --git a/.gitignore b/.gitignore index c0ec93709..e14c49060 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ yarn-error.log /.tmp/ /coverage/ /storybook-static/ +/screenshots/.current/ +/screenshots/.diff/ ## temp files local.log diff --git a/package.json b/package.json index ea2718728..1f2477848 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "test:browser:all": "wtr \"packages/**/*/test/**/*.test.js\" --playwright --browsers webkit chromium firefox --coverage", "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: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": { "@changesets/cli": "^2.9.2", @@ -55,14 +57,21 @@ "chai": "^4.2.0", "chalk": "^4.1.0", "concurrently": "^5.2.0", + "cross-env": "^7.0.2", "eclint": "^2.8.1", + "es-dev-server": "^1.57.1", + "es6-promisify": "^6.1.1", "eslint": "^6.1.0", "eslint-config-prettier": "^6.11.0", "husky": "^1.0.0", "lint-staged": "^10.0.0", + "looks-same": "^7.2.3", "markdownlint-cli": "^0.17.0", + "minimist": "^1.2.5", + "mkdirp-promise": "^5.0.1", "mocha": "^7.1.1", "npm-run-all": "^4.1.5", + "playwright": "^1.2.1", "prettier": "^2.0.5", "prettier-package-json": "^2.1.3", "rimraf": "^2.6.3", diff --git a/packages/button/test/demos.screenshots-test.js b/packages/button/test/demos.screenshots-test.js new file mode 100644 index 000000000..a903eca51 --- /dev/null +++ b/packages/button/test/demos.screenshots-test.js @@ -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 }); + }); +}); diff --git a/packages/providence-analytics/package.json b/packages/providence-analytics/package.json index b044e4da7..f380645fd 100644 --- a/packages/providence-analytics/package.json +++ b/packages/providence-analytics/package.json @@ -39,7 +39,7 @@ "chalk": "^4.1.0", "commander": "^2.20.0", "deepmerge": "^4.0.0", - "es-dev-server": "^1.18.1", + "es-dev-server": "^1.57.1", "es-module-lexer": "^0.3.6", "glob": "^7.1.6", "htm": "^3.0.3", diff --git a/packages/select-rich/test/demos.screenshots-test.js b/packages/select-rich/test/demos.screenshots-test.js new file mode 100644 index 000000000..e04ffcdf5 --- /dev/null +++ b/packages/select-rich/test/demos.screenshots-test.js @@ -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 }); + }); +}); diff --git a/screenshots/buttons/button/disabled.png b/screenshots/buttons/button/disabled.png new file mode 100644 index 000000000..0127134d9 Binary files /dev/null and b/screenshots/buttons/button/disabled.png differ diff --git a/screenshots/buttons/button/handler.png b/screenshots/buttons/button/handler.png new file mode 100644 index 000000000..5b1d60ece Binary files /dev/null and b/screenshots/buttons/button/handler.png differ diff --git a/screenshots/buttons/button/icon-button.png b/screenshots/buttons/button/icon-button.png new file mode 100644 index 000000000..8418c0d82 Binary files /dev/null and b/screenshots/buttons/button/icon-button.png differ diff --git a/screenshots/buttons/button/icon-only-focused.png b/screenshots/buttons/button/icon-only-focused.png new file mode 100644 index 000000000..d29c72e40 Binary files /dev/null and b/screenshots/buttons/button/icon-only-focused.png differ diff --git a/screenshots/buttons/button/icon-only-hovered.png b/screenshots/buttons/button/icon-only-hovered.png new file mode 100644 index 000000000..2c2c4283d Binary files /dev/null and b/screenshots/buttons/button/icon-only-hovered.png differ diff --git a/screenshots/buttons/button/icon-only.png b/screenshots/buttons/button/icon-only.png new file mode 100644 index 000000000..875843f90 Binary files /dev/null and b/screenshots/buttons/button/icon-only.png differ diff --git a/screenshots/buttons/button/main-focused.png b/screenshots/buttons/button/main-focused.png new file mode 100644 index 000000000..68375f48a Binary files /dev/null and b/screenshots/buttons/button/main-focused.png differ diff --git a/screenshots/buttons/button/main-hovered.png b/screenshots/buttons/button/main-hovered.png new file mode 100644 index 000000000..d438d1573 Binary files /dev/null and b/screenshots/buttons/button/main-hovered.png differ diff --git a/screenshots/buttons/button/main.png b/screenshots/buttons/button/main.png new file mode 100644 index 000000000..4b35d9b14 Binary files /dev/null and b/screenshots/buttons/button/main.png differ diff --git a/screenshots/buttons/button/within-form.png b/screenshots/buttons/button/within-form.png new file mode 100644 index 000000000..8e7b74147 Binary files /dev/null and b/screenshots/buttons/button/within-form.png differ diff --git a/screenshots/forms/select-rich/disabled-option-opened.png b/screenshots/forms/select-rich/disabled-option-opened.png new file mode 100644 index 000000000..da9eebbef Binary files /dev/null and b/screenshots/forms/select-rich/disabled-option-opened.png differ diff --git a/screenshots/forms/select-rich/disabled-select.png b/screenshots/forms/select-rich/disabled-select.png new file mode 100644 index 000000000..7a862a8c3 Binary files /dev/null and b/screenshots/forms/select-rich/disabled-select.png differ diff --git a/screenshots/forms/select-rich/interaction-mode-mac.png b/screenshots/forms/select-rich/interaction-mode-mac.png new file mode 100644 index 000000000..550131dcb Binary files /dev/null and b/screenshots/forms/select-rich/interaction-mode-mac.png differ diff --git a/screenshots/forms/select-rich/interaction-mode-windows-linux.png b/screenshots/forms/select-rich/interaction-mode-windows-linux.png new file mode 100644 index 000000000..acfcf8f6f Binary files /dev/null and b/screenshots/forms/select-rich/interaction-mode-windows-linux.png differ diff --git a/screenshots/forms/select-rich/main-opened.png b/screenshots/forms/select-rich/main-opened.png new file mode 100644 index 000000000..c5e3fd023 Binary files /dev/null and b/screenshots/forms/select-rich/main-opened.png differ diff --git a/screenshots/forms/select-rich/main.png b/screenshots/forms/select-rich/main.png new file mode 100644 index 000000000..848b1e3c1 Binary files /dev/null and b/screenshots/forms/select-rich/main.png differ diff --git a/screenshots/forms/select-rich/many-options-with-scrolling-opened.png b/screenshots/forms/select-rich/many-options-with-scrolling-opened.png new file mode 100644 index 000000000..a55ccaec3 Binary files /dev/null and b/screenshots/forms/select-rich/many-options-with-scrolling-opened.png differ diff --git a/screenshots/forms/select-rich/no-default-selection.png b/screenshots/forms/select-rich/no-default-selection.png new file mode 100644 index 000000000..c58c17a52 Binary files /dev/null and b/screenshots/forms/select-rich/no-default-selection.png differ diff --git a/screenshots/forms/select-rich/options-with-html-opened.png b/screenshots/forms/select-rich/options-with-html-opened.png new file mode 100644 index 000000000..d30eb671e Binary files /dev/null and b/screenshots/forms/select-rich/options-with-html-opened.png differ diff --git a/screenshots/forms/select-rich/options-with-html.png b/screenshots/forms/select-rich/options-with-html.png new file mode 100644 index 000000000..ade0b201f Binary files /dev/null and b/screenshots/forms/select-rich/options-with-html.png differ diff --git a/screenshots/forms/select-rich/read-only-prefilled.png b/screenshots/forms/select-rich/read-only-prefilled.png new file mode 100644 index 000000000..2d51299e9 Binary files /dev/null and b/screenshots/forms/select-rich/read-only-prefilled.png differ diff --git a/screenshots/forms/select-rich/render-options.png b/screenshots/forms/select-rich/render-options.png new file mode 100644 index 000000000..0e75479a7 Binary files /dev/null and b/screenshots/forms/select-rich/render-options.png differ diff --git a/screenshots/forms/select-rich/single-option.png b/screenshots/forms/select-rich/single-option.png new file mode 100644 index 000000000..851eee2aa Binary files /dev/null and b/screenshots/forms/select-rich/single-option.png differ diff --git a/screenshots/forms/select-rich/validation.png b/screenshots/forms/select-rich/validation.png new file mode 100644 index 000000000..33efa904c Binary files /dev/null and b/screenshots/forms/select-rich/validation.png differ diff --git a/scripts/screenshots/bootstrap.js b/scripts/screenshots/bootstrap.js new file mode 100644 index 000000000..9e867848c --- /dev/null +++ b/scripts/screenshots/bootstrap.js @@ -0,0 +1,5 @@ +const { createCapture, getPage, getStoryPage } = require('./index.js'); + +global.capture = createCapture(); +global.getPage = getPage; +global.getStoryPage = getStoryPage; diff --git a/scripts/screenshots/index.js b/scripts/screenshots/index.js new file mode 100644 index 000000000..5058b6372 --- /dev/null +++ b/scripts/screenshots/index.js @@ -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; diff --git a/yarn.lock b/yarn.lock index 0e50d30a1..8ab2cfa3c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1651,9 +1651,9 @@ integrity sha512-1HpblP5edeENi0SKms7B+PKYdxHMBIQpaf0nAgTVsZeYgM9OJ3r9nrK/0MOUBZODAOZ1quvO3wlpuljq2hZPWA== "@open-wc/demoing-storybook@^2.0.2": - version "2.3.13" - resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-2.3.13.tgz#d258c3d09c4fb4fd3f587d72cf40428792c72066" - integrity sha512-zQ8HJs4DmyStRA3rOlTYyvxvJnDCi41+xRGLVwEyRZ+/bN+rChUwUz+A+2i9qcuxmc8Fkw0cvPJtak0IwDAmkg== + version "2.3.14" + resolved "https://registry.yarnpkg.com/@open-wc/demoing-storybook/-/demoing-storybook-2.3.14.tgz#a99c4f1709bacd14a69bbd3669d4ea1ad4e22920" + integrity sha512-UzgJXuSSywfEJ3lmBmldm9MEiP3XGZ8m5hRfa7dymGqOAMXMHzQaS5PAmC5RdPIcLtUmd7CeFW3feDBadTr0wA== dependencies: "@babel/core" "^7.9.0" "@babel/generator" "^7.9.6" @@ -1671,7 +1671,7 @@ command-line-args "^5.0.2" command-line-usage "^6.1.0" deepmerge "^4.2.2" - es-dev-server "^1.57.0" + es-dev-server "^1.57.1" es-module-lexer "^0.3.13" fs-extra "^8.1.0" glob "^7.1.3" @@ -3812,6 +3812,16 @@ color-convert@^2.0.1: dependencies: 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: version "1.1.3" 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" 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: version "5.2.0" 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" 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: version "4.0.2" 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" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 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.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" resolved "https://registry.yarnpkg.com/es-dev-server/-/es-dev-server-1.57.0.tgz#79a30dcaec7a2cd0aa998baa572551794c21ef45" 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" 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: version "0.3.24" 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" 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: version "3.0.2" 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" 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: version "1.2.0" 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" 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" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" 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" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" 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" 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: version "1.4.0" 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" 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: version "0.5.4" 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: 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: version "6.2.3" 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" 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: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -8543,6 +8676,13 @@ parse-author@^2.0.0: dependencies: 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: version "1.2.2" 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" 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: version "5.0.0" 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" 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" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -10998,6 +11143,11 @@ typedarray-to-buffer@^3.1.5: dependencies: 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: version "3.9.6" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a"