From cd06112da7f51a2f770511ed7b6bf776ad378eef Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Thu, 4 Mar 2021 12:32:16 +0100 Subject: [PATCH 01/11] test: cypress initial commit --- .circleci/config.yml | 13 +- .gitignore | 4 + giraffe/package.json | 13 +- package.json | 12 +- stories/cypress.json | 4 + stories/cypress/index.d.ts | 27 + stories/cypress/integration/test.test.ts | 7 + stories/cypress/plugins/index.js | 24 + .../test.test.ts/gauge-test-1.snap.png | Bin 0 -> 50263 bytes stories/cypress/support/commands.ts | 83 +++ stories/cypress/support/index.js | 20 + stories/cypress/tsconfig.json | 13 + stories/package.json | 26 +- tsconfig.json | 15 +- yarn.lock | 560 +++++++++++++++++- 15 files changed, 786 insertions(+), 35 deletions(-) create mode 100644 stories/cypress.json create mode 100644 stories/cypress/index.d.ts create mode 100644 stories/cypress/integration/test.test.ts create mode 100644 stories/cypress/plugins/index.js create mode 100644 stories/cypress/snapshots/test.test.ts/gauge-test-1.snap.png create mode 100644 stories/cypress/support/commands.ts create mode 100644 stories/cypress/support/index.js create mode 100644 stories/cypress/tsconfig.json diff --git a/.circleci/config.yml b/.circleci/config.yml index b06735da..0a134f37 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,14 +14,15 @@ jobs: paths: - node_modules key: v1-dependencies-{{ checksum "yarn.lock" }} - - run: cd giraffe && yarn run lint - - run: cd giraffe && yarn run typecheck - - run: cd giraffe && yarn run test --collectCoverage - - run: cd giraffe && yarn run build - - run: cd stories && yarn run lint - - run: cd stories && yarn run typecheck + - run: yarn lint + - run: yarn --cwd giraffe test --collectCoverage + - run: yarn --cwd giraffe build + # todo: stories-test runs lint too (lint runs two multiple times) + - run: yarn --cwd stories test - store_artifacts: path: giraffe/coverage + - store_artifacts: + path: stories/cypress/snapshots/**/__diff_output__ # See: https://github.com/influxdata/giraffe/issues/129 # - run: # name: chromatic diff --git a/.gitignore b/.gitignore index 1889071e..8eec7a47 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ dist .env .vscode coverage + +__diff_output__ +videos +screenshots diff --git a/giraffe/package.json b/giraffe/package.json index 893415bc..0d95ffb9 100644 --- a/giraffe/package.json +++ b/giraffe/package.json @@ -6,14 +6,17 @@ "license": "MIT", "scripts": { "build": "rm -rf dist && NODE_ENV=production webpack --config webpack.config.js", - "lint": "eslint '{src,../stories/src}/**/*.{ts,tsx}'", - "prettier": "prettier --config ../.prettierrc.json --check '{src,../stories/src}/**/*.{ts,tsx}'", - "prettier:fix": "prettier --config ../.prettierrc.json --write '{src,../stories/src}/**/*.{ts,tsx}'", + "lint": "run-s typecheck eslint prettier", + "lint:fix": "run-s typecheck eslint:fix prettier:fix", + "typecheck": "tsc --noEmit --pretty", + "eslint": "eslint \"{,../stories}/{src,cypress}/**/*.{ts,tsx}\"", + "eslint:fix": "eslint \"{,../stories}/{src,cypress}/**/*.{ts,tsx}\" --fix", + "prettier": "prettier --config ../.prettierrc.json --check \"{,../stories}/{src,cypress}/**/*.{ts,tsx}\"", + "prettier:fix": "prettier --config ../.prettierrc.json --write \"{,../stories}/{src,cypress}/**/*.{ts,tsx}\"", "publish": "echo 'To publish giraffe, run ./publish $version'", "start": "webpack --config webpack.config.js --watch --progress", "test": "jest --collectCoverage --maxWorkers=2", - "test:watch": "jest --collectCoverage --watch --verbose false", - "typecheck": "tsc --noEmit --pretty" + "test:watch": "jest --collectCoverage --watch --verbose false" }, "keywords": [ "band", diff --git a/package.json b/package.json index 6bfbf0ba..21024d86 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,13 @@ "private": true, "workspaces": ["giraffe", "stories"], "scripts": { - "lint": "eslint '{giraffe,stories}/src/**/*.{ts,tsx}'", - "prettier": "prettier --config .prettierrc.json --check '{giraffe,stories}/**/*.{ts,tsx}'", - "prettier:fix": "prettier --config .prettierrc.json --write '{giraffe,stories}/**/*.{ts,tsx}'", - "publish": "echo 'To publish giraffe, run ./giraffe/publish $version'" + "lint": "run-s typecheck eslint prettier", + "lint:fix": "run-s typecheck eslint:fix prettier:fix", + "typecheck": "tsc --noEmit --pretty", + "eslint": "eslint \"{giraffe,stories}/{src,cypress}/**/*.{ts,tsx}\"", + "eslint:fix": "eslint \"{giraffe,stories}/{src,cypress}/**/*.{ts,tsx}\" --fix", + "prettier": "prettier --config .prettierrc.json --check \"{giraffe,stories}/{src,cypress}/**/*.{ts,tsx}\"", + "prettier:fix": "prettier --config .prettierrc.json --write \"{giraffe,stories}/{src,cypress}/**/*.{ts,tsx}\"", + "publish": "echo \"To publish giraffe, run ./giraffe/publish $version\"" } } diff --git a/stories/cypress.json b/stories/cypress.json new file mode 100644 index 00000000..40812548 --- /dev/null +++ b/stories/cypress.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://on.cypress.io/cypress.schema.json", + "baseUrl": "http://localhost:50000/?path=/story/" +} \ No newline at end of file diff --git a/stories/cypress/index.d.ts b/stories/cypress/index.d.ts new file mode 100644 index 00000000..04705a2c --- /dev/null +++ b/stories/cypress/index.d.ts @@ -0,0 +1,27 @@ +/* eslint @typescript-eslint/no-unused-vars: "off" */ +import 'jest' + +import { + getByTestID, + getByInputName, + getByInputValue, + getByTitle, + getByTestIDSubStr, + visitTest, + snapshotComponent, +} from './support/commands' + +declare global { + namespace Cypress { + interface Chainable { + + getByTestID: typeof getByTestID + getByInputName: typeof getByInputName + getByInputValue: typeof getByInputValue + getByTitle: typeof getByTitle + getByTestIDSubStr: typeof getByTestIDSubStr + visitTest: typeof visitTest + snapshotComponent: typeof snapshotComponent + } + } +} diff --git a/stories/cypress/integration/test.test.ts b/stories/cypress/integration/test.test.ts new file mode 100644 index 00000000..c9f3f413 --- /dev/null +++ b/stories/cypress/integration/test.test.ts @@ -0,0 +1,7 @@ +describe('testing test', () => { + it('should make snapshot of gauge', () => { + cy.visitTest('Gauge', 'Gauge') + + cy.snapshotComponent('gauge-test-1') + }) +}) diff --git a/stories/cypress/plugins/index.js b/stories/cypress/plugins/index.js new file mode 100644 index 00000000..1625c0d0 --- /dev/null +++ b/stories/cypress/plugins/index.js @@ -0,0 +1,24 @@ +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +const {addMatchImageSnapshotPlugin} = require('cypress-image-snapshot/plugin') + +/** + * @type {Cypress.PluginConfig} + */ +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config + addMatchImageSnapshotPlugin(on, config) +} diff --git a/stories/cypress/snapshots/test.test.ts/gauge-test-1.snap.png b/stories/cypress/snapshots/test.test.ts/gauge-test-1.snap.png new file mode 100644 index 0000000000000000000000000000000000000000..b7322ed71dadeeb630b2659646354850958d081d GIT binary patch literal 50263 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 literal 0 HcmV?d00001 diff --git a/stories/cypress/support/commands.ts b/stories/cypress/support/commands.ts new file mode 100644 index 00000000..192abd3a --- /dev/null +++ b/stories/cypress/support/commands.ts @@ -0,0 +1,83 @@ +import {addMatchImageSnapshotCommand} from 'cypress-image-snapshot/command' +import {Options as ImageSnapshotOptions} from 'cypress-image-snapshot' + +addMatchImageSnapshotCommand() + +// DOM node getters +export const getByTestID = ( + dataTest: string, + options?: Partial< + Cypress.Loggable & Cypress.Timeoutable & Cypress.Withinable & Cypress.Shadow + > +): Cypress.Chainable => { + return cy.get(`[data-testid="${dataTest}"]`, options) +} + +export const getByTestIDSubStr = (dataTest: string): Cypress.Chainable => { + return cy.get(`[data-testid*="${dataTest}"]`) +} + +export const getByInputName = (name: string): Cypress.Chainable => { + return cy.get(`input[name=${name}]`) +} + +export const getByInputValue = (value: string): Cypress.Chainable => { + return cy.get(`input[value='${value}']`) +} + +export const getByTitle = (name: string): Cypress.Chainable => { + return cy.get(`[title="${name}"]`) +} + +/** + * name is tab group name, storiname is tab name. + */ +export const visitTest = (name: string, storyname: string) => { + /** format string to stories url format */ + const f = (str: string) => + str + .replace(' ', '-') + .replace(':', '') + .toLowerCase() + cy.visit(`${f(name)}--${f(storyname)}`) +} + +// todo: replace waitings with reasonable checks(of iframe) +/** + * check snapshot of component with existing + */ +export const snapshotComponent = ( + snapshotName: string, + options: ImageSnapshotOptions = {} +) => { + // cy.get("iframe").its("0.contentDocument").should('exist') + // .its('body').should('not.be.undefined') + // .its(`p[content*='placeholder for']`).contains + + // enter full screen component + cy.wait(1_000) + // todo: better selector for zoom button + cy.get('.css-pvky73 > span > button').click() + + cy.wait(1_000) + cy.matchImageSnapshot(snapshotName, options) + + // exit full screen component + cy.get('.css-pvky73 > span > button').click() + cy.wait(1_000) +} + +/* eslint-disable */ + +// getters +Cypress.Commands.add('getByTestID', getByTestID) +Cypress.Commands.add('getByInputName', getByInputName) +Cypress.Commands.add('getByInputValue', getByInputValue) +Cypress.Commands.add('getByTitle', getByTitle) +Cypress.Commands.add('getByTestIDSubStr', getByTestIDSubStr) + +// helpers +Cypress.Commands.add('visitTest', visitTest) +Cypress.Commands.add('snapshotComponent', snapshotComponent) + +/* eslint-enable */ diff --git a/stories/cypress/support/index.js b/stories/cypress/support/index.js new file mode 100644 index 00000000..d68db96d --- /dev/null +++ b/stories/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/stories/cypress/tsconfig.json b/stories/cypress/tsconfig.json new file mode 100644 index 00000000..bbb2c160 --- /dev/null +++ b/stories/cypress/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "strict": true, + "baseUrl": "../node_modules", + "target": "es5", + "lib": ["es2017", "dom"], + "types": ["cypress", "mocha", "node"], + "jsx": "react", + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true + }, + "include": ["**/*.ts","../../node_modules/cypress"] +} diff --git a/stories/package.json b/stories/package.json index b6f60ed1..7ffbd3a9 100644 --- a/stories/package.json +++ b/stories/package.json @@ -3,14 +3,23 @@ "version": "1.0.0", "license": "MIT", "scripts": { - "lint": "eslint '{src,../giraffe/src}/**/*.{ts,tsx}'", - "prettier": "prettier --config ../.prettierrc.json --check '{src,../giraffe/src}/**/*.{ts,tsx}'", - "prettier:fix": "prettier --config ../.prettierrc.json --write '{src,../giraffe/src}/**/*.{ts,tsx}'", + "test": "npm-run-all lint --parallel -r start:ci cy:ci", + "lint": "run-s typecheck eslint prettier", + "lint:fix": "run-s typecheck eslint:fix prettier:fix", "typecheck": "tsc --noEmit --pretty", + "eslint": "eslint \"{,../giraffe}/{src,cypress}/**/*.{ts,tsx}\"", + "eslint:fix": "eslint \"{,../giraffe}/{src,cypress}/**/*.{ts,tsx}\" --fix", + "prettier": "prettier --config ../.prettierrc.json --check \"{,../giraffe}/{src,cypress}/**/*.{ts,tsx}\"", + "prettier:fix": "prettier --config ../.prettierrc.json --write \"{,../giraffe}/{src,cypress}/**/*.{ts,tsx}\"", "start": "yarn -s run prettier:fix && yarn -s run storybook", + "start:ci": "yarn storybook --ci", "storybook": "start-storybook -p 50000", "publish": "storybook-to-ghpages --out=.out", - "chromatic": "chromatic test" + "chromatic": "chromatic test", + "cy": "cypress open", + "cy:ci": "wait-on http://localhost:50000/ && cypress run", + "cy:update": "cypress run --env updateSnapshots=true", + "cy:gen-diff": "cypress run --env failOnSnapshotDiff=false" }, "devDependencies": { "@babel/core": "^7.4.3", @@ -18,6 +27,7 @@ "@storybook/core": "5", "@storybook/react": "^5.0.10", "@storybook/storybook-deployer": "^2.8.1", + "@types/cypress-image-snapshot": "^3.1.5", "@types/memoize-one": "^4.1.1", "@types/node-sass": "^4.11.0", "@types/react": "^16.8.3", @@ -26,8 +36,11 @@ "@typescript-eslint/eslint-plugin": "^1.5.0", "@typescript-eslint/parser": "^1.5.0", "babel-loader": "^8.0.5", + "cross-env": "^7.0.3", "css-loader": "^3.6.0", "css-modules-typescript-loader": "4.0.0", + "cypress": "^6.5.0", + "cypress-image-snapshot": "^4.0.1", "eslint": "^5.15.3", "eslint-config-prettier": "^5.0.0", "eslint-plugin-prettier": "^3.0.1", @@ -35,7 +48,9 @@ "file-loader": "^3.0.1", "intl-dateformat": "^0.1.1", "memoize-one": "^5.0.2", + "npm-run-all": "^4.1.5", "prettier": "^1.19.1", + "process": "^0.11.10", "react-virtualized-auto-sizer": "^1.0.2", "resolve-url-loader": "^3.0.1", "sass": "^1.22.7", @@ -44,6 +59,7 @@ "ts-loader": "^6.0.4", "typescript": "3.8.3", "version-bump-prompt": "^5.0.0", + "wait-on": "^5.2.1", "webpack": "^4.35.3" }, "dependencies": { @@ -53,4 +69,4 @@ "react-dev-utils": "11.0.1", "react-dom": "^16.9.0" } -} +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 5e85b2af..68f07d65 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,15 @@ "compilerOptions": { "target": "es5", "module": "es2015", - "lib": ["es5", "es2015", "es2016", "es2017", "es2018", "esnext", "dom"], + "lib": [ + "es5", + "es2015", + "es2016", + "es2017", + "es2018", + "esnext", + "dom" + ], "sourceMap": true, "allowJs": false, "jsx": "react", @@ -16,5 +24,8 @@ "strictNullChecks": false, "downlevelIteration": true, "skipLibCheck": true - } + }, + "exclude": [ + "./stories/cypress" + ] } diff --git a/yarn.lock b/yarn.lock index cb33fed4..6a07f0c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1272,6 +1272,50 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@cypress/listr-verbose-renderer@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" + integrity sha1-p3SS9LEdzHxEajSz4ochr9M8ZCo= + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +"@cypress/request@^2.88.5": + version "2.88.5" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.5.tgz#8d7ecd17b53a849cfd5ab06d5abe7d84976375d7" + integrity sha512-TzEC1XMi1hJkywWpRfD2clreTa/Z+lOrXDCxxBTBPEcY5azdPi56A6Xw+O4tWJnaJH3iIE7G5aDXZC6JgRZLcA== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + "@discoveryjs/json-ext@^0.5.0": version "0.5.2" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" @@ -1425,6 +1469,18 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz#63985d3d8b02530e0869962f4da09142ee8e200e" integrity sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA== +"@hapi/hoek@^9.0.0": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.1.1.tgz#9daf5745156fd84b8e9889a2dc721f0c58e894aa" + integrity sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw== + +"@hapi/topo@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.0.0.tgz#c19af8577fa393a06e9c77b60995af959be721e7" + integrity sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@icons/material@^0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" @@ -1778,6 +1834,23 @@ dependencies: any-observable "^0.3.0" +"@sideway/address@^4.1.0": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.1.tgz#9e321e74310963fdf8eebfbee09c7bd69972de4d" + integrity sha512-+I5aaQr3m0OAmMr7RQ3fR9zx55sejEYR2BFJaxL+zT3VM2611X0SHvPWIbAUBZVTn/YzYKbV8gJ2oT/QELknfQ== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sindresorhus/is@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" @@ -2294,6 +2367,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/cypress-image-snapshot@^3.1.5": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@types/cypress-image-snapshot/-/cypress-image-snapshot-3.1.5.tgz#03d5b8e089e96a493da31bdb9aef16f34c0000c1" + integrity sha512-PoYmfojdRxrAi5kXi60NKSw3f9tmP7wGtyoRpMMcV4rlEOUzpyO6zmE9G0Z42u7i9erlyrwLVqFpbsN+uLq1Kg== + "@types/d3-array@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-2.0.0.tgz#a0d63a296a2d8435a9ec59393dcac746c6174a96" @@ -2487,6 +2565,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.10.tgz#51babf9c7deadd5343620055fc8aff7995c8b031" integrity sha512-LcsGbPomWsad6wmMNv7nBLw7YYYyfdYcz6xryKYQhx89c3XXan+8Q6AJ43G5XDIaklaVkK3mE4fCb0SBvMiPSQ== +"@types/node@12.12.50": + version "12.12.50" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.50.tgz#e9b2e85fafc15f2a8aa8fdd41091b983da5fd6ee" + integrity sha512-5ImO01Fb8YsEOYpV+aeyGYztcYcjGsBvN4D7G5r1ef2cuQOpymjWNQi5V0rKHE6PC2ru3HkoUr/Br2/8GUA84w== + "@types/node@>= 8": version "14.14.20" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340" @@ -2527,6 +2610,16 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/sinonjs__fake-timers@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" + integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== + +"@types/sizzle@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" + integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -3203,6 +3296,13 @@ ansi-escapes@^3.0.0, ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== +ansi-escapes@^4.1.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + ansi-escapes@^4.2.1: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" @@ -3275,6 +3375,13 @@ anymatch@^3.0.3, anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" +app-path@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/app-path/-/app-path-3.2.0.tgz#06d426e0c988885264e0aa0a766dfa2723491633" + integrity sha512-PQPaKXi64FZuofJkrtaO3I5RZESm9Yjv7tkeJaDz4EZMeBBfGtr5MyQ3m5AC7F0HVrISBLatPxAAAgvbe418fQ== + dependencies: + execa "^1.0.0" + app-root-dir@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" @@ -3285,6 +3392,11 @@ aproba@^1.0.3, aproba@^1.1.1: resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +arch@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -3501,6 +3613,11 @@ async@^2.1.4: dependencies: lodash "^4.17.11" +async@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -3561,6 +3678,13 @@ axios@0.19.0: follow-redirects "1.5.10" is-buffer "^2.0.2" +axios@^0.21.1: + version "0.21.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" + integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== + dependencies: + follow-redirects "^1.10.0" + babel-code-frame@^6.22.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -3973,6 +4097,11 @@ base64-js@^1.0.2: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== +base64-js@^1.2.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -4013,6 +4142,11 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== +blob-util@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" @@ -4025,6 +4159,11 @@ bluebird@^3.3.5, bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.9" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" @@ -4223,6 +4362,11 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -4300,6 +4444,11 @@ cacheable-request@^2.1.1: normalize-url "2.0.1" responselike "1.0.2" +cachedir@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" @@ -4518,6 +4667,11 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= +check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + "chokidar@>=2.0.0 <4.0.0": version "3.4.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" @@ -4655,6 +4809,13 @@ cli-boxes@^2.2.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.0.tgz#538ecae8f9c6ca508e3c3c95b453fe93cb4c168d" integrity sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w== +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= + dependencies: + restore-cursor "^1.0.1" + cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -4679,6 +4840,16 @@ cli-table3@0.5.1: optionalDependencies: colors "^1.1.2" +cli-table3@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee" + integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ== + dependencies: + object-assign "^4.1.0" + string-width "^4.2.0" + optionalDependencies: + colors "^1.1.2" + cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -4868,6 +5039,11 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + commander@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff" @@ -4905,7 +5081,7 @@ 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.5.0: +concat-stream@^1.5.0, 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== @@ -5114,6 +5290,13 @@ create-react-context@^0.2.1: fbjs "^0.8.0" gud "^1.0.0" +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -5125,7 +5308,7 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -5350,6 +5533,64 @@ cyclist@~0.2.2: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= +cypress-image-snapshot@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/cypress-image-snapshot/-/cypress-image-snapshot-4.0.1.tgz#59084e713a8d03500c8e053ad7a76f3f18609648" + integrity sha512-PBpnhX/XItlx3/DAk5ozsXQHUi72exybBNH5Mpqj1DVmjq+S5Jd9WE5CRa4q5q0zuMZb2V2VpXHth6MjFpgj9Q== + dependencies: + chalk "^2.4.1" + fs-extra "^7.0.1" + glob "^7.1.3" + jest-image-snapshot "4.2.0" + pkg-dir "^3.0.0" + term-img "^4.0.0" + +cypress@^6.5.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.6.0.tgz#659c64cdb06e51b6be18fdac39d8f192deb54fa0" + integrity sha512-+Xx3Zn653LJHUsCb9h1Keql2jlazbr1ROmbY6DFJMmXKLgXP4ez9cE403W93JNGRbZK0Tng3R/oP8mvd9XAPVg== + dependencies: + "@cypress/listr-verbose-renderer" "^0.4.1" + "@cypress/request" "^2.88.5" + "@cypress/xvfb" "^1.2.4" + "@types/node" "12.12.50" + "@types/sinonjs__fake-timers" "^6.0.1" + "@types/sizzle" "^2.3.2" + arch "^2.1.2" + blob-util "2.0.2" + bluebird "^3.7.2" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-table3 "~0.6.0" + commander "^5.1.0" + common-tags "^1.8.0" + dayjs "^1.9.3" + debug "4.3.2" + eventemitter2 "^6.4.2" + execa "^4.0.2" + executable "^4.1.1" + extract-zip "^1.7.0" + fs-extra "^9.0.1" + getos "^3.2.1" + is-ci "^2.0.0" + is-installed-globally "^0.3.2" + lazy-ass "^1.6.0" + listr "^0.14.3" + lodash "^4.17.19" + log-symbols "^4.0.0" + minimist "^1.2.5" + moment "^2.29.1" + ospath "^1.2.2" + pretty-bytes "^5.4.1" + ramda "~0.27.1" + request-progress "^3.0.0" + supports-color "^7.2.0" + tmp "~0.2.1" + untildify "^4.0.0" + url "^0.11.0" + yauzl "^2.10.0" + "d3-array@^1.2.0 || 2", d3-array@^2.0.3: version "2.2.0" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.2.0.tgz#a9e966b8f8d78f0888d98db1fb840fc8da8ac5c7" @@ -5460,7 +5701,12 @@ date-now@^0.1.4: resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0: +dayjs@^1.9.3: + version "1.10.4" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" + integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw== + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -5474,6 +5720,13 @@ debug@3.2.6, debug@^3.2.5, debug@^3.2.6: dependencies: ms "^2.1.1" +debug@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + debug@=3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -5481,7 +5734,7 @@ debug@=3.1.0: dependencies: ms "2.0.0" -debug@^3.0.1: +debug@^3.0.1, debug@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -6402,6 +6655,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +eventemitter2@^6.4.2: + version "6.4.4" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.4.tgz#aa96e8275c4dbeb017a5d0e03780c65612a1202b" + integrity sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw== + eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -6463,7 +6721,7 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^4.0.0: +execa@^4.0.0, execa@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== @@ -6493,6 +6751,18 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -6609,6 +6879,16 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-zip@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" + integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== + dependencies: + concat-stream "^1.6.2" + debug "^2.6.9" + mkdirp "^0.5.4" + yauzl "^2.10.0" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -6729,6 +7009,13 @@ fbjs@^0.8.0, fbjs@^0.8.1: setimmediate "^1.0.5" ua-parser-js "^0.7.18" +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" @@ -6929,6 +7216,11 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" +follow-redirects@^1.10.0: + version "1.13.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" + integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== + for-in@^0.1.3: version "0.1.8" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" @@ -7028,6 +7320,15 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.0.1.tgz#90294081f978b1f182f347a440a209154344285b" @@ -7047,6 +7348,16 @@ fs-extra@^9.0.0: jsonfile "^6.0.1" universalify "^1.0.0" +fs-extra@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-minipass@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" @@ -7172,6 +7483,11 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= + get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -7206,6 +7522,13 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -7297,6 +7620,13 @@ glob@^7.0.3, glob@^7.1.6, glob@~7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" + integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== + dependencies: + ini "1.3.7" + global-modules@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -7390,6 +7720,11 @@ globule@^1.0.0: lodash "~4.17.10" minimatch "~3.0.2" +glur@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/glur/-/glur-1.1.2.tgz#f20ea36db103bfc292343921f1f91e83c3467689" + integrity sha1-8g6jbbEDv8KSNDkh8fkeg8NGdok= + good-listener@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" @@ -7911,7 +8246,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.5, ini@~1.3.0: +ini@1.3.7, ini@^1.3.5, ini@~1.3.0: version "1.3.7" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== @@ -8235,6 +8570,14 @@ is-hexadecimal@^1.0.0: resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.3.tgz#e8a426a69b6d31470d3a33a47bb825cda02506ee" integrity sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA== +is-installed-globally@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== + dependencies: + global-dirs "^2.0.1" + is-path-inside "^3.0.1" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -8259,6 +8602,11 @@ is-observable@^1.1.0: dependencies: symbol-observable "^1.1.0" +is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -8455,6 +8803,14 @@ isurl@^1.0.0-alpha5: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" +iterm2-version@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/iterm2-version/-/iterm2-version-4.2.0.tgz#b78069f747f34a772bc7dc17bda5bd9ed5e09633" + integrity sha512-IoiNVk4SMPu6uTcK+1nA5QaHNok2BMDLjSl5UomrOixe5g4GkylhPwuiGdw00ysSCrXAKNMfFTu+u/Lk5f6OLQ== + dependencies: + app-path "^3.2.0" + plist "^3.0.1" + java-properties@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/java-properties/-/java-properties-1.0.2.tgz#ccd1fa73907438a5b5c38982269d0e771fe78211" @@ -8610,6 +8966,21 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" +jest-image-snapshot@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/jest-image-snapshot/-/jest-image-snapshot-4.2.0.tgz#559d7ade69e9918517269cef184261c80029a69e" + integrity sha512-6aAqv2wtfOgxiJeBayBCqHo1zX+A12SUNNzo7rIxiXh6W6xYVu8QyHWkada8HeRi+QUTHddp0O0Xa6kmQr+xbQ== + dependencies: + chalk "^1.1.3" + get-stdin "^5.0.1" + glur "^1.1.2" + lodash "^4.17.4" + mkdirp "^0.5.1" + pixelmatch "^5.1.0" + pngjs "^3.4.0" + rimraf "^2.6.2" + ssim.js "^3.1.1" + jest-jasmine2@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" @@ -8884,6 +9255,17 @@ jest@^26.6.3: import-local "^3.0.2" jest-cli "^26.6.3" +joi@^17.3.0: + version "17.4.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.0.tgz#b5c2277c8519e016316e49ababd41a1908d9ef20" + integrity sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.0" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + js-base64@^2.1.8: version "2.6.3" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.3.tgz#7afdb9b57aa7717e15d370b66e8f36a9cb835dc3" @@ -9148,6 +9530,11 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== +lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + lazy-cache@^0.2.3: version "0.2.7" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" @@ -9265,7 +9652,7 @@ listr-verbose-renderer@^0.5.0: date-fns "^1.27.2" figures "^2.0.0" -listr@0.14.3: +listr@0.14.3, listr@^0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== @@ -9500,6 +9887,11 @@ lodash.min@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.min/-/lodash.min-4.0.1.tgz#4ac1b9a8baf8b6d28a690d716512510cfc14708c" integrity sha1-SsG5qLr4ttKKaQ1xZRJRDPwUcIw= +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash.pairs@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash.pairs/-/lodash.pairs-3.0.1.tgz#bbe08d5786eeeaa09a15c91ebf0dcb7d2be326a9" @@ -9552,6 +9944,11 @@ lodash@^4.0.0, lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== +lodash@^4.17.20, lodash@^4.17.4: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log-symbols@2.2.0, log-symbols@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -9573,6 +9970,13 @@ log-symbols@^3.0.0: dependencies: chalk "^2.4.2" +log-symbols@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" + integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== + dependencies: + chalk "^4.0.0" + log-update@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" @@ -10101,7 +10505,7 @@ mkdirp@1.x: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -"mkdirp@>=0.5 0": +"mkdirp@>=0.5 0", mkdirp@^0.5.4: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -10137,7 +10541,7 @@ mocha@^6.1.4: yargs-parser "13.0.0" yargs-unparser "1.5.0" -moment@^2.18.1: +moment@^2.18.1, moment@^2.29.1: version "2.29.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== @@ -10727,6 +11131,11 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -10847,6 +11256,11 @@ osenv@0, osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + p-cancelable@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" @@ -11171,6 +11585,11 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -11191,7 +11610,7 @@ pidtree@^0.3.0: resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.0.tgz#f6fada10fccc9f99bf50e90d0b23d72c9ebc2e6b" integrity sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg== -pify@^2.0.0: +pify@^2.0.0, pify@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= @@ -11225,6 +11644,13 @@ pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" +pixelmatch@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-5.2.1.tgz#9e4e4f4aa59648208a31310306a5bed5522b0d65" + integrity sha512-WjcAdYSnKrrdDdqTcVEY7aB7UhhwjYQKYhHiBXdJef0MOaQeYpUdQ+iVyBLa5YBKS8MPVPPMX7rpOByISLpeEQ== + dependencies: + pngjs "^4.0.1" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -11253,11 +11679,30 @@ pkg-up@3.1.0, pkg-up@^3.1.0: dependencies: find-up "^3.0.0" +plist@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c" + integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ== + dependencies: + base64-js "^1.2.3" + xmlbuilder "^9.0.7" + xmldom "0.1.x" + pluralize@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +pngjs@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" + integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== + +pngjs@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-4.0.1.tgz#f803869bb2fc1bfe1bf99aa4ec21c108117cfdbe" + integrity sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg== + pnp-webpack-plugin@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.4.3.tgz#0a100b63f4a1d09cee6ee55a87393b69f03ab5c7" @@ -11502,6 +11947,11 @@ prettier@^1.19.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== +pretty-bytes@^5.4.1: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + pretty-error@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" @@ -11771,6 +12221,11 @@ ramda@^0.21.0: resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU= +ramda@~0.27.1: + version "0.27.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" + integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -12500,6 +12955,13 @@ replace-ext@1.0.0: resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.0.0" + request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -12673,6 +13135,14 @@ responselike@1.0.2: dependencies: lowercase-keys "^1.0.0" +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -12717,7 +13187,7 @@ rework@1.0.1: convert-source-map "^0.3.3" css "^2.0.0" -rimraf@2: +rimraf@2, rimraf@^2.6.2: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -12789,6 +13259,13 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" +rxjs@^6.6.3: + version "6.6.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" + integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== + dependencies: + tslib "^1.9.0" + s2-geometry@^1.2.10: version "1.2.10" resolved "https://registry.yarnpkg.com/s2-geometry/-/s2-geometry-1.2.10.tgz#c6ff22f3eccafd0eea491b60b44c141b9887acab" @@ -13435,6 +13912,11 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +ssim.js@^3.1.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/ssim.js/-/ssim.js-3.5.0.tgz#d7276b9ee99b57a5ff0db34035f02f35197e62df" + integrity sha512-Aj6Jl2z6oDmgYFFbQqK7fght19bXdOxY7Tj03nF+03M9gCBAjeIiO8/PlEGMfKDwYpw4q6iBqVq2YuREorGg/g== + ssri@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" @@ -13772,7 +14254,7 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -13879,6 +14361,14 @@ telejson@^2.2.1: lodash.get "^4.4.2" memoizerific "^1.11.3" +term-img@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/term-img/-/term-img-4.1.0.tgz#5b170961f7aa20b2f3b22deb8ad504beb963a8a5" + integrity sha512-DFpBhaF5j+2f7kheKFc1ajsAUUDGOaNPpKPtiIMxlbfud6mvfFZuWGnTRpaujUa5J7yl6cIw/h6nyr4mSsENPg== + dependencies: + ansi-escapes "^4.1.0" + iterm2-version "^4.1.0" + term-size@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" @@ -13959,6 +14449,11 @@ throat@^5.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + through2@^2.0.0, through2@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -14008,7 +14503,7 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@^0.2.0: +tmp@^0.2.0, tmp@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== @@ -14234,6 +14729,11 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + type-fest@^0.18.0: version "0.18.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" @@ -14433,6 +14933,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + upath@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" @@ -14659,6 +15164,17 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +wait-on@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-5.2.1.tgz#05b66fcb4d7f5da01537f03e7cf96e8836422996" + integrity sha512-H2F986kNWMU9hKlI9l/ppO6tN8ZSJd35yBljMLa1/vjzWP++Qh6aXyt77/u7ySJFZQqBtQxnvm/xgG48AObXcw== + dependencies: + axios "^0.21.1" + joi "^17.3.0" + lodash "^4.17.20" + minimist "^1.2.5" + rxjs "^6.6.3" + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -15060,11 +15576,21 @@ xmlbuilder@^10.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz#8cae6688cc9b38d850b7c8d3c0a4161dcaf475b0" integrity sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg== +xmlbuilder@^9.0.7: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmldom@0.1.x: + version "0.1.31" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -15250,6 +15776,14 @@ yarn-or-npm@^3.0.1: cross-spawn "^6.0.5" pkg-dir "^4.2.0" +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" From ec4c874eecf04d3f20c975681bb73b9e3e55c6b3 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Fri, 5 Mar 2021 12:56:19 +0100 Subject: [PATCH 02/11] test: cypress - inputKnobs --- stories/cypress/index.d.ts | 2 ++ stories/cypress/integration/test.test.ts | 2 ++ stories/cypress/support/commands.ts | 38 ++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/stories/cypress/index.d.ts b/stories/cypress/index.d.ts index 04705a2c..67c65552 100644 --- a/stories/cypress/index.d.ts +++ b/stories/cypress/index.d.ts @@ -9,6 +9,7 @@ import { getByTestIDSubStr, visitTest, snapshotComponent, + inputKnobs, } from './support/commands' declare global { @@ -22,6 +23,7 @@ declare global { getByTestIDSubStr: typeof getByTestIDSubStr visitTest: typeof visitTest snapshotComponent: typeof snapshotComponent + inputKnobs: typeof inputKnobs } } } diff --git a/stories/cypress/integration/test.test.ts b/stories/cypress/integration/test.test.ts index c9f3f413..14baab0b 100644 --- a/stories/cypress/integration/test.test.ts +++ b/stories/cypress/integration/test.test.ts @@ -2,6 +2,8 @@ describe('testing test', () => { it('should make snapshot of gauge', () => { cy.visitTest('Gauge', 'Gauge') + cy.inputKnobs('Decimal Places', 2) + cy.snapshotComponent('gauge-test-1') }) }) diff --git a/stories/cypress/support/commands.ts b/stories/cypress/support/commands.ts index 192abd3a..c26166b7 100644 --- a/stories/cypress/support/commands.ts +++ b/stories/cypress/support/commands.ts @@ -1,5 +1,5 @@ -import {addMatchImageSnapshotCommand} from 'cypress-image-snapshot/command' -import {Options as ImageSnapshotOptions} from 'cypress-image-snapshot' +import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command' +import { Options as ImageSnapshotOptions } from 'cypress-image-snapshot' addMatchImageSnapshotCommand() @@ -67,6 +67,37 @@ export const snapshotComponent = ( cy.wait(1_000) } +export const inputKnobs: { + (label: string, value: number, range?: true): void + (label: string, value: string): void +} = (label: string, value: number | string, range: boolean = false) => { + const valueStr = typeof value === 'number' ? value.toString() : value + const escapeSpaces = (str: string) => str.replace(/ /, '\\ ') + if (!range) { + cy.get(`#${escapeSpaces(label)}`) + .clear() + .type(valueStr) + } else { + throw new Error('Range input not implemented'); + // todo: input - range + cy.get(`[name='${escapeSpaces(label)}']`) + // .then(x => { + // x.first().trigger("change", {value}) + // }) + // .invoke('val', value) + // .invoke('attr', 'value', value) + // .trigger('input', { force: true, data: value }) + // .trigger('change', { force: true, value }) + // .invoke('mouseup') + // .trigger('blur') + // .type(100) + // .trigger('mousedown', { which: 1 }) + // .trigger('mousemove', { clientX: 0, clientY: 100 }) + // .trigger('mouseup', {force: true}) + cy.wait(1000) + } +} + /* eslint-disable */ // getters @@ -76,8 +107,9 @@ Cypress.Commands.add('getByInputValue', getByInputValue) Cypress.Commands.add('getByTitle', getByTitle) Cypress.Commands.add('getByTestIDSubStr', getByTestIDSubStr) -// helpers +// storybook helpers Cypress.Commands.add('visitTest', visitTest) Cypress.Commands.add('snapshotComponent', snapshotComponent) +Cypress.Commands.add('inputKnobs', inputKnobs) /* eslint-enable */ From 122df97d1d95053cfa71ecfabbe554de296e7974 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 8 Mar 2021 10:23:59 +0100 Subject: [PATCH 03/11] test: fixed inputKnobs command --- CONTRIBUTING.md | 4 ++++ stories/cypress/integration/test.test.ts | 8 +++++++- stories/cypress/support/commands.ts | 6 +++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e131514e..28d8fd8f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,3 +61,7 @@ Then from the root of the repo, run the publish script: InfluxData takes security and our user's trust very seriously. If you believe you have found a security issue in any of our open source projects, please responsibly disclose it by contacting security@influxdata.com. More details about security vulnerability reporting, including our GPG key, [can be found here](https://www.influxdata.com/how-to-report-security-vulnerabilities/). + +## Cypress + + - TODO diff --git a/stories/cypress/integration/test.test.ts b/stories/cypress/integration/test.test.ts index 14baab0b..f7411046 100644 --- a/stories/cypress/integration/test.test.ts +++ b/stories/cypress/integration/test.test.ts @@ -2,8 +2,14 @@ describe('testing test', () => { it('should make snapshot of gauge', () => { cy.visitTest('Gauge', 'Gauge') + cy.snapshotComponent('gauge-test-1') + cy.inputKnobs('Decimal Places', 2) - cy.snapshotComponent('gauge-test-1') + cy.snapshotComponent('gauge-test-2-decimal-places') + + cy.inputKnobs('Gauge Min', 20) + + cy.snapshotComponent('gauge-test-start-20') }) }) diff --git a/stories/cypress/support/commands.ts b/stories/cypress/support/commands.ts index c26166b7..0f9c9d2c 100644 --- a/stories/cypress/support/commands.ts +++ b/stories/cypress/support/commands.ts @@ -74,9 +74,9 @@ export const inputKnobs: { const valueStr = typeof value === 'number' ? value.toString() : value const escapeSpaces = (str: string) => str.replace(/ /, '\\ ') if (!range) { - cy.get(`#${escapeSpaces(label)}`) - .clear() - .type(valueStr) + cy.get(`#${escapeSpaces(label)}, [name='${label}']`) + .clear({force: true}) + .type(valueStr, {force: true}) } else { throw new Error('Range input not implemented'); // todo: input - range From 91fe6bfc4f01744f92b8d111b7b188dc165d00a5 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 8 Mar 2021 11:08:52 +0100 Subject: [PATCH 04/11] test: using select knobs --- stories/cypress/integration/test.test.ts | 5 ++++ stories/cypress/support/commands.ts | 38 +++++++++++++++--------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/stories/cypress/integration/test.test.ts b/stories/cypress/integration/test.test.ts index f7411046..12b0de9b 100644 --- a/stories/cypress/integration/test.test.ts +++ b/stories/cypress/integration/test.test.ts @@ -1,4 +1,9 @@ describe('testing test', () => { + it.only('should make snapsot of band chart', () => { + cy.visitTest('Band Chart', 'Static: groupBy applied') + cy.inputKnobs('Time Format', 'HH:mm', 'select') + }) + it('should make snapshot of gauge', () => { cy.visitTest('Gauge', 'Gauge') diff --git a/stories/cypress/support/commands.ts b/stories/cypress/support/commands.ts index 0f9c9d2c..e9d1e002 100644 --- a/stories/cypress/support/commands.ts +++ b/stories/cypress/support/commands.ts @@ -36,8 +36,8 @@ export const visitTest = (name: string, storyname: string) => { /** format string to stories url format */ const f = (str: string) => str - .replace(' ', '-') - .replace(':', '') + .replace(/ /g, '-') + .replace(/:/g, '') .toLowerCase() cy.visit(`${f(name)}--${f(storyname)}`) } @@ -68,19 +68,26 @@ export const snapshotComponent = ( } export const inputKnobs: { - (label: string, value: number, range?: true): void (label: string, value: string): void -} = (label: string, value: number | string, range: boolean = false) => { - const valueStr = typeof value === 'number' ? value.toString() : value + (label: string, value: string, type: 'select'): void +} = (label: string, value: number | string, type: 'range' | 'select' | 'number' | 'text' = 'text') => { const escapeSpaces = (str: string) => str.replace(/ /, '\\ ') - if (!range) { - cy.get(`#${escapeSpaces(label)}, [name='${label}']`) - .clear({force: true}) - .type(valueStr, {force: true}) - } else { - throw new Error('Range input not implemented'); - // todo: input - range - cy.get(`[name='${escapeSpaces(label)}']`) + const selector = `#${escapeSpaces(label)}, [name='${label}']`; + switch (type) { + case 'text': + const valueStr = typeof value === 'number' ? value.toString() : value + cy.get(selector) + .clear({ force: true }) + .type(valueStr, { force: true }) + break + case 'select': + cy.get(selector) + .select(value as string) + break + case 'range': + throw new Error(`Input knobs of type ${type} not implemented`) + // todo: input - range + cy.get(`[name='${escapeSpaces(label)}']`) // .then(x => { // x.first().trigger("change", {value}) // }) @@ -94,7 +101,10 @@ export const inputKnobs: { // .trigger('mousedown', { which: 1 }) // .trigger('mousemove', { clientX: 0, clientY: 100 }) // .trigger('mouseup', {force: true}) - cy.wait(1000) + cy.wait(1000) + break + default: + throw new Error(`Input knobs of type ${type} not implemented`) } } From 6fa084611f0229c84bed98cc037cb10c6ce6da2d Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 8 Mar 2021 12:56:40 +0100 Subject: [PATCH 05/11] test: ability to fix random values --- stories/src/annotation.stories.tsx | 54 ++++++++++---------- stories/src/customLayer.stories.tsx | 23 +++++---- stories/src/data/annotation.ts | 33 ++++++------ stories/src/data/bandLayer.ts | 47 +++++++++-------- stories/src/data/gaugeLayer.ts | 33 ++++-------- stories/src/data/geoLayer.ts | 51 ++++++++----------- stories/src/data/randomTable.ts | 15 +++--- stories/src/data/singleStatLayer.ts | 34 ++++++------- stories/src/data/stackedLineLayer.ts | 75 ++++++++++++++-------------- stories/src/data/utils.ts | 20 ++++++++ stories/src/gauge.stories.tsx | 21 ++++---- stories/src/geo.stories.tsx | 20 +++++--- stories/src/index.stories.tsx | 5 +- stories/src/linegraph.stories.tsx | 4 +- stories/src/utilities.stories.tsx | 5 +- 15 files changed, 219 insertions(+), 221 deletions(-) create mode 100644 stories/src/data/utils.ts diff --git a/stories/src/annotation.stories.tsx b/stories/src/annotation.stories.tsx index 542ea71d..be2255bb 100644 --- a/stories/src/annotation.stories.tsx +++ b/stories/src/annotation.stories.tsx @@ -1,8 +1,8 @@ import * as React from 'react' -import {storiesOf} from '@storybook/react' -import {boolean, number, select, text, withKnobs} from '@storybook/addon-knobs' -import {Config, Plot, LayerConfig, timeFormatter} from '../../giraffe/src' -import {TIME, VALUE} from '../../giraffe/src/constants/columnKeys' +import { storiesOf } from '@storybook/react' +import { boolean, number, select, text, withKnobs } from '@storybook/addon-knobs' +import { Config, Plot, LayerConfig, timeFormatter } from '../../giraffe/src' +import { TIME, VALUE } from '../../giraffe/src/constants/columnKeys' import { PlotContainer, @@ -22,12 +22,12 @@ import { annotationPinKnob, } from './helpers' -import {annotationsTable, matchAnnotationsToTable} from './data/annotation' +import { matchAnnotationsToTable, getAnnotationsTable } from './data/annotation' storiesOf('Annotations', module) .addDecorator(withKnobs) .add('Annotations: mark at every point', () => { - const table = annotationsTable + const table = getAnnotationsTable(boolean('Fixed data', false)) const includeLineLayer = boolean('Line Layer', false) const annotationColor = text('Annotation color string', 'green') const annotationDimension = select( @@ -73,7 +73,7 @@ storiesOf('Annotations', module) const yTotalTicks = number('Y Total Ticks', 10) const position = select( 'Line Position', - {stacked: 'stacked', overlaid: 'overlaid'}, + { stacked: 'stacked', overlaid: 'overlaid' }, 'overlaid' ) const interpolation = interpolationKnob() @@ -81,7 +81,7 @@ storiesOf('Annotations', module) const lineWidth = number('Line Width', 1) const hoverDimension = select( 'Hover Dimension', - {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, + { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, 'auto' ) @@ -122,10 +122,9 @@ storiesOf('Annotations', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({timeZone, format: timeFormat}), + _time: timeFormatter({ timeZone, format: timeFormat }), _value: val => - `${val.toFixed(2)}${ - valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, @@ -145,7 +144,7 @@ storiesOf('Annotations', module) ) }) .add('Annotations: overridden double click behavior', () => { - const table = annotationsTable + const table = getAnnotationsTable(boolean('Fixed data', false)) const includeLineLayer = boolean('Line Layer', false) const annotationColor = text('Annotation color string', 'green') const annotationDimension = select( @@ -190,7 +189,7 @@ storiesOf('Annotations', module) const yTotalTicks = number('Y Total Ticks', 10) const position = select( 'Line Position', - {stacked: 'stacked', overlaid: 'overlaid'}, + { stacked: 'stacked', overlaid: 'overlaid' }, 'overlaid' ) const interpolation = interpolationKnob() @@ -198,7 +197,7 @@ storiesOf('Annotations', module) const lineWidth = number('Line Width', 1) const hoverDimension = select( 'Hover Dimension', - {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, + { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, 'auto' ) @@ -256,10 +255,9 @@ storiesOf('Annotations', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({timeZone, format: timeFormat}), + _time: timeFormatter({ timeZone, format: timeFormat }), _value: val => - `${val.toFixed(2)}${ - valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, @@ -280,7 +278,7 @@ storiesOf('Annotations', module) ) }) .add('Annotations: selectable marks', () => { - const table = annotationsTable + const table = getAnnotationsTable(boolean('Fixed data', false)) const includeLineLayer = boolean('Line Layer', true) const annotationColor = text('Annotation color string', 'green') const annotationDimension = select( @@ -326,7 +324,7 @@ storiesOf('Annotations', module) const yTotalTicks = number('Y Total Ticks', 10) const position = select( 'Line Position', - {stacked: 'stacked', overlaid: 'overlaid'}, + { stacked: 'stacked', overlaid: 'overlaid' }, 'overlaid' ) const interpolation = interpolationKnob() @@ -334,7 +332,7 @@ storiesOf('Annotations', module) const lineWidth = number('Line Width', 1) const hoverDimension = select( 'Hover Dimension', - {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, + { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, 'auto' ) @@ -392,10 +390,9 @@ storiesOf('Annotations', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({timeZone, format: timeFormat}), + _time: timeFormatter({ timeZone, format: timeFormat }), _value: val => - `${val.toFixed(2)}${ - valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, @@ -415,7 +412,7 @@ storiesOf('Annotations', module) ) }) .add('Annotations: add your own marks', () => { - const table = annotationsTable + const table = getAnnotationsTable(boolean('Fixed data', false)) const includeLineLayer = boolean('Line Layer', true) const annotationColor = text('Annotation color string', 'green') const annotationDimension = select( @@ -472,7 +469,7 @@ storiesOf('Annotations', module) const yTotalTicks = number('Y Total Ticks', 10) const position = select( 'Line Position', - {stacked: 'stacked', overlaid: 'overlaid'}, + { stacked: 'stacked', overlaid: 'overlaid' }, 'overlaid' ) const interpolation = interpolationKnob() @@ -480,7 +477,7 @@ storiesOf('Annotations', module) const lineWidth = number('Line Width', 1) const hoverDimension = select( 'Hover Dimension', - {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, + { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, 'auto' ) @@ -519,10 +516,9 @@ storiesOf('Annotations', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({timeZone, format: timeFormat}), + _time: timeFormatter({ timeZone, format: timeFormat }), _value: val => - `${val.toFixed(2)}${ - valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, diff --git a/stories/src/customLayer.stories.tsx b/stories/src/customLayer.stories.tsx index 4d3bd8af..5550458d 100644 --- a/stories/src/customLayer.stories.tsx +++ b/stories/src/customLayer.stories.tsx @@ -1,6 +1,6 @@ import * as React from 'react' -import {storiesOf} from '@storybook/react' -import {boolean, number, select, text, withKnobs} from '@storybook/addon-knobs' +import { storiesOf } from '@storybook/react' +import { boolean, number, select, text, withKnobs } from '@storybook/addon-knobs' import { Config, Plot, @@ -26,8 +26,8 @@ import { tooltipColorizeRowsKnob, } from './helpers' -import {CPU} from './data/cpu' -import {singleStatTable} from './data/singleStatLayer' +import { CPU } from './data/cpu' +import { getSingleStatTable } from './data/singleStatLayer' storiesOf('Custom Layer', module) .addDecorator(withKnobs) @@ -42,7 +42,7 @@ storiesOf('Custom Layer', module) }, { type: 'custom', - render: ({key, yScale}) => { + render: ({ key, yScale }) => { return (
{ + const fixed = boolean('Fixed data', false) + const singleStatTable = getSingleStatTable(fixed) const decimalPlaces = Number(text('Decimal Places', '4')) const textOpacity = number('Single Stat Opacity', 1, { range: true, @@ -123,6 +125,8 @@ storiesOf('Custom Layer', module) ) }) .add('Single Stat on top of Line Layer', () => { + const fixed = boolean('Fixed data', false) + const singleStatTable = getSingleStatTable(fixed) const includeSingleStatLayer = boolean('Single Stat', true) const decimalPlaces = Number(text('Decimal Places', '2')) const textOpacity = number('Single Stat Opacity', 1, { @@ -181,7 +185,7 @@ storiesOf('Custom Layer', module) const fill = fillKnob(table, ['cpu']) const position = select( 'Line Position', - {stacked: 'stacked', overlaid: 'overlaid'}, + { stacked: 'stacked', overlaid: 'overlaid' }, 'overlaid' ) const interpolation = interpolationKnob() @@ -191,7 +195,7 @@ storiesOf('Custom Layer', module) const shadeBelowOpacity = number('Area Opacity', 0.1) const hoverDimension = select( 'Hover Dimension', - {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, + { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, 'auto' ) const legendOrientationThreshold = tooltipOrientationThresholdKnob() @@ -234,10 +238,9 @@ storiesOf('Custom Layer', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({timeZone, format: timeFormat}), + _time: timeFormatter({ timeZone, format: timeFormat }), _value: val => - `${val.toFixed(2)}${ - valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, diff --git a/stories/src/data/annotation.ts b/stories/src/data/annotation.ts index 63dba6fa..456d57ba 100644 --- a/stories/src/data/annotation.ts +++ b/stories/src/data/annotation.ts @@ -5,32 +5,31 @@ import { Table, } from '../../../giraffe/src/types' import {newTable} from '../../../giraffe/src' +import { getRandomOrFixed, nowOrFixed } from "./utils" -const now = Date.now() const numberOfRecords = 20 const recordsPerLine = 20 const maxValue = 10 -const TIME_COL = [] -const VALUE_COL = [] -const CPU_COL = [] - const DEFAULT_COLOR = 'green' -function getRandomNumber(max) { - return Math.random() * Math.floor(max) -} -for (let i = 0; i < numberOfRecords; i += 1) { - VALUE_COL.push(getRandomNumber(maxValue)) - CPU_COL.push(`cpu${Math.floor(i / recordsPerLine)}`) - TIME_COL.push(now + (i % recordsPerLine) * 1000 * 60) +export const getAnnotationsTable = (fixed: boolean) => { + const TIME_COL = [] + const VALUE_COL = [] + const CPU_COL = [] + + for (let i = 0; i < numberOfRecords; i += 1) { + VALUE_COL.push(getRandomOrFixed(fixed, i, maxValue)) + CPU_COL.push(`cpu${Math.floor(i / recordsPerLine)}`) + TIME_COL.push(nowOrFixed(fixed) + (i % recordsPerLine) * 1000 * 60) + } + + return newTable(numberOfRecords) + .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) + .addColumn('_value', 'system', 'number', VALUE_COL) + .addColumn('cpu', 'string', 'string', CPU_COL) } -export const annotationsTable = newTable(numberOfRecords) - .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) - .addColumn('_value', 'system', 'number', VALUE_COL) - .addColumn('cpu', 'string', 'string', CPU_COL) - interface SampleAnnotationsCreatorOptions { color: string dimension: AnnotationDimension diff --git a/stories/src/data/bandLayer.ts b/stories/src/data/bandLayer.ts index d6165b53..2bfdb0ed 100644 --- a/stories/src/data/bandLayer.ts +++ b/stories/src/data/bandLayer.ts @@ -1,33 +1,32 @@ -import {newTable} from '../../../giraffe/src' +import { newTable } from '../../../giraffe/src' +import { getRandomOrFixed, nowOrFixed } from "./utils" -const now = Date.now() const numberOfRecords = 40 const recordsPerLine = 20 const maxValue = 100 -const TIME_COL = [] -const VALUE_COL = [] -const CPU_COL = [] -const FIELD_COL = [] -const MEASUREMENT_COL = [] - const fieldName = 'usage_system' const measurementName = 'cpu' -function getRandomNumber(max) { - return Math.random() * Math.floor(max) -} -for (let i = 0; i < numberOfRecords; i += 1) { - FIELD_COL.push(fieldName) - MEASUREMENT_COL.push(measurementName) - VALUE_COL.push(getRandomNumber(maxValue)) - CPU_COL.push(`cpu${Math.floor(i / recordsPerLine)}`) - TIME_COL.push(now + (i % recordsPerLine) * 1000 * 60) -} +export const getBandTable = (fixed: boolean) => { + const TIME_COL = [] + const VALUE_COL = [] + const CPU_COL = [] + const FIELD_COL = [] + const MEASUREMENT_COL = [] -export const bandTable = newTable(numberOfRecords) - .addColumn('_field', 'string', 'string', FIELD_COL) - .addColumn('_measurement', 'string', 'string', MEASUREMENT_COL) - .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) - .addColumn('_value', 'system', 'number', VALUE_COL) - .addColumn('cpu', 'string', 'string', CPU_COL) + for (let i = 0; i < numberOfRecords; i += 1) { + FIELD_COL.push(fieldName) + MEASUREMENT_COL.push(measurementName) + VALUE_COL.push(getRandomOrFixed(fixed, i, maxValue)) + CPU_COL.push(`cpu${Math.floor(i / recordsPerLine)}`) + TIME_COL.push(nowOrFixed(fixed) + (i % recordsPerLine) * 1000 * 60) + } + + return newTable(numberOfRecords) + .addColumn('_field', 'string', 'string', FIELD_COL) + .addColumn('_measurement', 'string', 'string', MEASUREMENT_COL) + .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) + .addColumn('_value', 'system', 'number', VALUE_COL) + .addColumn('cpu', 'string', 'string', CPU_COL) +} diff --git a/stories/src/data/gaugeLayer.ts b/stories/src/data/gaugeLayer.ts index f69f1cde..b70596c1 100644 --- a/stories/src/data/gaugeLayer.ts +++ b/stories/src/data/gaugeLayer.ts @@ -1,30 +1,19 @@ -import {newTable, Table} from '../../../giraffe/src' -import memoizeOne from 'memoize-one' +import { newTable, Table } from '../../../giraffe/src' +import { getRandomOrFixed, nowOrFixed } from "./utils" -const now = Date.now() const numberOfRecords = 20 const recordsPerLine = 20 -let TIME_COL: Array -let VALUE_COL: Array +export const getGaugeTable = (fixed: boolean, minValue: number, maxValue: number) => { + const TIME_COL: Array = [] + const VALUE_COL: Array = [] -function getRandomNumber(min: number, max: number) { - return Math.random() * (max - min) + min -} -const createColumns = (minValue: number, maxValue: number) => { - TIME_COL = [] - VALUE_COL = [] for (let i = 0; i < numberOfRecords; i += 1) { - VALUE_COL.push(getRandomNumber(minValue, maxValue)) - TIME_COL.push(now + (i % recordsPerLine) * 1000 * 60) + VALUE_COL.push(getRandomOrFixed(fixed, i, minValue, maxValue)) + TIME_COL.push(nowOrFixed(fixed) + (i % recordsPerLine) * 1000 * 60) } -} -export const gaugeTable = memoizeOne( - (minValue: number, maxValue: number): Table => { - createColumns(minValue, maxValue) - return newTable(numberOfRecords) - .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) - .addColumn('_value', 'system', 'number', VALUE_COL) - } -) + return newTable(numberOfRecords) + .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) + .addColumn('_value', 'system', 'number', VALUE_COL) +} diff --git a/stories/src/data/geoLayer.ts b/stories/src/data/geoLayer.ts index aa6530f4..5a53a74e 100644 --- a/stories/src/data/geoLayer.ts +++ b/stories/src/data/geoLayer.ts @@ -1,57 +1,46 @@ -import {newTable, Table} from '../../../giraffe/src' +import { newTable, Table } from '../../../giraffe/src' import memoizeOne from 'memoize-one' +import { getRandomOrFixed, nowOrFixed } from "./utils" -const now = Date.now() - -function getRandomNumber(center: number, spread: number) { - return center + (0.5 - Math.random()) * spread * 2 -} - -const createDataColumns = (numberOfRecords: number) => { +export const getGeoTable = (fixed: boolean, numberOfRecords = 200) => { const TIME_COL = [] const VALUE1_COL = [] const VALUE2_COL = [] const LAT_COL = [] const LON_COL = [] for (let i = 0; i < numberOfRecords; i += 1) { - VALUE1_COL.push(getRandomNumber(3.5, 3.5)) - VALUE2_COL.push(getRandomNumber(50, 30)) - LAT_COL.push(getRandomNumber(40, 3)) - LON_COL.push(getRandomNumber(-78, 3)) - TIME_COL.push(now + i * 1000 * 60) + VALUE1_COL.push(getRandomOrFixed(fixed, i, 0, 7)) + VALUE2_COL.push(getRandomOrFixed(fixed, i, 20, 80)) + LAT_COL.push(getRandomOrFixed(fixed, i, 37, 43)) + LON_COL.push(getRandomOrFixed(fixed, i, -81, -75)) + TIME_COL.push(nowOrFixed(fixed) + i * 1000 * 60) } - return {TIME_COL, VALUE1_COL, VALUE2_COL, LAT_COL, LON_COL} -} -export const geoTable = memoizeOne( - (numberOfRecords = 200): Table => { - const columns = createDataColumns(numberOfRecords) - return newTable(numberOfRecords) - .addColumn('_time', 'dateTime:RFC3339', 'time', columns.TIME_COL) - .addColumn('magnitude', 'double', 'number', columns.VALUE1_COL) - .addColumn('duration', 'double', 'number', columns.VALUE2_COL) - .addColumn('lat', 'double', 'number', columns.LAT_COL) - .addColumn('lon', 'double', 'number', columns.LON_COL) - } -) + return newTable(numberOfRecords) + .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) + .addColumn('magnitude', 'double', 'number', VALUE1_COL) + .addColumn('duration', 'double', 'number', VALUE2_COL) + .addColumn('lat', 'double', 'number', LAT_COL) + .addColumn('lon', 'double', 'number', LON_COL) +} -const addTrack = (data, startLat: number, startLon: number) => { +const addTrack = (fixed: boolean, data, startLat: number, startLon: number) => { const tid = Math.floor(Math.random() * 1000) let lat = startLat, lon = startLon for (let i = 0; i < 10; i++) { - const time = now + i * 1000 * 60 + const time = nowOrFixed(fixed) + i * 1000 * 60 lat += Math.random() * 1.5 lon += Math.random() * 1.5 - data.push({time, lat, lon, tid}) + data.push({ time, lat, lon, tid }) } } export const geoTracks = memoizeOne( - (lon: number, lat: number, count = 1): Table => { + (fixed: boolean, lon: number, lat: number, count = 1): Table => { const data = [] for (let i = 0; i < count; i++) { - addTrack(data, lat - 4, lon - 6) + addTrack(fixed, data, lat - 4, lon - 6) } return newTable(data.length) .addColumn( diff --git a/stories/src/data/randomTable.ts b/stories/src/data/randomTable.ts index 6adf6f55..d41f914f 100644 --- a/stories/src/data/randomTable.ts +++ b/stories/src/data/randomTable.ts @@ -1,17 +1,14 @@ -import {newTable} from '../../../giraffe/src/utils/newTable' +import { newTable } from '../../../giraffe/src/utils/newTable' import memoizeOne from 'memoize-one' +import { getRandomOrFixed, nowOrFixed } from "./utils" -const now = Date.now() const defaultNumberOfRecords = 80 const defaultRecordsPerLine = 20 -function getRandomNumber(max) { - return Math.random() * Math.floor(max) - max / 2 -} - export const getRandomTable = memoizeOne( ( - maxValue: number, + fixed: boolean, + maxValue: number = 200, numberOfRecords: number = defaultNumberOfRecords, recordsPerLine: number = defaultRecordsPerLine ) => { @@ -20,9 +17,9 @@ export const getRandomTable = memoizeOne( const cpuColumn = [] for (let i = 0; i < numberOfRecords; i += 1) { - valueColumn.push(getRandomNumber(maxValue)) + valueColumn.push(getRandomOrFixed(fixed, i, maxValue)) cpuColumn.push(`cpu${Math.floor(i / recordsPerLine)}`) - timeColumn.push(now + (i % recordsPerLine) * 1000 * 60) + timeColumn.push(nowOrFixed(fixed) + (i % recordsPerLine) * 1000 * 60) } return newTable(numberOfRecords) diff --git a/stories/src/data/singleStatLayer.ts b/stories/src/data/singleStatLayer.ts index 3418f4f8..0d7e9ca0 100644 --- a/stories/src/data/singleStatLayer.ts +++ b/stories/src/data/singleStatLayer.ts @@ -1,24 +1,24 @@ -import {newTable} from '../../../giraffe/src' +import { newTable } from '../../../giraffe/src' +import { getRandomOrFixed, nowOrFixed } from "./utils" -const now = Date.now() const numberOfRecords = 20 const recordsPerLine = 20 const maxValue = 10 -const TIME_COL = [] -const VALUE_COL = [] -const CPU_COL = [] +export const getSingleStatTable = (fixed: boolean) => { + const TIME_COL = [] + const VALUE_COL = [] + const CPU_COL = [] -function getRandomNumber(max) { - return Math.random() * Math.floor(max) -} -for (let i = 0; i < numberOfRecords; i += 1) { - VALUE_COL.push(getRandomNumber(maxValue)) - CPU_COL.push(`cpu${Math.floor(i / recordsPerLine)}`) - TIME_COL.push(now + (i % recordsPerLine) * 1000 * 60) -} + for (let i = 0; i < numberOfRecords; i += 1) { + VALUE_COL.push(getRandomOrFixed(fixed, i, maxValue)) + CPU_COL.push(`cpu${Math.floor(i / recordsPerLine)}`) + TIME_COL.push(nowOrFixed(fixed) + (i % recordsPerLine) * 1000 * 60) + } -export const singleStatTable = newTable(numberOfRecords) - .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) - .addColumn('_value', 'system', 'number', VALUE_COL) - .addColumn('cpu', 'string', 'string', CPU_COL) + return newTable(numberOfRecords) + .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) + .addColumn('_value', 'system', 'number', VALUE_COL) + .addColumn('cpu', 'string', 'string', CPU_COL) + +} diff --git a/stories/src/data/stackedLineLayer.ts b/stories/src/data/stackedLineLayer.ts index 9b9a8be8..e8729f94 100644 --- a/stories/src/data/stackedLineLayer.ts +++ b/stories/src/data/stackedLineLayer.ts @@ -1,6 +1,6 @@ -import {newTable} from '../../../giraffe/src' +import { newTable } from '../../../giraffe/src' +import { getRandomOrFixed, nowOrFixed } from "./utils" -const now = Date.now() const numberOfRecords = 80 const recordsPerLine = 20 const maxValue = 100 @@ -35,49 +35,48 @@ const alphabet = [ 'z', ] -const TIME_COL = [] -const VALUE_COL = [] -const CPU_COL = [] -const TEST_COL_A = [] -const TEST_COL_B = [] -const TEST_COL_C = [] -const TEST_COL_D = [] - -function getRandomNumber(max) { - return Math.random() * Math.floor(max) -} - -function getRandomAlphabetChar() { - const index = Math.floor(Math.random() * (alphabet.length - 1)) - return alphabet[index] -} +function getRandomString(fixed: boolean, i: number) { + function getRandomAlphabetChar(ii: number) { + const index = Math.floor(getRandomOrFixed(fixed, ii + i, alphabet.length - 1)) + return alphabet[index] + } -function getRandomString() { - const stringLength = getRandomNumber(maxStringLength) + const stringLength = getRandomOrFixed(fixed, i, maxStringLength) let randomString = '' for (let i = 0; i <= stringLength; i++) { - randomString += getRandomAlphabetChar() + randomString += getRandomAlphabetChar(i) } return randomString } -for (let i = 0; i < numberOfRecords; i += 1) { - VALUE_COL.push(getRandomNumber(maxValue)) - CPU_COL.push(`cpu${Math.floor(i / recordsPerLine)}`) - TIME_COL.push(now + (i % recordsPerLine) * 1000 * 60) - TEST_COL_A.push(getRandomString()) - TEST_COL_B.push(getRandomString()) - TEST_COL_C.push(getRandomString()) - TEST_COL_D.push(getRandomString()) -} +export const getStackedLineTable = (fixed: boolean) => { + const TIME_COL = [] + const VALUE_COL = [] + const CPU_COL = [] + const TEST_COL_A = [] + const TEST_COL_B = [] + const TEST_COL_C = [] + const TEST_COL_D = [] + + for (let i = 0; i < numberOfRecords; i += 1) { + VALUE_COL.push(getRandomOrFixed(fixed, i, maxValue)) + CPU_COL.push(`cpu${Math.floor(i / recordsPerLine)}`) + TIME_COL.push(nowOrFixed(fixed) + (i % recordsPerLine) * 1000 * 60) + TEST_COL_A.push(getRandomString(fixed, i * 4 + 0)) + TEST_COL_B.push(getRandomString(fixed, i * 4 + 1)) + TEST_COL_C.push(getRandomString(fixed, i * 4 + 2)) + TEST_COL_D.push(getRandomString(fixed, i * 4 + 3)) + } + + return newTable(numberOfRecords) + .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) + .addColumn('_value', 'system', 'number', VALUE_COL) + .addColumn('cpu', 'string', 'string', CPU_COL) + .addColumn('test_col_a', 'string', 'string', TEST_COL_A) + .addColumn('test_col_b', 'string', 'string', TEST_COL_B) + .addColumn('test_col_c', 'string', 'string', TEST_COL_C) + .addColumn('test_col_d', 'string', 'string', TEST_COL_D) -export const stackedLineTable = newTable(numberOfRecords) - .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) - .addColumn('_value', 'system', 'number', VALUE_COL) - .addColumn('cpu', 'string', 'string', CPU_COL) - .addColumn('test_col_a', 'string', 'string', TEST_COL_A) - .addColumn('test_col_b', 'string', 'string', TEST_COL_B) - .addColumn('test_col_c', 'string', 'string', TEST_COL_C) - .addColumn('test_col_d', 'string', 'string', TEST_COL_D) +} \ No newline at end of file diff --git a/stories/src/data/utils.ts b/stories/src/data/utils.ts new file mode 100644 index 00000000..7e51d780 --- /dev/null +++ b/stories/src/data/utils.ts @@ -0,0 +1,20 @@ +// utilities for fixing data sources of stories for regression testing + +export const getRandomOrFixed: { + (fixed: boolean, index: number, minValue: number, maxValue: number): number + (fixed: boolean, index: number, maxValue: number): number +} = (fixed: boolean, index: number, minValue: number, maxValue?: number) => { + if (maxValue === undefined) { + return getRandomOrFixed(fixed, index, 0, minValue) + } + + if (fixed) { + return (((Math.sin(index) + 1) / 2)) * (maxValue - minValue) + minValue + } else { + return Math.random() * (maxValue - minValue) + minValue + } +} + +export const nowOrFixed = (fixed: boolean) => { + return fixed ? 0 : Date.now() +} diff --git a/stories/src/gauge.stories.tsx b/stories/src/gauge.stories.tsx index 73c65016..6eb3f79b 100644 --- a/stories/src/gauge.stories.tsx +++ b/stories/src/gauge.stories.tsx @@ -1,15 +1,16 @@ import * as React from 'react' -import {storiesOf} from '@storybook/react' -import {withKnobs, number, text} from '@storybook/addon-knobs' -import {Config, Plot, GaugeTheme} from '../../giraffe/src' -import {DEFAULT_GAUGE_COLORS} from '../../giraffe/src' +import { storiesOf } from '@storybook/react' +import { withKnobs, number, text, boolean } from '@storybook/addon-knobs' +import { Config, Plot, GaugeTheme } from '../../giraffe/src' +import { DEFAULT_GAUGE_COLORS } from '../../giraffe/src' -import {PlotContainer} from './helpers' -import {gaugeTable} from './data/gaugeLayer' +import { PlotContainer } from './helpers' +import { getGaugeTable } from './data/gaugeLayer' storiesOf('Gauge', module) .addDecorator(withKnobs) .add('Gauge', () => { + const fixed = boolean('Fixed data', false) const decimalPlaces = Number(text('Decimal Places', '4')) const lineCount = Number(text('Gauge Lines', '6')) const smallLineCount = Number(text('Ticks between lines', '10')) @@ -37,7 +38,7 @@ storiesOf('Gauge', module) max: 200, step: 1, }) - const gaugeMin = Number(text('Gauge Min', '0')) + const gaugeMin = number('Gauge Min', 0) const gaugeMax = Number(text('Gauge Max', '100')) const prefix = text('Prefix', '') const suffix = text('Suffix', '') @@ -45,7 +46,7 @@ storiesOf('Gauge', module) const tickSuffix = text('TickSuffix', '') const config: Config = { - table: gaugeTable(gaugeMin, gaugeMax), + table: getGaugeTable(fixed, gaugeMin, gaugeMax), layers: [ { type: 'gauge', @@ -58,8 +59,8 @@ storiesOf('Gauge', module) digits: decimalPlaces, }, gaugeColors: [ - {...DEFAULT_GAUGE_COLORS[0], value: gaugeMin}, - {...DEFAULT_GAUGE_COLORS[1], value: gaugeMax}, + { ...DEFAULT_GAUGE_COLORS[0], value: gaugeMin }, + { ...DEFAULT_GAUGE_COLORS[1], value: gaugeMax }, ], gaugeSize, gaugeTheme: { diff --git a/stories/src/geo.stories.tsx b/stories/src/geo.stories.tsx index 9d280ceb..12069741 100644 --- a/stories/src/geo.stories.tsx +++ b/stories/src/geo.stories.tsx @@ -3,7 +3,7 @@ import {storiesOf} from '@storybook/react' import {Config, Plot} from '../../giraffe/src' import {PlotContainer} from './helpers' -import {geoTable, geoTracks} from './data/geoLayer' +import {getGeoTable, geoTracks} from './data/geoLayer' import { boolean, color, @@ -53,6 +53,7 @@ const genericKnobs = () => { } const buildCircleMapStory = tileServerConfiguration => () => { + const fixed = boolean('Fixed data', false) const numberOfRecords = number('Circle count', 26, { range: true, min: 0, @@ -61,7 +62,7 @@ const buildCircleMapStory = tileServerConfiguration => () => { }) const {allowPanAndZoom, latitude, longitude, zoom} = genericKnobs() const config: Config = { - table: geoTable(numberOfRecords), + table: getGeoTable(fixed, numberOfRecords), showAxes: false, layers: [ { @@ -99,6 +100,7 @@ const buildCircleMapStory = tileServerConfiguration => () => { geo.add('Circle Markers', buildCircleMapStory(osmTileServerConfiguration)) geo.add('Map Markers Static', () => { + const fixed = boolean('Fixed data', false) const numberOfRecords = number('Marker count', 20, { range: true, min: 0, @@ -107,7 +109,7 @@ geo.add('Map Markers Static', () => { }) const {allowPanAndZoom, latitude, longitude, zoom} = genericKnobs() const config: Config = { - table: geoTable(numberOfRecords), + table: getGeoTable(fixed, numberOfRecords), showAxes: false, layers: [ { @@ -182,6 +184,7 @@ geo.add('Map Markers Custom CSV', () => { }) geo.add('Marker Clustering', () => { + const fixed = boolean('Fixed data', false) const {allowPanAndZoom, latitude, longitude, zoom} = genericKnobs() const maxClusterRadius = number('Maximum Cluster Radius', 50, { range: true, @@ -201,7 +204,7 @@ geo.add('Marker Clustering', () => { ClusterAggregation.mean ) const config: Config = { - table: geoTable(200), + table: getGeoTable(fixed, 200), showAxes: false, layers: [ { @@ -256,6 +259,7 @@ const heatmapKnobs = () => { } geo.add('Heatmap', () => { + const fixed = boolean('Fixed data', false) const numberOfPoints = number('Data point count', 200, { range: true, min: 0, @@ -265,7 +269,7 @@ geo.add('Heatmap', () => { const {allowPanAndZoom, latitude, longitude, zoom} = genericKnobs() const {radius, blur} = heatmapKnobs() const config: Config = { - table: geoTable(numberOfPoints), + table: getGeoTable(fixed, numberOfPoints), showAxes: false, layers: [ { @@ -333,6 +337,7 @@ const trackKnobs = () => { } geo.add('Tracks', () => { + const fixed = boolean('Fixed data', false) const numberOfTracks = number('Track count', 3, { range: true, min: 0, @@ -350,7 +355,7 @@ geo.add('Tracks', () => { endStopMarkerRadius, } = trackKnobs() const config: Config = { - table: geoTracks(-74, 40, numberOfTracks), + table: geoTracks(fixed, -74, 40, numberOfTracks), showAxes: false, layers: [ { @@ -388,6 +393,7 @@ geo.add('Tracks', () => { }) geo.add('Layering visualizations', () => { + const fixed = boolean('Fixed data', false) const {allowPanAndZoom, latitude, longitude, zoom} = genericKnobs() const { speed, @@ -398,7 +404,7 @@ geo.add('Layering visualizations', () => { endStopMarkerRadius, } = trackKnobs() const config: Config = { - table: geoTracks(-74, 40), + table: geoTracks(fixed, -74, 40), showAxes: false, layers: [ { diff --git a/stories/src/index.stories.tsx b/stories/src/index.stories.tsx index 7dd651e1..c0640e16 100644 --- a/stories/src/index.stories.tsx +++ b/stories/src/index.stories.tsx @@ -3,7 +3,7 @@ import {storiesOf} from '@storybook/react' import {withKnobs, number, select, boolean, text} from '@storybook/addon-knobs' import {Config, Plot, MAGMA, timeFormatter} from '../../giraffe/src' -import {stackedLineTable} from './data/stackedLineLayer' +import {getStackedLineTable} from './data/stackedLineLayer' import { PlotContainer, @@ -27,7 +27,8 @@ import { storiesOf('XY Plot', module) .addDecorator(withKnobs) .add('Stacked Line Layer', () => { - const table = tableKnob(stackedLineTable) + const baseTable = getStackedLineTable(boolean('Fixed data', false)) + const table = tableKnob(baseTable) const colors = colorSchemeKnob() const legendFont = legendFontKnob() const tickFont = tickFontKnob() diff --git a/stories/src/linegraph.stories.tsx b/stories/src/linegraph.stories.tsx index b1282fb5..f9c6e0b6 100644 --- a/stories/src/linegraph.stories.tsx +++ b/stories/src/linegraph.stories.tsx @@ -25,12 +25,10 @@ import { import {tooltipFalsyValues} from './data/fluxCSV' -const maxValue = Math.random() * Math.floor(200) - storiesOf('Line Graph', module) .addDecorator(withKnobs) .add('User defined ticks', () => { - let table = getRandomTable(maxValue) + let table = getRandomTable(boolean('Fixed data', false)) const xTickStart = number('xTickStart', new Date().getTime()) const xTickStep = number('xTickStep', 200_000) const xTotalTicks = number('xTotalTicks', 5) diff --git a/stories/src/utilities.stories.tsx b/stories/src/utilities.stories.tsx index c280f69e..0142f75a 100644 --- a/stories/src/utilities.stories.tsx +++ b/stories/src/utilities.stories.tsx @@ -10,7 +10,7 @@ import { fromFlux, timeFormatter, } from '../../giraffe/src' -import {stackedLineTable} from './data/stackedLineLayer' +import {getStackedLineTable} from './data/stackedLineLayer' import {PlotEnv} from '../../giraffe/src/utils/PlotEnv' @@ -51,7 +51,8 @@ import { storiesOf('Utilities', module) .addDecorator(withKnobs) .add('Screenshot A Stacked Line Layer', () => { - const table = tableKnob(stackedLineTable) + const baseTable = getStackedLineTable(boolean('fixed data', false)) + const table = tableKnob(baseTable) const colors = colorSchemeKnob() const legendFont = legendFontKnob() const tickFont = tickFontKnob() From 9623d6d1506749ea6d295af81b86d1074ff2d683 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 8 Mar 2021 13:16:04 +0100 Subject: [PATCH 06/11] test: inputKnobs checkbox --- stories/cypress/integration/test.test.ts | 11 ++++++++--- stories/cypress/support/commands.ts | 12 +++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/stories/cypress/integration/test.test.ts b/stories/cypress/integration/test.test.ts index 12b0de9b..9dc626ea 100644 --- a/stories/cypress/integration/test.test.ts +++ b/stories/cypress/integration/test.test.ts @@ -1,5 +1,10 @@ describe('testing test', () => { - it.only('should make snapsot of band chart', () => { + it.only('should fix geo data', () => { + cy.visitTest('Geo', 'Circle Markers') + cy.inputKnobs('Fixed data', true, 'boolean') + }) + + it('should make snapsot of band chart', () => { cy.visitTest('Band Chart', 'Static: groupBy applied') cy.inputKnobs('Time Format', 'HH:mm', 'select') }) @@ -9,11 +14,11 @@ describe('testing test', () => { cy.snapshotComponent('gauge-test-1') - cy.inputKnobs('Decimal Places', 2) + cy.inputKnobs('Decimal Places', 2, 'number') cy.snapshotComponent('gauge-test-2-decimal-places') - cy.inputKnobs('Gauge Min', 20) + cy.inputKnobs('Gauge Min', 20, 'number') cy.snapshotComponent('gauge-test-start-20') }) diff --git a/stories/cypress/support/commands.ts b/stories/cypress/support/commands.ts index e9d1e002..02a0e6ad 100644 --- a/stories/cypress/support/commands.ts +++ b/stories/cypress/support/commands.ts @@ -70,12 +70,14 @@ export const snapshotComponent = ( export const inputKnobs: { (label: string, value: string): void (label: string, value: string, type: 'select'): void -} = (label: string, value: number | string, type: 'range' | 'select' | 'number' | 'text' = 'text') => { + (label: string, value: number, type: 'number'): void + (label: string, value: boolean, type: 'boolean'): void +} = (label: string, value: number | string | boolean, type: 'range' | 'select' | 'number' | 'text' | 'boolean' = 'text') => { const escapeSpaces = (str: string) => str.replace(/ /, '\\ ') - const selector = `#${escapeSpaces(label)}, [name='${label}']`; + const selector = `#${escapeSpaces(label)}, [name='${label}']` switch (type) { case 'text': - const valueStr = typeof value === 'number' ? value.toString() : value + const valueStr = value as string cy.get(selector) .clear({ force: true }) .type(valueStr, { force: true }) @@ -84,6 +86,10 @@ export const inputKnobs: { cy.get(selector) .select(value as string) break + case 'boolean': + cy.get(selector + ' input') + [value ? 'check' : 'uncheck']() + break case 'range': throw new Error(`Input knobs of type ${type} not implemented`) // todo: input - range From cd856a2f4189ca8281ef02911021d460814d4e29 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 8 Mar 2021 13:17:54 +0100 Subject: [PATCH 07/11] style: lint:fix --- stories/cypress/support/commands.ts | 54 ++++++++++++++-------------- stories/src/annotation.stories.tsx | 46 +++++++++++++----------- stories/src/customLayer.stories.tsx | 19 +++++----- stories/src/data/annotation.ts | 2 +- stories/src/data/bandLayer.ts | 4 +-- stories/src/data/gaugeLayer.ts | 10 ++++-- stories/src/data/geoLayer.ts | 6 ++-- stories/src/data/randomTable.ts | 4 +-- stories/src/data/singleStatLayer.ts | 5 ++- stories/src/data/stackedLineLayer.ts | 11 +++--- stories/src/data/utils.ts | 2 +- stories/src/gauge.stories.tsx | 16 ++++----- 12 files changed, 95 insertions(+), 84 deletions(-) diff --git a/stories/cypress/support/commands.ts b/stories/cypress/support/commands.ts index 02a0e6ad..67b690ac 100644 --- a/stories/cypress/support/commands.ts +++ b/stories/cypress/support/commands.ts @@ -1,5 +1,5 @@ -import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command' -import { Options as ImageSnapshotOptions } from 'cypress-image-snapshot' +import {addMatchImageSnapshotCommand} from 'cypress-image-snapshot/command' +import {Options as ImageSnapshotOptions} from 'cypress-image-snapshot' addMatchImageSnapshotCommand() @@ -72,43 +72,45 @@ export const inputKnobs: { (label: string, value: string, type: 'select'): void (label: string, value: number, type: 'number'): void (label: string, value: boolean, type: 'boolean'): void -} = (label: string, value: number | string | boolean, type: 'range' | 'select' | 'number' | 'text' | 'boolean' = 'text') => { +} = ( + label: string, + value: number | string | boolean, + type: 'range' | 'select' | 'number' | 'text' | 'boolean' = 'text' +) => { const escapeSpaces = (str: string) => str.replace(/ /, '\\ ') const selector = `#${escapeSpaces(label)}, [name='${label}']` switch (type) { case 'text': const valueStr = value as string cy.get(selector) - .clear({ force: true }) - .type(valueStr, { force: true }) + .clear({force: true}) + .type(valueStr, {force: true}) break case 'select': - cy.get(selector) - .select(value as string) + cy.get(selector).select(value as string) break case 'boolean': - cy.get(selector + ' input') - [value ? 'check' : 'uncheck']() + cy.get(selector + ' input')[value ? 'check' : 'uncheck']() break case 'range': throw new Error(`Input knobs of type ${type} not implemented`) - // todo: input - range - cy.get(`[name='${escapeSpaces(label)}']`) - // .then(x => { - // x.first().trigger("change", {value}) - // }) - // .invoke('val', value) - // .invoke('attr', 'value', value) - // .trigger('input', { force: true, data: value }) - // .trigger('change', { force: true, value }) - // .invoke('mouseup') - // .trigger('blur') - // .type(100) - // .trigger('mousedown', { which: 1 }) - // .trigger('mousemove', { clientX: 0, clientY: 100 }) - // .trigger('mouseup', {force: true}) - cy.wait(1000) - break + // todo: input - range + // cy.get(`[name='${escapeSpaces(label)}']`) + // // .then(x => { + // // x.first().trigger("change", {value}) + // // }) + // // .invoke('val', value) + // // .invoke('attr', 'value', value) + // // .trigger('input', { force: true, data: value }) + // // .trigger('change', { force: true, value }) + // // .invoke('mouseup') + // // .trigger('blur') + // // .type(100) + // // .trigger('mousedown', { which: 1 }) + // // .trigger('mousemove', { clientX: 0, clientY: 100 }) + // // .trigger('mouseup', {force: true}) + // cy.wait(1000) + // break default: throw new Error(`Input knobs of type ${type} not implemented`) } diff --git a/stories/src/annotation.stories.tsx b/stories/src/annotation.stories.tsx index be2255bb..74a4dece 100644 --- a/stories/src/annotation.stories.tsx +++ b/stories/src/annotation.stories.tsx @@ -1,8 +1,8 @@ import * as React from 'react' -import { storiesOf } from '@storybook/react' -import { boolean, number, select, text, withKnobs } from '@storybook/addon-knobs' -import { Config, Plot, LayerConfig, timeFormatter } from '../../giraffe/src' -import { TIME, VALUE } from '../../giraffe/src/constants/columnKeys' +import {storiesOf} from '@storybook/react' +import {boolean, number, select, text, withKnobs} from '@storybook/addon-knobs' +import {Config, Plot, LayerConfig, timeFormatter} from '../../giraffe/src' +import {TIME, VALUE} from '../../giraffe/src/constants/columnKeys' import { PlotContainer, @@ -22,7 +22,7 @@ import { annotationPinKnob, } from './helpers' -import { matchAnnotationsToTable, getAnnotationsTable } from './data/annotation' +import {matchAnnotationsToTable, getAnnotationsTable} from './data/annotation' storiesOf('Annotations', module) .addDecorator(withKnobs) @@ -73,7 +73,7 @@ storiesOf('Annotations', module) const yTotalTicks = number('Y Total Ticks', 10) const position = select( 'Line Position', - { stacked: 'stacked', overlaid: 'overlaid' }, + {stacked: 'stacked', overlaid: 'overlaid'}, 'overlaid' ) const interpolation = interpolationKnob() @@ -81,7 +81,7 @@ storiesOf('Annotations', module) const lineWidth = number('Line Width', 1) const hoverDimension = select( 'Hover Dimension', - { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, + {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, 'auto' ) @@ -122,9 +122,10 @@ storiesOf('Annotations', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({ timeZone, format: timeFormat }), + _time: timeFormatter({timeZone, format: timeFormat}), _value: val => - `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${ + valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, @@ -189,7 +190,7 @@ storiesOf('Annotations', module) const yTotalTicks = number('Y Total Ticks', 10) const position = select( 'Line Position', - { stacked: 'stacked', overlaid: 'overlaid' }, + {stacked: 'stacked', overlaid: 'overlaid'}, 'overlaid' ) const interpolation = interpolationKnob() @@ -197,7 +198,7 @@ storiesOf('Annotations', module) const lineWidth = number('Line Width', 1) const hoverDimension = select( 'Hover Dimension', - { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, + {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, 'auto' ) @@ -255,9 +256,10 @@ storiesOf('Annotations', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({ timeZone, format: timeFormat }), + _time: timeFormatter({timeZone, format: timeFormat}), _value: val => - `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${ + valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, @@ -324,7 +326,7 @@ storiesOf('Annotations', module) const yTotalTicks = number('Y Total Ticks', 10) const position = select( 'Line Position', - { stacked: 'stacked', overlaid: 'overlaid' }, + {stacked: 'stacked', overlaid: 'overlaid'}, 'overlaid' ) const interpolation = interpolationKnob() @@ -332,7 +334,7 @@ storiesOf('Annotations', module) const lineWidth = number('Line Width', 1) const hoverDimension = select( 'Hover Dimension', - { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, + {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, 'auto' ) @@ -390,9 +392,10 @@ storiesOf('Annotations', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({ timeZone, format: timeFormat }), + _time: timeFormatter({timeZone, format: timeFormat}), _value: val => - `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${ + valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, @@ -469,7 +472,7 @@ storiesOf('Annotations', module) const yTotalTicks = number('Y Total Ticks', 10) const position = select( 'Line Position', - { stacked: 'stacked', overlaid: 'overlaid' }, + {stacked: 'stacked', overlaid: 'overlaid'}, 'overlaid' ) const interpolation = interpolationKnob() @@ -477,7 +480,7 @@ storiesOf('Annotations', module) const lineWidth = number('Line Width', 1) const hoverDimension = select( 'Hover Dimension', - { auto: 'auto', x: 'x', y: 'y', xy: 'xy' }, + {auto: 'auto', x: 'x', y: 'y', xy: 'xy'}, 'auto' ) @@ -516,9 +519,10 @@ storiesOf('Annotations', module) const config: Config = { table, valueFormatters: { - _time: timeFormatter({ timeZone, format: timeFormat }), + _time: timeFormatter({timeZone, format: timeFormat}), _value: val => - `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${ + valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, diff --git a/stories/src/customLayer.stories.tsx b/stories/src/customLayer.stories.tsx index 5550458d..c3eca75e 100644 --- a/stories/src/customLayer.stories.tsx +++ b/stories/src/customLayer.stories.tsx @@ -1,6 +1,6 @@ import * as React from 'react' -import { storiesOf } from '@storybook/react' -import { boolean, number, select, text, withKnobs } from '@storybook/addon-knobs' +import {storiesOf} from '@storybook/react' +import {boolean, number, select, text, withKnobs} from '@storybook/addon-knobs' import { Config, Plot, @@ -26,8 +26,8 @@ import { tooltipColorizeRowsKnob, } from './helpers' -import { CPU } from './data/cpu' -import { getSingleStatTable } from './data/singleStatLayer' +import {CPU} from './data/cpu' +import {getSingleStatTable} from './data/singleStatLayer' storiesOf('Custom Layer', module) .addDecorator(withKnobs) @@ -42,7 +42,7 @@ storiesOf('Custom Layer', module) }, { type: 'custom', - render: ({ key, yScale }) => { + render: ({key, yScale}) => { return (
- `${val.toFixed(2)}${valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel + `${val.toFixed(2)}${ + valueAxisLabel ? ` ${valueAxisLabel}` : valueAxisLabel }`, }, xScale, diff --git a/stories/src/data/annotation.ts b/stories/src/data/annotation.ts index 456d57ba..fda38f09 100644 --- a/stories/src/data/annotation.ts +++ b/stories/src/data/annotation.ts @@ -5,7 +5,7 @@ import { Table, } from '../../../giraffe/src/types' import {newTable} from '../../../giraffe/src' -import { getRandomOrFixed, nowOrFixed } from "./utils" +import {getRandomOrFixed, nowOrFixed} from './utils' const numberOfRecords = 20 const recordsPerLine = 20 diff --git a/stories/src/data/bandLayer.ts b/stories/src/data/bandLayer.ts index 2bfdb0ed..ce8eab58 100644 --- a/stories/src/data/bandLayer.ts +++ b/stories/src/data/bandLayer.ts @@ -1,5 +1,5 @@ -import { newTable } from '../../../giraffe/src' -import { getRandomOrFixed, nowOrFixed } from "./utils" +import {newTable} from '../../../giraffe/src' +import {getRandomOrFixed, nowOrFixed} from './utils' const numberOfRecords = 40 const recordsPerLine = 20 diff --git a/stories/src/data/gaugeLayer.ts b/stories/src/data/gaugeLayer.ts index b70596c1..03414a15 100644 --- a/stories/src/data/gaugeLayer.ts +++ b/stories/src/data/gaugeLayer.ts @@ -1,10 +1,14 @@ -import { newTable, Table } from '../../../giraffe/src' -import { getRandomOrFixed, nowOrFixed } from "./utils" +import {newTable} from '../../../giraffe/src' +import {getRandomOrFixed, nowOrFixed} from './utils' const numberOfRecords = 20 const recordsPerLine = 20 -export const getGaugeTable = (fixed: boolean, minValue: number, maxValue: number) => { +export const getGaugeTable = ( + fixed: boolean, + minValue: number, + maxValue: number +) => { const TIME_COL: Array = [] const VALUE_COL: Array = [] diff --git a/stories/src/data/geoLayer.ts b/stories/src/data/geoLayer.ts index 5a53a74e..3a4d1271 100644 --- a/stories/src/data/geoLayer.ts +++ b/stories/src/data/geoLayer.ts @@ -1,6 +1,6 @@ -import { newTable, Table } from '../../../giraffe/src' +import {newTable, Table} from '../../../giraffe/src' import memoizeOne from 'memoize-one' -import { getRandomOrFixed, nowOrFixed } from "./utils" +import {getRandomOrFixed, nowOrFixed} from './utils' export const getGeoTable = (fixed: boolean, numberOfRecords = 200) => { const TIME_COL = [] @@ -32,7 +32,7 @@ const addTrack = (fixed: boolean, data, startLat: number, startLon: number) => { const time = nowOrFixed(fixed) + i * 1000 * 60 lat += Math.random() * 1.5 lon += Math.random() * 1.5 - data.push({ time, lat, lon, tid }) + data.push({time, lat, lon, tid}) } } diff --git a/stories/src/data/randomTable.ts b/stories/src/data/randomTable.ts index d41f914f..bcf70c12 100644 --- a/stories/src/data/randomTable.ts +++ b/stories/src/data/randomTable.ts @@ -1,6 +1,6 @@ -import { newTable } from '../../../giraffe/src/utils/newTable' +import {newTable} from '../../../giraffe/src/utils/newTable' import memoizeOne from 'memoize-one' -import { getRandomOrFixed, nowOrFixed } from "./utils" +import {getRandomOrFixed, nowOrFixed} from './utils' const defaultNumberOfRecords = 80 const defaultRecordsPerLine = 20 diff --git a/stories/src/data/singleStatLayer.ts b/stories/src/data/singleStatLayer.ts index 0d7e9ca0..2a7a6a2f 100644 --- a/stories/src/data/singleStatLayer.ts +++ b/stories/src/data/singleStatLayer.ts @@ -1,5 +1,5 @@ -import { newTable } from '../../../giraffe/src' -import { getRandomOrFixed, nowOrFixed } from "./utils" +import {newTable} from '../../../giraffe/src' +import {getRandomOrFixed, nowOrFixed} from './utils' const numberOfRecords = 20 const recordsPerLine = 20 @@ -20,5 +20,4 @@ export const getSingleStatTable = (fixed: boolean) => { .addColumn('_time', 'dateTime:RFC3339', 'time', TIME_COL) .addColumn('_value', 'system', 'number', VALUE_COL) .addColumn('cpu', 'string', 'string', CPU_COL) - } diff --git a/stories/src/data/stackedLineLayer.ts b/stories/src/data/stackedLineLayer.ts index e8729f94..3ab50374 100644 --- a/stories/src/data/stackedLineLayer.ts +++ b/stories/src/data/stackedLineLayer.ts @@ -1,5 +1,5 @@ -import { newTable } from '../../../giraffe/src' -import { getRandomOrFixed, nowOrFixed } from "./utils" +import {newTable} from '../../../giraffe/src' +import {getRandomOrFixed, nowOrFixed} from './utils' const numberOfRecords = 80 const recordsPerLine = 20 @@ -37,7 +37,9 @@ const alphabet = [ function getRandomString(fixed: boolean, i: number) { function getRandomAlphabetChar(ii: number) { - const index = Math.floor(getRandomOrFixed(fixed, ii + i, alphabet.length - 1)) + const index = Math.floor( + getRandomOrFixed(fixed, ii + i, alphabet.length - 1) + ) return alphabet[index] } @@ -78,5 +80,4 @@ export const getStackedLineTable = (fixed: boolean) => { .addColumn('test_col_b', 'string', 'string', TEST_COL_B) .addColumn('test_col_c', 'string', 'string', TEST_COL_C) .addColumn('test_col_d', 'string', 'string', TEST_COL_D) - -} \ No newline at end of file +} diff --git a/stories/src/data/utils.ts b/stories/src/data/utils.ts index 7e51d780..f5d62f15 100644 --- a/stories/src/data/utils.ts +++ b/stories/src/data/utils.ts @@ -9,7 +9,7 @@ export const getRandomOrFixed: { } if (fixed) { - return (((Math.sin(index) + 1) / 2)) * (maxValue - minValue) + minValue + return ((Math.sin(index) + 1) / 2) * (maxValue - minValue) + minValue } else { return Math.random() * (maxValue - minValue) + minValue } diff --git a/stories/src/gauge.stories.tsx b/stories/src/gauge.stories.tsx index 6eb3f79b..1a5a721f 100644 --- a/stories/src/gauge.stories.tsx +++ b/stories/src/gauge.stories.tsx @@ -1,11 +1,11 @@ import * as React from 'react' -import { storiesOf } from '@storybook/react' -import { withKnobs, number, text, boolean } from '@storybook/addon-knobs' -import { Config, Plot, GaugeTheme } from '../../giraffe/src' -import { DEFAULT_GAUGE_COLORS } from '../../giraffe/src' +import {storiesOf} from '@storybook/react' +import {withKnobs, number, text, boolean} from '@storybook/addon-knobs' +import {Config, Plot, GaugeTheme} from '../../giraffe/src' +import {DEFAULT_GAUGE_COLORS} from '../../giraffe/src' -import { PlotContainer } from './helpers' -import { getGaugeTable } from './data/gaugeLayer' +import {PlotContainer} from './helpers' +import {getGaugeTable} from './data/gaugeLayer' storiesOf('Gauge', module) .addDecorator(withKnobs) @@ -59,8 +59,8 @@ storiesOf('Gauge', module) digits: decimalPlaces, }, gaugeColors: [ - { ...DEFAULT_GAUGE_COLORS[0], value: gaugeMin }, - { ...DEFAULT_GAUGE_COLORS[1], value: gaugeMax }, + {...DEFAULT_GAUGE_COLORS[0], value: gaugeMin}, + {...DEFAULT_GAUGE_COLORS[1], value: gaugeMax}, ], gaugeSize, gaugeTheme: { From 81bdebd3ec6bccfa77069a6965c9322b006b0220 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 8 Mar 2021 13:40:56 +0100 Subject: [PATCH 08/11] test: number input knobs --- stories/cypress/support/commands.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stories/cypress/support/commands.ts b/stories/cypress/support/commands.ts index 67b690ac..16cef53e 100644 --- a/stories/cypress/support/commands.ts +++ b/stories/cypress/support/commands.ts @@ -86,6 +86,11 @@ export const inputKnobs: { .clear({force: true}) .type(valueStr, {force: true}) break + case 'number': + cy.get(selector) + .clear({force: true}) + .type((value as number).toString(), {force: true}) + break case 'select': cy.get(selector).select(value as string) break From 81d118cbda3e8a70cc7681c7399e82fae73e6ec3 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 8 Mar 2021 13:44:45 +0100 Subject: [PATCH 09/11] test: fix resize observer error --- stories/cypress/support/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/stories/cypress/support/index.js b/stories/cypress/support/index.js index d68db96d..94fb7e12 100644 --- a/stories/cypress/support/index.js +++ b/stories/cypress/support/index.js @@ -18,3 +18,13 @@ import './commands' // Alternatively you can use CommonJS syntax: // require('./commands') + +const resizeObserverLoopErrRe = /^ResizeObserver loop limit exceeded/ + +Cypress.on('uncaught:exception', err => { + if (resizeObserverLoopErrRe.test(err.message)) { + // returning false here prevents Cypress from + // failing the test + return false + } +}) From 9df94dbd430eee1eb4cdf1143732a3b2c56b35e4 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Mon, 8 Mar 2021 13:50:53 +0100 Subject: [PATCH 10/11] test: skip error on dev snapshots --- stories/cypress/support/index.js | 2 +- stories/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stories/cypress/support/index.js b/stories/cypress/support/index.js index 94fb7e12..811b152d 100644 --- a/stories/cypress/support/index.js +++ b/stories/cypress/support/index.js @@ -19,7 +19,7 @@ import './commands' // Alternatively you can use CommonJS syntax: // require('./commands') -const resizeObserverLoopErrRe = /^ResizeObserver loop limit exceeded/ +const resizeObserverLoopErrRe = /> ResizeObserver loop limit exceeded/ Cypress.on('uncaught:exception', err => { if (resizeObserverLoopErrRe.test(err.message)) { diff --git a/stories/package.json b/stories/package.json index 7ffbd3a9..5c771a60 100644 --- a/stories/package.json +++ b/stories/package.json @@ -16,7 +16,7 @@ "storybook": "start-storybook -p 50000", "publish": "storybook-to-ghpages --out=.out", "chromatic": "chromatic test", - "cy": "cypress open", + "cy": "cypress open --env failOnSnapshotDiff=false", "cy:ci": "wait-on http://localhost:50000/ && cypress run", "cy:update": "cypress run --env updateSnapshots=true", "cy:gen-diff": "cypress run --env failOnSnapshotDiff=false" From c4df4adfe16fe62fba621ad4c3f32f859ea4dde4 Mon Sep 17 00:00:00 2001 From: Sciator <39964450+Sciator@users.noreply.github.com> Date: Tue, 9 Mar 2021 16:09:00 +0100 Subject: [PATCH 11/11] test: basic tests --- .circleci/config.yml | 25 +++++++++- stories/cypress/integration/test.test.ts | 59 +++++++++++++++++++----- stories/cypress/support/commands.ts | 1 + 3 files changed, 73 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0a134f37..7589e3c9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ version: 2 jobs: - build: + test: docker: - image: circleci/node:lts working_directory: ~/repo @@ -35,3 +35,26 @@ jobs: # else \ # yarn run chromatic -- --auto-accept-changes # fi + update-snapshots: + docker: + - image: circleci/node:lts + working_directory: ~/repo + steps: + - checkout + - restore_cache: + keys: + - v1-dependencies-{{ checksum "yarn.lock" }} + - run: yarn install --frozen-lockfile + - run: yarn --cwd stories cy:update + + +workflows: + version: 2 + test: + jobs: + - test + - update-snapshots: + type: approval + requires: + - test + diff --git a/stories/cypress/integration/test.test.ts b/stories/cypress/integration/test.test.ts index 9dc626ea..2bcf818d 100644 --- a/stories/cypress/integration/test.test.ts +++ b/stories/cypress/integration/test.test.ts @@ -1,25 +1,62 @@ + describe('testing test', () => { - it.only('should fix geo data', () => { - cy.visitTest('Geo', 'Circle Markers') + it('should make Gauge snapshot', () => { + cy.visitTest('Gauge', 'Gauge') cy.inputKnobs('Fixed data', true, 'boolean') }) - it('should make snapsot of band chart', () => { - cy.visitTest('Band Chart', 'Static: groupBy applied') - cy.inputKnobs('Time Format', 'HH:mm', 'select') + it('should make snapshot of gauge', () => { + cy.visitTest('Gauge', 'Gauge') + cy.inputKnobs('Fixed data', true, 'boolean') + cy.inputKnobs('Gauge Lines', 12, 'number') + cy.wait(4000) + cy.inputKnobs('Ticks between lines', 3, 'number') + + cy.snapshotComponent('gauge-test-3-ticks-between-lines-12-gauge-lines') + + cy.inputKnobs('Gauge Min', 10, 'number') + + cy.inputKnobs('Prefix', 'haf ', 'text') + cy.wait(2000) + + cy.snapshotComponent('gauge-test-Haf-prefix-10-gauge-min') }) it('should make snapshot of gauge', () => { cy.visitTest('Gauge', 'Gauge') + cy.inputKnobs('Fixed data', true, 'boolean') + + cy.inputKnobs('Ticks between lines', 3, 'number') - cy.snapshotComponent('gauge-test-1') + cy.snapshotComponent('gauge-test-3-ticks-between-lines') + cy.wait(2000) - cy.inputKnobs('Decimal Places', 2, 'number') + cy.inputKnobs('Gauge Max', 23, 'number') + cy.inputKnobs('Suffix', 'HKS', 'text') + cy.wait(2000) + cy.inputKnobs('Suffix', 'halsper ', 'text') - cy.snapshotComponent('gauge-test-2-decimal-places') + cy.snapshotComponent('gauge-test-suffix-prefix-type') - cy.inputKnobs('Gauge Min', 20, 'number') - cy.snapshotComponent('gauge-test-start-20') }) -}) + + it('should make snapchot of gauge', () => { + cy.visitTest('Gauge', 'Gauge') + cy.inputKnobs('Fixed data', true, 'boolean') + + cy.inputKnobs('Decimal Places', 1, 'number') + cy.wait(3000) + + cy.inputKnobs('Gauge Lines', 1, 'number') + cy.inputKnobs('Ticks between lines', 39, 'number') + + cy.inputKnobs('TickPrefix', 'kdfakdhj', 'text') + cy.snapshotComponent('gauge-test-tick-prefix') + + cy.inputKnobs('TickPrefix', ' ', 'text') + cy.inputKnobs('TickSuffix', 'kdkdhj', 'text') + cy.snapshotComponent('gaure-test-ticksSuffix') + } + ) +}) \ No newline at end of file diff --git a/stories/cypress/support/commands.ts b/stories/cypress/support/commands.ts index 16cef53e..a92ae24f 100644 --- a/stories/cypress/support/commands.ts +++ b/stories/cypress/support/commands.ts @@ -69,6 +69,7 @@ export const snapshotComponent = ( export const inputKnobs: { (label: string, value: string): void + (label: string, value: string, type: 'text'): void (label: string, value: string, type: 'select'): void (label: string, value: number, type: 'number'): void (label: string, value: boolean, type: 'boolean'): void