diff --git a/.eslintrc.yml b/.eslintrc.yml index 71b59ef4..eabfb564 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,10 +1,43 @@ +root: true +parser: '@typescript-eslint/parser' parserOptions: - ecmaVersion: 2019 - sourceType: script + project: './tsconfig.json' + sourceType: module env: - node: true - es6: true - jest: true -extends: eslint:recommended + node: true + es6: true + jest: true + mocha: true +plugins: + - 'import' +extends: + - 'eslint:recommended' + - 'plugin:@typescript-eslint/recommended' + - 'plugin:@typescript-eslint/recommended-requiring-type-checking' + - 'plugin:prettier/recommended' + - 'plugin:import/errors' + - 'plugin:import/warnings' + - 'plugin:import/typescript' +ignorePatterns: + - 'build/*' + - 'tests/**/*.js' + - 'examples/*' + - 'bin/*' + - '.yarn/*' + - 'scripts/*' + - 'jest.config.js' rules: - no-unused-vars: off + 'no-console': 'warn' + 'no-duplicate-imports': 'error' + 'no-return-await': 'error' + 'no-unneeded-ternary': 'error' + 'no-unused-vars': [ 'off' ] + 'prefer-object-spread': 'error' + 'require-await': 'off' + '@typescript-eslint/ban-ts-comment': 'warn' + '@typescript-eslint/restrict-template-expressions': 'warn' + '@typescript-eslint/unbound-method': 'warn' + '@typescript-eslint/no-unsafe-argument': 'warn' + '@typescript-eslint/explicit-function-return-type': 'error' + '@typescript-eslint/no-unsafe-assignment': 'warn' + '@typescript-eslint/no-unsafe-member-access': 'warn' diff --git a/.gitignore b/.gitignore index fde0da51..025bf989 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ coverage # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release +build/ # Dependency directories node_modules diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..3f4897ae --- /dev/null +++ b/.prettierrc @@ -0,0 +1,17 @@ +# .prettierrc +arrowParens: always +printWidth: 100 +semi: false +singleQuote: true +tabWidth: 4 +overrides: + - files: + - "*.yml" + - "*.yaml" + options: + bracketSpacing: false + tabWidth: 2 + - files: "*.json" + options: + trailingComma: all + tabWidth: 4 diff --git a/.yarnrc.yml b/.yarnrc.yml index f5d0218c..cafa4775 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,3 +1,5 @@ +nodeLinker: node-modules + plugins: - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs spec: "@yarnpkg/plugin-interactive-tools" diff --git a/examples/features/http_api/fixtures/fixtures/module_00.js b/examples/features/http_api/fixtures/fixtures/module_00.js index c71da3f8..0f22f541 100644 --- a/examples/features/http_api/fixtures/fixtures/module_00.js +++ b/examples/features/http_api/fixtures/fixtures/module_00.js @@ -1,5 +1,5 @@ 'use strict' module.exports = () => ({ - data: 'fixture loaded from javascript module' + data: 'fixture loaded from javascript module', }) diff --git a/examples/support/definitions.js b/examples/support/definitions.js deleted file mode 100644 index 532ebf8d..00000000 --- a/examples/support/definitions.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict' - -const { Given } = require('@cucumber/cucumber') - -Given(/^(?:I )?define http mock from (.+)$/, function (fixture) {}) diff --git a/examples/support/definitions.ts b/examples/support/definitions.ts new file mode 100644 index 00000000..2ca71af6 --- /dev/null +++ b/examples/support/definitions.ts @@ -0,0 +1,4 @@ + +import { Given } from "@cucumber/cucumber" + +Given(/^(?:I )?define http mock from (.+)$/, function () {}) diff --git a/examples/support/http_api/fixtures.steps.js b/examples/support/http_api/fixtures.steps.js deleted file mode 100644 index 57f68d88..00000000 --- a/examples/support/http_api/fixtures.steps.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict' - -const querystring = require('querystring') -const { Given, Then } = require('@cucumber/cucumber') -const nock = require('nock') -const { expect } = require('chai') - -Given(/^I mock http call to forward request body for path (.+)$/, function (path) { - nock('http://fake.io') - .post(path) - .reply(200, (uri, requestBody) => requestBody) -}) - -Then(/^response should match url encoded snapshot (.+)$/, function (snapshotId) { - const httpResponse = this.httpApiClient.getResponse() - expect(httpResponse).to.not.be.empty - return this.fixtures.load(snapshotId).then((snapshot) => { - expect(httpResponse.body).to.equal(querystring.stringify(snapshot)) - }) -}) diff --git a/examples/support/http_api/fixtures.steps.ts b/examples/support/http_api/fixtures.steps.ts new file mode 100644 index 00000000..bf185ffb --- /dev/null +++ b/examples/support/http_api/fixtures.steps.ts @@ -0,0 +1,11 @@ +import { Given } from "@cucumber/cucumber" +import nock from "nock" + +Given( + /^I mock http call to forward request body for path (.+)$/, + function (path) { + nock("http://fake.io") + .post(path) + .reply(200, (uri, requestBody) => requestBody) + } +) diff --git a/examples/support/world.js b/examples/support/world.ts similarity index 65% rename from examples/support/world.js rename to examples/support/world.ts index e514cd39..aefa0813 100644 --- a/examples/support/world.js +++ b/examples/support/world.ts @@ -1,7 +1,13 @@ -'use strict' -const { setWorldConstructor } = require('@cucumber/cucumber') -const { state, fixtures, httpApi, cli, fileSystem, snapshot } = require('../../src') +import { setWorldConstructor } from "@cucumber/cucumber" +import { + state, + fixtures, + httpApi, + cli, + fileSystem, + snapshot, +} from "../../src/index" setWorldConstructor(function () { state.extendWorld(this) diff --git a/jest.config.js b/jest.config.js index a13473f9..6ebc1f56 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,5 +7,7 @@ module.exports = { transform: { '^.+\\.(t|j)s$': 'ts-jest', }, + modulePathIgnorePatterns: ['/build'], + collectCoverageFrom: ['src/**/*.ts'], coverageDirectory: './coverage', } diff --git a/package.json b/package.json index 41a5c9e6..aa0d0071 100644 --- a/package.json +++ b/package.json @@ -1,88 +1,110 @@ { - "name": "@ekino/veggies", - "version": "1.1.0", - "description": "Veggies is an awesome cucumberjs library for API/CLI testing. Great for testing APIs built upon Express, Koa, HAPI, Loopback and others. It's also the perfect companion for testing CLI applications built with commander, meow & Co.", - "tags": [ - "bdd", - "cucumber", - "gherkin", - "testing", - "api", - "http", - "cli" - ], - "repository": { - "type": "git", - "url": "https://github.com/ekino/veggies.git" - }, - "main": "src/index.js", - "bin": "./bin/veggies.js", - "author": "plouc ", - "maintainers": [ - { - "name": "Raphaël Benitte" + "name": "@ekino/veggies", + "version": "1.1.0", + "description": "Veggies is an awesome cucumberjs library for API/CLI testing. Great for testing APIs built upon Express, Koa, HAPI, Loopback and others. It's also the perfect companion for testing CLI applications built with commander, meow & Co.", + "tags": [ + "bdd", + "cucumber", + "gherkin", + "testing", + "api", + "http", + "cli" + ], + "repository": { + "type": "git", + "url": "https://github.com/ekino/veggies.git" + }, + "main": "build/src/index.js", + "types": "build/src/index.d.ts", + "bin": "build/bin/veggies.js", + "author": "plouc ", + "maintainers": [ + { + "name": "Raphaël Benitte" + } + ], + "license": "MIT", + "engines": { + "node": ">=12" + }, + "engineStrict": true, + "dependencies": { + "arg": "5.0.1", + "chai": "4.3", + "fs-extra": "10.0.0", + "glob": "7.2.0", + "jest-diff": "27.4.2", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "moment-timezone": "0.5.34", + "natural-compare": "1.4.0", + "pretty-format": "27.4.2", + "request": "2.88.2", + "tough-cookie": "4.0.0" + }, + "devDependencies": { + "@commitlint/config-conventional": "15.x", + "@cucumber/cucumber": "7.x", + "@types/babylon": "6.x", + "@types/chai": "4.x", + "@types/fs-extra": "9.x", + "@types/glob": "7.x", + "@types/jest": "27.x", + "@types/js-yaml": "4.x", + "@types/lodash": "4.x", + "@types/moment-timezone": "0.5.x", + "@types/mustache": "4.x", + "@types/natural-compare": "1.x", + "@types/node": "16.x", + "@types/proxyquire": "^1.3.28", + "@types/request": "2.x", + "@types/sinon": "10.x", + "@types/tough-cookie": "4.x", + "@typescript-eslint/eslint-plugin": "5.x", + "@typescript-eslint/parser": "5.x", + "babylon": "6.x", + "chalk": "4.x", + "commitlint": "15.x", + "conventional-changelog-cli": "2.x", + "coveralls": "3.x", + "eslint": "8.x", + "eslint-config-prettier": "8.x", + "eslint-plugin-import": "2.x", + "eslint-plugin-prettier": "4.x", + "gh-pages": "3.x", + "husky": "7.0.4", + "jest": "27.x", + "jsdoc": "3.x", + "lint-staged": "12.x", + "minami": "1.x", + "mustache": "4.x", + "nock": "13.x", + "prettier": "2.x", + "proxyquire": "^2.1.3", + "sinon": "12.x", + "ts-jest": "27.x", + "ts-node": "10.x", + "typescript": "4.5.x" + }, + "peerDependencies": { + "@cucumber/cucumber": ">=7.0.0" + }, + "scripts": { + "build": "rm -rf build & tsc -d", + "test": "jest --verbose --colors tests", + "test-cover": "jest --verbose --colors --coverage", + "test-cli": "veggies --require tests/cli/support tests/cli/features", + "coverage": "cat ./coverage/lcov.info | coveralls", + "fmt": "prettier --print-width 100 --tab-width=4 --single-quote --bracket-spacing --no-semi --color --write \"{src,tests,scripts}/**/*.js\"", + "check-fmt": "prettier --print-width 100 --tab-width=4 --single-quote --bracket-spacing --no-semi --list-different \"{src,tests,scripts}/**/*.js\"", + "lint": "eslint . --quiet", + "lint-fix": "eslint --fix . --quiet", + "readme": "ts-node -T -s scripts/generate_readme.ts", + "check-readme": "ts-node -T -s scripts/generate_readme.ts --check", + "doc": "jsdoc -c .jsdoc.json --verbose", + "doc-pub": "yarn run readme && yarn run doc && gh-pages -d _doc", + "examples": "veggies --require examples/support examples/features", + "changelog": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s" } - ], - "license": "MIT", - "engines": { - "node": ">=12" - }, - "engineStrict": true, - "dependencies": { - "arg": "5.0.1", - "chai": "4.3", - "fs-extra": "10.0.0", - "glob": "7.2.0", - "jest-diff": "27.4.2", - "js-yaml": "4.1.0", - "lodash": "4.17.21", - "moment-timezone": "0.5.34", - "natural-compare": "1.4.0", - "pretty-format": "27.4.2", - "request": "2.88.2", - "tough-cookie": "4.0.0" - }, - "devDependencies": { - "@commitlint/config-conventional": "15.x", - "@cucumber/cucumber": "7.x", - "@types/jest": "27.x", - "@types/node": "16.x", - "babylon": "6.x", - "chalk": "4.x", - "commitlint": "15.x", - "conventional-changelog-cli": "2.x", - "coveralls": "3.x", - "eslint": "8.x", - "gh-pages": "3.x", - "husky": "^7.0.4", - "jest": "27.x", - "jsdoc": "3.x", - "lint-staged": "12.x", - "minami": "1.x", - "mustache": "4.x", - "nock": "13.x", - "prettier": "2.x", - "sinon": "12.x", - "ts-jest": "27.x", - "typescript": "4.5.x" - }, - "peerDependencies": { - "@cucumber/cucumber": ">=7.0.0" - }, - "scripts": { - "test": "jest --verbose --colors tests", - "test-cover": "jest --verbose --colors --coverage", - "test-cli": "veggies --require tests/cli/support tests/cli/features", - "coverage": "cat ./coverage/lcov.info | coveralls", - "fmt": "prettier --print-width 100 --tab-width=4 --single-quote --bracket-spacing --no-semi --color --write \"{src,tests,scripts}/**/*.js\"", - "check-fmt": "prettier --print-width 100 --tab-width=4 --single-quote --bracket-spacing --no-semi --list-different \"{src,tests,scripts}/**/*.js\"", - "lint": "eslint .", - "lint-fix": "eslint --fix .", - "readme": "node scripts/generate_readme", - "check-readme": "node scripts/generate_readme --check", - "doc": "jsdoc -c .jsdoc.json --verbose", - "doc-pub": "yarn run readme && yarn run doc && gh-pages -d _doc", - "examples": "veggies --require examples/support examples/features", - "changelog": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s" - } } diff --git a/scripts/generate_readme.js b/scripts/generate_readme.ts similarity index 59% rename from scripts/generate_readme.js rename to scripts/generate_readme.ts index f868a363..6c80ef17 100644 --- a/scripts/generate_readme.js +++ b/scripts/generate_readme.ts @@ -1,16 +1,16 @@ -/* eslint no-console: "off" */ -'use strict' - /** * This script is used to extract step definitions * from extensions' definition files. */ -const assert = require('assert') -const fs = require('fs') -const chalk = require('chalk') -const { parse } = require('babylon') -const Mustache = require('mustache') +import { ExpressionStatement } from '@babel/types' +import assert from 'assert' +import fs from 'fs' +import chalk from 'chalk' +import { parse } from 'babylon' +import Mustache from 'mustache' +import { File, ModuleDeclaration, Statement } from 'babel-types' +import { isDefined } from '../src/utils/type_guards' /** * Flag allowing to only check if README was generated @@ -27,6 +27,11 @@ const readmeTemplatePath = './doc/README.tpl.md' */ const readmePath = './README.md' +export type DefinitionInfo = { + type?: string + matcher?: unknown +} + /** * Mustache partials. */ @@ -60,15 +65,22 @@ Then: `.trim(), } +export type DefinitionFiles = { + cli: string + httpApi: string + state: string + fileSystem: string + snapshot: string +} /** * Available definition files. */ -const definitionFiles = { - cli: './src/extensions/cli/definitions.js', - httpApi: './src/extensions/http_api/definitions.js', - state: './src/extensions/state/definitions.js', - fileSystem: './src/extensions/file_system/definitions.js', - snapshot: './src/extensions/snapshot/definitions.js', +const definitionFiles: DefinitionFiles = { + cli: './src/extensions/cli/definitions.ts', + httpApi: './src/extensions/http_api/definitions.ts', + state: './src/extensions/state/definitions.ts', + fileSystem: './src/extensions/file_system/definitions.ts', + snapshot: './src/extensions/snapshot/definitions.ts', } /** @@ -81,7 +93,7 @@ const extensions = Object.keys(definitionFiles) * * @param {string} file - File path */ -const getFileContent = (file) => +const getFileContent = (file: string): Promise => new Promise((resolve, reject) => { console.log(chalk.yellow(`- loading file: ${chalk.white(file)}`)) @@ -99,11 +111,11 @@ const getFileContent = (file) => * @param {string} file - File path * @param {string} content - File content */ -const writeFile = (file, content) => +const writeFile = (file: string, content: string): Promise => new Promise((resolve, reject) => { fs.writeFile(file, content, (err) => { if (err) return reject(err) - resolve(true) + resolve() }) }) @@ -113,7 +125,7 @@ const writeFile = (file, content) => * @param {Object} node - AST node * @return {boolean} */ -const isExportInstall = (node) => { +const isExportInstall = (node: Statement | ModuleDeclaration): boolean => { return ( node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression' && @@ -126,9 +138,9 @@ const isExportInstall = (node) => { ) } -const extractModuleExportBody = (file, node) => { +const extractModuleExportBody = (file: string, node?: any): any => { if ( - node.type !== 'ArrowFunctionExpression' || + node?.type !== 'ArrowFunctionExpression' || !['BlockStatement', 'ArrowFunctionExpression'].includes(node.body.type) ) { console.error( @@ -144,8 +156,8 @@ const extractModuleExportBody = (file, node) => { process.exit(1) } - if (node.body.type === 'BlockStatement') return node.body.body - return extractModuleExportBody(file, node.body) + if (node?.body?.type === 'BlockStatement') return node?.body?.body + return extractModuleExportBody(file, node?.body) } /** @@ -154,7 +166,7 @@ const extractModuleExportBody = (file, node) => { * @param {Object} node - AST node * @return {boolean} */ -const isDefinition = (node) => { +const isDefinition = (node: ExpressionStatement): boolean => { return ( node.type === 'ExpressionStatement' && node.expression.type === 'CallExpression' && @@ -170,18 +182,21 @@ const isDefinition = (node) => { * @param {Object} node - AST node to extract info from * @return {{type: string, matcher: *}} */ -const extractDefinitionInfo = (file, node) => { - const [regex] = node.expression.arguments +const extractDefinitionInfo = (file: string, node: ExpressionStatement): DefinitionInfo => { + const [regex] = 'arguments' in node.expression ? node.expression.arguments : [] if (!regex || regex.type !== 'RegExpLiteral') { console.error( - chalk.red(`! Found invalid definition in: '${file}' at line ${node.loc.start.line}`) + chalk.red(`! Found invalid definition in: '${file}' at line ${node.loc?.start.line}`) ) process.exit(1) } return { - type: node.expression.callee.name, - matcher: regex.extra.raw, + type: + 'callee' in node.expression && 'name' in node.expression.callee + ? node.expression.callee.name + : undefined, + matcher: regex.extra?.raw, } } @@ -191,29 +206,31 @@ const extractDefinitionInfo = (file, node) => { * @param {string} file - Current definitions file * @param {string} code - Definitions raw code */ -const parseDefinitions = (file, code) => { +const parseDefinitions = (file: string, code: string): Record => { console.log(chalk.yellow(`- parsing definitions file: ${chalk.white(file)}`)) - const parsed = parse(code, { - ranges: false, - tokens: false, - }) + const parsed: File = parse(code) - const [exportStatement] = parsed.program.body.filter(isExportInstall) + const [exportStatement] = parsed.program.body.filter((node) => isExportInstall(node)) if (!exportStatement) { console.error(chalk.red(`! No 'module.exports' found in '${chalk.white(file)}'`)) process.exit(1) } - const body = extractModuleExportBody(file, exportStatement.expression.right) + const nodeStatement = + 'expression' in exportStatement && 'right' in exportStatement.expression + ? exportStatement.expression.right + : undefined + const body = extractModuleExportBody(file, nodeStatement) return body - .filter(isDefinition) - .map((def) => extractDefinitionInfo(file, def)) - .reduce((byType, { type, matcher }) => { + .filter((x: ExpressionStatement) => isDefinition(x)) + .map((def: ExpressionStatement) => extractDefinitionInfo(file, def)) + .reduce((byType: Record, { type, matcher }: DefinitionInfo) => { + if (!type) return {} if (!byType[type]) byType[type] = [] - byType[type].push({ type, matcher }) + byType[type]?.push({ type, matcher }) return byType }, {}) @@ -224,19 +241,25 @@ const parseDefinitions = (file, code) => { * * @param {string} file - Definitions file */ -const getDefinitionsFromFile = (file) => { +const getDefinitionsFromFile = (file: string) => { return getFileContent(file).then((code) => parseDefinitions(file, code)) } -const generateReadme = async () => { +const generateReadme = async (): Promise => { try { - const definitions = await Promise.all( - extensions.map((extensionId) => getDefinitionsFromFile(definitionFiles[extensionId])) + const definitions: Record[] = await Promise.all( + extensions + .map((extensionId) => { + const file = definitionFiles[extensionId as keyof DefinitionFiles] + if (file) return getDefinitionsFromFile(file) + }) + .filter(isDefined) ) - const definitionsByExtension = {} + const definitionsByExtension: Record> = {} definitions.forEach((defs, index) => { - definitionsByExtension[extensions[index]] = defs + const extension = extensions[index] + if (extension) definitionsByExtension[extension] = defs }) const readmeTemplateContent = await getFileContent(readmeTemplatePath) @@ -265,4 +288,4 @@ const generateReadme = async () => { } } -generateReadme() +generateReadme().then((x) => x) diff --git a/src/@types/chai.d.ts b/src/@types/chai.d.ts new file mode 100644 index 00000000..020b7f5c --- /dev/null +++ b/src/@types/chai.d.ts @@ -0,0 +1,10 @@ +declare module 'chai' { + global { + export namespace Chai { + interface Assertion { + startWith(expected: string): void + endWith(expected: string): void + } + } + } +} diff --git a/src/cli/index.js b/src/cli/index.ts similarity index 74% rename from src/cli/index.js rename to src/cli/index.ts index cdd321bb..10163b8d 100644 --- a/src/cli/index.js +++ b/src/cli/index.ts @@ -1,7 +1,5 @@ -'use strict' - -const arg = require('arg') -const CucumberCli = require('@cucumber/cucumber/lib/cli/index').default +import arg from 'arg' +import CucumberCli from '@cucumber/cucumber/lib/cli/index' const veggiesArgsDefinitions = { '--cleanSnapshots': Boolean, @@ -10,7 +8,8 @@ const veggiesArgsDefinitions = { '--preventSnapshotsCreation': Boolean, } -const printHelp = () => { +/* eslint-disable no-console */ +export const printHelp = (): void => { console.log('veggies help') console.log(` Options: @@ -23,8 +22,11 @@ For more details please visit https://github.com/ekino/veggies/blob/master/READM console.log('cucumber-js help\n') } -exports.run = async (argv) => { - const { _: cucumberArgs } = arg(veggiesArgsDefinitions, { argv, permissive: true }) +export const run = async (argv: string[]): Promise => { + const { _: cucumberArgs } = arg(veggiesArgsDefinitions, { + argv, + permissive: true, + }) try { if (cucumberArgs.includes('--help')) { @@ -43,3 +45,4 @@ exports.run = async (argv) => { process.exit(1) } } +/* eslint-enable no-console */ diff --git a/src/core/assertions.js b/src/core/assertions.ts similarity index 84% rename from src/core/assertions.js rename to src/core/assertions.ts index 617f980b..4e1be022 100644 --- a/src/core/assertions.js +++ b/src/core/assertions.ts @@ -1,14 +1,14 @@ -'use strict' - /** * @module Assertions */ -const _ = require('lodash') -const { expect, use } = require('chai') -const moment = require('moment-timezone') -const Cast = require('./cast') -const { registerChaiAssertion } = require('./custom_chai_assertions') +import _ from 'lodash' +import { expect, use } from 'chai' +import moment from 'moment-timezone' +import * as Cast from './cast' +import { registerChaiAssertion } from './custom_chai_assertions' +import { MatchingRule, ObjectFieldSpec } from './core_types' +import { DurationInputArg2 } from 'moment' use(registerChaiAssertion) @@ -23,7 +23,7 @@ const typeRegex = new RegExp(`^(${negationRegex})?(type|#=)$`) const relativeDateRegex = new RegExp(`^(${negationRegex})?(equalRelativeDate)$`) const relativeDateValueRegex = /^(\+?\d|-?\d),([A-Za-z]+),([A-Za-z-]{2,5}),(.+)$/ -const RuleName = Object.freeze({ +export const RuleName = Object.freeze({ Match: Symbol('match'), Contain: Symbol('contain'), StartWith: Symbol('startWith'), @@ -55,14 +55,14 @@ const RuleName = Object.freeze({ * }) * // => 4 (c is ignored because it's a nested object) * - * @param {Object} object + * @param {Object} obj * @return {number} */ -exports.countNestedProperties = (object) => { +export const countNestedProperties = (obj: object): number => { let propertiesCount = 0 - Object.keys(object).forEach((key) => { - if (!_.isEmpty(object[key]) && typeof object[key] === 'object') { - const count = exports.countNestedProperties(object[key]) + Object.keys(obj).forEach((key: keyof typeof obj) => { + if (!_.isEmpty(obj[key]) && typeof obj[key] === 'object') { + const count = countNestedProperties(obj[key]) propertiesCount += count } else { propertiesCount++ @@ -108,16 +108,21 @@ exports.countNestedProperties = (object) => { * * @see ObjectFieldSpec * - * @param {object} object - object to test + * @param {object} bodyObject - object to test * @param {ObjectFieldSpec[]} spec - specification * @param {boolean} [exact=false] - if `true`, specification must match all object's properties */ -exports.assertObjectMatchSpec = (object, spec, exact = false) => { +export const assertObjectMatchSpec = ( + bodyObject: object, + spec: ObjectFieldSpec[], + exact = false +): void => { spec.forEach(({ field, matcher, value }) => { - const currentValue = _.get(object, field) - const expectedValue = Cast.value(value) + field = field || '' + const currentValue = _.get(bodyObject, field) as string + const expectedValue = Cast.value(value) as string - const rule = exports.getMatchingRule(matcher) + const rule = getMatchingRule(matcher) switch (rule.name) { case RuleName.Match: { @@ -192,9 +197,14 @@ exports.assertObjectMatchSpec = (object, spec, exact = false) => { const match = relativeDateValueRegex.exec(expectedValue) if (match === null) throw new Error('relative date arguments are invalid') const [, amount, unit, locale, format] = match + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access const normalizedLocale = Intl.getCanonicalLocales(locale)[0] const expectedDate = moment() - .add(amount, unit) + .add(amount, unit as DurationInputArg2) + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument .locale(normalizedLocale) .format(format) const baseExpect = expect( @@ -242,8 +252,8 @@ exports.assertObjectMatchSpec = (object, spec, exact = false) => { }) // We check we have exactly the same number of properties as expected - if (exact === true) { - const propertiesCount = exports.countNestedProperties(object) + if (exact) { + const propertiesCount = countNestedProperties(bodyObject) expect( propertiesCount, 'Expected json response to fully match spec, but it does not' @@ -268,7 +278,9 @@ exports.assertObjectMatchSpec = (object, spec, exact = false) => { * @param {string} matcher * @return {Rule} the result of the matching */ -exports.getMatchingRule = (matcher) => { +export const getMatchingRule = (matcher?: string): MatchingRule => { + if (!matcher) expect.fail(`Matcher empty did not match any supported assertions`) + const matchGroups = matchRegex.exec(matcher) if (matchGroups) { return { name: RuleName.Match, isNegated: !!matchGroups[1] } @@ -306,7 +318,10 @@ exports.getMatchingRule = (matcher) => { const relativeDateGroups = relativeDateRegex.exec(matcher) if (relativeDateGroups) { - return { name: RuleName.RelativeDate, isNegated: !!relativeDateGroups[1] } + return { + name: RuleName.RelativeDate, + isNegated: !!relativeDateGroups[1], + } } expect.fail(`Matcher "${matcher}" did not match any supported assertions`) diff --git a/src/core/cast.js b/src/core/cast.ts similarity index 56% rename from src/core/cast.js rename to src/core/cast.ts index 1fe4979e..2d8eacd2 100644 --- a/src/core/cast.js +++ b/src/core/cast.ts @@ -1,10 +1,9 @@ -'use strict' - /** * @module Cast */ -const _ = require('lodash') +import _ from 'lodash' +import { CastedValue, CastFunction, CastFunctions, CastType } from './core_types' /** * @name CastFunction @@ -13,75 +12,74 @@ const _ = require('lodash') * @return {*} casted value */ -const castFunctions = {} +const castFunctions: CastFunctions = {} /** * Cast to undefined * @return {undefined} */ -castFunctions['undefined'] = () => { - return undefined -} +castFunctions['undefined'] = (): undefined => undefined /** * Cast to null * @return {null} */ -castFunctions['null'] = () => { - return null -} +castFunctions['null'] = (): null => null /** * Cast to number. If is NaN, it throws an error - * @param {string} value + * @param {string} val * @return {number} */ -castFunctions['number'] = (value) => { - const result = Number(value) +castFunctions['number'] = (val: string): number => { + const result = Number(val) if (_.isNaN(result)) { - throw new TypeError(`Unable to cast value to number '${value}'`) + throw new TypeError(`Unable to cast value to number '${val}'`) } return result } /** * Cast to a boolean. - * @param {string} value - true or false + * @param {string} val - true or false * @return {boolean} - true if true. False in all other case. */ -castFunctions['boolean'] = (value) => { - return value === 'true' +castFunctions['boolean'] = (val: string): boolean => { + return val === 'true' } /** * Cast to an array - * @param {string} value - Should follow the pattern "value1, value2, ..." + * @param {string} vals - Should follow the pattern "value1, value2, ..." * @return {Array} */ -castFunctions['array'] = (value) => { - return value ? value.replace(/\s/g, '').split(',').map(exports.value) : [] +castFunctions['array'] = (vals: string): CastedValue[] => { + return vals + ? vals + .replace(/\s/g, '') + .split(',') + .map((item) => value(item)) + : [] } /** * Cast to as date - * @param {string} value - today or a date as string + * @param {string} val - today or a date as string * @return {string} - A date json formatted */ -castFunctions['date'] = (value) => { - if (value === 'today') { - return new Date().toJSON().slice(0, 10) - } +castFunctions['date'] = (val: string): string => { + if (val === 'today') return new Date().toJSON().slice(0, 10) - return new Date(value).toJSON() + return new Date(val).toJSON() } /** * Cast to a string - * @param {string} value + * @param {string} val * @return {string} */ -castFunctions['string'] = (value) => { - return `${value}` +castFunctions['string'] = (val: string): string => { + return `${val || ''}` } /** @@ -94,7 +92,7 @@ castFunctions['string'] = (value) => { * @param {string} typeName - New type name to add. It will be used in the "(( ))" * @param {CastFunction} castFunction */ -exports.addType = (typeName, castFunction) => { +export const addType = (typeName: string, castFunction: CastFunction): void => { if (!_.isFunction(castFunction)) throw new TypeError( `Invalid cast function provided, must be a function (${typeof castFunction})` @@ -124,34 +122,35 @@ exports.addType = (typeName, castFunction) => { * // > null * // > 'raw' * - * @param {string} value - The value to cast + * @param {string} val - The value to cast * @return {*} The casted value or untouched value if no casting directive found */ -exports.value = (value) => { - if (!_.isString(value)) return value +export const value = (val?: string): CastedValue => { + if (!val) return undefined - const matchResult = value.match(/^(.*)\(\((\w+)\)\)$/) + const matchResult = val.match(/^(.*)\(\((\w+)\)\)$/) if (matchResult) { - const type = matchResult[2] + const type = matchResult[2] as CastType const castFunction = castFunctions[type] - if (!castFunction) throw new TypeError(`Invalid type provided: ${type} '${value}'`) + if (!castFunction) throw new TypeError(`Invalid type provided: ${type} '${val}'`) + return castFunction(matchResult[1]) } - return value + return val } /** * Casts object all properties. * - * @param {Object} object - The object containing values to cast + * @param {Object} obj - The object containing values to cast * @return {Object} The object with casted values */ -exports.object = (object) => { +export const object = (obj: Record): Record => { const castedObject = {} - Object.keys(object).forEach((key) => { - _.set(castedObject, key, exports.value(object[key])) + Object.keys(obj).forEach((key: string) => { + _.set(castedObject, key, value(obj[key])) }) return castedObject @@ -171,13 +170,14 @@ exports.object = (object) => { * // > { username: 'john', is_active: false, age: 32 }, * // > ] * - * @param {Array.} objects + * @param {Array.} objectList */ -exports.objects = (objects) => objects.map((object) => exports.object(object)) +export const objects = (objectList: Record[]): Record[] => + objectList.map((obj) => object(obj)) /** * Casts an array of values. * - * @param {Array.<*>} array + * @param {Array.} arr */ -exports.array = (array) => array.map((value) => exports.value(value)) +export const array = (arr: string[]): CastedValue[] => arr.map((val) => value(val)) diff --git a/src/core/core_types.ts b/src/core/core_types.ts new file mode 100644 index 00000000..856a357b --- /dev/null +++ b/src/core/core_types.ts @@ -0,0 +1,45 @@ +import { World } from '@cucumber/cucumber' +import { State } from '../extensions/state' +import { FileSystem } from '../extensions/file_system/file_system_types' +import { Cli } from '../extensions/cli' +import { Fixtures } from '../extensions/fixtures' +import { HttpApiClient } from '../extensions/http_api' +import { Snapshot } from '../extensions/snapshot' + +export type CastFunctions = Record + +export interface CastFunction { + (value?: string | null): CastedValue | undefined +} + +export type CastedValue = + | string + | number + | boolean + | object + | (string | number | boolean | object)[] + | null + | undefined + +export type CastType = 'string' | 'boolean' | 'number' | 'date' | 'array' | 'null' | 'undefined' + +export type VeggiesWorld = Partial & { + _registredExtensions?: string[] + state?: State + fixtures?: Fixtures + fileSystem?: FileSystem + cli?: Cli + httpApiClient?: HttpApiClient + snapshot?: Snapshot +} + +export type MatchingRule = { + name: symbol + isNegated: boolean +} + +export type ObjectFieldSpec = { + field?: string + matcher?: string + value?: string +} diff --git a/src/core/custom_chai_assertions.js b/src/core/custom_chai_assertions.ts similarity index 69% rename from src/core/custom_chai_assertions.js rename to src/core/custom_chai_assertions.ts index 25e1c6c9..a1caaa7a 100644 --- a/src/core/custom_chai_assertions.js +++ b/src/core/custom_chai_assertions.ts @@ -1,5 +1,7 @@ -exports.registerChaiAssertion = (chai, utils) => { - chai.Assertion.addMethod('startWith', function (expected) { +import { Assertion } from 'chai' + +export const registerChaiAssertion = (): void => { + Assertion.addMethod('startWith', function (expected: string) { return this.assert( typeof this._obj === 'string' && this._obj.startsWith(expected), `expected #{this} to start with #{exp}`, @@ -7,7 +9,7 @@ exports.registerChaiAssertion = (chai, utils) => { expected ) }) - chai.Assertion.addMethod('endWith', function (expected) { + Assertion.addMethod('endWith', function (expected: string) { return this.assert( typeof this._obj === 'string' && this._obj.endsWith(expected), `expected #{this} to end with #{exp}`, diff --git a/src/core/registry.js b/src/core/registry.ts similarity index 61% rename from src/core/registry.js rename to src/core/registry.ts index a6693043..de1ed9aa 100644 --- a/src/core/registry.js +++ b/src/core/registry.ts @@ -1,4 +1,4 @@ -'use strict' +import { VeggiesWorld } from './core_types' const VEGGIES_NAMESPACE = '_registredExtensions' @@ -8,7 +8,7 @@ const VEGGIES_NAMESPACE = '_registredExtensions' * @param {Object} world - Cucumber world object * @param {string} extensionId - Unique veggies extension identifier */ -exports.registerExtension = (world, extensionId) => { +export const registerExtension = (world: VeggiesWorld, extensionId: string): void => { world._registredExtensions = world[VEGGIES_NAMESPACE] || [] world._registredExtensions.push(extensionId) } @@ -20,9 +20,7 @@ exports.registerExtension = (world, extensionId) => { * @param {string} extensionId - Unique veggies extension identifier * @return {boolean} */ -exports.hasExtension = (world, extensionId) => { - if (!world[VEGGIES_NAMESPACE]) return false - if (!world[VEGGIES_NAMESPACE].includes(extensionId)) return false - - return true +export const hasExtension = (world: VeggiesWorld, extensionId: string): boolean => { + const extensions = world[VEGGIES_NAMESPACE] + return extensions ? extensions.includes(extensionId) : false } diff --git a/src/extensions/cli/cli.js b/src/extensions/cli/cli.ts similarity index 70% rename from src/extensions/cli/cli.js rename to src/extensions/cli/cli.ts index d0eab6fc..cdde1895 100644 --- a/src/extensions/cli/cli.js +++ b/src/extensions/cli/cli.ts @@ -1,60 +1,32 @@ -'use strict' - /** * The CLI helper used by the CLI extension. * * @module extensions/Cli/Cli */ -const { spawn } = require('child_process') -const path = require('path') +import process from 'process' +import { spawn } from 'child_process' +import path from 'path' /** * Cli extension. - * - * @class */ -class Cli { +export class Cli { + public cwd: string + public env: Record + public killSignal: number | NodeJS.Signals | null + public killDelay: number + public exitCode: number | null + public stdout: string + public stderr: string + constructor() { - /** - * The Current Working Directory. - * - * @type {string} - */ this.cwd = process.cwd() - - /** - * An object containing environment variables to inject when running your command. - * - * @type {Object} - */ this.env = {} - - /** @type {string} */ this.killSignal = null - - /** @type {number} */ this.killDelay = 0 - - /** - * Latest command execution exit code. - * - * @type {number} - */ this.exitCode = null - - /** - * The command's output. - * - * @type {string} - */ this.stdout = '' - - /** - * The command's error output. - * - * @type {string} - */ this.stderr = '' } @@ -63,7 +35,7 @@ class Cli { * * @param {string} cwd - The new CWD */ - setCwd(cwd) { + setCwd(cwd: string): void { if (cwd.indexOf('/') === 0) { this.cwd = cwd } else { @@ -76,7 +48,7 @@ class Cli { * * @return {string} */ - getCwd() { + getCwd(): string { return this.cwd } @@ -86,7 +58,7 @@ class Cli { * * @param {Object} env - The environment variables object */ - setEnvironmentVariables(env) { + setEnvironmentVariables(env: Record): void { this.env = env } @@ -96,11 +68,11 @@ class Cli { * @param {string} name - The environment variable name * @param {string} value - The value associated to the variable */ - setEnvironmentVariable(name, value) { + setEnvironmentVariable(name: string, value: string): void { this.env[name] = value } - scheduleKillProcess(delay, signal) { + scheduleKillProcess(delay: number, signal: number | NodeJS.Signals): void { this.killDelay = delay this.killSignal = signal } @@ -110,7 +82,7 @@ class Cli { * * @return {number} The exit code */ - getExitCode() { + getExitCode(): number | null { return this.exitCode } @@ -121,7 +93,7 @@ class Cli { * @param {string} [type=stdout] - The standard stream type * @returns {string} The captured output */ - getOutput(type = 'stdout') { + getOutput(type = 'stdout'): string { if (type === 'stdout') return this.stdout else if (type === 'stderr') return this.stderr @@ -137,7 +109,7 @@ class Cli { * - stdout is set to an empty string * - stderr is set to an empty string */ - reset() { + reset(): void { this.cwd = process.cwd() this.env = {} this.killDelay = 0 @@ -153,28 +125,28 @@ class Cli { * @param {string} rawCommand - The command string * @returns {Promise.} The resulting `Promise` */ - run(rawCommand) { + run(rawCommand: string): Promise { const [command, ...args] = rawCommand.split(' ') return new Promise((resolve, reject) => { // we inherit from current env vars // otherwise, we can have problem with PATH - const cmd = spawn(command, args, { + const cmd = spawn(command || '', args, { cwd: this.cwd, - env: Object.assign({}, process.env, this.env), + env: { ...process.env, ...this.env }, }) - let killer + let killer: NodeJS.Timeout | undefined let killed = false if (this.killSignal !== null) { killer = setTimeout(() => { - cmd.kill(this.killSignal) + cmd.kill(this.killSignal ?? undefined) killed = true }, this.killDelay) } - const cmdStdout = [] - const cmdStderr = [] + const cmdStdout: Uint8Array[] = [] + const cmdStderr: Uint8Array[] = [] cmd.stdout.on('data', cmdStdout.push.bind(cmdStdout)) cmd.stderr.on('data', cmdStderr.push.bind(cmdStderr)) @@ -186,7 +158,9 @@ class Cli { return reject( new Error( - `process.kill('${this.killSignal}') scheduled but process exited (delay: ${this.killDelay}ms)` + `process.kill('${ + this.killSignal || '' + }') scheduled but process exited (delay: ${this.killDelay}ms)` ) ) } @@ -197,22 +171,9 @@ class Cli { this.stdout = Buffer.concat(cmdStdout).toString() this.stderr = Buffer.concat(cmdStderr).toString() - resolve(true) + resolve() }) }) } } - -/** - * Create a new isolated Cli - * @return {Cli} - */ -module.exports = function (...args) { - return new Cli(...args) -} - -/** - * Cli extension. - * @type {Cli} - */ -module.exports.Cli = Cli +export const cli = new Cli() diff --git a/src/extensions/cli/definitions.js b/src/extensions/cli/definitions.js deleted file mode 100644 index c438601f..00000000 --- a/src/extensions/cli/definitions.js +++ /dev/null @@ -1,78 +0,0 @@ -'use strict' - -const { Given, Then, When } = require('@cucumber/cucumber') -const { expect } = require('chai') - -exports.install = () => { - Given(/^(?:I )?set (?:working directory|cwd) to (.+)$/, function (cwd) { - this.cli.setCwd(cwd) - }) - - Given( - /^(?:I )?set ([^ ]+) (?:env|environment) (?:var|variable) to (.+)$/, - function (name, value) { - this.cli.setEnvironmentVariable(name, value) - } - ) - - Given(/^(?:I )?set (?:env|environment) (?:vars|variables)$/, function (step) { - this.cli.setEnvironmentVariables(step.rowsHash()) - }) - - Given(/^(?:I )?kill the process with ([^ ]+) in (\d+)(ms|s)/, function (signal, _delay, unit) { - let delay = Number(_delay) - if (unit === 's') { - delay = delay * 1000 - } - - this.cli.scheduleKillProcess(delay, signal) - }) - - When(/^(?:I )?run command (.+)$/, function (command) { - return this.cli.run(command) - }) - - When(/^(?:I )?dump (stderr|stdout)$/, function (type) { - const output = this.cli.getOutput(type) - console.log(output) // eslint-disable-line no-console - }) - - Then(/^(?:the )?(?:command )?exit code should be (\d+)$/, function (expectedExitCode) { - const exitCode = this.cli.getExitCode() - - expect( - exitCode, - `The command exit code doesn't match expected ${expectedExitCode}, found: ${exitCode}` - ).to.equal(Number(expectedExitCode)) - }) - - Then(/^(stderr|stdout) should be empty$/, function (type) { - const output = this.cli.getOutput(type) - - expect(output).to.be.empty - }) - - Then(/^(stderr|stdout) should contain (.+)$/, function (type, expected) { - const output = this.cli.getOutput(type) - - expect(output).to.contain(expected) - }) - - Then(/^(stderr|stdout) should not contain (.+)$/, function (type, expected) { - const output = this.cli.getOutput(type) - - expect(output).to.not.contain(expected) - }) - - Then(/^(stderr|stdout) should match (.+)$/, function (type, regex) { - const output = this.cli.getOutput(type) - - expect(output).to.match(new RegExp(regex, 'gim')) - }) - - Then(/^(stderr|stdout) should not match (.+)$/, function (type, regex) { - const output = this.cli.getOutput(type) - - expect(output).to.not.match(new RegExp(regex, 'gim')) - }) -} diff --git a/src/extensions/cli/definitions.ts b/src/extensions/cli/definitions.ts new file mode 100644 index 00000000..a83038b9 --- /dev/null +++ b/src/extensions/cli/definitions.ts @@ -0,0 +1,86 @@ +import { DataTable, Given, Then, When } from '@cucumber/cucumber' +import { expect } from 'chai' +import { cli } from './' + +export const install = (): void => { + Given(/^(?:I )?set (?:working directory|cwd) to (.+)$/, function (cwd: string) { + cli.setCwd(cwd) + }) + + Given( + /^(?:I )?set ([^ ]+) (?:env|environment) (?:var|variable) to (.+)$/, + function (name: string, value: string) { + cli.setEnvironmentVariable(name, value) + } + ) + + Given(/^(?:I )?set (?:env|environment) (?:vars|variables)$/, function (step: DataTable) { + cli.setEnvironmentVariables(step.rowsHash()) + }) + + Given( + /^(?:I )?kill the process with ([^ ]+) in (\d+)(ms|s)/, + function (signal: number | NodeJS.Signals, _delay: number, unit: string) { + let delay = Number(_delay) + if (unit === 's') { + delay = delay * 1000 + } + + cli.scheduleKillProcess(delay, signal) + } + ) + + When(/^(?:I )?run command (.+)$/, function (command: string) { + return cli.run(command) + }) + + When(/^(?:I )?dump (stderr|stdout)$/, function (type: string) { + const output = cli.getOutput(type) + console.log(output) // eslint-disable-line no-console + }) + + Then( + /^(?:the )?(?:command )?exit code should be (\d+)$/, + function (expectedExitCode: string | number) { + const exitCode = cli.getExitCode() ?? -1 + + expect( + exitCode, + `The command exit code doesn't match expected ${expectedExitCode}, found: ${exitCode}` + ).to.equal(Number(expectedExitCode)) + } + ) + + Then(/^(stderr|stdout) should be empty$/, function (type: string) { + const output = cli.getOutput(type) + + expect(output).to.be.empty + }) + + Then(/^(stderr|stdout) should contain (.+)$/, function (type: string, expected: string) { + const output = cli.getOutput(type) + + expect(output).to.contain(expected) + }) + + Then(/^(stderr|stdout) should not contain (.+)$/, function (type: string, expected: string) { + const output = cli.getOutput(type) + + expect(output).to.not.contain(expected) + }) + + Then(/^(stderr|stdout) should match (.+)$/, function (type: string, regex: RegExp | string) { + const output = cli.getOutput(type) + + expect(output).to.match(new RegExp(regex, 'gim')) + }) + + Then( + /^(stderr|stdout) should not match (.+)$/, + function (type: string, regex: RegExp | string) { + const output = cli.getOutput(type) + + expect(output).to.not.match(new RegExp(regex, 'gim')) + } + ) +} diff --git a/src/extensions/cli/extend_world.js b/src/extensions/cli/extend_world.js deleted file mode 100644 index 46d01b91..00000000 --- a/src/extensions/cli/extend_world.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -const Registry = require('../../core/registry') -const Cli = require('./cli') - -module.exports = (world) => { - if (!Registry.hasExtension(world, 'state')) { - throw new Error( - `Unable to init "cli" extension as it requires "state" extension which is not installed` - ) - } - - world.cli = Cli() - Registry.registerExtension(world, 'cli') -} diff --git a/src/extensions/cli/extend_world.ts b/src/extensions/cli/extend_world.ts new file mode 100644 index 00000000..f5125c10 --- /dev/null +++ b/src/extensions/cli/extend_world.ts @@ -0,0 +1,14 @@ +import { Cli } from './cli' +import { VeggiesWorld } from '../../core/core_types' +import { hasExtension, registerExtension } from '../../core/registry' + +export const extendWorld = (world: VeggiesWorld): void => { + if (!hasExtension(world, 'state')) { + throw new Error( + `Unable to init "cli" extension as it requires "state" extension which is not installed` + ) + } + + world.cli = new Cli() + registerExtension(world, 'cli') +} diff --git a/src/extensions/cli/index.js b/src/extensions/cli/index.ts similarity index 85% rename from src/extensions/cli/index.js rename to src/extensions/cli/index.ts index fec5d3f1..b4003fa0 100644 --- a/src/extensions/cli/index.js +++ b/src/extensions/cli/index.ts @@ -1,10 +1,8 @@ -'use strict' - /** * @module extensions/Cli */ -const definitions = require('./definitions') +import * as definitions from './definitions' /** * Extends cucumber world object. @@ -24,7 +22,7 @@ const definitions = require('./definitions') * @function * @param {Object} world - The cucumber world object */ -exports.extendWorld = require('./extend_world') +export { extendWorld } from './extend_world' /** * Installs the extension. @@ -43,6 +41,8 @@ exports.extendWorld = require('./extend_world') * state.install() * cli.install() */ -exports.install = () => { +export const install = (): void => { definitions.install() } + +export { cli, Cli } from './cli' diff --git a/src/extensions/file_system/definitions.js b/src/extensions/file_system/definitions.js deleted file mode 100644 index 3ac1d414..00000000 --- a/src/extensions/file_system/definitions.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict' - -const { Given, Then } = require('@cucumber/cucumber') -const { expect } = require('chai') - -exports.install = () => { - /** - * Creating a directory. - */ - Given(/^(?:I )?create directory (.+)$/, function (directory) { - return this.fileSystem.createDirectory(this.cli.getCwd(), directory) - }) - - /** - * Remove a file or directory. - */ - Given(/^(?:I )?remove (?:file|directory) (.+)$/, function (fileOrDirectory) { - return this.fileSystem.remove(this.cli.getCwd(), fileOrDirectory) - }) - - /** - * Checking file/directory presence. - */ - Then(/^(file|directory) (.+) should (not )?exist$/, function (type, file, flag) { - return this.fileSystem.getFileInfo(this.cli.getCwd(), file).then((info) => { - if (flag === 'not ') { - expect(info, `${type} '${file}' exists`).to.be.null - } else { - expect(info, `${type} '${file}' does not exist`).not.to.be.null - if (type === 'file') { - expect(info.isFile(), `'${file}' is not a file`).to.be.true - } else { - expect(info.isDirectory(), `'${file}' is not a directory`).to.be.true - } - } - }) - }) - - /** - * Checking file content. - */ - Then( - /^file (.+) content should (not )?(equal|contain|match) (.+)$/, - function (file, flag, comparator, expectedValue) { - return this.fileSystem - .getFileContent(this.cli.getCwd(), file) - .then((content) => { - let expectFn = expect( - content, - `Expected file '${file}' to ${ - flag ? flag : '' - }${comparator} '${expectedValue}', but found '${content}' which does${ - flag ? '' : ' not' - }` - ).to - if (flag != undefined) { - expectFn = expectFn.not - } - - expectFn[comparator]( - comparator === 'match' ? new RegExp(expectedValue) : expectedValue - ) - }) - .catch((err) => { - if (err.code === 'ENOENT') - return expect.fail('', '', `File '${file}' should exist`) - - return Promise.reject(err) - }) - } - ) -} diff --git a/src/extensions/file_system/definitions.ts b/src/extensions/file_system/definitions.ts new file mode 100644 index 00000000..c356fb46 --- /dev/null +++ b/src/extensions/file_system/definitions.ts @@ -0,0 +1,78 @@ +import { Given, Then } from '@cucumber/cucumber' +import { expect } from 'chai' +import { cli } from '../cli' +import * as fileSystem from './file_system' + +export const install = (): void => { + /** + * Creating a directory. + */ + Given(/^(?:I )?create directory (.+)$/, function (directory: string) { + return fileSystem.createDirectory(cli.getCwd(), directory) + }) + + /** + * Remove a file or directory. + */ + Given(/^(?:I )?remove (?:file|directory) (.+)$/, function (fileOrDirectory: string) { + return fileSystem.remove(cli.getCwd(), fileOrDirectory) + }) + + /** + * Checking file/directory presence. + */ + Then( + /^(file|directory) (.+) should (not )?exist$/, + function (type: string, file: string, flag: string) { + return fileSystem.getFileInfo(cli.getCwd(), file).then((info) => { + if (flag === 'not ') { + expect(info, `${type} '${file}' exists`).to.be.null + } else { + expect(info, `${type} '${file}' does not exist`).not.to.be.null + if (type === 'file') { + expect(info?.isFile(), `'${file}' is not a file`).to.be.true + } else { + expect(info?.isDirectory(), `'${file}' is not a directory`).to.be.true + } + } + }) + } + ) + + /** + * Checking file content. + */ + Then( + /^file (.+) content should (not )?(equal|contain|match) (.+)$/, + function (file: string, flag: string, comparator: string, expectedValue: string) { + return fileSystem + .getFileContent(cli.getCwd(), file) + .then((content) => { + let expectFn = expect( + content, + `Expected file '${file}' to ${ + flag ? flag : '' + }${comparator} '${expectedValue}', but found '${content}' which does${ + flag ? '' : ' not' + }` + ).to + if (flag != undefined) { + expectFn = expectFn.not + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + expectFn[comparator]( + comparator === 'match' ? new RegExp(expectedValue) : expectedValue + ) + }) + .catch((err: NodeJS.ErrnoException) => { + if (err.code === 'ENOENT') + return expect.fail('', '', `File '${file}' should exist`) + + return Promise.reject(err) + }) + } + ) +} diff --git a/src/extensions/file_system/extend_world.js b/src/extensions/file_system/extend_world.js deleted file mode 100644 index e1ad8225..00000000 --- a/src/extensions/file_system/extend_world.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -const Registry = require('../../core/registry') -const fileSystem = require('./file_system') - -module.exports = (world) => { - if (!Registry.hasExtension(world, 'cli')) { - throw new Error( - `Unable to init "file_system" extension as it requires "cli" extension which is not installed` - ) - } - - world.fileSystem = fileSystem - Registry.registerExtension(world, 'file_system') -} diff --git a/src/extensions/file_system/extend_world.ts b/src/extensions/file_system/extend_world.ts new file mode 100644 index 00000000..ba9eb119 --- /dev/null +++ b/src/extensions/file_system/extend_world.ts @@ -0,0 +1,15 @@ +import { VeggiesWorld } from '../../core/core_types' +import { hasExtension, registerExtension } from '../../core/registry' + +import * as fileSystem from './file_system' + +export const extendWorld = (world: VeggiesWorld): void => { + if (!hasExtension(world, 'cli')) { + throw new Error( + `Unable to init "file_system" extension as it requires "cli" extension which is not installed` + ) + } + + world.fileSystem = fileSystem + registerExtension(world, 'file_system') +} diff --git a/src/extensions/file_system/file_system.js b/src/extensions/file_system/file_system.ts similarity index 60% rename from src/extensions/file_system/file_system.js rename to src/extensions/file_system/file_system.ts index 3729c416..83c08bf2 100644 --- a/src/extensions/file_system/file_system.js +++ b/src/extensions/file_system/file_system.ts @@ -1,13 +1,11 @@ -'use strict' - /** * The FileSystem helper used by the FileSystem extension. * * @module extensions/FileSystem/FileSystem */ -const path = require('path') -const fs = require('fs-extra') +import path from 'path' +import { mkdirs, readFile, stat, Stats, remove as fsRemove } from 'fs-extra' /** * Loads file content. @@ -17,9 +15,13 @@ const fs = require('fs-extra') * @param {string} [encoding='utf8'] - Content encoding * @return {Promise.} File content */ -exports.getFileContent = (cwd, file, encoding = 'utf8') => +export const getFileContent = ( + cwd: string, + file: string, + encoding: BufferEncoding = 'utf8' +): Promise => new Promise((resolve, reject) => { - fs.readFile(path.join(cwd, file), (err, data) => { + readFile(path.join(cwd, file), (err, data) => { if (err) return reject(err) resolve(data.toString(encoding)) }) @@ -30,17 +32,17 @@ exports.getFileContent = (cwd, file, encoding = 'utf8') => * * @param {string} cwd - Current Working Directory * @param {string} file - File name - * @return {Promise.} File/directory info or null if file/directory does not exist + * @return {Promise.} File/directory info or null if file/directory does not exist */ -exports.getFileInfo = (cwd, file) => +export const getFileInfo = (cwd: string, file: string): Promise => new Promise((resolve, reject) => { - fs.stat(path.join(cwd, file), (err, stat) => { + stat(path.join(cwd, file), (err, stats) => { if (err) { if (err.code === 'ENOENT') return resolve(null) return reject(err) } - resolve(stat) + resolve(stats) }) }) @@ -51,7 +53,8 @@ exports.getFileInfo = (cwd, file) => * @param {string} directory - Directory name * @return {Promise.} */ -exports.createDirectory = (cwd, directory) => fs.mkdirs(path.join(cwd, directory)) +export const createDirectory = (cwd: string, directory: string): Promise => + mkdirs(path.join(cwd, directory)) /** * Removes a file or directory. @@ -60,4 +63,5 @@ exports.createDirectory = (cwd, directory) => fs.mkdirs(path.join(cwd, directory * @param {string} fileOrDirectory - File or directory name * @return {Promise.} */ -exports.remove = (cwd, fileOrDirectory) => fs.remove(path.join(cwd, fileOrDirectory)) +export const remove = (cwd: string, fileOrDirectory: string): Promise => + fsRemove(path.join(cwd, fileOrDirectory)) diff --git a/src/extensions/file_system/file_system_types.ts b/src/extensions/file_system/file_system_types.ts new file mode 100644 index 00000000..374a3b35 --- /dev/null +++ b/src/extensions/file_system/file_system_types.ts @@ -0,0 +1,8 @@ +import { Stats } from 'fs-extra' + +export interface FileSystem { + getFileContent(cwd: string, file: string, encoding: BufferEncoding): Promise + getFileInfo(cwd: string, file: string): Promise + createDirectory(cwd: string, directory: string): Promise + remove(cwd: string, fileOrDirectory: string): Promise +} diff --git a/src/extensions/file_system/index.js b/src/extensions/file_system/index.ts similarity index 84% rename from src/extensions/file_system/index.js rename to src/extensions/file_system/index.ts index e387f189..b5fd2da0 100644 --- a/src/extensions/file_system/index.js +++ b/src/extensions/file_system/index.ts @@ -1,10 +1,8 @@ -'use strict' - /** * @module extensions/FileSystem */ -const definitions = require('./definitions') +import * as definitions from './definitions' /** * Extends cucumber world object. @@ -25,7 +23,7 @@ const definitions = require('./definitions') * @function * @param {Object} world - The cucumber world object */ -exports.extendWorld = require('./extend_world') +export { extendWorld } from './extend_world' /** * Installs the extension. @@ -46,6 +44,8 @@ exports.extendWorld = require('./extend_world') * cli.install() * fileSystem.install() */ -exports.install = () => { +export const install = (): void => { definitions.install() } + +export { getFileContent, getFileInfo, createDirectory, remove } from './file_system' diff --git a/src/extensions/fixtures/extend_world.js b/src/extensions/fixtures/extend_world.js deleted file mode 100644 index 0809ab2d..00000000 --- a/src/extensions/fixtures/extend_world.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const Registry = require('../../core/registry') -const Loader = require('./fixtures_loader') - -module.exports = (world, options) => { - world.fixtures = Loader(options) - Registry.registerExtension(world, 'fixtures') -} diff --git a/src/extensions/fixtures/extend_world.ts b/src/extensions/fixtures/extend_world.ts new file mode 100644 index 00000000..184c7a78 --- /dev/null +++ b/src/extensions/fixtures/extend_world.ts @@ -0,0 +1,8 @@ +import { VeggiesWorld } from '../../core/core_types' +import { registerExtension } from '../../core/registry' +import { FixturesOptions, Fixtures } from './fixtures' + +export const extendWorld = (world: VeggiesWorld, options?: FixturesOptions): void => { + world.fixtures = new Fixtures(options) + registerExtension(world, 'fixtures') +} diff --git a/src/extensions/fixtures/fixtures_loader.js b/src/extensions/fixtures/fixtures.ts similarity index 84% rename from src/extensions/fixtures/fixtures_loader.js rename to src/extensions/fixtures/fixtures.ts index a5911837..1136e2a7 100644 --- a/src/extensions/fixtures/fixtures_loader.js +++ b/src/extensions/fixtures/fixtures.ts @@ -1,21 +1,26 @@ -'use strict' - /** * @module extensions/fixtures/FixturesLoader */ -const fs = require('fs') -const path = require('path') -const glob = require('glob') -const yaml = require('js-yaml') -const _ = require('lodash') +import fs from 'fs' +import path from 'path' +import glob from 'glob' +import { load } from 'js-yaml' +import _ from 'lodash' + +export type FixturesOptions = { + fixturesDir: string +} /** * Fixtures loader extension. * * @class */ -class FixturesLoader { +export class Fixtures { + public fixturesDir: string + public featureUri?: string + /** * @param {string} [fixturesDir='fixtures'] - The name of the fixtures directory relative to feature */ @@ -29,7 +34,7 @@ class FixturesLoader { * * @param {string} [fixturesDir='fixtures'] - The name of the fixtures directory relative to feature */ - configure({ fixturesDir } = { fixturesDir: 'fixtures' }) { + configure({ fixturesDir } = { fixturesDir: 'fixtures' }): void { this.fixturesDir = fixturesDir } @@ -42,7 +47,7 @@ class FixturesLoader { * * @param {string} featureUri - Feature uri */ - setFeatureUri(featureUri) { + setFeatureUri(featureUri?: string): void { this.featureUri = featureUri } @@ -52,7 +57,7 @@ class FixturesLoader { * @param {string} file - File path * @return {Promise.} File content */ - loadText(file) { + loadText(file: string): Promise { return new Promise((resolve, reject) => { fs.readFile(file, (err, data) => { if (err) return reject(err) @@ -67,10 +72,10 @@ class FixturesLoader { * @param {string} file - File path * @return {Promise.} Parsed yaml data */ - loadYaml(file) { + loadYaml(file: string): Promise { return this.loadText(file).then((content) => { try { - const data = yaml.load(content) + const data = load(content) if (data === undefined) { return Promise.reject( new Error( @@ -94,12 +99,10 @@ class FixturesLoader { * @param {string} file - File path * @return {Promise.} Json data */ - loadJson(file) { + loadJson(file: string): Promise> { return this.loadText(file).then((content) => { try { - const data = JSON.parse(content) - - return data + return JSON.parse(content) as Record } catch (err) { return Promise.reject( new Error(`Unable to parse json fixture file: ${file}.\nerror: ${err.message}`) @@ -114,9 +117,10 @@ class FixturesLoader { * @param {string} file - File path * @return {Promise.<*>} Data generated from the module */ - loadModule(file) { + loadModule(file: string): Promise { try { const relativePath = path.relative(__dirname, file) + // eslint-disable-next-line @typescript-eslint/no-var-requires,@typescript-eslint/no-unsafe-assignment const mod = require(relativePath) if (!_.isFunction(mod)) { @@ -152,7 +156,7 @@ class FixturesLoader { * @param {string} fixture - Fixture file name without extension * @return {Promise.} Fixture content */ - load(fixture) { + load(fixture: string): Promise { if (this.featureUri === undefined) return Promise.reject( new Error(`Cannot load fixture: ${fixture}, no feature uri defined`) @@ -162,7 +166,7 @@ class FixturesLoader { const pattern = `${featureDir}/${this.fixturesDir}/${fixture}.@(yaml|yml|js|json|txt)` return new Promise((resolve, reject) => { - glob(pattern, (err, files) => { + glob(pattern, (err: Error, files: string[]) => { const fixturesCount = files.length if (fixturesCount === 0) @@ -179,7 +183,7 @@ class FixturesLoader { ) } - const fixtureFile = files[0] + const fixtureFile = files[0] || '' const ext = path.extname(fixtureFile).substr(1) switch (ext) { @@ -203,21 +207,13 @@ class FixturesLoader { /** * Resets fixtures loader. */ - reset() { + reset(): void { this.featureUri = undefined } } /** * Create a new isolated fixtures loader - * @return {FixturesLoader} - */ -module.exports = function (...args) { - return new FixturesLoader(...args) -} - -/** - * fixtures loader extension. - * @type {FixturesLoader} + * @return {Fixtures} */ -module.exports.Fixture = FixturesLoader +export const fixtures = new Fixtures() diff --git a/src/extensions/fixtures/hooks.js b/src/extensions/fixtures/hooks.js deleted file mode 100644 index ef60c07e..00000000 --- a/src/extensions/fixtures/hooks.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -const { Before } = require('@cucumber/cucumber') - -/** - * Registers hooks for the fixtures extension. - * - * @module extensions/fixtures/hooks - */ - -exports.install = () => { - Before(function (scenarioInfos) { - this.fixtures.setFeatureUri(scenarioInfos.gherkinDocument.uri) - }) -} diff --git a/src/extensions/fixtures/hooks.ts b/src/extensions/fixtures/hooks.ts new file mode 100644 index 00000000..34f4a22b --- /dev/null +++ b/src/extensions/fixtures/hooks.ts @@ -0,0 +1,14 @@ +import { Before } from '@cucumber/cucumber' +import { fixtures } from './fixtures' + +/** + * Registers hooks for the fixtures extension. + * + * @module extensions/fixtures/hooks + */ + +export const install = (): void => { + Before(function (scenarioInfos) { + fixtures.setFeatureUri(scenarioInfos.gherkinDocument.uri) + }) +} diff --git a/src/extensions/fixtures/index.js b/src/extensions/fixtures/index.ts similarity index 82% rename from src/extensions/fixtures/index.js rename to src/extensions/fixtures/index.ts index a46af146..f110fe15 100644 --- a/src/extensions/fixtures/index.js +++ b/src/extensions/fixtures/index.ts @@ -1,10 +1,8 @@ -'use strict' - /** * @module extensions/fixtures */ -const hooks = require('./hooks') +import * as hooks from './hooks' /** * Extends cucumber world object. @@ -23,7 +21,7 @@ const hooks = require('./hooks') * @function * @param {Object} world - The cucumber world object */ -exports.extendWorld = require('./extend_world') +export { extendWorld } from './extend_world' /** * Installs the extension. @@ -40,6 +38,8 @@ exports.extendWorld = require('./extend_world') * * fixtures.install(defineSupportCode) */ -exports.install = () => { +export const install = (): void => { hooks.install() } + +export { fixtures, Fixtures } from './fixtures' diff --git a/src/extensions/http_api/client.js b/src/extensions/http_api/client.ts similarity index 73% rename from src/extensions/http_api/client.js rename to src/extensions/http_api/client.ts index 71d8fd28..f8e78849 100644 --- a/src/extensions/http_api/client.js +++ b/src/extensions/http_api/client.ts @@ -1,14 +1,17 @@ -'use strict' - /** * The http client used by the http API extension. * * @module extensions/httpApi/client */ -const _ = require('lodash') -const request = require('request').defaults({ json: true }) -const { Cookie } = require('tough-cookie') +import _ from 'lodash' +import { Cookie } from 'tough-cookie' +import request, { CookieJar, Response } from 'request' +import { Headers, RequestOptions } from './http_api_types' +import Properties = Cookie.Properties +import { CastedValue } from '../../core/core_types' + +request.defaults({ json: true }) const BODY_TYPE_JSON = 'json' const BODY_TYPE_FORM = 'form' @@ -21,26 +24,25 @@ const verbsAcceptingBody = ['POST', 'PUT', 'DELETE', 'PATCH'] * * @class */ -class HttpApiClient { - constructor() { - // REQUEST INFORMATION - this.body = null - this.bodyType = null - this.headers = null - this.query = null - this.cookies = [] - this.cookieJar = null - this.followRedirect = true +export class HttpApiClient { + public body: object | null + public bodyType: string | null + public headers: Headers | null + public query: object | null + public cookies: Properties[] + public cookieJar: CookieJar | null + public followRedirect: boolean + public response: Response | null + public responseCookies: Record - // RESPONSE INFORMATION - this.response = null - this.responseCookies = {} + constructor() { + this.reset() } /** * Resets the client. */ - reset() { + reset(): void { this.body = null this.bodyType = null this.headers = null @@ -59,7 +61,7 @@ class HttpApiClient { * * @param {Object} payload */ - setJsonBody(payload) { + setJsonBody(payload: object): void { this.bodyType = BODY_TYPE_JSON this.body = payload } @@ -69,7 +71,7 @@ class HttpApiClient { * * @param {Object} payload */ - setFormBody(payload) { + setFormBody(payload: object): void { this.bodyType = BODY_TYPE_FORM this.body = payload } @@ -78,7 +80,7 @@ class HttpApiClient { * Sets Follow Redirect option. * */ - setFollowRedirect(isEnabled) { + setFollowRedirect(isEnabled: boolean): void { this.followRedirect = isEnabled } @@ -87,7 +89,7 @@ class HttpApiClient { * * @param {Object} payload */ - setMultipartBody(payload) { + setMultipartBody(payload: object): void { this.bodyType = BODY_TYPE_MULTIPART this.body = payload } @@ -95,7 +97,7 @@ class HttpApiClient { /** * Clears current request body */ - clearBody() { + clearBody(): void { this.body = null this.bodyType = null } @@ -105,7 +107,7 @@ class HttpApiClient { * * @param {Object} query */ - setQuery(query) { + setQuery(query: object): void { this.query = query } @@ -114,7 +116,7 @@ class HttpApiClient { * * @param {Object} headers */ - setHeaders(headers) { + setHeaders(headers: Record): void { this.headers = headers } @@ -124,7 +126,7 @@ class HttpApiClient { * @param {string} key * @param {string} value */ - setHeader(key, value) { + setHeader(key: string, value: CastedValue): void { this.headers = this.headers || {} this.headers[key] = value } @@ -132,24 +134,25 @@ class HttpApiClient { /** * Clears current request headers. */ - clearHeaders() { + clearHeaders(): void { this.headers = null } /** * Enables cookie jar. */ - enableCookies() { + enableCookies(): void { if (this.cookieJar !== null) return this.cookieJar = request.jar() - this.cookieJar._jar.rejectPublicSuffixes = false + // Property '_jar' does not exist on type 'CookieJar'. + // this.cookieJar._jar.rejectPublicSuffixes = false } /** * Disables cookie jar. */ - disableCookies() { + disableCookies(): void { this.cookieJar = null } @@ -161,7 +164,7 @@ class HttpApiClient { * * @param {string|Object} cookie - Cookie string or Object */ - setCookie(cookie) { + setCookie(cookie: Properties): void { if (!_.isPlainObject(cookie) && !_.isString(cookie)) { throw new TypeError(`"cookie" must be a string or a cookie object`) } @@ -174,7 +177,7 @@ class HttpApiClient { * Clears registered request cookies. * Be aware that it does not clear existing response cookies. */ - clearRequestCookies() { + clearRequestCookies(): void { this.cookies = [] } @@ -184,9 +187,9 @@ class HttpApiClient { * @param {string} key - Cookie key * @return {Object|null} The cookie object if any, or null */ - getCookie(key) { - if (this.responseCookies === null) return null - if (this.responseCookies[key] === undefined) return null + getCookie(key: string): Cookie | undefined { + if (this.responseCookies === null) return undefined + if (this.responseCookies[key] === undefined) return undefined return this.responseCookies[key] } @@ -196,14 +199,14 @@ class HttpApiClient { * * @return {Object} current response cookies */ - getCookies() { + getCookies(): Record { return this.responseCookies } /** * Returns the latest collected response. */ - getResponse() { + getResponse(): Response | null { return this.response } @@ -217,15 +220,15 @@ class HttpApiClient { * @param {string} path - The path * @param {string} [baseUrl] - The base url */ - makeRequest(method, path, baseUrl) { + makeRequest(method: string, path: string, baseUrl: string): Promise { return new Promise((resolve, reject) => { - const options = { + const options: RequestOptions = { baseUrl: baseUrl, uri: path, method, qs: this.query || {}, - headers: this.headers, - jar: this.cookieJar, + headers: this.headers || undefined, + jar: this.cookieJar || undefined, followRedirect: this.followRedirect, } @@ -253,9 +256,9 @@ class HttpApiClient { if (this.cookieJar !== null) { this.cookies.forEach((cookie) => { if (_.isPlainObject(cookie)) { - this.cookieJar.setCookie(new Cookie(cookie), fullUri) + this.cookieJar?.setCookie(new Cookie(cookie), fullUri) } else if (_.isString(cookie)) { - this.cookieJar.setCookie(cookie, fullUri) + this.cookieJar?.setCookie(cookie, fullUri) } }) } @@ -285,12 +288,5 @@ class HttpApiClient { * Create a new isolated http api client * @return {HttpApiClient} */ -module.exports = function (...args) { - return new HttpApiClient(...args) -} -/** - * Http api client extension. - * @type {HttpApiClient} - */ -module.exports.HttpApiClient = HttpApiClient +export const httpApiClient = new HttpApiClient() diff --git a/src/extensions/http_api/definitions.js b/src/extensions/http_api/definitions.js deleted file mode 100644 index 2043cde1..00000000 --- a/src/extensions/http_api/definitions.js +++ /dev/null @@ -1,369 +0,0 @@ -'use strict' - -const { Given, Then, When } = require('@cucumber/cucumber') -const { inspect } = require('util') -const { expect } = require('chai') -const _ = require('lodash') - -const Cast = require('../../core/cast') -const { assertObjectMatchSpec } = require('../../core/assertions') - -const { STATUS_CODES } = require('http') -const { parseMatchExpression } = require('./utils') -const STATUS_MESSAGES = _.values(STATUS_CODES).map(_.lowerCase) - -/** - * Ensures there's a response available and returns it. - * - * @param {Object} client - */ -const mustGetResponse = (client) => { - const response = client.getResponse() - expect(response, 'No response available').to.not.be.empty - - return response -} - -exports.install = ({ baseUrl = '' } = {}) => { - /** - * Setting http headers - */ - Given(/^(?:I )?set request headers$/, function (step) { - this.httpApiClient.setHeaders(Cast.object(this.state.populateObject(step.rowsHash()))) - }) - - /** - * Setting http option followRedirect to false - */ - Given(/^(?:I )?do not follow redirect$/, function () { - this.httpApiClient.setFollowRedirect(false) - }) - - /** - * Setting http option followRedirect to true - */ - Given(/^(?:I )?follow redirect$/, function () { - this.httpApiClient.setFollowRedirect(true) - }) - - /** - * Assign http headers - * The difference from "set request headers" is that "set" set the whole headers object - * "assign" replace or set the given headers, keeping untouched the ones already set - */ - Given(/^(?:I )?assign request headers$/, function (step) { - const headers = Cast.object(this.state.populateObject(step.rowsHash())) - _.each(headers, (value, key) => this.httpApiClient.setHeader(key, value)) - }) - - /** - * Setting a single http header - */ - Given(/^(?:I )?set ([a-zA-Z0-9-_]+) request header to (.+)$/, function (key, value) { - this.httpApiClient.setHeader(key, Cast.value(this.state.populate(value))) - }) - - /** - * Clearing headers - */ - Given(/^(?:I )?clear request headers/, function () { - this.httpApiClient.clearHeaders() - }) - - /** - * Setting json payload - */ - Given(/^(?:I )?set request json body$/, function (step) { - this.httpApiClient.setJsonBody(Cast.object(this.state.populateObject(step.rowsHash()))) - }) - - /** - * Setting json payload from fixture file - */ - Given(/^(?:I )?set request json body from (.+)$/, function (fixture) { - return this.fixtures.load(fixture).then((data) => { - this.httpApiClient.setJsonBody(data) - }) - }) - - /** - * Setting form data - */ - Given(/^(?:I )?set request form body$/, function (step) { - this.httpApiClient.setFormBody(Cast.object(this.state.populateObject(step.rowsHash()))) - }) - - /** - * Setting form data from fixture file - */ - Given(/^(?:I )?set request form body from (.+)$/, function (fixture) { - return this.fixtures.load(fixture).then((data) => { - this.httpApiClient.setFormBody(data) - }) - }) - - /** - * Setting multipart data from fixture file - */ - Given(/^(?:I )?set request multipart body from (.+)$/, function (fixture) { - return this.fixtures.load(fixture).then((data) => { - this.httpApiClient.setMultipartBody(data) - }) - }) - - /** - * Clearing body - */ - Given(/^(?:I )?clear request body$/, function () { - this.httpApiClient.clearBody() - }) - - /** - * Setting query parameters - */ - Given(/^(?:I )?set request query$/, function (step) { - this.httpApiClient.setQuery(Cast.object(this.state.populateObject(step.rowsHash()))) - }) - - Given(/^(?:I )?pick response json (.+) as (.+)$/, function (path, key) { - const response = this.httpApiClient.getResponse() - const body = response.body - - this.state.set(key, _.get(body, path)) - }) - - /** - * Enabling cookies - */ - Given(/^(?:I )?enable cookies$/, function () { - this.httpApiClient.enableCookies() - }) - - /** - * Disabling cookies - */ - Given(/^(?:I )?disable cookies$/, function () { - this.httpApiClient.disableCookies() - }) - - /** - * Setting a cookie from fixture file - */ - Given(/^(?:I )?set cookie from (.+)$/, function (fixture) { - return this.fixtures.load(fixture).then((cookie) => { - this.httpApiClient.setCookie(cookie) - }) - }) - - /** - * Clearing client request cookies - */ - Given(/^(?:I )?clear request cookies$/, function () { - this.httpApiClient.clearRequestCookies() - }) - - /** - * Resetting the client's state - */ - When(/^(?:I )?reset http client$/, function () { - this.httpApiClient.reset() - }) - - /** - * Performing a request - */ - When(/^(?:I )?(GET|POST|PUT|DELETE|PATCH) (.+)$/, function (method, path) { - return this.httpApiClient.makeRequest(method, this.state.populate(path), baseUrl) - }) - - /** - * Dumping response body - */ - When(/^(?:I )?dump response body$/, function () { - const response = mustGetResponse(this.httpApiClient) - console.log(inspect(response.body, { colors: true, depth: null })) // eslint-disable-line no-console - }) - - /** - * Dumping response headers - */ - When(/^(?:I )?dump response headers$/, function () { - const response = mustGetResponse(this.httpApiClient) - console.log(response.headers) // eslint-disable-line no-console - }) - - /** - * Dumping response cookies - */ - When(/^(?:I )?dump response cookies$/, function () { - mustGetResponse(this.httpApiClient) - console.log(this.httpApiClient.getCookies()) // eslint-disable-line no-console - }) - - /** - * Checking response status code - */ - Then(/^response status code should be ([1-5][0-9][0-9])$/, function (statusCode) { - const response = mustGetResponse(this.httpApiClient) - expect( - response.statusCode, - `Expected status code to be: ${statusCode}, but found: ${response.statusCode}` - ).to.equal(Number(statusCode)) - }) - - /** - * Checking response status by message - */ - Then(/^response status should be (.+)$/, function (statusMessage) { - if (!STATUS_MESSAGES.includes(_.lowerCase(statusMessage))) { - throw new TypeError(`'${statusMessage}' is not a valid status message`) - } - - const response = mustGetResponse(this.httpApiClient) - const statusCode = _.findKey(STATUS_CODES, (msg) => _.lowerCase(msg) === statusMessage) - const currentStatusMessage = STATUS_CODES[`${response.statusCode}`] || response.statusCode - - expect( - response.statusCode, - `Expected status to be: '${statusMessage}', but found: '${_.lowerCase( - currentStatusMessage - )}'` - ).to.equal(Number(statusCode)) - }) - - /** - * Checking response cookie is present|absent - */ - Then(/^response should (not )?have an? (.+) cookie$/, function (flag, key) { - const cookie = this.httpApiClient.getCookie(key) - - if (flag == undefined) { - expect(cookie, `No cookie found for key '${key}'`).to.not.be.null - } else { - expect(cookie, `A cookie exists for key '${key}'`).to.be.null - } - }) - - /** - * Checking response cookie is|isn't secure - */ - Then(/^response (.+) cookie should (not )?be secure$/, function (key, flag) { - const cookie = this.httpApiClient.getCookie(key) - expect(cookie, `No cookie found for key '${key}'`).to.not.be.null - - if (flag == undefined) { - expect(cookie.secure, `Cookie '${key}' is not secure`).to.be.true - } else { - expect(cookie.secure, `Cookie '${key}' is secure`).to.be.false - } - }) - - /** - * Checking response cookie httpOnly - */ - Then(/^response (.+) cookie should (not )?be http only$/, function (key, flag) { - const cookie = this.httpApiClient.getCookie(key) - expect(cookie, `No cookie found for key '${key}'`).to.not.be.null - - if (flag == undefined) { - expect(cookie.httpOnly, `Cookie '${key}' is not http only`).to.be.true - } else { - expect(cookie.httpOnly, `Cookie '${key}' is http only`).to.be.false - } - }) - - /** - * Checking response cookie domain - */ - Then(/^response (.+) cookie domain should (not )?be (.+)$/, function (key, flag, domain) { - const cookie = this.httpApiClient.getCookie(key) - expect(cookie, `No cookie found for key '${key}'`).to.not.be.null - - if (flag == undefined) { - expect( - cookie.domain, - `Expected cookie '${key}' domain to be '${domain}', found '${cookie.domain}'` - ).to.equal(domain) - } else { - expect(cookie.domain, `Cookie '${key}' domain is '${domain}'`).to.not.equal(domain) - } - }) - - /** - * This definition can be used for checking an object response. - * It check that the properties of this object match with the expected properties - * The columns header are | field | matcher | value | - * @see Assertions.assertObjectMatchSpec - */ - Then(/^(?:I )?json response should (fully )?match$/, function (fully, table) { - const response = mustGetResponse(this.httpApiClient) - const { body } = response - - // We check the response has json content-type - expect(response.headers['content-type']).to.contain('application/json') - - // First we populate spec values if it contains some placeholder - const specifications = table.hashes().map((fieldSpec) => { - const spec = fieldSpec.expression - ? parseMatchExpression(fieldSpec.expression) - : fieldSpec - return _.assign({}, spec, { - value: this.state.populate(spec.value), - }) - }) - - assertObjectMatchSpec(body, specifications, !!fully) - }) - - /** - * This definition verify that an array for a given path has the expected length - */ - Then( - /^(?:I )?should receive a collection of ([0-9]+) items?(?: for path )?(.+)?$/, - function (size, path) { - const response = mustGetResponse(this.httpApiClient) - const { body } = response - - const array = path != undefined ? _.get(body, path) : body - - expect(array.length).to.be.equal(Number(size)) - } - ) - - /** - * Verifies that response matches a fixture. - **/ - Then(/^response should match fixture (.+)$/, function (fixtureId) { - const response = mustGetResponse(this.httpApiClient) - - return this.fixtures.load(fixtureId).then((snapshot) => { - expect(response.body).to.deep.equal(snapshot) - }) - }) - - /** - * Checking response header. - */ - Then( - /^response header (.+) should (not )?(equal|contain|match) (.+)$/, - function (key, flag, comparator, expectedValue) { - const response = mustGetResponse(this.httpApiClient) - const header = response.headers[key.toLowerCase()] - - expect(header, `Header '${key}' does not exist`).to.not.be.undefined - - let expectFn = expect( - header, - `Expected header '${key}' to ${ - flag ? flag : '' - }${comparator} '${expectedValue}', but found '${header}' which does${ - flag ? '' : ' not' - }` - ).to - if (flag != undefined) { - expectFn = expectFn.not - } - expectFn[comparator](comparator === 'match' ? new RegExp(expectedValue) : expectedValue) - } - ) -} diff --git a/src/extensions/http_api/definitions.ts b/src/extensions/http_api/definitions.ts new file mode 100644 index 00000000..7942d769 --- /dev/null +++ b/src/extensions/http_api/definitions.ts @@ -0,0 +1,392 @@ +import { DataTable, Given, Then, When } from '@cucumber/cucumber' +import { inspect } from 'util' +import { expect } from 'chai' +import _ from 'lodash' +import * as Cast from '../../core/cast' +import { assertObjectMatchSpec } from '../../core/assertions' +import { STATUS_CODES } from 'http' +import { parseMatchExpression } from './utils' +import { httpApiClient, HttpApiClient } from './client' +import { fixtures } from '../fixtures' +import { state } from '../state' +import { Cookie } from 'tough-cookie' +import Properties = Cookie.Properties +import { Response } from 'request' + +const STATUS_MESSAGES = _.values(STATUS_CODES).map(_.lowerCase) + +/** + * Ensures there's a response available and returns it. + * + * @param {Object} client + */ +const mustGetResponse = (client: HttpApiClient): Response | null => { + const response = client.getResponse() + expect(response, 'No response available').to.not.be.empty + + return response +} + +export const install = ({ baseUrl = '' } = {}): void => { + /** + * Setting http headers + */ + Given(/^(?:I )?set request headers$/, function (step: DataTable) { + httpApiClient.setHeaders(Cast.object(state.populateObject(step.rowsHash()))) + }) + + /** + * Setting http option followRedirect to false + */ + Given(/^(?:I )?do not follow redirect$/, function () { + httpApiClient.setFollowRedirect(false) + }) + + /** + * Setting http option followRedirect to true + */ + Given(/^(?:I )?follow redirect$/, function () { + httpApiClient.setFollowRedirect(true) + }) + + /** + * Assign http headers + * The difference from "set request headers" is that "set" set the whole headers object + * "assign" replace or set the given headers, keeping untouched the ones already set + */ + Given(/^(?:I )?assign request headers$/, function (step: DataTable) { + const headers = Cast.object(state.populateObject(step.rowsHash())) + _.each(headers, (value, key) => httpApiClient.setHeader(key, value)) + }) + + /** + * Setting a single http header + */ + Given( + /^(?:I )?set ([a-zA-Z0-9-_]+) request header to (.+)$/, + function (key: string, value: string) { + httpApiClient.setHeader(key, Cast.value(state.populate(value))) + } + ) + + /** + * Clearing headers + */ + Given(/^(?:I )?clear request headers/, function () { + httpApiClient.clearHeaders() + }) + + /** + * Setting json payload + */ + Given(/^(?:I )?set request json body$/, function (step: DataTable) { + httpApiClient.setJsonBody(Cast.object(state.populateObject(step.rowsHash()))) + }) + + /** + * Setting json payload from fixture file + */ + Given(/^(?:I )?set request json body from (.+)$/, function (fixture: string) { + return fixtures.load(fixture).then((data: object) => { + httpApiClient.setJsonBody(data) + }) + }) + + /** + * Setting form data + */ + Given(/^(?:I )?set request form body$/, function (step: DataTable) { + httpApiClient.setFormBody(Cast.object(state.populateObject(step.rowsHash()))) + }) + + /** + * Setting form data from fixture file + */ + Given(/^(?:I )?set request form body from (.+)$/, function (fixture: string) { + return fixtures.load(fixture).then((data: object) => { + httpApiClient.setFormBody(data) + }) + }) + + /** + * Setting multipart data from fixture file + */ + Given(/^(?:I )?set request multipart body from (.+)$/, function (fixture: string) { + return fixtures.load(fixture).then((data: object) => { + httpApiClient.setMultipartBody(data) + }) + }) + + /** + * Clearing body + */ + Given(/^(?:I )?clear request body$/, function () { + httpApiClient.clearBody() + }) + + /** + * Setting query parameters + */ + Given(/^(?:I )?set request query$/, function (step: DataTable) { + httpApiClient.setQuery(Cast.object(state.populateObject(step.rowsHash()))) + }) + + Given(/^(?:I )?pick response json (.+) as (.+)$/, function (path: string, key: string) { + const response = httpApiClient.getResponse() + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const body = response?.body + + state.set(key, _.get(body, path)) + }) + + /** + * Enabling cookies + */ + Given(/^(?:I )?enable cookies$/, function () { + httpApiClient.enableCookies() + }) + + /** + * Disabling cookies + */ + Given(/^(?:I )?disable cookies$/, function () { + httpApiClient.disableCookies() + }) + + /** + * Setting a cookie from fixture file + */ + Given(/^(?:I )?set cookie from (.+)$/, function (fixture: string) { + return fixtures.load(fixture).then((cookie: Properties) => { + httpApiClient.setCookie(cookie) + }) + }) + + /** + * Clearing client request cookies + */ + Given(/^(?:I )?clear request cookies$/, function () { + httpApiClient.clearRequestCookies() + }) + + /** + * Resetting the client's state + */ + When(/^(?:I )?reset http client$/, function () { + httpApiClient.reset() + }) + + /** + * Performing a request + */ + When(/^(?:I )?(GET|POST|PUT|DELETE|PATCH) (.+)$/, function (method: string, path: string) { + return httpApiClient.makeRequest(method, state.populate(path), baseUrl) + }) + + /** + * Dumping response body + */ + When(/^(?:I )?dump response body$/, function () { + const response = mustGetResponse(httpApiClient) + console.log(inspect(response?.body, { colors: true, depth: null })) // eslint-disable-line no-console + }) + + /** + * Dumping response headers + */ + When(/^(?:I )?dump response headers$/, function () { + const response = mustGetResponse(httpApiClient) + console.log(response?.headers) // eslint-disable-line no-console + }) + + /** + * Dumping response cookies + */ + When(/^(?:I )?dump response cookies$/, function () { + mustGetResponse(httpApiClient) + console.log(httpApiClient.getCookies()) + }) + + /** + * Checking response status code + */ + Then(/^response status code should be ([1-5]\d\d)$/, function (statusCode: number) { + const response = mustGetResponse(httpApiClient) + expect( + response?.statusCode, + `Expected status code to be: ${statusCode}, but found: ${ + response?.statusCode ?? 'unknown' + }` + ).to.equal(Number(statusCode)) + }) + + /** + * Checking response status by message + */ + Then(/^response status should be (.+)$/, function (statusMessage: string) { + if (!STATUS_MESSAGES.includes(_.lowerCase(statusMessage))) { + throw new TypeError(`'${statusMessage}' is not a valid status message`) + } + + const response = mustGetResponse(httpApiClient) + const statusCode = _.findKey(STATUS_CODES, (msg) => _.lowerCase(msg) === statusMessage) + const statusCodeResponse = response?.statusCode ?? 0 + const currentStatusMessage = + STATUS_CODES[`${statusCodeResponse}`] || `${statusCodeResponse}` + + expect( + statusCodeResponse, + `Expected status to be: '${statusMessage}', but found: '${_.lowerCase( + currentStatusMessage + )}'` + ).to.equal(Number(statusCode)) + }) + + /** + * Checking response cookie is present|absent + */ + Then(/^response should (not )?have an? (.+) cookie$/, function (flag: string, key: string) { + const cookie = httpApiClient.getCookie(key) + + if (flag == undefined) { + expect(cookie, `No cookie found for key '${key}'`).to.not.be.null + } else { + expect(cookie, `A cookie exists for key '${key}'`).to.be.null + } + }) + + /** + * Checking response cookie is|isn't secure + */ + Then(/^response (.+) cookie should (not )?be secure$/, function (key: string, flag: string) { + const cookie = httpApiClient.getCookie(key) + expect(cookie, `No cookie found for key '${key}'`).to.not.be.null + + if (flag == undefined) { + expect(cookie?.secure, `Cookie '${key}' is not secure`).to.be.true + } else { + expect(cookie?.secure, `Cookie '${key}' is secure`).to.be.false + } + }) + + /** + * Checking response cookie httpOnly + */ + Then(/^response (.+) cookie should (not )?be http only$/, function (key: string, flag: string) { + const cookie = httpApiClient.getCookie(key) + expect(cookie, `No cookie found for key '${key}'`).to.not.be.null + + if (flag == undefined) { + expect(cookie?.httpOnly, `Cookie '${key}' is not http only`).to.be.true + } else { + expect(cookie?.httpOnly, `Cookie '${key}' is http only`).to.be.false + } + }) + + /** + * Checking response cookie domain + */ + Then( + /^response (.+) cookie domain should (not )?be (.+)$/, + function (key: string, flag: string, domain: string) { + const cookie = httpApiClient.getCookie(key) + expect(cookie, `No cookie found for key '${key}'`).to.not.be.null + + const cookieDomain = cookie?.domain || '' + + if (flag == undefined) { + expect( + cookieDomain, + `Expected cookie '${key}' domain to be '${domain}', found '${cookieDomain}'` + ).to.equal(domain) + } else { + expect(cookieDomain, `Cookie '${key}' domain is '${domain}'`).to.not.equal(domain) + } + } + ) + + /** + * This definition can be used for checking an object response. + * It check that the properties of this object match with the expected properties + * The columns header are | field | matcher | value | + * @see Assertions.assertObjectMatchSpec + */ + Then( + /^(?:I )?json response should (fully )?match$/, + function (fully: string, table: DataTable) { + const response = mustGetResponse(httpApiClient) + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const body = response?.body + + // We check the response has json content-type + expect(response?.headers['content-type']).to.contain('application/json') + + const specifications = table.hashes().map((fieldSpec) => { + const spec = fieldSpec.expression + ? parseMatchExpression(fieldSpec.expression) + : fieldSpec + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return _.assign({}, spec, { + value: state.populate(spec.value), + }) + }) + + assertObjectMatchSpec(body, specifications, !!fully) + } + ) + + /** + * This definition verify that an array for a given path has the expected length + */ + Then( + /^(?:I )?should receive a collection of (\d+) items?(?: for path )?(.+)?$/, + function (size: number, path: string) { + const response = mustGetResponse(httpApiClient) + const body = response?.body + + const array = path != undefined ? _.get(body, path) : body + + expect(array.length).to.be.equal(Number(size)) + } + ) + + /** + * Verifies that response matches a fixture. + **/ + Then(/^response should match fixture (.+)$/, function (fixtureId: string) { + const response = mustGetResponse(httpApiClient) + + return fixtures.load(fixtureId).then((snapshot) => { + expect(response?.body).to.deep.equal(snapshot) + }) + }) + + /** + * Checking response header. + */ + Then( + /^response header (.+) should (not )?(equal|contain|match) (.+)$/, + function (key: string, flag: string, comparator: string, expectedValue: string) { + const response = mustGetResponse(httpApiClient) + const header = response?.headers[key.toLowerCase()] + + expect(header, `Header '${key}' does not exist`).to.not.be.undefined + + let expectFn = expect( + header, + `Expected header '${key}' to ${ + flag ? flag : '' + }${comparator} '${expectedValue}', but found '${header}' which does${ + flag ? '' : ' not' + }` + ).to + if (flag != undefined) { + expectFn = expectFn.not + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + expectFn[comparator](comparator === 'match' ? new RegExp(expectedValue) : expectedValue) + } + ) +} diff --git a/src/extensions/http_api/extend_world.js b/src/extensions/http_api/extend_world.js deleted file mode 100644 index a939c25f..00000000 --- a/src/extensions/http_api/extend_world.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict' - -const Registry = require('../../core/registry') -const Client = require('./client') - -module.exports = (world) => { - if (!Registry.hasExtension(world, 'state')) { - throw new Error( - `Unable to init "http_api" extension as it requires "state" extension which is not installed` - ) - } - - world.httpApiClient = Client() - Registry.registerExtension(world, 'http_api') -} diff --git a/src/extensions/http_api/extend_world.ts b/src/extensions/http_api/extend_world.ts new file mode 100644 index 00000000..150330d0 --- /dev/null +++ b/src/extensions/http_api/extend_world.ts @@ -0,0 +1,14 @@ +import { VeggiesWorld } from '../../core/core_types' +import { hasExtension, registerExtension } from '../../core/registry' +import { HttpApiClient } from './client' + +export const extendWorld = (world: VeggiesWorld): void => { + if (!hasExtension(world, 'state')) { + throw new Error( + `Unable to init "http_api" extension as it requires "state" extension which is not installed` + ) + } + + world.httpApiClient = new HttpApiClient() + registerExtension(world, 'http_api') +} diff --git a/src/extensions/http_api/http_api_types.ts b/src/extensions/http_api/http_api_types.ts new file mode 100644 index 00000000..fc711fe3 --- /dev/null +++ b/src/extensions/http_api/http_api_types.ts @@ -0,0 +1,16 @@ +import { CoreOptions } from 'request' + +export type MatchExpressionGroups = { + [key: string]: string +} + +export type Headers = { + [key: string]: unknown +} + +export type RequestOptions = CoreOptions & { uri: string } + +export type HttpApiConfig = { + baseUrl: string + [props: string]: unknown +} diff --git a/src/extensions/http_api/index.js b/src/extensions/http_api/index.ts similarity index 85% rename from src/extensions/http_api/index.js rename to src/extensions/http_api/index.ts index c3d5e54f..be8246bf 100644 --- a/src/extensions/http_api/index.js +++ b/src/extensions/http_api/index.ts @@ -1,10 +1,8 @@ -'use strict' - /** * @module extensions/httpApi */ -const definitions = require('./definitions') +import * as definitions from './definitions' /** * Extends cucumber world object. @@ -24,7 +22,7 @@ const definitions = require('./definitions') * @function * @param {Object} world - The cucumber world object */ -exports.extendWorld = require('./extend_world') +export { extendWorld } from './extend_world' /** * The http API configuration object. @@ -52,6 +50,8 @@ exports.extendWorld = require('./extend_world') * * @param {HttpApiConfig} config - Http global conf */ -exports.install = ({ baseUrl = '' } = {}) => { +export const install = ({ baseUrl = '' } = {}): void => { definitions.install({ baseUrl }) } + +export { HttpApiClient, httpApiClient } from './client' diff --git a/src/extensions/http_api/utils.js b/src/extensions/http_api/utils.js deleted file mode 100644 index 697c10ae..00000000 --- a/src/extensions/http_api/utils.js +++ /dev/null @@ -1,7 +0,0 @@ -const expressionRegex = /(?[^\s]+)\s+(?!?(?:[#~*^$]?=|\?))(?:\s+(?.+))?/ - -exports.parseMatchExpression = (expression) => { - const results = expressionRegex.exec(expression) - if (results) return results.groups - throw new TypeError(`'${expression}' is not a valid expression`) -} diff --git a/src/extensions/http_api/utils.ts b/src/extensions/http_api/utils.ts new file mode 100644 index 00000000..97c962a4 --- /dev/null +++ b/src/extensions/http_api/utils.ts @@ -0,0 +1,10 @@ +import { MatchExpressionGroups } from './http_api_types' + +const expressionRegex = /(?[^\s]+)\s+(?!?(?:[#~*^$]?=|\?))(?:\s+(?.+))?/ + +export const parseMatchExpression = (expression?: string): MatchExpressionGroups | undefined => { + const results = expression && expressionRegex.exec(expression) + if (results) return results.groups + + throw new TypeError(`'${expression}' is not a valid expression`) +} diff --git a/src/extensions/snapshot/clean.js b/src/extensions/snapshot/clean.js deleted file mode 100644 index 967d585c..00000000 --- a/src/extensions/snapshot/clean.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict' - -/** - * @module extensions/snapshot/cleanup - */ - -const _ = require('lodash') - -const snapshot = require('./snapshot') -const fileSystem = require('./fs') -const statistics = require('./statistics') - -exports._snapshots = {} - -/** - * Store a snapshot name for a snapshot file - * This can be used after to clean up unused snapshots - * @param {string} file - File path - * @param {string} snapshotName - Snapshot name - */ -exports.referenceSnapshot = function (file, snapshotName) { - exports._snapshots[file] = exports._snapshots[file] || [] - exports._snapshots[file].push(snapshotName) -} - -/** - * Clean snapshots names and files - * Used after tests to clear entries - */ -exports.resetReferences = function () { - exports._snapshots = {} -} - -/** - * Clean snapshots file from removed snapshots - * If a snapshot file is empty, it's deleted - * Only files that have been referenced will be cleaned - */ -exports.cleanSnapshots = function () { - _.forOwn(exports._snapshots, (snapshotNames, file) => { - if (_.isEmpty(snapshotNames)) { - fileSystem.remove(file) - return true - } - - const content = snapshot.readSnapshotFile(file) - const newContent = _.pick(content, snapshotNames) - snapshot.writeSnapshotFile(file, newContent) - - const omitedContent = _.omit(content, snapshotNames) - _.forOwn(omitedContent, (snapshotContent, snapshotName) => { - statistics.removed.push({ file: file, name: snapshotName }) - }) - }) -} diff --git a/src/extensions/snapshot/clean.ts b/src/extensions/snapshot/clean.ts new file mode 100644 index 00000000..a7fd2480 --- /dev/null +++ b/src/extensions/snapshot/clean.ts @@ -0,0 +1,59 @@ +/** + * @module extensions/snapshot/cleanup + */ + +import _ from 'lodash' + +import * as snapshotActions from './snapshot_actions' +import * as fileSystem from './file_system' +import * as statistics from './statistics' + +let _snapshots: Record = {} + +/** + * Store a snapshot name for a snapshot file + * This can be used after to clean up unused snapshots + * @param {string} file - File path + * @param {string} snapshotName - Snapshot name + */ +export const referenceSnapshot = (file: string, snapshotName: string): void => { + if (!_snapshots[file]) { + _snapshots[file] = [snapshotName] + return + } + _snapshots[file]?.push(snapshotName) +} + +/** + * Clean snapshots names and files + * Used after tests to clear entries + */ +export const resetReferences = (): void => { + _snapshots = {} +} + +/** + * Clean snapshots file from removed snapshots + * If a snapshot file is empty, it's deleted + * Only files that have been referenced will be cleaned + */ +export const cleanSnapshots = (): void => { + _.forOwn(_snapshots, (snapshotNames, file) => { + if (_.isEmpty(snapshotNames)) { + fileSystem.remove(file) + return true + } + + const content = snapshotActions.readSnapshotFile(file) + const newContent = _.pick(content, snapshotNames) + snapshotActions.writeSnapshotFile(file, newContent) + + const omittedContent = _.omit(content, snapshotNames) + + _.forOwn(omittedContent, (snapshotContent, snapshotName) => { + statistics.removed.push({ file: file, name: snapshotName }) + }) + }) +} + +export { _snapshots } diff --git a/src/extensions/snapshot/cmdOptions.js b/src/extensions/snapshot/cmdOptions.ts similarity index 50% rename from src/extensions/snapshot/cmdOptions.js rename to src/extensions/snapshot/cmdOptions.ts index 8c5b503a..e919043f 100644 --- a/src/extensions/snapshot/cmdOptions.js +++ b/src/extensions/snapshot/cmdOptions.ts @@ -1,6 +1,4 @@ -'use strict' - -const { hasArg, hasOneArgOf } = require('../../utils/commandLine') +import { hasArg, hasOneArgOf } from '../../utils/command_line' /** * @module extensions/snapshot/cmdOptions @@ -10,28 +8,22 @@ const { hasArg, hasOneArgOf } = require('../../utils/commandLine') * Read command line option. If there is --cleanSnapshots, then we should clean snapshots * @type {boolean} */ -exports.cleanSnapshots = false +export let cleanSnapshots = false /** * Read command line option. If there is --updateSnapshots or -u, then we should update snapshots * @type {boolean} */ -exports.updateSnapshots = false +export let updateSnapshots = false /** * Read command line option. If there is --preventSnapshotsCreation, then we should not create missing snapshots * @type {boolean} */ -exports.preventSnapshotsCreation = false +export let preventSnapshotsCreation = false -if (hasOneArgOf(['--updateSnapshots', '-u'])) { - exports.updateSnapshots = true -} +if (hasOneArgOf(['--updateSnapshots', '-u'])) updateSnapshots = true -if (hasArg('--cleanSnapshots')) { - exports.cleanSnapshots = true -} +if (hasArg('--cleanSnapshots')) cleanSnapshots = true -if (hasArg('--preventSnapshotsCreation')) { - exports.preventSnapshotsCreation = true -} +if (hasArg('--preventSnapshotsCreation')) preventSnapshotsCreation = true diff --git a/src/extensions/snapshot/dedent.js b/src/extensions/snapshot/dedent.ts similarity index 75% rename from src/extensions/snapshot/dedent.js rename to src/extensions/snapshot/dedent.ts index b42af51a..7977bab4 100644 --- a/src/extensions/snapshot/dedent.js +++ b/src/extensions/snapshot/dedent.ts @@ -1,5 +1,3 @@ -'use strict' - /** * @module extensions/snapshot/dedent */ @@ -9,9 +7,10 @@ * @param {string} text * @returns {number} length tab and space before first char */ -const getSpacesLength = (text) => { - let length = 0 +export const getSpacesLength = (text?: string): number => { + if (!text) return 0 + let length = 0 while (length < text.length) { const char = text[length] if (char !== ' ' && char !== '\t') break @@ -57,8 +56,8 @@ const getSpacesLength = (text) => { * @param {string} text * @return {string} */ -module.exports = (text) => { - if (typeof text !== 'string') text = text[0] +export const dedent = (text: string | string[] | TemplateStringsArray): string => { + if (typeof text !== 'string') text = text[0] || '' let lines = text.split('\n') if (lines.length < 3) return text @@ -66,23 +65,19 @@ module.exports = (text) => { lines = lines.slice(1, lines.length - 1) let skipLength = getSpacesLength(lines[0]) - if ( - lines[0].substr(skipLength, 3) === '"""' && - lines[lines.length - 1].substr(skipLength, 3) === '"""' + lines[0]?.substring(skipLength, skipLength + 3) === '"""' && + lines[lines.length - 1]?.substring(skipLength, skipLength + 3) === '"""' ) { lines = lines.slice(1, lines.length - 1) } else { - for (let i = 0; i < lines.length; i++) { - const line = lines[i] + for (const line of lines) { skipLength = Math.min(skipLength, getSpacesLength(line)) } } const resultLines = [] - for (let i = 0; i < lines.length; i++) { - resultLines.push(lines[i].substring(skipLength)) - } + for (const line of lines) resultLines.push(line.substring(skipLength)) return resultLines.join('\n') } diff --git a/src/extensions/snapshot/definitions.js b/src/extensions/snapshot/definitions.js deleted file mode 100644 index d5f7ee2f..00000000 --- a/src/extensions/snapshot/definitions.js +++ /dev/null @@ -1,84 +0,0 @@ -'use strict' - -const { Then } = require('@cucumber/cucumber') -const _ = require('lodash') - -exports.install = () => { - /** - * Checking if an http response body match a snapshot - */ - Then(/^response body should match snapshot$/, function () { - this.snapshot.expectToMatch(this.httpApiClient.getResponse().body) - }) - - /** - * Checking if an http response body match a snapshot - * Allow to omit field by checking their type or if they contain a value - */ - Then(/^response json body should match snapshot$/, function (table) { - let spec = [] - if (table) { - spec = table.hashes().map((fieldSpec) => - _.assign({}, fieldSpec, { - value: this.state.populate(fieldSpec.value), - }) - ) - } - - this.snapshot.expectToMatchJson(this.httpApiClient.getResponse().body, spec) - }) - - /** - * Checking a cli stdout or stderr match snapshot - */ - Then(/^(stderr|stdout) output should match snapshot$/, function (type) { - this.snapshot.expectToMatch(this.cli.getOutput(type)) - }) - - /** - * Checking a cli stdout or stderr match snapshot - * Allow to omit field by checking their type or if they contain a value - */ - Then(/^(stderr|stdout) json output should match snapshot$/, function (type, table) { - let spec = [] - if (table) { - spec = table.hashes().map((fieldSpec) => - _.assign({}, fieldSpec, { - value: this.state.populate(fieldSpec.value), - }) - ) - } - - const output = JSON.parse(this.cli.getOutput(type)) - this.snapshot.expectToMatchJson(output, spec) - }) - - /** - * Checking that a file content matches the snapshot - * Allow to omit field by checking their type or if they contain a value - */ - Then(/^file (.+) should match snapshot$/, function (file) { - return this.fileSystem.getFileContent(this.cli.getCwd(), file).then((content) => { - this.snapshot.expectToMatch(content) - }) - }) - - /** - * Checking that a file content matches the snapshot - */ - Then(/^json file (.+) content should match snapshot$/, function (file, table) { - let spec = [] - if (table) { - spec = table.hashes().map((fieldSpec) => - _.assign({}, fieldSpec, { - value: this.state.populate(fieldSpec.value), - }) - ) - } - - return this.fileSystem.getFileContent(this.cli.getCwd(), file).then((content) => { - const parsedContent = JSON.parse(content) - this.snapshot.expectToMatchJson(parsedContent, spec) - }) - }) -} diff --git a/src/extensions/snapshot/definitions.ts b/src/extensions/snapshot/definitions.ts new file mode 100644 index 00000000..46fb1885 --- /dev/null +++ b/src/extensions/snapshot/definitions.ts @@ -0,0 +1,97 @@ +import { DataTable, Then } from '@cucumber/cucumber' +import _ from 'lodash' +import { httpApiClient } from '../http_api' +import { state } from '../state' +import { snapshot } from './snapshot' +import { cli } from '../cli' +import * as fileSystem from '../file_system' +import { ObjectFieldSpec } from '../../core/core_types' + +export const install = (): void => { + /** + * Checking if an http response body match a snapshot + */ + Then(/^response body should match snapshot$/, function (): void { + snapshot.expectToMatch(httpApiClient.getResponse()?.body) + }) + + /** + * Checking if an http response body match a snapshot + * Allow to omit field by checking their type or if they contain a value + */ + Then(/^response json body should match snapshot$/, function (table: DataTable): void { + let spec = [] + if (table) { + spec = table.hashes().map((fieldSpec) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + _.assign({}, fieldSpec, { + value: state.populate(fieldSpec.value), + }) + ) + } + + snapshot.expectToMatchJson(httpApiClient.getResponse()?.body, spec) + }) + + /** + * Checking a cli stdout or stderr match snapshot + */ + Then(/^(stderr|stdout) output should match snapshot$/, function (type: string): void { + snapshot.expectToMatch(cli.getOutput(type)) + }) + + /** + * Checking a cli stdout or stderr match snapshot + * Allow to omit field by checking their type or if they contain a value + */ + Then( + /^(stderr|stdout) json output should match snapshot$/, + function (type: string, table: DataTable): void { + let spec = [] + if (table) { + spec = table.hashes().map((fieldSpec) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + _.assign({}, fieldSpec, { + value: state.populate(fieldSpec.value), + }) + ) + } + + const output = JSON.parse(cli.getOutput(type)) + snapshot.expectToMatchJson(output, spec) + } + ) + + /** + * Checking that a file content matches the snapshot + * Allow to omit field by checking their type or if they contain a value + */ + Then(/^file (.+) should match snapshot$/, function (file: string): Promise { + return fileSystem.getFileContent(cli.getCwd(), file).then((content) => { + snapshot.expectToMatch(content) + }) + }) + + /** + * Checking that a file content matches the snapshot + */ + Then( + /^json file (.+) content should match snapshot$/, + function (file: string, table: DataTable): Promise { + let spec: ObjectFieldSpec[] = [] + if (table) { + spec = table.hashes().map((fieldSpec) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + _.assign({}, fieldSpec, { + value: state.populate(fieldSpec.value), + }) + ) + } + + return fileSystem.getFileContent(cli.getCwd(), file).then((content) => { + const parsedContent = JSON.parse(content) + snapshot.expectToMatchJson(parsedContent, spec) + }) + } + ) +} diff --git a/src/extensions/snapshot/extend_world.js b/src/extensions/snapshot/extend_world.js deleted file mode 100644 index 6900dc13..00000000 --- a/src/extensions/snapshot/extend_world.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - -const Registry = require('../../core/registry') -const snapshot = require('./extension') -const cmdOptions = require('./cmdOptions') -const _ = require('lodash') - -module.exports = (world, options) => { - options = _.assign({}, cmdOptions, options) - - world.snapshot = snapshot(options) - Registry.registerExtension(world, 'snapshot') -} diff --git a/src/extensions/snapshot/extend_world.ts b/src/extensions/snapshot/extend_world.ts new file mode 100644 index 00000000..46b1850f --- /dev/null +++ b/src/extensions/snapshot/extend_world.ts @@ -0,0 +1,13 @@ +import * as cmdOptions from './cmdOptions' +import _ from 'lodash' +import { registerExtension } from '../../core/registry' +import { VeggiesWorld } from '../../core/core_types' +import { SnapshotOptions } from './snapshot_types' +import { Snapshot } from './snapshot' + +export const extendWorld = (world: VeggiesWorld, options?: SnapshotOptions): void => { + options = _.assign({}, cmdOptions, options) + + world.snapshot = new Snapshot(options) + registerExtension(world, 'snapshot') +} diff --git a/src/extensions/snapshot/file_system.ts b/src/extensions/snapshot/file_system.ts new file mode 100644 index 00000000..e4bedb1c --- /dev/null +++ b/src/extensions/snapshot/file_system.ts @@ -0,0 +1,69 @@ +/** + * The FileSystem helper used by the FileSystem extension. + * + * @module extensions/snapshot/FileSystem + */ + +import path from 'path' +import { Stats, readFileSync, statSync, writeFileSync, mkdirsSync, removeSync } from 'fs-extra' + +/** + * Loads file content. + * + * @param {string} file - File path + * @param {string} [encoding='utf8'] - Content encoding + * @return {string} File content + */ +export const getFileContent = (file: string, encoding: BufferEncoding = 'utf8'): string => { + const data = readFileSync(file) + return data.toString(encoding) +} + +/** + * + * @param {string} file - File path + * @param {string} content - Content to write in the file + * @param {object} [options] - Options + * @param {boolean} [options.createDir = true] - Create path dir if it doesn't exists + */ +export const writeFileContent = ( + file: string, + content: string, + { createDir = true } = {} +): void => { + if (createDir) createDirectory(path.dirname(file)) + return writeFileSync(file, content) +} + +/** + * Gets info about file/directory. + * + * @param {string} file - File path + * @return {Stats|null} File/directory info or null if file/directory does not exist + */ +export const getFileInfo = (file: string): Stats | undefined => { + try { + return statSync(file) + } catch (err) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (err.code !== 'ENOENT') throw err + } +} + +/** + * Creates a directory. + * + * @param {string} dir - directory path + */ +export const createDirectory = (dir: string): void => { + return mkdirsSync(dir) +} + +/** + * Removes a file or directory. + * + * @param {string} fileOrDir - File or directory path + */ +export const remove = (fileOrDir: string): void => { + return removeSync(fileOrDir) +} diff --git a/src/extensions/snapshot/fs.js b/src/extensions/snapshot/fs.js deleted file mode 100644 index 5e46a059..00000000 --- a/src/extensions/snapshot/fs.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict' - -/** - * The FileSystem helper used by the FileSystem extension. - * - * @module extensions/snapshot/FileSystem - */ - -const path = require('path') -const fs = require('fs-extra') - -/** - * Loads file content. - * - * @param {string} file - File path - * @param {string} [encoding='utf8'] - Content encoding - * @return {string} File content - */ -exports.getFileContent = (file, encoding = 'utf8') => { - const data = fs.readFileSync(file) - return data.toString(encoding) -} - -/** - * - * @param {string} file - File path - * @param {string} content - Content to write in the file - * @param {object} [options] - Options - * @param {boolean} [options.createDir = true] - Create path dir if it doesn't exists - */ -exports.writeFileContent = (file, content, { createDir = true } = {}) => { - if (createDir) exports.createDirectory(path.dirname(file)) - return fs.writeFileSync(file, content) -} - -/** - * Gets info about file/directory. - * - * @param {string} file - File path - * @return {fs.Stat|null} File/directory info or null if file/directory does not exist - */ -exports.getFileInfo = (file) => { - let result = null - try { - result = fs.statSync(file) - } catch (err) { - if (err.code !== 'ENOENT') throw err - } - - return result -} - -/** - * Creates a directory. - * - * @param {string} dir - directory path - * @return {boolean} - */ -exports.createDirectory = (dir) => { - return fs.mkdirsSync(dir) -} - -/** - * Removes a file or directory. - * - * @param {string} fileOrDirectory - File or directory path - * @return {boolean} - */ -exports.remove = (fileOrDir) => { - return fs.removeSync(fileOrDir) -} diff --git a/src/extensions/snapshot/hooks.js b/src/extensions/snapshot/hooks.js deleted file mode 100644 index 7fac478c..00000000 --- a/src/extensions/snapshot/hooks.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict' - -const { Before, BeforeAll, AfterAll } = require('@cucumber/cucumber') -const _ = require('lodash') - -const clean = require('./clean') -const cmdOptions = require('./cmdOptions') -const statistics = require('./statistics') - -function getCurrentScenarioLineNumber({ gherkinDocument, pickle }) { - const currentScenarioId = pickle.astNodeIds[0] - const { scenario } = gherkinDocument.feature.children.find( - ({ scenario: { id } }) => id === currentScenarioId - ) - return scenario.location.line -} - -/** - * Registers hooks for the fixtures extension. - * - * @module extensions/fixtures/hooks - */ - -exports.install = () => { - Before(function (scenarioInfos) { - const file = scenarioInfos.gherkinDocument.uri - const line = getCurrentScenarioLineNumber(scenarioInfos) - - this.snapshot.featureFile = file - this.snapshot.scenarioLine = line - }) - - BeforeAll(function () { - clean.resetReferences() - }) - - AfterAll(function () { - if (cmdOptions.cleanSnapshots) clean.cleanSnapshots() - statistics.printReport() - }) -} diff --git a/src/extensions/snapshot/hooks.ts b/src/extensions/snapshot/hooks.ts new file mode 100644 index 00000000..c54e9186 --- /dev/null +++ b/src/extensions/snapshot/hooks.ts @@ -0,0 +1,40 @@ +import { Before, BeforeAll, AfterAll } from '@cucumber/cucumber' + +import * as clean from './clean' +import * as cmdOptions from './cmdOptions' +import { snapshot } from './snapshot' +import { ScenarioInfos } from './snapshot_types' +import * as statistics from './statistics' + +export const getCurrentScenarioLineNumber = (scenarioInfos: ScenarioInfos): number | undefined => { + const currentScenarioId = scenarioInfos.pickle.astNodeIds[0] + const scenarioChild = scenarioInfos.gherkinDocument.feature?.children.find( + (child) => child.scenario?.id === currentScenarioId + ) + return scenarioChild?.scenario?.location.line +} + +/** + * Registers hooks for the fixtures extension. + * + * @module extensions/fixtures/hooks + */ + +export const install = (): void => { + Before(function (scenarioInfos) { + const file = scenarioInfos.gherkinDocument.uri + const line = getCurrentScenarioLineNumber(scenarioInfos) + + snapshot.featureFile = file || '' + snapshot.scenarioLine = line ?? -1 + }) + + BeforeAll(function () { + clean.resetReferences() + }) + + AfterAll(function () { + if (cmdOptions.cleanSnapshots) clean.cleanSnapshots() + statistics.printReport() + }) +} diff --git a/src/extensions/snapshot/index.js b/src/extensions/snapshot/index.ts similarity index 78% rename from src/extensions/snapshot/index.js rename to src/extensions/snapshot/index.ts index b51b6c89..cab65d74 100644 --- a/src/extensions/snapshot/index.js +++ b/src/extensions/snapshot/index.ts @@ -1,11 +1,9 @@ -'use strict' - /** * @module extensions/snapshot */ -const definitions = require('./definitions') -const hooks = require('./hooks') +import * as definitions from './definitions' +import * as hooks from './hooks' /** * Extends cucumber world object. @@ -24,7 +22,7 @@ const hooks = require('./hooks') * @function * @param {Object} world - The cucumber world object */ -exports.extendWorld = require('./extend_world') +export { extendWorld } from './extend_world' /** * Installs the extension. @@ -41,7 +39,9 @@ exports.extendWorld = require('./extend_world') * * snapshot.install() */ -exports.install = () => { +export const install = (): void => { hooks.install() definitions.install() } + +export { snapshot, Snapshot } from './snapshot' diff --git a/src/extensions/snapshot/extension.js b/src/extensions/snapshot/snapshot.ts similarity index 55% rename from src/extensions/snapshot/extension.js rename to src/extensions/snapshot/snapshot.ts index bcd67512..7be9abfb 100644 --- a/src/extensions/snapshot/extension.js +++ b/src/extensions/snapshot/snapshot.ts @@ -1,63 +1,77 @@ -'use strict' - /** * @module extensions/snapshot/Snapshot */ -const { format: prettyFormat } = require('pretty-format') -const _ = require('lodash') +import { format as prettyFormat } from 'pretty-format' +import _ from 'lodash' -const snapshot = require('./snapshot') -const clean = require('./clean') -const statistics = require('./statistics') -const assertions = require('../../core/assertions') +import * as snapshotActions from './snapshot_actions' +import * as clean from './clean' +import * as statistics from './statistics' +import * as assertions from '../../core/assertions' +import { SnapshotContents, SnapshotOptions } from './snapshot_types' +import { ObjectFieldSpec } from '../../core/core_types' /** * Snapshot extension. * * @class */ -class Snapshot { +export class Snapshot { + public options: SnapshotOptions + public shouldUpdate: boolean + public cleanSnapshots: boolean + public preventSnapshotsCreation: boolean + public featureFile: string + public scenarioLine: number + public _snapshotsCount: number + /** * @param {Object} options - Options * @param {boolean} [options.updateSnapshots=false] - Should we update the snapshots * @param {boolean} [options.cleanSnapshots=false] - Should we clean the snapshot to remove unused snapshots * @param {boolean} [options.preventSnapshotsCreation=false] - Should we avoid creating missing snapshots */ - constructor(options) { - this.options = options || {} - this.shouldUpdate = this.options.updateSnapshots - this.cleanSnapshots = this.options.cleanSnapshots - this.preventSnapshotsCreation = this.options.preventSnapshotsCreation - - this.featureFile = null - this.scenarioLine = -1 - - this._snapshotsCount = 0 + constructor(options: SnapshotOptions = {}) { + this.reset(options) } /** * When you do snapshots, it happens that some fields change at each snapshot check (ids, dates ...). * This work the same way as expectToMath but allow you to check some fields in a json objects against a matcher * and ignore them in the snapshot diff replacing them with a generic value. - * @param {*} expectedContent - Content to compare to snapshot + * @param {*} expectedContents - Content to compare to snapshot * @param {ObjectFieldSpec[]} spec - specification * @throws {string} If snapshot and expected content doesn't match, it throws diff between both */ - expectToMatchJson(expectedContent, spec) { - assertions.assertObjectMatchSpec(expectedContent, spec) // Check optional fields + expectToMatchJson(expectedContents: SnapshotContents, spec: ObjectFieldSpec[]): void { + assertions.assertObjectMatchSpec(expectedContents, spec) - const copy = _.cloneDeep(expectedContent) + const copy = _.cloneDeep(expectedContents) spec.forEach(({ field, matcher, value }) => { // Replace value with generic one - _.set(copy, field, `${matcher}(${value})`) + if (field) _.set(copy, field, `${matcher}(${value})`) }) this.expectToMatch(copy) } /** - * Compare a content to it's snapshot. + * Resets snapshot: + */ + reset(options: SnapshotOptions = {}): void { + this.options = options || {} + this.shouldUpdate = this.options.updateSnapshots ?? false + this.cleanSnapshots = this.options.cleanSnapshots ?? false + this.preventSnapshotsCreation = this.options.preventSnapshotsCreation ?? false + + this.featureFile = '' + this.scenarioLine = -1 + this._snapshotsCount = 0 + } + + /** + * Compare a content to it's snapshotActions. * If no snapshot yet, it create it. * * It uses the context to name the snapshot: feature file, scenario name and nth snapshot of scenario @@ -68,16 +82,16 @@ class Snapshot { * * If option "-u" or "--updateSnapshots" is used, all snapshots will be updated * If options "--cleanSnapshots" is used, unused stored snapshots will be removed. - * @param {*} expectedContent - Content to compare to snapshot + * @param {*} expectedContents - Content to compare to snapshot * @throws {string} If snapshot and expected content doesn't match, it throws diff between both */ - expectToMatch(expectedContent) { - expectedContent = prettyFormat(expectedContent) - expectedContent = snapshot.normalizeNewlines(expectedContent) - let snapshotsFile = snapshot.snapshotsPath(this.featureFile, this.options) + expectToMatch(expectedContents: SnapshotContents | string): void { + let expectedContent = prettyFormat(expectedContents) + expectedContent = snapshotActions.normalizeNewlines(expectedContent) + const snapshotsFile = snapshotActions.snapshotsPath(this.featureFile, this.options) - const scenarios = snapshot.extractScenarios(this.featureFile) - const snapshotsPrefix = snapshot.prefixSnapshots(scenarios)[this.scenarioLine] + const scenarios = snapshotActions.extractScenarios(this.featureFile) + const snapshotsPrefix = snapshotActions.prefixSnapshots(scenarios)[this.scenarioLine] if (!snapshotsPrefix) throw new Error( @@ -88,25 +102,31 @@ class Snapshot { const snapshotName = `${snapshotsPrefix.prefix}.${this._snapshotsCount}` if (this.cleanSnapshots) clean.referenceSnapshot(snapshotsFile, snapshotName) // To clean after all unreferenced snapshots - const snapshotsContents = snapshot.readSnapshotFile(snapshotsFile) + const snapshotsContents = snapshotActions.readSnapshotFile(snapshotsFile) let snapshotContent = snapshotsContents[snapshotName] if (this.preventSnapshotsCreation && !snapshotContent) throw new Error("The snapshot does not exist and won't be created.") if (!snapshotContent) { - statistics.created.push({ file: this.featureFile, name: snapshotName }) + statistics.created.push({ + file: this.featureFile, + name: snapshotName, + }) } else if (this.shouldUpdate) { - statistics.updated.push({ file: this.featureFile, name: snapshotName }) + statistics.updated.push({ + file: this.featureFile, + name: snapshotName, + }) } if (!snapshotContent || this.shouldUpdate) { snapshotsContents[snapshotName] = expectedContent - snapshot.writeSnapshotFile(snapshotsFile, snapshotsContents) + snapshotActions.writeSnapshotFile(snapshotsFile, snapshotsContents) snapshotContent = expectedContent } - const diff = snapshot.diff(snapshotContent, expectedContent) + const diff = snapshotActions.diff(snapshotContent, expectedContent) if (diff) throw new Error(diff) } } @@ -115,12 +135,5 @@ class Snapshot { * Create a new isolated Snapshot module * @return {Snapshot} */ -module.exports = function (...args) { - return new Snapshot(...args) -} -/** - * Snapshot extension. - * @type {Snapshot} - */ -module.exports.Snapshot = Snapshot +export const snapshot = new Snapshot() diff --git a/src/extensions/snapshot/snapshot.js b/src/extensions/snapshot/snapshot_actions.ts similarity index 61% rename from src/extensions/snapshot/snapshot.js rename to src/extensions/snapshot/snapshot_actions.ts index df7e0c1d..a69bb82c 100644 --- a/src/extensions/snapshot/snapshot.js +++ b/src/extensions/snapshot/snapshot_actions.ts @@ -1,41 +1,39 @@ -'use strict' - /** * @module extensions/snapshot/snapshot */ -const _ = require('lodash') -const path = require('path') -const { diff } = require('jest-diff') -const naturalCompare = require('natural-compare') -const chalk = require('chalk') +import _ from 'lodash' +import path from 'path' +import { diff as jestDiff } from 'jest-diff' +import naturalCompare from 'natural-compare' +import chalk from 'chalk' -const fileSystem = require('./fs') +import * as fileSystem from './file_system' +import { Scenario, SnapshotContents, SnapshotOptions } from './snapshot_types' const EXPECTED_COLOR = chalk.green const RECEIVED_COLOR = chalk.red const JEST_NO_DIFF_MESSAGE = 'Compared values have no visual difference.' -exports.scenarioRegex = /^[\s]*Scenario:[\s]*(.*[^\s])[\s]*$/ +export const scenarioRegex = /^[\s]*Scenario:[\s]*(.*[^\s])[\s]*$/ +export const snapshotsContents: SnapshotContents = {} /** * Extract scenarios from a feature file * @param {string} file - Feature file path * @return {Array} - Scenarios names */ -exports.extractScenarios = (file) => { - if (_.isNil(file)) { - throw new TypeError(`Invalid feature file ${file}`) - } +export const extractScenarios = (file?: string): Scenario[] => { + if (!file) throw new TypeError(`Invalid feature file ${file}`) const content = fileSystem.getFileContent(file) const linesContent = _.split(content, '\n') - let result = [] + const result: Scenario[] = [] _.forEach(linesContent, (lineContent, idx) => { const line = idx + 1 - const scenarioInfos = this.scenarioRegex.exec(lineContent) - if (scenarioInfos) result.push({ line, name: scenarioInfos[1] }) + const scenarioInfos = scenarioRegex.exec(lineContent) + if (scenarioInfos) result.push({ line, name: scenarioInfos[1] || '' }) }) return result @@ -65,21 +63,23 @@ exports.extractScenarios = (file) => { * @param {Array} scenarios - Scenarios names * @return {Object} - Read above for result format */ -exports.prefixSnapshots = (scenarios) => { - if (_.isNil(scenarios)) { - throw new Error(`Scenarios are required to prefix snapshots`) - } +export const prefixSnapshots = (scenarios?: Scenario[]): Record => { + if (!scenarios?.length) throw new Error(`Scenarios are required to prefix snapshots`) - const nameCount = {} - const result = {} + const nameCount: Record = {} + const result: Record = {} - _.forEach(scenarios, (scenario) => { - nameCount[scenario.name] = nameCount[scenario.name] | 0 + _.forEach(scenarios, (scenario: Scenario) => { + nameCount[scenario.name] = nameCount[scenario.name] ?? 0 nameCount[scenario.name]++ const prefix = `${scenario.name} ${nameCount[scenario.name]}` - result[scenario.line] = { name: scenario.name, line: scenario.line, prefix: prefix } + result[scenario.line] = { + name: scenario.name, + line: scenario.line, + prefix: prefix, + } }) return result @@ -91,17 +91,15 @@ exports.prefixSnapshots = (scenarios) => { * @param {string} file - snapshot file path * @return {Object} - Return follows the pattern : {snapshot_name: snapshot_content} */ -exports.readSnapshotFile = (file) => { - if (_.isNil(file)) { - throw new Error(`Missing snapshot file ${file} to read snapshots`) - } +export const readSnapshotFile = (file: string): SnapshotContents => { + if (!file) throw new Error(`Missing snapshot file ${file} to read snapshots`) const info = fileSystem.getFileInfo(file) if (!info) return {} const content = fileSystem.getFileContent(file) - return exports.parseSnapshotFile(content) + return parseSnapshotFile(content) } /** @@ -109,8 +107,8 @@ exports.readSnapshotFile = (file) => { * @param {string} file - file path * @param {Object} content - snapshot file content following the pattern : {snapshot_name: snapshot_content} */ -exports.writeSnapshotFile = (file, content) => { - const serializedContent = exports.formatSnapshotFile(content) +export const writeSnapshotFile = (file: string, content: Record): void => { + const serializedContent = formatSnapshotFile(content) return fileSystem.writeFileContent(file, serializedContent) } @@ -118,11 +116,11 @@ exports.writeSnapshotFile = (file, content) => { * Get snapshot file path base on feature file path * @param {string} featureFile - Feature file path * @param {Object} opts - * @param {Object} [opts.snaphotsDirname = '__snapshots__'] - Snapshots dirname + * @param {Object} [opts.snapshotsDirname = '__snapshots__'] - Snapshots dirname * @param {Object} [opts.snapshotsFileExtension = 'snap'] - Snapshots files extension */ -exports.snapshotsPath = (featureFile, opts) => { - const dirname = opts.snaphotsDirname || '__snapshots__' +export const snapshotsPath = (featureFile: string, opts: SnapshotOptions): string => { + const dirname = opts.snapshotsDirname || '__snapshots__' const dir = path.join(path.dirname(featureFile), dirname) const filename = `${path.basename(featureFile)}.${opts.snapshotsFileExtension || 'snap'}` @@ -131,16 +129,14 @@ exports.snapshotsPath = (featureFile, opts) => { /** * Compute diff between two contents. - * If no diff, it returns null + * If no diff, it returns undefined * @param {string} snapshot - snapshot content * @param {string} expected - expected content * @returns {string} Diff message */ -exports.diff = (snapshot, expected) => { - let diffMessage = diff(snapshot, expected, { +export const diff = (snapshot: string, expected: string): string | null => { + let diffMessage = jestDiff(snapshot, expected, { expand: false, - colors: true, - //contextLines: -1, // Forces to use default from Jest aAnnotation: 'Snapshot', bAnnotation: 'Received', }) @@ -157,32 +153,32 @@ exports.diff = (snapshot, expected) => { * @param {string} str - snapshot content * @return {string} wrapped content */ -exports.wrapWithBacktick = (str) => { +export const wrapWithBacktick = (str: string): string => { return '`' + str.replace(/`|\\|\${/g, '\\$&') + '`' } /** * Normalize new lines to be \n only - * @param {string} string - Content to normalize + * @param {string} str - Content to normalize */ -exports.normalizeNewlines = (string) => { - return string.replace(/\r\n|\r/g, '\n') +export const normalizeNewlines = (str: string): string => { + return str.replace(/\r\n|\r/g, '\n') } /** * For a snapshot file by add backticks and format it as js files with keys * @param {object} content - snapshots content - * @return {string} formated snapshot file + * @return {string} formatted snapshot file */ -exports.formatSnapshotFile = (content) => { +export const formatSnapshotFile = (content: Record): string => { const snapshots = Object.keys(content) .sort(naturalCompare) .map( (key) => 'exports[' + - exports.wrapWithBacktick(key) + + wrapWithBacktick(key) + '] = ' + - exports.wrapWithBacktick(exports.normalizeNewlines(content[key])) + + wrapWithBacktick(normalizeNewlines(content[key] || '')) + ';' ) return '\n\n' + snapshots.join('\n\n') + '\n' @@ -193,8 +189,9 @@ exports.formatSnapshotFile = (content) => { * @param {string} content - Snapshot file content * @return {Object} - should follow the pattern {snapshot_name: snapshot_content} */ -exports.parseSnapshotFile = (content) => { +export const parseSnapshotFile = (content: string): SnapshotContents => { const data = {} + // eslint-disable-next-line @typescript-eslint/no-implied-eval const populate = new Function('exports', content) populate(data) diff --git a/src/extensions/snapshot/snapshot_types.ts b/src/extensions/snapshot/snapshot_types.ts new file mode 100644 index 00000000..aada22fd --- /dev/null +++ b/src/extensions/snapshot/snapshot_types.ts @@ -0,0 +1,27 @@ +import { GherkinDocument, Pickle } from '@cucumber/messages' + +export type Scenario = { + line: number + name: string + prefix?: string +} + +export type SnapshotContents = Record + +export type SnapshotFile = { + file: string + name: string +} + +export type SnapshotOptions = { + cleanSnapshots?: boolean + updateSnapshots?: boolean + preventSnapshotsCreation?: boolean + snapshotsDirname?: string + snapshotsFileExtension?: string +} + +export type ScenarioInfos = { + gherkinDocument: GherkinDocument + pickle: Pickle +} diff --git a/src/extensions/snapshot/statistics.js b/src/extensions/snapshot/statistics.js deleted file mode 100644 index 4b739017..00000000 --- a/src/extensions/snapshot/statistics.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict' -const chalk = require('chalk') - -/** - * @module extensions/snapshot/statistics - */ - -/** - * @typedef {object} SnapshotIdentifier - * @property {string} file - scenario file path - * @property {string} name - snapshot name - */ - -/** - * Store snapshot identifier of created snapshots - * @type {Array} - */ -exports.created = [] - -/** - * Store snapshot identifier of updated snapshots - * @type {Array} - */ -exports.updated = [] - -/** - * Store snapshot identifier of removed snapshots - * @type {Array} - */ -exports.removed = [] - -exports.printReport = () => { - const total = exports.created.length + exports.updated.length + exports.removed.length - if (total) { - let result = '`\n\nSnapshots: ' - if (exports.created.length > 0) result += chalk.green(`${exports.created.length} created, `) - if (exports.updated.length > 0) - result += chalk.yellow(`${exports.updated.length} updated, `) - if (exports.removed.length > 0) result += chalk.red(`${exports.removed.length} removed, `) - result += `${total} total\n` - - console.log(result) //eslint-disable-line - } -} diff --git a/src/extensions/snapshot/statistics.ts b/src/extensions/snapshot/statistics.ts new file mode 100644 index 00000000..03a6c428 --- /dev/null +++ b/src/extensions/snapshot/statistics.ts @@ -0,0 +1,49 @@ +import chalk from 'chalk' +import { SnapshotFile } from './snapshot_types' + +/** + * @module extensions/snapshot/statistics + */ + +/** + * @typedef {object} SnapshotIdentifier + * @property {string} file - scenario file path + * @property {string} name - snapshot name + */ + +/** + * Store snapshot identifier of created snapshots + * @type {Array} + */ +export let created: SnapshotFile[] = [] + +/** + * Store snapshot identifier of updated snapshots + * @type {Array} + */ +export let updated: SnapshotFile[] = [] + +/** + * Store snapshot identifier of removed snapshots + * @type {Array} + */ +export let removed: SnapshotFile[] = [] + +export const printReport = (): void => { + const total = created.length + updated.length + removed.length + if (total) { + let result = '`\n\nSnapshots: ' + if (created.length > 0) result += chalk.green(`${created.length} created, `) + if (updated.length > 0) result += chalk.yellow(`${updated.length} updated, `) + if (removed.length > 0) result += chalk.red(`${removed.length} removed, `) + result += `${total} total\n` + + console.log(result) //eslint-disable-line + } +} + +export const reset = (): void => { + created = [] + updated = [] + removed = [] +} diff --git a/src/extensions/state/definitions.js b/src/extensions/state/definitions.js deleted file mode 100644 index 290361b6..00000000 --- a/src/extensions/state/definitions.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict' - -const { Given, When } = require('@cucumber/cucumber') -const Cast = require('../../core/cast') - -exports.install = () => { - Given(/^(?:I )?set state (.+) to (.+)$/, function (key, value) { - this.state.set(key, Cast.value(value)) - }) - - When(/^(?:I )?clear state$/, function () { - this.state.clear() - }) - - When(/^(?:I )?dump state$/, function () { - console.log(this.state.dump()) // eslint-disable-line no-console - }) -} diff --git a/src/extensions/state/definitions.ts b/src/extensions/state/definitions.ts new file mode 100644 index 00000000..48897b1c --- /dev/null +++ b/src/extensions/state/definitions.ts @@ -0,0 +1,17 @@ +import { Given, When } from '@cucumber/cucumber' +import * as Cast from '../../core/cast' +import { state } from './state' + +export const install = (): void => { + Given(/^(?:I )?set state (.+) to (.+)$/, function (key: string, value: string) { + state.set(key, Cast.value(value)) + }) + + When(/^(?:I )?clear state$/, function () { + state.clear() + }) + + When(/^(?:I )?dump state$/, function () { + console.log(state.dump()) // eslint-disable-line no-console + }) +} diff --git a/src/extensions/state/extend_world.js b/src/extensions/state/extend_world.js deleted file mode 100644 index deb699df..00000000 --- a/src/extensions/state/extend_world.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - -const Registry = require('../../core/registry') -const State = require('./state') - -module.exports = (world) => { - world.state = State() - Registry.registerExtension(world, 'state') -} diff --git a/src/extensions/state/extend_world.ts b/src/extensions/state/extend_world.ts new file mode 100644 index 00000000..5a118c43 --- /dev/null +++ b/src/extensions/state/extend_world.ts @@ -0,0 +1,8 @@ +import { VeggiesWorld } from '../../core/core_types' +import { registerExtension } from '../../core/registry' +import { State } from './state' + +export const extendWorld = (world: VeggiesWorld): void => { + world.state = new State() + registerExtension(world, 'state') +} diff --git a/src/extensions/state/index.js b/src/extensions/state/index.ts similarity index 81% rename from src/extensions/state/index.js rename to src/extensions/state/index.ts index 9d4c14dd..080104a3 100644 --- a/src/extensions/state/index.js +++ b/src/extensions/state/index.ts @@ -1,10 +1,8 @@ -'use strict' - /** * @module extensions/state */ -const definitions = require('./definitions') +import * as definitions from './definitions' /** * Extends cucumber world object. @@ -23,7 +21,7 @@ const definitions = require('./definitions') * @function * @param {Object} world - The cucumber world object */ -exports.extendWorld = require('./extend_world') +export { extendWorld } from './extend_world' /** * Installs the extension. @@ -40,6 +38,8 @@ exports.extendWorld = require('./extend_world') * * state.install() */ -exports.install = () => { +export const install = (): void => { definitions.install() } + +export { State, state } from './state' diff --git a/src/extensions/state/state.js b/src/extensions/state/state.js deleted file mode 100644 index 2d7a41bb..00000000 --- a/src/extensions/state/state.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict' - -/** - * @module extensions/state/State - */ - -const _ = require('lodash') - -/** - * State extension. - * - * @class - */ -class State { - constructor() { - /** - * World state - * @type {Object} - */ - this.state = {} - } - - /** - * Sets value for given key. - * - * @param {string} key - The key you wish to set a value for - * @param {*} value - The value - */ - set(key, value) { - return _.set(this.state, key, value) - } - - /** - * Retrieves a value for given key. - * - * @param {string} key - The key you wish to retrieve a value for - * @return {*} - */ - get(key) { - return _.get(this.state, key) - } - - /** - * Clear the state - */ - clear() { - this.state = {} - } - - /** - * Dump state content - * @return {Object|{}|*} - */ - dump() { - return this.state - } - - populate(value) { - return _.template(value, { interpolate: /{{([\s\S]+?)}}/g })(this.state) - } - - populateObject(object) { - return _.mapValues(object, (value) => { - if (_.isPlainObject(value)) return this.populateObject(value) - return this.populate(value) - }) - } -} - -/** - * Create a new isolated state - * @return {State} - */ -module.exports = function (...args) { - return new State(...args) -} - -/** - * State extension. - * @type {State} - */ -module.exports.State = State diff --git a/src/extensions/state/state.ts b/src/extensions/state/state.ts new file mode 100644 index 00000000..6c05eb5d --- /dev/null +++ b/src/extensions/state/state.ts @@ -0,0 +1,64 @@ +/** + * @module extensions/state/State + */ + +import _ from 'lodash' +import { CastedValue } from '../../core/core_types' + +/** + * State extension. + * + * @class + */ +export class State { + public collections: Record + + constructor() { + this.collections = {} + } + + /** + * Sets value for given key. + * + * @param {string} key - The key you wish to set a value for + * @param {*} value - The value + */ + set(key: string, value?: CastedValue): void { + _.set(this.collections, key, value) + } + + /** + * Retrieves a value for given key. + * + * @param {string} key - The key you wish to retrieve a value for + * @return {*} + */ + get(key: string): unknown { + return _.get(this.collections, key) + } + + /** + * Clear the state + */ + clear(): void { + this.collections = {} + } + + /** + * Dump state content + * @return {Object|{}|*} + */ + dump(): Record { + return this.collections + } + + populate(value: string): string { + return _.template(value, { interpolate: /{{([\s\S]+?)}}/g })(this.collections) + } + + populateObject(object: Record): Record { + return _.mapValues(object, (value) => this.populate(value)) + } +} + +export const state = new State() diff --git a/src/index.js b/src/index.ts similarity index 52% rename from src/index.js rename to src/index.ts index 50af0381..5d4c38b7 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,17 +1,17 @@ -'use strict' - //********************************************************************************************************************** // Extensions //********************************************************************************************************************** -exports.state = require('./extensions/state') -exports.fixtures = require('./extensions/fixtures') -exports.httpApi = require('./extensions/http_api') -exports.cli = require('./extensions/cli') -exports.fileSystem = require('./extensions/file_system') -exports.snapshot = require('./extensions/snapshot') +import * as state from './extensions/state' +import * as fixtures from './extensions/fixtures' +import * as httpApi from './extensions/http_api' +import * as cli from './extensions/cli' +import * as fileSystem from './extensions/file_system' +import * as snapshot from './extensions/snapshot' //********************************************************************************************************************** // Core //********************************************************************************************************************** -exports.cast = require('./core/cast') -exports.assertions = require('./core/assertions') +import * as cast from './core/cast' +import * as assertions from './core/assertions' + +export { state, fixtures, httpApi, cli, fileSystem, snapshot, cast, assertions } diff --git a/src/utils/commandLine.js b/src/utils/commandLine.js deleted file mode 100644 index 29117816..00000000 --- a/src/utils/commandLine.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports.hasArg = (arg) => process.argv.includes(arg) - -module.exports.hasOneArgOf = (args) => - Array.isArray(args) ? args.some((arg) => process.argv.includes(arg)) : false diff --git a/src/utils/command_line.ts b/src/utils/command_line.ts new file mode 100644 index 00000000..dcc6833c --- /dev/null +++ b/src/utils/command_line.ts @@ -0,0 +1,4 @@ +export const hasArg = (arg: string): boolean => process.argv.includes(arg) + +export const hasOneArgOf = (args: string | string[]): boolean => + Array.isArray(args) ? args.some((arg) => process.argv.includes(arg)) : false diff --git a/src/utils/type_guards.ts b/src/utils/type_guards.ts new file mode 100644 index 00000000..e03deb60 --- /dev/null +++ b/src/utils/type_guards.ts @@ -0,0 +1,8 @@ +/* Custom type guards helping TS to infer types after filtering arrays + * const array: Array = [] + * const filteredArray = array.filter(n => !!n) // filteredArray inferred as Array + * const filteredArray = array.filter(isDefined) // filteredArray inferred as Array =) + */ +export const isDefined = (toTest: T | undefined | null): toTest is T => { + return !!toTest +} diff --git a/tests/__mocks__/fs.js b/tests/__mocks__/fs.js deleted file mode 100644 index ef3e38a6..00000000 --- a/tests/__mocks__/fs.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict' - -const path = require('path') - -const fs = jest.genMockFromModule('fs') - -let mockFiles = {} - -const __setMockFiles = (_mockFiles) => { - mockFiles = _mockFiles -} - -const readFile = (file, cb) => { - const mockedContent = mockFiles[file] - if (mockedContent !== undefined) return cb(null, mockedContent) - cb(new Error(`File does not exist (${file})`)) -} - -fs.__setMockFiles = __setMockFiles -fs.readFile = readFile - -module.exports = fs diff --git a/tests/__mocks__/glob.js b/tests/__mocks__/glob.js deleted file mode 100644 index 775e6efd..00000000 --- a/tests/__mocks__/glob.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -const glob = jest.genMockFromModule('glob') - -let mockedResults = {} - -module.exports = (pattern, cb) => { - if (pattern === '__defineMocks') { - mockedResults = cb - return - } - - const mockedResult = mockedResults[pattern] - if (mockedResult === undefined) return cb(null, []) - cb(null, mockedResult) -} diff --git a/tests/cli/support/world.js b/tests/cli/support/world.ts similarity index 56% rename from tests/cli/support/world.js rename to tests/cli/support/world.ts index 67810648..9a589d5f 100644 --- a/tests/cli/support/world.js +++ b/tests/cli/support/world.ts @@ -1,7 +1,5 @@ -'use strict' - -const { setWorldConstructor } = require('@cucumber/cucumber') -const { cli, snapshot, state } = require('../../../src') +import { setWorldConstructor } from '@cucumber/cucumber' +import { cli, snapshot, state } from '../../../src' setWorldConstructor(function () { state.extendWorld(this) diff --git a/tests/core/assertions.test.js b/tests/core/assertions.test.js deleted file mode 100644 index 13b7e953..00000000 --- a/tests/core/assertions.test.js +++ /dev/null @@ -1,732 +0,0 @@ -'use strict' - -const { countNestedProperties, assertObjectMatchSpec } = require('../../src/core/assertions') - -beforeAll(() => { - const MockDate = (lastDate) => () => new lastDate(2018, 4, 1) - global.Date = jest.fn(MockDate(global.Date)) -}) - -afterAll(() => { - global.Date.mockRestore() -}) - -beforeEach(() => {}) - -test('should allow to count object properties', () => { - expect( - countNestedProperties({ - a: true, - b: true, - c: true, - }) - ).toBe(3) - - expect( - countNestedProperties({ - a: true, - b: true, - c: true, - d: { - a: true, - b: true, - }, - }) - ).toBe(5) -}) - -test('should allow to count nested objects properties', () => { - expect( - countNestedProperties({ - a: true, - b: true, - c: { - d: 'value1', - e: 'value2', - }, - }) - ).toBe(4) -}) - -test('should allow to count object properties with null, undefined properties ', () => { - expect( - countNestedProperties({ - a: null, - b: undefined, - c: 'value3', - }) - ).toBe(3) -}) - -test('should allow to count object with properties array property', () => { - expect( - countNestedProperties({ - a: [1, 2], - b: true, - c: true, - }) - ).toBe(4) -}) - -test('should allow to count object properties with empty array property', () => { - expect( - countNestedProperties({ - a: true, - b: true, - c: { - d: '', - e: [], - }, - }) - ).toBe(4) -}) - -test('object property is defined', () => { - const spec = [ - { - field: 'name', - matcher: 'defined', - }, - { - field: 'gender', - matcher: 'present', - }, - { - field: 'age', - matcher: '?', - }, - ] - - expect(() => - assertObjectMatchSpec({ name: 'john', gender: 'male', age: 31 }, spec) - ).not.toThrow() - expect(() => assertObjectMatchSpec({ name: 'john', gender: 'male' }, spec)).toThrow( - `Property 'age' is undefined: expected undefined not to be undefined` - ) - expect(() => assertObjectMatchSpec({ name: 'john' }, spec)).toThrow( - `Property 'gender' is undefined: expected undefined not to be undefined` - ) - expect(() => assertObjectMatchSpec({ gender: 'john' }, spec)).toThrow( - `Property 'name' is undefined: expected undefined not to be undefined` - ) - expect(() => assertObjectMatchSpec({}, spec)).toThrow( - `Property 'name' is undefined: expected undefined not to be undefined` - ) -}) - -test('object property is not defined', () => { - const spec = [ - { - field: 'name', - matcher: 'not defined', - }, - { - field: 'gender', - matcher: 'not present', - }, - { - field: 'city', - matcher: 'is not defined', - }, - { - field: 'street', - matcher: `isn't defined`, - }, - { - field: 'age', - matcher: `!?`, - }, - ] - - expect(() => assertObjectMatchSpec({}, spec)).not.toThrow() - expect(() => assertObjectMatchSpec({ name: 'john' }, spec)).toThrow( - `Property 'name' is defined: expected 'john' to be undefined` - ) - expect(() => assertObjectMatchSpec({ gender: 'john' }, spec)).toThrow( - `Property 'gender' is defined: expected 'john' to be undefined` - ) - expect(() => assertObjectMatchSpec({ city: 'paris' }, spec)).toThrow( - `Property 'city' is defined: expected 'paris' to be undefined` - ) - expect(() => assertObjectMatchSpec({ street: 'rue du chat qui pêche' }, spec)).toThrow( - `Property 'street' is defined: expected 'rue du chat qui pêche' to be undefined` - ) - expect(() => assertObjectMatchSpec({ age: 31 }, spec)).toThrow( - `Property 'age' is defined: expected 31 to be undefined` - ) - expect(() => - assertObjectMatchSpec( - { name: 'john', gender: 'male', city: 'paris', street: 'rue du chat qui pêche' }, - spec - ) - ).toThrow(`Property 'name' is defined: expected 'john' to be undefined`) -}) - -test('check object property equals expected value', () => { - const spec = [ - { - field: 'name', - matcher: 'equals', - value: 'john', - }, - { - field: 'city', - matcher: '=', - value: 'Bordeaux', - }, - ] - - expect(() => assertObjectMatchSpec({ name: 'john', city: 'Bordeaux' }, spec)).not.toThrow() - expect(() => assertObjectMatchSpec({ name: 'plouc' }, spec)).toThrow( - `Expected property 'name' to equal 'john', but found 'plouc': expected 'plouc' to deeply equal 'john'` - ) - expect(() => assertObjectMatchSpec({ name: 'john', city: 'Paris' }, spec)).toThrow( - `Expected property 'city' to equal 'Bordeaux', but found 'Paris': expected 'Paris' to deeply equal 'Bordeaux'` - ) -}) - -test('check object property does not equal expected value', () => { - const spec = [ - { - field: 'name', - matcher: 'does not equal', - value: 'john', - }, - { - field: 'city', - matcher: '!=', - value: 'Paris', - }, - ] - - expect(() => assertObjectMatchSpec({ name: 'plouc', city: 'Bordeaux' }, spec)).not.toThrow() - expect(() => assertObjectMatchSpec({ name: 'plouc', city: 'Paris' }, spec)).toThrow( - `Expected property 'city' to not equal 'Paris', but found 'Paris': expected 'Paris' to not deeply equal 'Paris` - ) - expect(() => assertObjectMatchSpec({ name: 'john' }, spec)).toThrow( - `Expected property 'name' to not equal 'john', but found 'john': expected 'john' to not deeply equal 'john` - ) -}) - -test('check object property contains value', () => { - const spec = [ - { - field: 'first_name', - matcher: 'contain', - value: 'john', - }, - { - field: 'last_name', - matcher: 'contains', - value: 'doe', - }, - { - field: 'city', - matcher: '*=', - value: 'ord', - }, - ] - - expect(() => - assertObjectMatchSpec({ first_name: 'johnny', last_name: 'doet', city: 'Bordeaux' }, spec) - ).not.toThrow() - expect(() => - assertObjectMatchSpec({ first_name: 'johnny', last_name: 'doe', city: 'Paris' }, spec) - ).toThrow(`Property 'city' (Paris) does not contain 'ord': expected 'Paris' to include 'ord'`) - expect(() => assertObjectMatchSpec({ first_name: 'john', last_name: 'john' }, spec)).toThrow( - `Property 'last_name' (john) does not contain 'doe': expected 'john' to include 'doe'` - ) - expect(() => assertObjectMatchSpec({ first_name: 'doe', last_name: 'doe' }, spec)).toThrow( - `Property 'first_name' (doe) does not contain 'john': expected 'doe' to include 'john'` - ) -}) - -test('check object property does not contain value', () => { - const spec = [ - { - field: 'first_name', - matcher: `doesn't contain`, - value: 'john', - }, - { - field: 'last_name', - matcher: 'does not contain', - value: 'doe', - }, - { - field: 'city', - matcher: `doesn't contains`, - value: 'york', - }, - { - field: 'street', - matcher: 'does not contains', - value: 'avenue', - }, - { - field: 'postal_code', - matcher: '!*=', - value: '44', - }, - ] - - expect(() => - assertObjectMatchSpec( - { - first_name: 'foo', - last_name: 'bar', - city: 'miami', - street: 'calle ocho', - postal_code: 'FL 33135', - }, - spec - ) - ).not.toThrow() - expect(() => - assertObjectMatchSpec( - { - first_name: 'foo', - last_name: 'bar', - city: 'miami', - street: 'calle ocho', - postal_code: 'FL 44135', - }, - spec - ) - ).toThrow( - `Property 'postal_code' (FL 44135) contains '44': expected 'FL 44135' to not include '44'` - ) - expect(() => - assertObjectMatchSpec( - { first_name: 'johnny', last_name: 'bar', city: 'miami', street: 'calle ocho' }, - spec - ) - ).toThrow( - `Property 'first_name' (johnny) contains 'john': expected 'johnny' to not include 'john'` - ) - expect(() => - assertObjectMatchSpec( - { first_name: 'foo', last_name: 'doet', city: 'miami', street: 'calle ocho' }, - spec - ) - ).toThrow(`Property 'last_name' (doet) contains 'doe': expected 'doet' to not include 'doe'`) - expect(() => - assertObjectMatchSpec( - { first_name: 'foo', last_name: 'bar', city: 'new york', street: 'calle ocho' }, - spec - ) - ).toThrow( - `Property 'city' (new york) contains 'york': expected 'new york' to not include 'york'` - ) - expect(() => - assertObjectMatchSpec( - { first_name: 'foo', last_name: 'bar', city: 'miami', street: 'krome avenue' }, - spec - ) - ).toThrow( - `Property 'street' (krome avenue) contains 'avenue': expected 'krome avenue' to not include 'avenue'` - ) -}) - -test('check object property starts with value', () => { - const spec = [ - { - field: 'first_name', - matcher: 'starts with', - value: 'john', - }, - { - field: 'last_name', - matcher: '^=', - value: 'do', - }, - ] - - expect(() => - assertObjectMatchSpec({ first_name: 'johnny', last_name: 'doe' }, spec) - ).not.toThrow() - expect(() => assertObjectMatchSpec({ first_name: 'john', last_name: 'john' }, spec)).toThrow( - `Property 'last_name' (john) does not start with 'do': expected 'john' to start with 'do'` - ) - expect(() => assertObjectMatchSpec({ first_name: 'doe', last_name: 'doe' }, spec)).toThrow( - `Property 'first_name' (doe) does not start with 'john': expected 'doe' to start with 'john'` - ) -}) - -test('check object property does not start with value', () => { - const spec = [ - { - field: 'first_name', - matcher: 'does not start with', - value: 'john', - }, - { - field: 'last_name', - matcher: '!^=', - value: 'do', - }, - ] - - expect(() => - assertObjectMatchSpec({ first_name: 'bob', last_name: 'dylan' }, spec) - ).not.toThrow() - expect(() => assertObjectMatchSpec({ first_name: 'bod', last_name: 'doe' }, spec)).toThrow( - `Property 'last_name' (doe) starts with 'do': expected 'doe' not to start with 'do'` - ) - expect(() => assertObjectMatchSpec({ first_name: 'johnny', last_name: 'dylan' }, spec)).toThrow( - `Property 'first_name' (johnny) starts with 'john': expected 'johnny' not to start with 'john'` - ) -}) - -test('check object property ends with value', () => { - const spec = [ - { - field: 'first_name', - matcher: 'ends with', - value: 'ny', - }, - { - field: 'last_name', - matcher: '$=', - value: 'oe', - }, - ] - - expect(() => - assertObjectMatchSpec({ first_name: 'johnny', last_name: 'doe' }, spec) - ).not.toThrow() - expect(() => assertObjectMatchSpec({ first_name: 'johnny', last_name: 'john' }, spec)).toThrow( - `Property 'last_name' (john) does not end with 'oe': expected 'john' to end with 'oe'` - ) - expect(() => assertObjectMatchSpec({ first_name: 'doe', last_name: 'doe' }, spec)).toThrow( - `Property 'first_name' (doe) does not end with 'ny': expected 'doe' to end with 'ny'` - ) -}) - -test('check object property does not end with value', () => { - const spec = [ - { - field: 'first_name', - matcher: 'does not end with', - value: 'ny', - }, - { - field: 'last_name', - matcher: '!$=', - value: 'oe', - }, - ] - - expect(() => - assertObjectMatchSpec({ first_name: 'bob', last_name: 'dylan' }, spec) - ).not.toThrow() - expect(() => assertObjectMatchSpec({ first_name: 'bob', last_name: 'doe' }, spec)).toThrow( - `Property 'last_name' (doe) ends with 'oe': expected 'doe' not to end with 'oe'` - ) - expect(() => assertObjectMatchSpec({ first_name: 'johnny', last_name: 'dylan' }, spec)).toThrow( - `Property 'first_name' (johnny) ends with 'ny': expected 'johnny' not to end with 'ny'` - ) -}) - -test('check object property matches regexp', () => { - const spec = [ - { - field: 'first_name', - matcher: 'matches', - value: '^john', - }, - { - field: 'last_name', - matcher: 'match', - value: '^doe', - }, - { - field: 'city', - matcher: '~=', - value: '(.+-){3}.+', - }, - ] - - expect(() => - assertObjectMatchSpec( - { first_name: 'johnny', last_name: 'doet', city: 'Saint-Pée-sur-Nivelle' }, - spec - ) - ).not.toThrow() - expect(() => - assertObjectMatchSpec({ first_name: 'johnny', last_name: 'doet', city: 'Bordeaux' }, spec) - ).toThrow( - `Property 'city' (Bordeaux) does not match '(.+-){3}.+': expected 'Bordeaux' to match /(.+-){3}.+/` - ) - expect(() => - assertObjectMatchSpec( - { first_name: 'johnny', last_name: 'john', city: 'Saint-Pée-sur-Nivelle' }, - spec - ) - ).toThrow(`Property 'last_name' (john) does not match '^doe': expected 'john' to match /^doe/`) - expect(() => - assertObjectMatchSpec( - { first_name: 'doe', last_name: 'doe', city: 'Saint-Pée-sur-Nivelle' }, - spec - ) - ).toThrow(`Property 'first_name' (doe) does not match '^john': expected 'doe' to match /^john/`) -}) - -test('check object property does not match regexp', () => { - const spec = [ - { - field: 'first_name', - matcher: `doesn't match`, - value: '^john', - }, - { - field: 'last_name', - matcher: '!~=', - value: '^[a-z]{3}$', - }, - ] - - expect(() => - assertObjectMatchSpec({ first_name: 'bob', last_name: 'dylan' }, spec) - ).not.toThrow() - expect(() => assertObjectMatchSpec({ first_name: 'bob', last_name: 'doe' }, spec)).toThrow( - `Property 'last_name' (doe) matches '^[a-z]{3}$': expected 'doe' not to match /^[a-z]{3}$/` - ) - expect(() => assertObjectMatchSpec({ first_name: 'john', last_name: 'doe' }, spec)).toThrow( - `Property 'first_name' (john) matches '^john': expected 'john' not to match /^john/` - ) -}) - -test('check object fully matches spec', () => { - const spec = [ - { - field: 'first_name', - matcher: 'equal', - value: 'john', - }, - { - field: 'last_name', - matcher: 'match', - value: '^doe', - }, - ] - - expect(() => - assertObjectMatchSpec({ first_name: 'john', last_name: 'doet' }, spec, true) - ).not.toThrow() - expect(() => - assertObjectMatchSpec({ first_name: 'john', last_name: 'doet', gender: 'male' }, spec, true) - ).toThrow(`Expected json response to fully match spec, but it does not: expected 3 to equal 2`) - expect(() => - assertObjectMatchSpec({ first_name: 'john', last_name: 'john' }, spec, true) - ).toThrow(`Property 'last_name' (john) does not match '^doe': expected 'john' to match /^doe/`) - expect(() => - assertObjectMatchSpec({ first_name: 'doe', last_name: 'doe' }, spec, true) - ).toThrow( - `Expected property 'first_name' to equal 'john', but found 'doe': expected 'doe' to deeply equal 'john'` - ) -}) - -test('check object property type', () => { - const spec = [ - { - field: 'first_name', - matcher: 'type', - value: 'string', - }, - { - field: 'last_name', - matcher: '#=', - value: 'string', - }, - { - field: 'age', - matcher: 'type', - value: 'number', - }, - ] - - expect(() => - assertObjectMatchSpec({ first_name: 'john', last_name: 'doe', age: 23 }, spec) - ).not.toThrow() - expect(() => - assertObjectMatchSpec({ first_name: true, last_name: 'doe', age: 23 }, spec) - ).toThrow(`Property 'first_name' (true) type is not 'string': expected true to be a string`) - expect(() => - assertObjectMatchSpec({ first_name: 'john', last_name: 45, age: 'test' }, spec) - ).toThrow(`Property 'last_name' (45) type is not 'string': expected 45 to be a string`) - expect(() => - assertObjectMatchSpec({ first_name: 'john', last_name: 'doe', age: 'test' }, spec) - ).toThrow(`Property 'age' (test) type is not 'number': expected 'test' to be a number`) -}) - -test('check object property type does not match', () => { - const spec = [ - { - field: 'first_name', - matcher: 'not type', - value: 'string', - }, - { - field: 'first_name', - matcher: '!#=', - value: 'string', - }, - ] - - expect(() => assertObjectMatchSpec({ first_name: true }, spec)).not.toThrow() - expect(() => assertObjectMatchSpec({ first_name: 'john' }, spec)).toThrow( - `Property 'first_name' (john) type is 'string': expected 'john' not to be a string` - ) -}) - -test("check object property equals 'equalRelativeDate' and format", () => { - const object = { - beginDate: '2018-04-30', - } - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: 'equalRelativeDate', - value: '-1,days,fr,YYYY-MM-DD', - }, - ]) - }).not.toThrow() - - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: 'equalRelativeDate', - value: '+2,days,fr,YYYY-MM-DD', - }, - ]) - }).toThrow( - `Expected property 'beginDate' to equal '2018-05-03', but found '2018-04-30': expected '2018-04-30' to deeply equal '2018-05-03'` - ) - - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: 'equalRelativeDate', - value: '-2,days,fr,YYYY-MM-DD', - }, - ]) - }).toThrow( - `Expected property 'beginDate' to equal '2018-04-29', but found '2018-04-30': expected '2018-04-30' to deeply equal '2018-04-29'` - ) - - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: 'equalRelativeDate', - value: "-2,days,fr,[Aujourd'hui] YYYY-MM-DD hh[h]mm", - }, - ]) - }).toThrow( - `Expected property 'beginDate' to equal 'Aujourd'hui 2018-04-29 12h00', but found '2018-04-30': expected '2018-04-30' to deeply equal 'Aujourd\\'hui 2018-04-29 12h00'` - ) - - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: 'equalRelativeDate', - value: '-2,days,EN-ZS,YYYY-MM-DD', - }, - ]) - }).toThrow( - `Expected property 'beginDate' to equal '2018-04-29', but found '2018-04-30': expected '2018-04-30' to deeply equal '2018-04-29'` - ) -}) - -test("check object property equals does not 'equalRelativeDate' and format", () => { - const object = { - beginDate: '2018-04-30', - } - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: 'not equalRelativeDate', - value: '-1,days,fr,YYYY-MM-DD', - }, - ]) - }).toThrow( - `Expected property 'beginDate' to not equal '2018-04-30', but found '2018-04-30': expected '2018-04-30' to not deeply equal '2018-04-30'` - ) - - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: 'does not equalRelativeDate', - value: '+2,days,fr,YYYY-MM-DD', - }, - ]) - }).not.toThrow() - - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: '!equalRelativeDate', - value: '-2,days,fr,YYYY-MM-DD', - }, - ]) - }).not.toThrow() - - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: `doesn't equalRelativeDate`, - value: "-2,days,fr,[Aujourd'hui] YYYY-MM-DD hh[h]mm", - }, - ]) - }).not.toThrow() - - expect(() => { - assertObjectMatchSpec(object, [ - { - field: 'beginDate', - matcher: 'not equalRelativeDate', - value: '-2,days,EN-ZS,YYYY-MM-DD', - }, - ]) - }).not.toThrow() -}) - -test('check dateOffset throw Exception given invalid locale set', () => { - const object = { - beginDate: '2018-05-04', - } - const spec = [ - { - field: 'beginDate', - matcher: 'equalRelativeDate', - value: '1,days,EN_US,YYYY-MM-DD', - }, - ] - - expect(() => assertObjectMatchSpec(object, spec)).toThrowError( - 'relative date arguments are invalid' - ) -}) - -test('check unsupported matcher should fail', () => { - const spec = [ - { - field: 'name', - matcher: 'unknown', - value: 'john', - }, - ] - - expect(() => assertObjectMatchSpec({ name: 'john' }, spec)).toThrow( - `Matcher "unknown" did not match any supported assertions` - ) -}) diff --git a/tests/core/assertions.test.ts b/tests/core/assertions.test.ts new file mode 100644 index 00000000..8ff399d0 --- /dev/null +++ b/tests/core/assertions.test.ts @@ -0,0 +1,804 @@ +import { countNestedProperties, assertObjectMatchSpec } from '../../src/core/assertions' + +describe('core > assertions', () => { + beforeAll(() => { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-return,@typescript-eslint/explicit-function-return-type + const MockDate = (lastDate) => () => new lastDate(2018, 4, 1) + // @ts-ignore + global.Date = jest.fn(MockDate(global.Date)) + }) + + afterAll(() => { + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + global.Date.mockRestore() + }) + + test('should allow to count object properties', () => { + expect( + countNestedProperties({ + a: true, + b: true, + c: true, + }) + ).toBe(3) + + expect( + countNestedProperties({ + a: true, + b: true, + c: true, + d: { + a: true, + b: true, + }, + }) + ).toBe(5) + }) + + test('should allow to count nested objects properties', () => { + expect( + countNestedProperties({ + a: true, + b: true, + c: { + d: 'value1', + e: 'value2', + }, + }) + ).toBe(4) + }) + + test('should allow to count object properties with null, undefined properties ', () => { + expect( + countNestedProperties({ + a: null, + b: undefined, + c: 'value3', + }) + ).toBe(3) + }) + + test('should allow to count object with properties array property', () => { + expect( + countNestedProperties({ + a: [1, 2], + b: true, + c: true, + }) + ).toBe(4) + }) + + test('should allow to count object properties with empty array property', () => { + expect( + countNestedProperties({ + a: true, + b: true, + c: { + d: '', + e: [], + }, + }) + ).toBe(4) + }) + + test('object property is defined', () => { + const spec = [ + { + field: 'name', + matcher: 'defined', + }, + { + field: 'gender', + matcher: 'present', + }, + { + field: 'age', + matcher: '?', + }, + ] + + expect(() => + assertObjectMatchSpec({ name: 'john', gender: 'male', age: 31 }, spec) + ).not.toThrow() + expect(() => assertObjectMatchSpec({ name: 'john', gender: 'male' }, spec)).toThrow( + `Property 'age' is undefined: expected undefined not to be undefined` + ) + expect(() => assertObjectMatchSpec({ name: 'john' }, spec)).toThrow( + `Property 'gender' is undefined: expected undefined not to be undefined` + ) + expect(() => assertObjectMatchSpec({ gender: 'john' }, spec)).toThrow( + `Property 'name' is undefined: expected undefined not to be undefined` + ) + expect(() => assertObjectMatchSpec({}, spec)).toThrow( + `Property 'name' is undefined: expected undefined not to be undefined` + ) + }) + + test('object property is not defined', () => { + const spec = [ + { + field: 'name', + matcher: 'not defined', + }, + { + field: 'gender', + matcher: 'not present', + }, + { + field: 'city', + matcher: 'is not defined', + }, + { + field: 'street', + matcher: `isn't defined`, + }, + { + field: 'age', + matcher: `!?`, + }, + ] + + expect(() => assertObjectMatchSpec({}, spec)).not.toThrow() + expect(() => assertObjectMatchSpec({ name: 'john' }, spec)).toThrow( + `Property 'name' is defined: expected 'john' to be undefined` + ) + expect(() => assertObjectMatchSpec({ gender: 'john' }, spec)).toThrow( + `Property 'gender' is defined: expected 'john' to be undefined` + ) + expect(() => assertObjectMatchSpec({ city: 'paris' }, spec)).toThrow( + `Property 'city' is defined: expected 'paris' to be undefined` + ) + expect(() => assertObjectMatchSpec({ street: 'rue du chat qui pêche' }, spec)).toThrow( + `Property 'street' is defined: expected 'rue du chat qui pêche' to be undefined` + ) + expect(() => assertObjectMatchSpec({ age: 31 }, spec)).toThrow( + `Property 'age' is defined: expected 31 to be undefined` + ) + expect(() => + assertObjectMatchSpec( + { + name: 'john', + gender: 'male', + city: 'paris', + street: 'rue du chat qui pêche', + }, + spec + ) + ).toThrow(`Property 'name' is defined: expected 'john' to be undefined`) + }) + + test('check object property equals expected value', () => { + const spec = [ + { + field: 'name', + matcher: 'equals', + value: 'john', + }, + { + field: 'city', + matcher: '=', + value: 'Bordeaux', + }, + ] + + expect(() => assertObjectMatchSpec({ name: 'john', city: 'Bordeaux' }, spec)).not.toThrow() + expect(() => assertObjectMatchSpec({ name: 'plouc' }, spec)).toThrow( + `Expected property 'name' to equal 'john', but found 'plouc': expected 'plouc' to deeply equal 'john'` + ) + expect(() => assertObjectMatchSpec({ name: 'john', city: 'Paris' }, spec)).toThrow( + `Expected property 'city' to equal 'Bordeaux', but found 'Paris': expected 'Paris' to deeply equal 'Bordeaux'` + ) + }) + + test('check object property does not equal expected value', () => { + const spec = [ + { + field: 'name', + matcher: 'does not equal', + value: 'john', + }, + { + field: 'city', + matcher: '!=', + value: 'Paris', + }, + ] + + expect(() => assertObjectMatchSpec({ name: 'plouc', city: 'Bordeaux' }, spec)).not.toThrow() + expect(() => assertObjectMatchSpec({ name: 'plouc', city: 'Paris' }, spec)).toThrow( + `Expected property 'city' to not equal 'Paris', but found 'Paris': expected 'Paris' to not deeply equal 'Paris` + ) + expect(() => assertObjectMatchSpec({ name: 'john' }, spec)).toThrow( + `Expected property 'name' to not equal 'john', but found 'john': expected 'john' to not deeply equal 'john` + ) + }) + + test('check object property contains value', () => { + const spec = [ + { + field: 'first_name', + matcher: 'contain', + value: 'john', + }, + { + field: 'last_name', + matcher: 'contains', + value: 'doe', + }, + { + field: 'city', + matcher: '*=', + value: 'ord', + }, + ] + + expect(() => + assertObjectMatchSpec( + { first_name: 'johnny', last_name: 'doet', city: 'Bordeaux' }, + spec + ) + ).not.toThrow() + expect(() => + assertObjectMatchSpec({ first_name: 'johnny', last_name: 'doe', city: 'Paris' }, spec) + ).toThrow( + `Property 'city' (Paris) does not contain 'ord': expected 'Paris' to include 'ord'` + ) + expect(() => + assertObjectMatchSpec({ first_name: 'john', last_name: 'john' }, spec) + ).toThrow( + `Property 'last_name' (john) does not contain 'doe': expected 'john' to include 'doe'` + ) + expect(() => assertObjectMatchSpec({ first_name: 'doe', last_name: 'doe' }, spec)).toThrow( + `Property 'first_name' (doe) does not contain 'john': expected 'doe' to include 'john'` + ) + }) + + test('check object property does not contain value', () => { + const spec = [ + { + field: 'first_name', + matcher: `doesn't contain`, + value: 'john', + }, + { + field: 'last_name', + matcher: 'does not contain', + value: 'doe', + }, + { + field: 'city', + matcher: `doesn't contains`, + value: 'york', + }, + { + field: 'street', + matcher: 'does not contains', + value: 'avenue', + }, + { + field: 'postal_code', + matcher: '!*=', + value: '44', + }, + ] + + expect(() => + assertObjectMatchSpec( + { + first_name: 'foo', + last_name: 'bar', + city: 'miami', + street: 'calle ocho', + postal_code: 'FL 33135', + }, + spec + ) + ).not.toThrow() + expect(() => + assertObjectMatchSpec( + { + first_name: 'foo', + last_name: 'bar', + city: 'miami', + street: 'calle ocho', + postal_code: 'FL 44135', + }, + spec + ) + ).toThrow( + `Property 'postal_code' (FL 44135) contains '44': expected 'FL 44135' to not include '44'` + ) + expect(() => + assertObjectMatchSpec( + { + first_name: 'johnny', + last_name: 'bar', + city: 'miami', + street: 'calle ocho', + }, + spec + ) + ).toThrow( + `Property 'first_name' (johnny) contains 'john': expected 'johnny' to not include 'john'` + ) + expect(() => + assertObjectMatchSpec( + { + first_name: 'foo', + last_name: 'doet', + city: 'miami', + street: 'calle ocho', + }, + spec + ) + ).toThrow( + `Property 'last_name' (doet) contains 'doe': expected 'doet' to not include 'doe'` + ) + expect(() => + assertObjectMatchSpec( + { + first_name: 'foo', + last_name: 'bar', + city: 'new york', + street: 'calle ocho', + }, + spec + ) + ).toThrow( + `Property 'city' (new york) contains 'york': expected 'new york' to not include 'york'` + ) + expect(() => + assertObjectMatchSpec( + { + first_name: 'foo', + last_name: 'bar', + city: 'miami', + street: 'krome avenue', + }, + spec + ) + ).toThrow( + `Property 'street' (krome avenue) contains 'avenue': expected 'krome avenue' to not include 'avenue'` + ) + }) + + test('check object property starts with value', () => { + const spec = [ + { + field: 'first_name', + matcher: 'starts with', + value: 'john', + }, + { + field: 'last_name', + matcher: '^=', + value: 'do', + }, + ] + + expect(() => + assertObjectMatchSpec({ first_name: 'johnny', last_name: 'doe' }, spec) + ).not.toThrow() + expect(() => + assertObjectMatchSpec({ first_name: 'john', last_name: 'john' }, spec) + ).toThrow( + `Property 'last_name' (john) does not start with 'do': expected 'john' to start with 'do'` + ) + expect(() => assertObjectMatchSpec({ first_name: 'doe', last_name: 'doe' }, spec)).toThrow( + `Property 'first_name' (doe) does not start with 'john': expected 'doe' to start with 'john'` + ) + }) + + test('check object property does not start with value', () => { + const spec = [ + { + field: 'first_name', + matcher: 'does not start with', + value: 'john', + }, + { + field: 'last_name', + matcher: '!^=', + value: 'do', + }, + ] + + expect(() => + assertObjectMatchSpec({ first_name: 'bob', last_name: 'dylan' }, spec) + ).not.toThrow() + expect(() => assertObjectMatchSpec({ first_name: 'bod', last_name: 'doe' }, spec)).toThrow( + `Property 'last_name' (doe) starts with 'do': expected 'doe' not to start with 'do'` + ) + expect(() => + assertObjectMatchSpec({ first_name: 'johnny', last_name: 'dylan' }, spec) + ).toThrow( + `Property 'first_name' (johnny) starts with 'john': expected 'johnny' not to start with 'john'` + ) + }) + + test('check object property ends with value', () => { + const spec = [ + { + field: 'first_name', + matcher: 'ends with', + value: 'ny', + }, + { + field: 'last_name', + matcher: '$=', + value: 'oe', + }, + ] + + expect(() => + assertObjectMatchSpec({ first_name: 'johnny', last_name: 'doe' }, spec) + ).not.toThrow() + expect(() => + assertObjectMatchSpec({ first_name: 'johnny', last_name: 'john' }, spec) + ).toThrow( + `Property 'last_name' (john) does not end with 'oe': expected 'john' to end with 'oe'` + ) + expect(() => assertObjectMatchSpec({ first_name: 'doe', last_name: 'doe' }, spec)).toThrow( + `Property 'first_name' (doe) does not end with 'ny': expected 'doe' to end with 'ny'` + ) + }) + + test('check object property does not end with value', () => { + const spec = [ + { + field: 'first_name', + matcher: 'does not end with', + value: 'ny', + }, + { + field: 'last_name', + matcher: '!$=', + value: 'oe', + }, + ] + + expect(() => + assertObjectMatchSpec({ first_name: 'bob', last_name: 'dylan' }, spec) + ).not.toThrow() + expect(() => assertObjectMatchSpec({ first_name: 'bob', last_name: 'doe' }, spec)).toThrow( + `Property 'last_name' (doe) ends with 'oe': expected 'doe' not to end with 'oe'` + ) + expect(() => + assertObjectMatchSpec({ first_name: 'johnny', last_name: 'dylan' }, spec) + ).toThrow( + `Property 'first_name' (johnny) ends with 'ny': expected 'johnny' not to end with 'ny'` + ) + }) + + test('check object property matches regexp', () => { + const spec = [ + { + field: 'first_name', + matcher: 'matches', + value: '^john', + }, + { + field: 'last_name', + matcher: 'match', + value: '^doe', + }, + { + field: 'city', + matcher: '~=', + value: '(.+-){3}.+', + }, + ] + + expect(() => + assertObjectMatchSpec( + { + first_name: 'johnny', + last_name: 'doet', + city: 'Saint-Pée-sur-Nivelle', + }, + spec + ) + ).not.toThrow() + expect(() => + assertObjectMatchSpec( + { first_name: 'johnny', last_name: 'doet', city: 'Bordeaux' }, + spec + ) + ).toThrow( + `Property 'city' (Bordeaux) does not match '(.+-){3}.+': expected 'Bordeaux' to match /(.+-){3}.+/` + ) + expect(() => + assertObjectMatchSpec( + { + first_name: 'johnny', + last_name: 'john', + city: 'Saint-Pée-sur-Nivelle', + }, + spec + ) + ).toThrow( + `Property 'last_name' (john) does not match '^doe': expected 'john' to match /^doe/` + ) + expect(() => + assertObjectMatchSpec( + { + first_name: 'doe', + last_name: 'doe', + city: 'Saint-Pée-sur-Nivelle', + }, + spec + ) + ).toThrow( + `Property 'first_name' (doe) does not match '^john': expected 'doe' to match /^john/` + ) + }) + + test('check object property does not match regexp', () => { + const spec = [ + { + field: 'first_name', + matcher: `doesn't match`, + value: '^john', + }, + { + field: 'last_name', + matcher: '!~=', + value: '^[a-z]{3}$', + }, + ] + + expect(() => + assertObjectMatchSpec({ first_name: 'bob', last_name: 'dylan' }, spec) + ).not.toThrow() + expect(() => assertObjectMatchSpec({ first_name: 'bob', last_name: 'doe' }, spec)).toThrow( + `Property 'last_name' (doe) matches '^[a-z]{3}$': expected 'doe' not to match /^[a-z]{3}$/` + ) + expect(() => assertObjectMatchSpec({ first_name: 'john', last_name: 'doe' }, spec)).toThrow( + `Property 'first_name' (john) matches '^john': expected 'john' not to match /^john/` + ) + }) + + test('check object fully matches spec', () => { + const spec = [ + { + field: 'first_name', + matcher: 'equal', + value: 'john', + }, + { + field: 'last_name', + matcher: 'match', + value: '^doe', + }, + ] + + expect(() => + assertObjectMatchSpec({ first_name: 'john', last_name: 'doet' }, spec, true) + ).not.toThrow() + expect(() => + assertObjectMatchSpec( + { first_name: 'john', last_name: 'doet', gender: 'male' }, + spec, + true + ) + ).toThrow( + `Expected json response to fully match spec, but it does not: expected 3 to equal 2` + ) + expect(() => + assertObjectMatchSpec({ first_name: 'john', last_name: 'john' }, spec, true) + ).toThrow( + `Property 'last_name' (john) does not match '^doe': expected 'john' to match /^doe/` + ) + expect(() => + assertObjectMatchSpec({ first_name: 'doe', last_name: 'doe' }, spec, true) + ).toThrow( + `Expected property 'first_name' to equal 'john', but found 'doe': expected 'doe' to deeply equal 'john'` + ) + }) + + test('check object property type', () => { + const spec = [ + { + field: 'first_name', + matcher: 'type', + value: 'string', + }, + { + field: 'last_name', + matcher: '#=', + value: 'string', + }, + { + field: 'age', + matcher: 'type', + value: 'number', + }, + ] + + expect(() => + assertObjectMatchSpec({ first_name: 'john', last_name: 'doe', age: 23 }, spec) + ).not.toThrow() + expect(() => + assertObjectMatchSpec({ first_name: true, last_name: 'doe', age: 23 }, spec) + ).toThrow(`Property 'first_name' (true) type is not 'string': expected true to be a string`) + expect(() => + assertObjectMatchSpec({ first_name: 'john', last_name: 45, age: 'test' }, spec) + ).toThrow(`Property 'last_name' (45) type is not 'string': expected 45 to be a string`) + expect(() => + assertObjectMatchSpec({ first_name: 'john', last_name: 'doe', age: 'test' }, spec) + ).toThrow(`Property 'age' (test) type is not 'number': expected 'test' to be a number`) + }) + + test('check object property type does not match', () => { + const spec = [ + { + field: 'first_name', + matcher: 'not type', + value: 'string', + }, + { + field: 'first_name', + matcher: '!#=', + value: 'string', + }, + ] + + expect(() => assertObjectMatchSpec({ first_name: true }, spec)).not.toThrow() + expect(() => assertObjectMatchSpec({ first_name: 'john' }, spec)).toThrow( + `Property 'first_name' (john) type is 'string': expected 'john' not to be a string` + ) + }) + + test("check object property equals 'equalRelativeDate' and format", () => { + const object = { + beginDate: '2018-04-30', + } + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: 'equalRelativeDate', + value: '-1,days,fr,YYYY-MM-DD', + }, + ]) + }).not.toThrow() + + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: 'equalRelativeDate', + value: '+2,days,fr,YYYY-MM-DD', + }, + ]) + }).toThrow( + `Expected property 'beginDate' to equal '2018-05-03', but found '2018-04-30': expected '2018-04-30' to deeply equal '2018-05-03'` + ) + + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: 'equalRelativeDate', + value: '-2,days,fr,YYYY-MM-DD', + }, + ]) + }).toThrow( + `Expected property 'beginDate' to equal '2018-04-29', but found '2018-04-30': expected '2018-04-30' to deeply equal '2018-04-29'` + ) + + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: 'equalRelativeDate', + value: "-2,days,fr,[Aujourd'hui] YYYY-MM-DD hh[h]mm", + }, + ]) + }).toThrow( + `Expected property 'beginDate' to equal 'Aujourd'hui 2018-04-29 12h00', but found '2018-04-30': expected '2018-04-30' to deeply equal 'Aujourd\\'hui 2018-04-29 12h00'` + ) + + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: 'equalRelativeDate', + value: '-2,days,EN-ZS,YYYY-MM-DD', + }, + ]) + }).toThrow( + `Expected property 'beginDate' to equal '2018-04-29', but found '2018-04-30': expected '2018-04-30' to deeply equal '2018-04-29'` + ) + }) + + test("check object property equals does not 'equalRelativeDate' and format", () => { + const object = { + beginDate: '2018-04-30', + } + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: 'not equalRelativeDate', + value: '-1,days,fr,YYYY-MM-DD', + }, + ]) + }).toThrow( + `Expected property 'beginDate' to not equal '2018-04-30', but found '2018-04-30': expected '2018-04-30' to not deeply equal '2018-04-30'` + ) + + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: 'does not equalRelativeDate', + value: '+2,days,fr,YYYY-MM-DD', + }, + ]) + }).not.toThrow() + + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: '!equalRelativeDate', + value: '-2,days,fr,YYYY-MM-DD', + }, + ]) + }).not.toThrow() + + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: `doesn't equalRelativeDate`, + value: "-2,days,fr,[Aujourd'hui] YYYY-MM-DD hh[h]mm", + }, + ]) + }).not.toThrow() + + expect(() => { + assertObjectMatchSpec(object, [ + { + field: 'beginDate', + matcher: 'not equalRelativeDate', + value: '-2,days,EN-ZS,YYYY-MM-DD', + }, + ]) + }).not.toThrow() + }) + + test('check dateOffset throw Exception given invalid locale set', () => { + const object = { + beginDate: '2018-05-04', + } + const spec = [ + { + field: 'beginDate', + matcher: 'equalRelativeDate', + value: '1,days,EN_US,YYYY-MM-DD', + }, + ] + + expect(() => assertObjectMatchSpec(object, spec)).toThrowError( + 'relative date arguments are invalid' + ) + }) + + test('check unsupported matcher should fail', () => { + const spec = [ + { + field: 'name', + matcher: 'unknown', + value: 'john', + }, + ] + + expect(() => assertObjectMatchSpec({ name: 'john' }, spec)).toThrow( + `Matcher "unknown" did not match any supported assertions` + ) + }) +}) diff --git a/tests/core/cast.test.js b/tests/core/cast.test.js deleted file mode 100644 index 7db820ef..00000000 --- a/tests/core/cast.test.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict' - -const Cast = require('../../src/core/cast') - -test('cast nulls', () => { - expect(Cast.value('((null))')).toBe(null) -}) - -test('cast undefined', () => { - expect(Cast.value('((undefined))')).toBe(undefined) -}) - -test('cast numbers', () => { - expect(Cast.value('1((number))')).toBe(1) - expect(Cast.value('.2((number))')).toBe(0.2) - expect(Cast.value('-3((number))')).toBe(-3) -}) - -test('cast dates', () => { - expect(Cast.value('today((date))')).toMatch(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) - expect(Cast.value('2010-10-10((date))')).toBe('2010-10-10T00:00:00.000Z') -}) - -test('throw when trying to cast invalid numbers', () => { - expect(() => { - Cast.value('nan((number))') - }).toThrow(`Unable to cast value to number 'nan'`) -}) - -test('cast booleans', () => { - expect(Cast.value('true((boolean))')).toBe(true) - expect(Cast.value('false((boolean))')).toBe(false) -}) - -test('cast arrays', () => { - expect(Cast.value('((array))')).toEqual([]) - expect(Cast.value('one((array))')).toEqual(['one']) - expect(Cast.value('one,2((number)),true((boolean))((array))')).toEqual(['one', 2, true]) -}) - -test('cast strings', () => { - expect(Cast.value('yay((string))')).toEqual('yay') -}) - -test('left value untouched if no casting directive were found', () => { - expect(Cast.value('untouched')).toBe('untouched') -}) - -test('throw when type is invalid', () => { - expect(() => { - Cast.value('test((invalid))') - }).toThrow(`Invalid type provided: invalid 'test((invalid))'`) -}) - -test('cast array of values', () => { - expect(Cast.array(['1((number))', 'true((boolean))', 'a,b,c((array))'])).toEqual([ - 1, - true, - ['a', 'b', 'c'], - ]) -}) - -test('cast array of objects', () => { - expect( - Cast.objects([{ a: '1((number))' }, { b: 'true((boolean))' }, { c: 'a,b,c((array))' }]) - ).toEqual([{ a: 1 }, { b: true }, { c: ['a', 'b', 'c'] }]) -}) - -test('Add a new type to cast', () => { - Cast.addType('newType', (value) => value === 'true') - expect(Cast.value('test((newType))')).toEqual(false) -}) - -test('throw when trying to add a type without providing a casting function', () => { - expect(() => { - Cast.addType('newType2', 'test') - }).toThrow('Invalid cast function provided, must be a function') - - expect(() => { - Cast.value('test((newType2))') - }).toThrow(`Invalid type provided: newType2 'test((newType2))'`) -}) diff --git a/tests/core/cast.test.ts b/tests/core/cast.test.ts new file mode 100644 index 00000000..1ad52f02 --- /dev/null +++ b/tests/core/cast.test.ts @@ -0,0 +1,83 @@ +import * as Cast from '../../src/core/cast' +import { CastFunction, CastType } from '../../src/core/core_types' + +describe('core > cast', () => { + test('cast nulls', () => { + expect(Cast.value('((null))')).toBe(null) + }) + + test('cast undefined', () => { + expect(Cast.value('((undefined))')).toBe(undefined) + }) + + test('cast numbers', () => { + expect(Cast.value('1((number))')).toBe(1) + expect(Cast.value('.2((number))')).toBe(0.2) + expect(Cast.value('-3((number))')).toBe(-3) + }) + + test('cast dates', () => { + expect(Cast.value('today((date))')).toMatch(/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) + expect(Cast.value('2010-10-10((date))')).toBe('2010-10-10T00:00:00.000Z') + }) + + test('throw when trying to cast invalid numbers', () => { + expect(() => { + Cast.value('nan((number))') + }).toThrow(`Unable to cast value to number 'nan'`) + }) + + test('cast booleans', () => { + expect(Cast.value('true((boolean))')).toBe(true) + expect(Cast.value('false((boolean))')).toBe(false) + }) + + test('cast arrays', () => { + expect(Cast.value('((array))')).toEqual([]) + expect(Cast.value('one((array))')).toEqual(['one']) + expect(Cast.value('one,2((number)),true((boolean))((array))')).toEqual(['one', 2, true]) + }) + + test('cast strings', () => { + expect(Cast.value('yay((string))')).toEqual('yay') + }) + + test('left value untouched if no casting directive were found', () => { + expect(Cast.value('untouched')).toBe('untouched') + }) + + test('throw when type is invalid', () => { + expect(() => { + Cast.value('test((invalid))') + }).toThrow(`Invalid type provided: invalid 'test((invalid))'`) + }) + + test('cast array of values', () => { + expect(Cast.array(['1((number))', 'true((boolean))', 'a,b,c((array))'])).toEqual([ + 1, + true, + ['a', 'b', 'c'], + ]) + }) + + test('cast array of objects', () => { + expect( + Cast.objects([{ a: '1((number))' }, { b: 'true((boolean))' }, { c: 'a,b,c((array))' }]) + ).toEqual([{ a: 1 }, { b: true }, { c: ['a', 'b', 'c'] }]) + }) + + test('Add a new type to cast', () => { + Cast.addType('newType', (value: string) => value === 'true') + expect(Cast.value('test((newType))')).toEqual(false) + }) + + test('throw when trying to add a type without providing a casting function', () => { + expect(() => { + Cast.addType('newType2' as CastType, 'test' as unknown as CastFunction) + }).toThrow('Invalid cast function provided, must be a function') + + expect(() => { + Cast.value('test((newType2))') + }).toThrow(`Invalid type provided: newType2 'test((newType2))'`) + }) +}) diff --git a/tests/core/custom_chai_assertions.test.js b/tests/core/custom_chai_assertions.test.js deleted file mode 100644 index 3e08acfe..00000000 --- a/tests/core/custom_chai_assertions.test.js +++ /dev/null @@ -1,54 +0,0 @@ -const chai = require('chai') -const sinon = require('sinon') -const { registerChaiAssertion } = require('../../src/core/custom_chai_assertions') - -beforeAll(() => { - chai.use(registerChaiAssertion) -}) - -test('registerChaiAssertion should add startWith and endWith assertion methods', () => { - const addMethodSpy = sinon.spy() - const fakeChai = { - Assertion: { - addMethod: addMethodSpy, - }, - } - - registerChaiAssertion(fakeChai, undefined) - - expect(addMethodSpy.calledTwice).toBeTruthy() - expect(addMethodSpy.calledWith('startWith', sinon.match.func)).toBeTruthy() - expect(addMethodSpy.calledWith('endWith', sinon.match.func)).toBeTruthy() -}) - -test('chai startWith should pass', () => { - expect(() => chai.expect('foo').to.startWith('fo')).not.toThrow() -}) - -test('chai startWith should fail', () => { - expect(() => chai.expect('foo').to.startWith('ba')).toThrowError( - "expected 'foo' to start with 'ba'" - ) -}) - -test('chai startWith should fail with a negated message', () => { - expect(() => chai.expect('foo').not.to.startWith('fo')).toThrowError( - "expected 'foo' not to start with 'fo'" - ) -}) - -test('chai endWith should pass', () => { - expect(() => chai.expect('foo').to.endWith('oo')).not.toThrow() -}) - -test('chai endWith should fail', () => { - expect(() => chai.expect('foo').to.endWith('ar')).toThrowError( - "expected 'foo' to end with 'ar'" - ) -}) - -test('chai endWith should fail with a negated message', () => { - expect(() => chai.expect('foo').not.to.endWith('oo')).toThrowError( - "expected 'foo' not to end with 'oo'" - ) -}) diff --git a/tests/core/custom_chai_assertions.test.ts b/tests/core/custom_chai_assertions.test.ts new file mode 100644 index 00000000..f267667a --- /dev/null +++ b/tests/core/custom_chai_assertions.test.ts @@ -0,0 +1,55 @@ +import chai from 'chai' +import { match, SinonSpy, spy } from 'sinon' +import { registerChaiAssertion } from '../../src/core/custom_chai_assertions' + +beforeAll(() => { + chai.use(registerChaiAssertion) +}) + +describe('core > custom_chai_assertions', () => { + let addMethodSpy: SinonSpy + beforeAll(() => { + addMethodSpy = spy(chai.Assertion, 'addMethod') + }) + afterAll(() => addMethodSpy.restore()) + + test('registerChaiAssertion should add startWith and endWith assertion methods', () => { + registerChaiAssertion() + + expect(addMethodSpy.calledTwice).toBeTruthy() + expect(addMethodSpy.calledWith('startWith', match.func)).toBeTruthy() + expect(addMethodSpy.calledWith('endWith', match.func)).toBeTruthy() + }) + + test('chai startWith should pass', () => { + expect(() => chai.expect('foo').to.startWith('fo')).not.toThrow() + }) + + test('chai startWith should fail', () => { + expect(() => chai.expect('foo').to.startWith('ba')).toThrowError( + "expected 'foo' to start with 'ba'" + ) + }) + + test('chai startWith should fail with a negated message', () => { + expect(() => chai.expect('foo').not.to.startWith('fo')).toThrowError( + "expected 'foo' not to start with 'fo'" + ) + }) + + test('chai endWith should pass', () => { + expect(() => chai.expect('foo').to.endWith('oo')).not.toThrow() + }) + + test('chai endWith should fail', () => { + expect(() => chai.expect('foo').to.endWith('ar')).toThrowError( + "expected 'foo' to end with 'ar'" + ) + }) + + test('chai endWith should fail with a negated message', () => { + expect(() => chai.expect('foo').not.to.endWith('oo')).toThrowError( + "expected 'foo' not to end with 'oo'" + ) + }) +}) diff --git a/tests/core/registry.test.js b/tests/core/registry.test.js deleted file mode 100644 index 2b3e4324..00000000 --- a/tests/core/registry.test.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict' - -const Registry = require('../../src/core/registry') - -test('should register an extension', () => { - const world = {} - Registry.registerExtension(world, 'test') - - expect(world).toHaveProperty('_registredExtensions', ['test']) -}) - -test('should allow to check if an extension were registered', () => { - expect(Registry.hasExtension({}, 'test')).toBe(false) - expect(Registry.hasExtension({ _registredExtensions: [] }, 'test')).toBe(false) - expect(Registry.hasExtension({ _registredExtensions: ['test'] }, 'test')).toBe(true) -}) diff --git a/tests/core/registry.test.ts b/tests/core/registry.test.ts new file mode 100644 index 00000000..b7a64a21 --- /dev/null +++ b/tests/core/registry.test.ts @@ -0,0 +1,16 @@ +import { hasExtension, registerExtension } from '../../src/core/registry' + +describe('core > registry', () => { + test('should register an extension', () => { + const world = {} + registerExtension(world, 'test') + + expect(world).toHaveProperty('_registredExtensions', ['test']) + }) + + test('should allow to check if an extension were registered', () => { + expect(hasExtension({}, 'test')).toBe(false) + expect(hasExtension({ _registredExtensions: [] }, 'test')).toBe(false) + expect(hasExtension({ _registredExtensions: ['test'] }, 'test')).toBe(true) + }) +}) diff --git a/tests/extensions/cli/definitions.test.js b/tests/extensions/cli/definitions.test.js deleted file mode 100644 index 87369076..00000000 --- a/tests/extensions/cli/definitions.test.js +++ /dev/null @@ -1,276 +0,0 @@ -'use strict' - -const sinon = require('sinon') - -const helper = require('../definitions_helper') -const definitions = require('../../../src/extensions/cli/definitions') - -beforeEach(() => { - definitions.install() -}) - -afterEach(() => { - helper.clearContext() -}) - -test('set current working directory', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('set (?:working directory|cwd) to') - def.shouldNotMatch('I set working directory to ') - def.shouldMatch('I set working directory to path', ['path']) - def.shouldMatch('I set cwd to path', ['path']) - def.shouldMatch('set working directory to path', ['path']) - def.shouldMatch('set cwd to path', ['path']) - - const cliMock = { cli: { setCwd: jest.fn() } } - def.exec(cliMock, 'path') - expect(cliMock.cli.setCwd).toHaveBeenCalledWith('path') -}) - -test('set environment variables', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('set (?:env|environment) (?:vars|variables)') - def.shouldMatch('I set environment variables') - def.shouldMatch('I set environment vars') - def.shouldMatch('I set env variables') - def.shouldMatch('I set env vars') - def.shouldMatch('set environment variables') - def.shouldMatch('set environment vars') - def.shouldMatch('set env variables') - def.shouldMatch('set env vars') - - const cliMock = { cli: { setEnvironmentVariables: jest.fn() } } - const envVars = { TEST_MODE: true } - def.exec(cliMock, { rowsHash: () => envVars }) - expect(cliMock.cli.setEnvironmentVariables).toHaveBeenCalledWith(envVars) -}) - -test('set a single environment variable', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(?:env|environment) (?:var|variable)') - def.shouldNotMatch('I set Accept env var to ') - def.shouldNotMatch('I set X User Id env var to 1') - def.shouldMatch('I set Accept environment variable to application/json') - def.shouldMatch('I set Accept environment var to application/json') - def.shouldMatch('I set Accept env variable to application/json') - def.shouldMatch('I set Accept env var to application/json') - def.shouldMatch('set Accept environment variable to application/json') - def.shouldMatch('set Accept environment var to application/json') - def.shouldMatch('set Accept env variable to application/json') - def.shouldMatch('set Accept env var to application/json') - - const cliMock = { cli: { setEnvironmentVariable: jest.fn() } } - def.exec(cliMock, 'Accept', 'application/json') - expect(cliMock.cli.setEnvironmentVariable).toHaveBeenCalledWith('Accept', 'application/json') -}) - -test('schedule process killing', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('kill the process with') - def.shouldNotMatch('I kill the process with sig in ') - def.shouldNotMatch('I kill the process with sig in 1mn') - def.shouldNotMatch('I kill the process with sig in xs') - def.shouldMatch('I kill the process with sig in 1s', ['sig', '1', 's']) - def.shouldMatch('I kill the process with sig in 10ms', ['sig', '10', 'ms']) - def.shouldMatch('kill the process with sig in 1s', ['sig', '1', 's']) - def.shouldMatch('kill the process with sig in 10ms', ['sig', '10', 'ms']) - - const cliMock = { cli: { scheduleKillProcess: jest.fn() } } - def.exec(cliMock, 'sig', '10', 'ms') - expect(cliMock.cli.scheduleKillProcess).toHaveBeenCalledWith(10, 'sig') - def.exec(cliMock, 'sig', '10', 's') - expect(cliMock.cli.scheduleKillProcess).toHaveBeenCalledWith(10000, 'sig') -}) - -test('run a command', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('run command') - def.shouldNotMatch('I run command ') - def.shouldMatch('I run command ls -al', ['ls -al']) - def.shouldMatch('run command ls -al', ['ls -al']) - - const cliMock = { cli: { run: jest.fn(() => Promise.resolve()) } } - def.exec(cliMock, 'ls -al') - expect(cliMock.cli.run).toHaveBeenCalledWith('ls -al') -}) - -test('dump stdout or stderr for debugging purpose', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('dump (stderr|stdout)') - def.shouldNotMatch('I dump stdcrap') - def.shouldMatch('I dump stdout', ['stdout']) - def.shouldMatch('I dump stderr', ['stderr']) - def.shouldMatch('dump stdout', ['stdout']) - def.shouldMatch('dump stderr', ['stderr']) - - const cliMock = { cli: { getOutput: jest.fn() } } - def.exec(cliMock, 'stdout') - expect(cliMock.cli.getOutput).toHaveBeenCalledWith('stdout') -}) - -test('check exit code', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(?:command )?exit code should be') - def.shouldNotMatch('the command exit code should be ') - def.shouldNotMatch('the command exit code should be nan') - def.shouldMatch('the command exit code should be 1', ['1']) - def.shouldMatch('command exit code should be 0', ['0']) - def.shouldMatch('exit code should be 32', ['32']) - - const cliMock = { cli: { getExitCode: jest.fn(() => 1) } } - expect(() => { - def.exec(cliMock, '0') - }).toThrow('0', `The command exit code doesn't match expected 0, found: 0`) - - expect(cliMock.cli.getExitCode).toHaveBeenCalled() -}) - -test('check if stdout or stderr is empty', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(stderr|stdout) should be empty') - def.shouldNotMatch('stdcrap should be empty') - def.shouldMatch('stdout should be empty', ['stdout']) - def.shouldMatch('stderr should be empty', ['stderr']) - - const getOutput = sinon.stub() - getOutput.withArgs('stdout').returns('not empty stdout') - getOutput.withArgs('stdout').onSecondCall().returns('') - getOutput.withArgs('stderr').returns('not empty stderr') - getOutput.withArgs('stderr').onSecondCall().returns('') - - const cliMock = { cli: { getOutput: getOutput } } - - expect(() => { - def.exec(cliMock, 'stdout') - }).toThrow("expected 'not empty stdout' to be empty") - expect(() => { - def.exec(cliMock, 'stdout') - }).not.toThrow() - - expect(() => { - def.exec(cliMock, 'stderr') - }).toThrow("expected 'not empty stderr' to be empty") - expect(() => { - def.exec(cliMock, 'stderr') - }).not.toThrow() -}) - -test('check if stdout or stderr contains something', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(stderr|stdout) should contain') - def.shouldNotMatch('stdcrap should contain something') - def.shouldNotMatch('stdout should contain ') - def.shouldMatch('stdout should contain something', ['stdout', 'something']) - def.shouldMatch('stderr should contain something', ['stderr', 'something']) - - const getOutput = sinon.stub() - getOutput.withArgs('stdout').returns('nothing on stdout') - getOutput.withArgs('stdout').onSecondCall().returns('something on stdout') - getOutput.withArgs('stderr').returns('nothing on stderr') - getOutput.withArgs('stderr').onSecondCall().returns('something on stderr') - const cliMock = { cli: { getOutput: getOutput } } - - expect(() => { - def.exec(cliMock, 'stdout', 'something') - }).toThrow(`expected 'nothing on stdout' to include 'something'`) - expect(() => { - def.exec(cliMock, 'stdout', 'something') - }).not.toThrow() - - expect(() => { - def.exec(cliMock, 'stderr', 'something') - }).toThrow(`expected 'nothing on stderr' to include 'something'`) - expect(() => { - def.exec(cliMock, 'stderr', 'something') - }).not.toThrow() -}) - -test('check if stdout or stderr does not contain something', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(stderr|stdout) should not contain') - def.shouldNotMatch('stdcrap should not contain something') - def.shouldNotMatch('stdout should not contain ') - def.shouldMatch('stdout should not contain something', ['stdout', 'something']) - def.shouldMatch('stderr should not contain something', ['stderr', 'something']) - - const getOutput = sinon.stub() - getOutput.withArgs('stdout').returns('something on stdout') - getOutput.withArgs('stdout').onSecondCall().returns('nothing on stdout') - getOutput.withArgs('stderr').returns('something on stderr') - getOutput.withArgs('stderr').onSecondCall().returns('nothing on stderr') - const cliMock = { cli: { getOutput: getOutput } } - - expect(() => { - def.exec(cliMock, 'stdout', 'something') - }).toThrow(`expected 'something on stdout' to not include 'something'`) - expect(() => { - def.exec(cliMock, 'stdout', 'something') - }).not.toThrow() - - expect(() => { - def.exec(cliMock, 'stderr', 'something') - }).toThrow(`expected 'something on stderr' to not include 'something'`) - expect(() => { - def.exec(cliMock, 'stderr', 'something') - }).not.toThrow() -}) - -test('check if stdout or stderr matches a regular expression', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(stderr|stdout) should match') - def.shouldNotMatch('stdcrap should match something') - def.shouldNotMatch('stdout should match ') - def.shouldMatch('stdout should match regex', ['stdout', 'regex']) - def.shouldMatch('stderr should match regex', ['stderr', 'regex']) - - const getOutput = sinon.stub() - getOutput.withArgs('stdout').returns('nothing on stdout') - getOutput.withArgs('stdout').onSecondCall().returns('something on stdout') - getOutput.withArgs('stderr').returns('nothing on stderr') - getOutput.withArgs('stderr').onSecondCall().returns('something on stderr') - const cliMock = { cli: { getOutput: getOutput } } - - expect(() => { - def.exec(cliMock, 'stdout', 'something') - }).toThrow(`expected 'nothing on stdout' to match /something/gim`) - expect(() => { - def.exec(cliMock, 'stdout', 'something') - }).not.toThrow() - - expect(() => { - def.exec(cliMock, 'stderr', 'something') - }).toThrow(`expected 'nothing on stderr' to match /something/gim`) - expect(() => { - def.exec(cliMock, 'stderr', 'something') - }).not.toThrow() -}) - -test('check if stdout or stderr does not match a regular expression', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(stderr|stdout) should not match') - def.shouldNotMatch('stdcrap should not match regex') - def.shouldNotMatch('stdout should not match ') - def.shouldMatch('stdout should not match regex', ['stdout', 'regex']) - def.shouldMatch('stderr should not match regex', ['stderr', 'regex']) - - const getOutput = sinon.stub() - getOutput.withArgs('stdout').returns('something on stdout') - getOutput.withArgs('stdout').onSecondCall().returns('nothing on stdout') - getOutput.withArgs('stderr').returns('something on stderr') - getOutput.withArgs('stderr').onSecondCall().returns('nothing on stderr') - const cliMock = { cli: { getOutput: getOutput } } - - expect(() => { - def.exec(cliMock, 'stdout', 'something') - }).toThrow(`expected 'something on stdout' not to match /something/gim`) - expect(() => { - def.exec(cliMock, 'stdout', 'something') - }).not.toThrow() - - expect(() => { - def.exec(cliMock, 'stderr', 'something') - }).toThrow(`expected 'something on stderr' not to match /something/gim`) - expect(() => { - def.exec(cliMock, 'stderr', 'something') - }).not.toThrow() -}) diff --git a/tests/extensions/cli/definitions.test.ts b/tests/extensions/cli/definitions.test.ts new file mode 100644 index 00000000..24b0c5b8 --- /dev/null +++ b/tests/extensions/cli/definitions.test.ts @@ -0,0 +1,298 @@ +import { createSandbox, SinonStub } from 'sinon' +import * as helper from '../definitions_helper' +import * as definitions from '../../../src/extensions/cli/definitions' +import { cli } from '../../../src/extensions/cli' + +describe('extensions > cli > definitions', () => { + const sandbox = createSandbox() + let setCwdStub: SinonStub, + setEnvironmentVariablesStub: SinonStub, + setEnvironmentVariableStub: SinonStub, + scheduleKillProcessStub: SinonStub, + runStub: SinonStub, + getOutputStub: SinonStub, + getExitCodeStub: SinonStub + + beforeAll(() => { + setCwdStub = sandbox.stub(cli, 'setCwd') + setEnvironmentVariablesStub = sandbox.stub(cli, 'setEnvironmentVariables') + setEnvironmentVariableStub = sandbox.stub(cli, 'setEnvironmentVariable') + scheduleKillProcessStub = sandbox.stub(cli, 'scheduleKillProcess') + runStub = sandbox.stub(cli, 'run') + getOutputStub = sandbox.stub(cli, 'getOutput') + getExitCodeStub = sandbox.stub(cli, 'getExitCode') + sandbox.stub(console, 'log') + }) + + beforeEach(() => definitions.install()) + + afterEach(() => { + helper.clearContext() + sandbox.reset() + }) + afterAll(() => sandbox.restore()) + + test('set current working directory', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('set (?:working directory|cwd) to') + def.shouldNotMatch('I set working directory to ') + def.shouldMatch('I set working directory to path', ['path']) + def.shouldMatch('I set cwd to path', ['path']) + def.shouldMatch('set working directory to path', ['path']) + def.shouldMatch('set cwd to path', ['path']) + + const cliMock = { cli: { setCwd: setCwdStub } } + def.exec(cliMock, 'path') + expect(cliMock.cli.setCwd.calledWithExactly('path')).toBeTruthy() + }) + + test('set environment variables', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('set (?:env|environment) (?:vars|variables)') + def.shouldMatch('I set environment variables') + def.shouldMatch('I set environment vars') + def.shouldMatch('I set env variables') + def.shouldMatch('I set env vars') + def.shouldMatch('set environment variables') + def.shouldMatch('set environment vars') + def.shouldMatch('set env variables') + def.shouldMatch('set env vars') + + const cliMock = { cli: { setEnvironmentVariables: setEnvironmentVariablesStub } } + const envVars = { TEST_MODE: true } + def.exec(cliMock, { rowsHash: () => envVars }) + expect(cliMock.cli.setEnvironmentVariables.calledWithExactly(envVars)).toBeTruthy() + }) + + test('set a single environment variable', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher( + '(?:I )?set ([^ ]+) (?:env|environment) (?:var|variable) to (.+)' + ) + def.shouldNotMatch('I set Accept env var to ') + def.shouldNotMatch('I set X User Id env var to 1') + def.shouldMatch('I set Accept environment variable to application/json') + def.shouldMatch('I set Accept environment var to application/json') + def.shouldMatch('I set Accept env variable to application/json') + def.shouldMatch('I set Accept env var to application/json') + def.shouldMatch('set Accept environment variable to application/json') + def.shouldMatch('set Accept environment var to application/json') + def.shouldMatch('set Accept env variable to application/json') + def.shouldMatch('set Accept env var to application/json') + + const cliMock = { cli: { setEnvironmentVariable: setEnvironmentVariableStub } } + def.exec(cliMock, 'Accept', 'application/json') + expect( + cliMock.cli.setEnvironmentVariable.calledWithExactly('Accept', 'application/json') + ).toBeTruthy() + }) + + test('schedule process killing', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('kill the process with') + def.shouldNotMatch('I kill the process with sig in ') + def.shouldNotMatch('I kill the process with sig in 1mn') + def.shouldNotMatch('I kill the process with sig in xs') + def.shouldMatch('I kill the process with sig in 1s', ['sig', '1', 's']) + def.shouldMatch('I kill the process with sig in 10ms', ['sig', '10', 'ms']) + def.shouldMatch('kill the process with sig in 1s', ['sig', '1', 's']) + def.shouldMatch('kill the process with sig in 10ms', ['sig', '10', 'ms']) + + const cliMock = { cli: { scheduleKillProcess: scheduleKillProcessStub } } + def.exec(cliMock, 'sig', '10', 'ms') + expect(cliMock.cli.scheduleKillProcess.calledWithExactly(10, 'sig')).toBeTruthy() + def.exec(cliMock, 'sig', '10', 's') + expect(cliMock.cli.scheduleKillProcess.calledWithExactly(10000, 'sig')).toBeTruthy() + }) + + test('run a command', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('run command') + def.shouldNotMatch('I run command ') + def.shouldMatch('I run command ls -al', ['ls -al']) + def.shouldMatch('run command ls -al', ['ls -al']) + + const cliMock = { cli: { run: runStub } } + def.exec(cliMock, 'ls -al') + expect(cliMock.cli.run.calledWithExactly('ls -al')).toBeTruthy() + }) + + test('dump stdout or stderr for debugging purpose', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('dump (stderr|stdout)') + def.shouldNotMatch('I dump stdcrap') + def.shouldMatch('I dump stdout', ['stdout']) + def.shouldMatch('I dump stderr', ['stderr']) + def.shouldMatch('dump stdout', ['stdout']) + def.shouldMatch('dump stderr', ['stderr']) + + const cliMock = { cli: { getOutput: getOutputStub } } + def.exec(cliMock, 'stdout') + expect(cliMock.cli.getOutput.calledWithExactly('stdout')).toBeTruthy() + }) + + test('check exit code', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('(?:command )?exit code should be') + def.shouldNotMatch('the command exit code should be ') + def.shouldNotMatch('the command exit code should be nan') + def.shouldMatch('the command exit code should be 1', ['1']) + def.shouldMatch('command exit code should be 0', ['0']) + def.shouldMatch('exit code should be 32', ['32']) + + const cliMock = { cli: { getExitCode: getExitCodeStub } } + getExitCodeStub.returns(1) + expect(() => { + def.exec(cliMock, '0') + }).toThrow( + `The command exit code doesn't match expected 0, found: 1: expected 1 to equal 0` + ) + + expect(cliMock.cli.getExitCode.calledOnce).toBeTruthy() + }) + + test('check if stdout or stderr is empty', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('(stderr|stdout) should be empty') + def.shouldNotMatch('stdcrap should be empty') + def.shouldMatch('stdout should be empty', ['stdout']) + def.shouldMatch('stderr should be empty', ['stderr']) + + getOutputStub.withArgs('stdout').returns('not empty stdout') + getOutputStub.withArgs('stdout').onSecondCall().returns('') + getOutputStub.withArgs('stderr').returns('not empty stderr') + getOutputStub.withArgs('stderr').onSecondCall().returns('') + + const cliMock = { cli: { getOutput: getOutputStub } } + + expect(() => { + def.exec(cliMock, 'stdout') + }).toThrow("expected 'not empty stdout' to be empty") + expect(() => { + def.exec(cliMock, 'stdout') + }).not.toThrow() + + expect(() => { + def.exec(cliMock, 'stderr') + }).toThrow("expected 'not empty stderr' to be empty") + expect(() => { + def.exec(cliMock, 'stderr') + }).not.toThrow() + }) + + test('check if stdout or stderr contains something', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('(stderr|stdout) should contain') + def.shouldNotMatch('stdcrap should contain something') + def.shouldNotMatch('stdout should contain ') + def.shouldMatch('stdout should contain something', ['stdout', 'something']) + def.shouldMatch('stderr should contain something', ['stderr', 'something']) + + getOutputStub.withArgs('stdout').returns('nothing on stdout') + getOutputStub.withArgs('stdout').onSecondCall().returns('something on stdout') + getOutputStub.withArgs('stderr').returns('nothing on stderr') + getOutputStub.withArgs('stderr').onSecondCall().returns('something on stderr') + const cliMock = { cli: { getOutput: getOutputStub } } + + expect(() => { + def.exec(cliMock, 'stdout', 'something') + }).toThrow(`expected 'nothing on stdout' to include 'something'`) + expect(() => { + def.exec(cliMock, 'stdout', 'something') + }).not.toThrow() + + expect(() => { + def.exec(cliMock, 'stderr', 'something') + }).toThrow(`expected 'nothing on stderr' to include 'something'`) + expect(() => { + def.exec(cliMock, 'stderr', 'something') + }).not.toThrow() + }) + + test('check if stdout or stderr does not contain something', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('(stderr|stdout) should not contain') + def.shouldNotMatch('stdcrap should not contain something') + def.shouldNotMatch('stdout should not contain ') + def.shouldMatch('stdout should not contain something', ['stdout', 'something']) + def.shouldMatch('stderr should not contain something', ['stderr', 'something']) + + getOutputStub.withArgs('stdout').returns('something on stdout') + getOutputStub.withArgs('stdout').onSecondCall().returns('nothing on stdout') + getOutputStub.withArgs('stderr').returns('something on stderr') + getOutputStub.withArgs('stderr').onSecondCall().returns('nothing on stderr') + const cliMock = { cli: { getOutput: getOutputStub } } + + expect(() => { + def.exec(cliMock, 'stdout', 'something') + }).toThrow(`expected 'something on stdout' to not include 'something'`) + expect(() => { + def.exec(cliMock, 'stdout', 'something') + }).not.toThrow() + + expect(() => { + def.exec(cliMock, 'stderr', 'something') + }).toThrow(`expected 'something on stderr' to not include 'something'`) + expect(() => { + def.exec(cliMock, 'stderr', 'something') + }).not.toThrow() + }) + + test('check if stdout or stderr matches a regular expression', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('(stderr|stdout) should match') + def.shouldNotMatch('stdcrap should match something') + def.shouldNotMatch('stdout should match ') + def.shouldMatch('stdout should match regex', ['stdout', 'regex']) + def.shouldMatch('stderr should match regex', ['stderr', 'regex']) + + getOutputStub.withArgs('stdout').returns('nothing on stdout') + getOutputStub.withArgs('stdout').onSecondCall().returns('something on stdout') + getOutputStub.withArgs('stderr').returns('nothing on stderr') + getOutputStub.withArgs('stderr').onSecondCall().returns('something on stderr') + const cliMock = { cli: { getOutput: getOutputStub } } + + expect(() => { + def.exec(cliMock, 'stdout', 'something') + }).toThrow(`expected 'nothing on stdout' to match /something/gim`) + expect(() => { + def.exec(cliMock, 'stdout', 'something') + }).not.toThrow() + + expect(() => { + def.exec(cliMock, 'stderr', 'something') + }).toThrow(`expected 'nothing on stderr' to match /something/gim`) + expect(() => { + def.exec(cliMock, 'stderr', 'something') + }).not.toThrow() + }) + + test('check if stdout or stderr does not match a regular expression', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('(stderr|stdout) should not match') + def.shouldNotMatch('stdcrap should not match regex') + def.shouldNotMatch('stdout should not match ') + def.shouldMatch('stdout should not match regex', ['stdout', 'regex']) + def.shouldMatch('stderr should not match regex', ['stderr', 'regex']) + + getOutputStub.withArgs('stdout').returns('something on stdout') + getOutputStub.withArgs('stdout').onSecondCall().returns('nothing on stdout') + getOutputStub.withArgs('stderr').returns('something on stderr') + getOutputStub.withArgs('stderr').onSecondCall().returns('nothing on stderr') + const cliMock = { cli: { getOutput: getOutputStub } } + + expect(() => { + def.exec(cliMock, 'stdout', 'something') + }).toThrow(`expected 'something on stdout' not to match /something/gim`) + expect(() => { + def.exec(cliMock, 'stdout', 'something') + }).not.toThrow() + + expect(() => { + def.exec(cliMock, 'stderr', 'something') + }).toThrow(`expected 'something on stderr' not to match /something/gim`) + expect(() => { + def.exec(cliMock, 'stderr', 'something') + }).not.toThrow() + }) +}) diff --git a/tests/extensions/definitions_helper.js b/tests/extensions/definitions_helper.js deleted file mode 100644 index 588adec7..00000000 --- a/tests/extensions/definitions_helper.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict' - -jest.mock('@cucumber/cucumber') -const _ = require('lodash') -const cucumber = require('@cucumber/cucumber') - -/** - * Tests a step definition against given string. - * - * @param {RegExp} regex - RegExp used to match the step definition - * @param {string} str - String to test - * @param {Array.} [expectedArgs=[]] - Ordered expected arguments - */ -const defShouldMatch = (regex, str, expectedArgs = []) => { - const matches = str.match(regex) - - expect(matches).not.toBeNull() - expectedArgs.forEach((expectedArg, index) => { - expect(matches[index + 1]).toBe(expectedArg) - }) -} - -/** - * Ensures a step definition does not match given string. - * - * @param {RegExp} regex - RegExp used to match the step definition - * @param {string} str - String to test - */ -const defShouldNotMatch = (regex, str) => { - expect(str).not.toMatch(regex) -} - -/** - * Executes step definition logic. - * - * @param {Function} execFn - Step definition logic - * @param {Object} thisContext - `this` context to emulate cucumber context - * @param args - */ -const execDef = (execFn, thisContext, ...args) => execFn.bind(thisContext)(...args) - -exports.getContext = () => { - const registeredDefinitions = cucumber.defineStep.mock.calls.map((def) => ({ - matcher: def[0], // The step definition regex - exec: _.partial(execDef, def[1]), // The step definition logic - shouldMatch: _.partial(defShouldMatch, def[0]), - shouldNotMatch: _.partial(defShouldNotMatch, def[0]), - })) - - const matchers = registeredDefinitions.map(({ matcher }) => matcher.toString()) - - return { - definitions: registeredDefinitions, - getDefinitionByMatcher: (pattern) => { - const found = registeredDefinitions.filter(({ matcher }) => - matcher.toString().includes(pattern) - ) - if (found.length === 0) { - throw new TypeError( - `No definition found for pattern: '${pattern}', available definition matchers:\n - ${matchers.join( - '\n - ' - )}` - ) - } - - if (found.length > 1) { - throw new TypeError( - `Pattern '${pattern}' is ambiguous, found ${ - found.length - } definitions:\n - ${found - .map(({ matcher }) => matcher.toString()) - .join('\n - ')}` - ) - } - - return found[0] - }, - } -} - -exports.clearContext = () => { - cucumber.defineStep.mockReset() -} diff --git a/tests/extensions/definitions_helper.ts b/tests/extensions/definitions_helper.ts new file mode 100644 index 00000000..1c3732c9 --- /dev/null +++ b/tests/extensions/definitions_helper.ts @@ -0,0 +1,102 @@ +import _ from 'lodash' +import { defineStep } from '@cucumber/cucumber' + +jest.mock('@cucumber/cucumber') +const defineStepMock = >defineStep + +export interface DefinitionsHelper { + defShouldMatch(regex: RegExp, str: string, expectedArgs: string[]): void + defShouldNotMatch(regex: RegExp, str: string): void +} +export interface RegisteredDefinition { + matcher: RegExp + exec(thisContext: object, ...args: unknown[]): Awaited + shouldMatch(str: string, expectedArgs?: (string | undefined)[] | undefined): void + shouldNotMatch(str: string): void +} +export interface DefinitionsHelperContext { + definitions: RegisteredDefinition[] + getDefinitionByMatcher(pattern: string): RegisteredDefinition +} + +/** + * Tests a step definition against given string. + * + * @param {RegExp} regex - RegExp used to match the step definition + * @param {string} str - String to test + * @param {Array.} [expectedArgs=[]] - Ordered expected arguments + */ +const defShouldMatch = (regex: RegExp, str: string, expectedArgs: string[] = []): void => { + const matches = str.match(regex) + + expect(matches).not.toBeNull() + expectedArgs.forEach((expectedArg, index) => { + expect(matches?.[index + 1]).toBe(expectedArg) + }) +} + +/** + * Ensures a step definition does not match given string. + * + * @param {RegExp} regex - RegExp used to match the step definition + * @param {string} str - String to test + */ +const defShouldNotMatch = (regex: RegExp, str: string): void => { + expect(str).not.toMatch(regex) +} + +/** + * Executes step definition logic. + * + * @param {Function} execFn - Step definition logic + * @param {Object} thisContext - `this` context to emulate cucumber context + * @param args + */ +const execDef = (execFn: CallableFunction, thisContext: object, ...args: unknown[]): unknown => { + return execFn.bind(thisContext)(...args) +} + +export const getContext = (): DefinitionsHelperContext => { + const registeredDefinitions: RegisteredDefinition[] = defineStepMock.mock.calls.map( + (def: [RegExp, CallableFunction]) => ({ + matcher: def[0], // The step definition regex + exec: _.partial(execDef, def[1]), // The step definition logic + shouldMatch: _.partial(defShouldMatch, def[0]), + shouldNotMatch: _.partial(defShouldNotMatch, def[0]), + }) + ) + + const matchers = registeredDefinitions.map(({ matcher }) => matcher?.toString()) + + return { + definitions: registeredDefinitions, + getDefinitionByMatcher: (pattern: string): RegisteredDefinition => { + const found = registeredDefinitions.filter(({ matcher }) => + matcher?.toString().includes(pattern) + ) + if (found.length === 0) { + throw new TypeError( + `No definition found for pattern: '${pattern}', available definition matchers:\n - ${matchers.join( + '\n - ' + )}` + ) + } + + if (found.length > 1) { + throw new TypeError( + `Pattern '${pattern}' is ambiguous, found ${ + found.length + } definitions:\n - ${found + .map(({ matcher }) => matcher?.toString()) + .join('\n - ')}` + ) + } + + return found[0] as RegisteredDefinition + }, + } +} + +export const clearContext = (): void => { + defineStepMock.mockReset() +} diff --git a/tests/extensions/file_system/definitions.test.js b/tests/extensions/file_system/definitions.test.js deleted file mode 100644 index 795ad234..00000000 --- a/tests/extensions/file_system/definitions.test.js +++ /dev/null @@ -1,382 +0,0 @@ -'use strict' - -const sinon = require('sinon') - -jest.mock('fs') - -const helper = require('../definitions_helper') -const definitions = require('../../../src/extensions/file_system/definitions') - -beforeEach(() => { - definitions.install() -}) - -afterEach(() => { - helper.clearContext() -}) - -test('create directory', () => { - const context = helper.getContext(definitions) - - const def = context.getDefinitionByMatcher('create directory') - def.shouldNotMatch('I create directory') - def.shouldNotMatch('create directory') - def.shouldMatch('I create directory test') - def.shouldMatch('create directory test') - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { createDirectory: jest.fn() }, - } - def.exec(world, 'test-directory') - expect(world.fileSystem.createDirectory).toHaveBeenCalledWith('test-cwd', 'test-directory') -}) - -test('remove file or directory', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('remove (?:file|directory)') - def.shouldNotMatch('I remove file') - def.shouldNotMatch('I remove directory') - def.shouldNotMatch('I remove file') - def.shouldNotMatch('I remove invalid crap') - def.shouldMatch('I remove directory test') - def.shouldMatch('remove directory test') - def.shouldMatch('I remove file test.txt') - def.shouldMatch('remove file test.txt') - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { remove: jest.fn() }, - } - def.exec(world, 'test-directory') - expect(world.fileSystem.remove).toHaveBeenCalledWith('test-cwd', 'test-directory') -}) - -test('file or directory presence', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') - def.shouldNotMatch('file should exist') - def.shouldNotMatch('file should not exist') - def.shouldNotMatch('directory should exist') - def.shouldNotMatch('directory should not exist') - def.shouldNotMatch('crap crap should exist') - def.shouldNotMatch('crap crap should not exist') - def.shouldMatch('file test.txt should exist') - def.shouldMatch('file test.txt should not exist') - def.shouldMatch('directory test should exist') - def.shouldMatch('directory test should not exist') -}) - -test('file should exist', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') - - const getFileInfo = sinon.stub() - getFileInfo.withArgs('test-cwd', 'file_exist').resolves({ isFile: () => true }) - getFileInfo.withArgs('test-cwd', 'a_directory').resolves({ isFile: () => false }) - getFileInfo.withArgs('test-cwd', 'file_dont_exist').resolves(null) - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileInfo: getFileInfo }, - } - - expect.assertions(3) - - return Promise.all([ - expect(def.exec(world, 'file', 'file_exist', undefined)).resolves.toBe(), - expect(def.exec(world, 'file', 'a_directory', undefined)).rejects.toThrow( - "'a_directory' is not a file: expected false to be true" - ), - expect(def.exec(world, 'file', 'file_dont_exist', undefined)).rejects.toThrow( - "file 'file_dont_exist' does not exist: expected null not to be null" - ), - ]) -}) - -test('file should not exist', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') - - const getFileInfo = sinon.stub() - getFileInfo.withArgs('test-cwd', 'file_exist').resolves({ isFile: () => true }) - getFileInfo.withArgs('test-cwd', 'a_directory').resolves({ isFile: () => false }) - getFileInfo.withArgs('test-cwd', 'file_dont_exist').resolves(null) - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileInfo: getFileInfo }, - } - - expect.assertions(3) - - return Promise.all([ - expect(def.exec(world, 'file', 'file_exist', 'not ')).rejects.toThrow( - `file 'file_exist' exists: expected { isFile: [Function: isFile] } to be null` - ), - expect(def.exec(world, 'file', 'a_directory', 'not ')).rejects.toThrow( - `file 'a_directory' exists: expected { isFile: [Function: isFile] } to be null` - ), - expect(def.exec(world, 'file', 'file_dont_exist', 'not ')).resolves.toBe(), - ]) -}) - -test('directory should exist', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') - - const getFileInfo = sinon.stub() - getFileInfo.withArgs('test-cwd', 'directory_exist').resolves({ isDirectory: () => true }) - getFileInfo.withArgs('test-cwd', 'a_file').resolves({ isDirectory: () => false }) - getFileInfo.withArgs('test-cwd', 'directory_dont_exist').resolves(null) - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileInfo: getFileInfo }, - } - - expect.assertions(3) - - return Promise.all([ - expect(def.exec(world, 'directory', 'directory_exist', undefined)).resolves.toBe(), - expect(def.exec(world, 'directory', 'a_file', undefined)).rejects.toThrow( - `'a_file' is not a directory: expected false to be true` - ), - expect(def.exec(world, 'directory', 'directory_dont_exist', undefined)).rejects.toThrow( - `directory 'directory_dont_exist' does not exist: expected null not to be null` - ), - ]) -}) - -test('directory should not exist', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') - - const getFileInfo = sinon.stub() - getFileInfo.withArgs('test-cwd', 'directory_exist').resolves({ isDirectory: () => true }) - getFileInfo.withArgs('test-cwd', 'a_file').resolves({ isDirectory: () => false }) - getFileInfo.withArgs('test-cwd', 'directory_dont_exist').resolves(null) - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileInfo: getFileInfo }, - } - - expect.assertions(3) - - return Promise.all([ - expect(def.exec(world, 'directory', 'directory_exist', 'not ')).rejects.toThrow( - `directory_exist' exists: expected { Object (isDirectory) } to be null` - ), - expect(def.exec(world, 'directory', 'a_file', 'not ')).rejects.toThrow( - `directory 'a_file' exists: expected { Object (isDirectory) } to be null` - ), - expect(def.exec(world, 'directory', 'directory_dont_exist', 'not ')).resolves.toBe(), - ]) -}) - -test('file content matcher without file existing', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher( - 'file (.+) content should (not )?(equal|contain|match)' - ) - - const error = new Error() - error.code = 'ENOENT' - - const getFileContent = sinon.stub() - getFileContent.withArgs('test-cwd', 'file_exists').resolves('expected content') - getFileContent.withArgs('test-cwd', 'file_dont_exist').rejects(error) - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileContent: getFileContent }, - } - - expect.assertions(2) - return Promise.all([ - expect( - def.exec(world, 'file_exists', undefined, 'equal', 'expected content') - ).resolves.toBe(), - expect( - def.exec(world, 'file_dont_exist', undefined, 'equal', 'expected content') - ).rejects.toThrow(`File 'file_dont_exist' should exist`), - ]) -}) - -test('file content should equal', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher( - 'file (.+) content should (not )?(equal|contain|match)' - ) - - const getFileContent = sinon.stub() - getFileContent.withArgs('test-cwd', 'file_some_content').resolves('some content') - getFileContent.withArgs('test-cwd', 'file_another_content').resolves('another content') - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileContent: getFileContent }, - } - - expect.assertions(2) - return Promise.all([ - expect( - def.exec(world, 'file_some_content', undefined, 'equal', 'some content') - ).resolves.toBe(), - expect( - def.exec(world, 'file_another_content', undefined, 'equal', 'some content') - ).rejects.toThrow( - `Expected file 'file_another_content' to equal 'some content', but found 'another content' which does not: expected 'another content' to equal 'some content'` - ), - ]) -}) - -test('file content should not equal', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher( - 'file (.+) content should (not )?(equal|contain|match)' - ) - - const getFileContent = sinon.stub() - getFileContent.withArgs('test-cwd', 'file_some_content').resolves('some content') - getFileContent.withArgs('test-cwd', 'file_another_content').resolves('another content') - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileContent: getFileContent }, - } - - expect.assertions(2) - - return Promise.all([ - expect( - def.exec(world, 'file_some_content', 'not ', 'equal', 'some content') - ).rejects.toThrow( - `Expected file 'file_some_content' to not equal 'some content', but found 'some content' which does: expected 'some content' to not equal 'some content'` - ), - expect( - def.exec(world, 'file_another_content', 'not ', 'equal', 'some content') - ).resolves.toBe(), - ]) -}) - -test('file content should contain', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher( - 'file (.+) content should (not )?(equal|contain|match)' - ) - - const getFileContent = sinon.stub() - getFileContent.withArgs('test-cwd', 'file_some_content').resolves('some content') - getFileContent.withArgs('test-cwd', 'file_another_content').resolves('another content') - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileContent: getFileContent }, - } - - expect.assertions(2) - return Promise.all([ - expect(def.exec(world, 'file_some_content', undefined, 'contain', 'some')).resolves.toBe(), - expect( - def.exec(world, 'file_another_content', undefined, 'contain', 'some') - ).rejects.toThrow( - `Expected file 'file_another_content' to contain 'some', but found 'another content' which does not: expected 'another content' to include 'some'` - ), - ]) -}) - -test('file content should not contain', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher( - 'file (.+) content should (not )?(equal|contain|match)' - ) - - const getFileContent = sinon.stub() - getFileContent.withArgs('test-cwd', 'file_some_content').resolves('some content') - getFileContent.withArgs('test-cwd', 'file_another_content').resolves('another content') - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileContent: getFileContent }, - } - - expect.assertions(2) - - return Promise.all([ - expect(def.exec(world, 'file_some_content', 'not ', 'contain', 'some')).rejects.toThrow( - `Expected file 'file_some_content' to not contain 'some', but found 'some content' which does: expected 'some content' to not include 'some'` - ), - expect(def.exec(world, 'file_another_content', 'not ', 'contain', 'some')).resolves.toBe(), - ]) -}) - -test('file content should match', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher( - 'file (.+) content should (not )?(equal|contain|match)' - ) - - const getFileContent = sinon.stub() - getFileContent.withArgs('test-cwd', 'file_some_content').resolves('some content') - getFileContent.withArgs('test-cwd', 'file_not_some_content').resolves('not some content') - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileContent: getFileContent }, - } - - expect.assertions(2) - - return Promise.all([ - expect( - def.exec(world, 'file_some_content', undefined, 'match', '^some.*$') - ).resolves.toBe(), - expect( - def.exec(world, 'file_not_some_content', undefined, 'match', '^some.*$') - ).rejects.toThrow( - `Expected file 'file_not_some_content' to match '^some.*$', but found 'not some content' which does not: expected 'not some content' to match /^some.*$/` - ), - ]) -}) - -test('file content should not match', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher( - 'file (.+) content should (not )?(equal|contain|match)' - ) - - const getFileContent = sinon.stub() - getFileContent.withArgs('test-cwd', 'file_some_content').resolves('some content') - getFileContent.withArgs('test-cwd', 'file_not_some_content').resolves('not some content') - - const world = { - cli: { getCwd: () => 'test-cwd' }, - fileSystem: { getFileContent: getFileContent }, - } - - expect.assertions(2) - - return Promise.all([ - expect(def.exec(world, 'file_some_content', 'not ', 'match', '^some.*$')).rejects.toThrow( - `Expected file 'file_some_content' to not match '^some.*$', but found 'some content' which does: expected 'some content' not to match /^some.*$/` - ), - expect( - def.exec(world, 'file_not_some_content', 'not ', 'match', '^some.*$') - ).resolves.toBe(), - ]) -}) diff --git a/tests/extensions/file_system/definitions.test.ts b/tests/extensions/file_system/definitions.test.ts new file mode 100644 index 00000000..62603f77 --- /dev/null +++ b/tests/extensions/file_system/definitions.test.ts @@ -0,0 +1,435 @@ +import { createSandbox, SinonStub, stub } from 'sinon' + +import * as helper from '../definitions_helper' +import * as definitions from '../../../src/extensions/file_system/definitions' +import * as fileSystem from '../../../src/extensions/file_system/file_system' +import { cli } from '../../../src/extensions/cli' + +describe('extensions > file_system', () => { + const sandbox = createSandbox() + let getFileContentStub: SinonStub, + getFileInfoStub: SinonStub, + createDirectoryStub: SinonStub, + removeStub: SinonStub, + getCwdStub: SinonStub + + beforeAll(() => { + getFileContentStub = sandbox.stub(fileSystem, 'getFileContent') + getFileInfoStub = sandbox.stub(fileSystem, 'getFileInfo') + createDirectoryStub = sandbox.stub(fileSystem, 'createDirectory') + removeStub = sandbox.stub(fileSystem, 'remove') + + getCwdStub = stub(cli, 'getCwd') + getCwdStub.returns('test-cwd') + }) + + beforeEach(() => { + definitions.install() + sandbox.reset() + getCwdStub.resetHistory() + }) + + afterAll(() => { + helper.clearContext() + sandbox.restore() + }) + + test('create directory', () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('create directory') + def.shouldNotMatch('I create directory') + def.shouldNotMatch('create directory') + def.shouldMatch('I create directory test') + def.shouldMatch('create directory test') + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { createDirectory: createDirectoryStub }, + } + def.exec(world, 'test-directory') + expect( + world.fileSystem.createDirectory.calledWithExactly('test-cwd', 'test-directory') + ).toBeTruthy() + }) + + test('remove file or directory', () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('remove (?:file|directory)') + def.shouldNotMatch('I remove file') + def.shouldNotMatch('I remove directory') + def.shouldNotMatch('I remove file') + def.shouldNotMatch('I remove invalid crap') + def.shouldMatch('I remove directory test') + def.shouldMatch('remove directory test') + def.shouldMatch('I remove file test.txt') + def.shouldMatch('remove file test.txt') + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { remove: removeStub }, + } + def.exec(world, 'test-directory') + expect(world.fileSystem.remove.calledWithExactly('test-cwd', 'test-directory')).toBeTruthy() + }) + + test('file or directory presence', () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') + def.shouldNotMatch('file should exist') + def.shouldNotMatch('file should not exist') + def.shouldNotMatch('directory should exist') + def.shouldNotMatch('directory should not exist') + def.shouldNotMatch('crap crap should exist') + def.shouldNotMatch('crap crap should not exist') + def.shouldMatch('file test.txt should exist') + def.shouldMatch('file test.txt should not exist') + def.shouldMatch('directory test should exist') + def.shouldMatch('directory test should not exist') + }) + + test('file should exist', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') + + getFileInfoStub.withArgs('test-cwd', 'file_exist').resolves({ isFile: () => true }) + getFileInfoStub.withArgs('test-cwd', 'a_directory').resolves({ isFile: () => false }) + getFileInfoStub.withArgs('test-cwd', 'file_dont_exist').resolves(null) + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileInfo: getFileInfoStub }, + } + expect.assertions(5) + + def.exec(world, 'file', 'file_exist', undefined) + expect( + world.fileSystem.getFileInfo.calledWithExactly('test-cwd', 'file_exist') + ).toBeTruthy() + + await expect(def.exec(world, 'file', 'a_directory', undefined)).rejects.toThrow( + "'a_directory' is not a file: expected false to be true" + ) + expect( + world.fileSystem.getFileInfo.calledWithExactly('test-cwd', 'a_directory') + ).toBeTruthy() + + await expect(def.exec(world, 'file', 'file_dont_exist', undefined)).rejects.toThrow( + "file 'file_dont_exist' does not exist: expected null not to be null" + ) + expect( + world.fileSystem.getFileInfo.calledWithExactly('test-cwd', 'file_dont_exist') + ).toBeTruthy() + }) + + test('file should not exist', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') + + getFileInfoStub.withArgs('test-cwd', 'file_exist').resolves({ isFile: () => true }) + getFileInfoStub.withArgs('test-cwd', 'a_directory').resolves({ isFile: () => false }) + getFileInfoStub.withArgs('test-cwd', 'file_dont_exist').resolves(null) + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileInfo: getFileInfoStub }, + } + + expect.assertions(5) + + await expect(def.exec(world, 'file', 'file_exist', 'not ')).rejects.toThrow( + `file 'file_exist' exists: expected { isFile: [Function: isFile] } to be null` + ) + expect(getFileInfoStub.calledWithExactly('test-cwd', 'file_exist')).toBeTruthy() + + await expect(def.exec(world, 'file', 'a_directory', 'not ')).rejects.toThrow( + `file 'a_directory' exists: expected { isFile: [Function: isFile] } to be null` + ) + expect(getFileInfoStub.calledWithExactly('test-cwd', 'a_directory')).toBeTruthy() + + expect(def.exec(world, 'file', 'file_dont_exist', 'not ')) + expect(getFileInfoStub.calledWithExactly('test-cwd', 'file_dont_exist')).toBeTruthy() + }) + + test('directory should exist', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') + + getFileInfoStub + .withArgs('test-cwd', 'directory_exist') + .resolves({ isDirectory: () => true }) + getFileInfoStub.withArgs('test-cwd', 'a_file').resolves({ isDirectory: () => false }) + getFileInfoStub.withArgs('test-cwd', 'directory_dont_exist').resolves(null) + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileInfo: getFileInfoStub }, + } + + expect.assertions(5) + + def.exec(world, 'directory', 'directory_exist', undefined) + expect(getFileInfoStub.calledWithExactly('test-cwd', 'directory_exist')).toBeTruthy() + + await expect(def.exec(world, 'directory', 'a_file', undefined)).rejects.toThrow( + `'a_file' is not a directory: expected false to be true` + ) + expect(getFileInfoStub.calledWithExactly('test-cwd', 'a_file')).toBeTruthy() + + await expect( + def.exec(world, 'directory', 'directory_dont_exist', undefined) + ).rejects.toThrow( + `directory 'directory_dont_exist' does not exist: expected null not to be null` + ) + expect(getFileInfoStub.calledWithExactly('test-cwd', 'directory_dont_exist')).toBeTruthy() + }) + + test('directory should not exist', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('(file|directory) (.+) should (not )?exist') + + getFileInfoStub + .withArgs('test-cwd', 'directory_exist') + .resolves({ isDirectory: () => true }) + getFileInfoStub.withArgs('test-cwd', 'a_file').resolves({ isDirectory: () => false }) + getFileInfoStub.withArgs('test-cwd', 'directory_dont_exist').resolves(null) + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileInfo: getFileInfoStub }, + } + + expect.assertions(5) + + await expect(def.exec(world, 'directory', 'directory_exist', 'not ')).rejects.toThrow( + `directory_exist' exists: expected { Object (isDirectory) } to be null` + ) + expect(getFileInfoStub.calledWithExactly('test-cwd', 'directory_exist')).toBeTruthy() + + await expect(def.exec(world, 'directory', 'a_file', 'not ')).rejects.toThrow( + `directory 'a_file' exists: expected { Object (isDirectory) } to be null` + ) + expect(getFileInfoStub.calledWithExactly('test-cwd', 'a_file')).toBeTruthy() + + def.exec(world, 'directory', 'directory_dont_exist', 'not ') + expect(getFileInfoStub.calledWithExactly('test-cwd', 'directory_dont_exist')).toBeTruthy() + }) + + test('file content matcher without file existing', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher( + 'file (.+) content should (not )?(equal|contain|match)' + ) + + const error: Error & { code?: string } = new Error() + error.code = 'ENOENT' + + getFileContentStub.withArgs('test-cwd', 'file_exists').resolves('expected content') + getFileContentStub.withArgs('test-cwd', 'file_dont_exist').rejects(error) + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileContent: getFileContentStub }, + } + + expect.assertions(3) + + def.exec(world, 'file_exists', undefined, 'equal', 'expected content') + expect(getFileContentStub.calledWithExactly('test-cwd', 'file_exists')).toBeTruthy() + + await expect( + def.exec(world, 'file_dont_exist', undefined, 'equal', 'expected content') + ).rejects.toThrow(`File 'file_dont_exist' should exist`) + expect(getFileContentStub.calledWithExactly('test-cwd', 'file_dont_exist')).toBeTruthy() + }) + + test('file content should equal', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher( + 'file (.+) content should (not )?(equal|contain|match)' + ) + + getFileContentStub.withArgs('test-cwd', 'file_some_content').resolves('some content') + getFileContentStub.withArgs('test-cwd', 'file_another_content').resolves('another content') + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileContent: getFileContentStub }, + } + + expect.assertions(3) + + def.exec(world, 'file_some_content', undefined, 'equal', 'some content') + expect(getFileContentStub.calledWithExactly('test-cwd', 'file_some_content')).toBeTruthy() + + await expect( + def.exec(world, 'file_another_content', undefined, 'equal', 'some content') + ).rejects.toThrow( + `Expected file 'file_another_content' to equal 'some content', but found 'another content' which does not: expected 'another content' to equal 'some content'` + ) + expect( + getFileContentStub.calledWithExactly('test-cwd', 'file_another_content') + ).toBeTruthy() + }) + + test('file content should not equal', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher( + 'file (.+) content should (not )?(equal|contain|match)' + ) + + getFileContentStub.withArgs('test-cwd', 'file_some_content').resolves('some content') + getFileContentStub.withArgs('test-cwd', 'file_another_content').resolves('another content') + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileContent: getFileContentStub }, + } + + expect.assertions(3) + + await expect( + def.exec(world, 'file_some_content', 'not ', 'equal', 'some content') + ).rejects.toThrow( + `Expected file 'file_some_content' to not equal 'some content', but found 'some content' which does: expected 'some content' to not equal 'some content'` + ) + expect(getFileContentStub.calledWithExactly('test-cwd', 'file_some_content')).toBeTruthy() + + def.exec(world, 'file_another_content', 'not ', 'equal', 'some content') + expect( + getFileContentStub.calledWithExactly('test-cwd', 'file_another_content') + ).toBeTruthy() + }) + + test('file content should contain', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher( + 'file (.+) content should (not )?(equal|contain|match)' + ) + + getFileContentStub.withArgs('test-cwd', 'file_some_content').resolves('some content') + getFileContentStub.withArgs('test-cwd', 'file_another_content').resolves('another content') + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileContent: getFileContentStub }, + } + + expect.assertions(3) + + def.exec(world, 'file_some_content', undefined, 'contain', 'some') + expect(getFileContentStub.calledWithExactly('test-cwd', 'file_some_content')).toBeTruthy() + + await expect( + def.exec(world, 'file_another_content', undefined, 'contain', 'some') + ).rejects.toThrow( + `Expected file 'file_another_content' to contain 'some', but found 'another content' which does not: expected 'another content' to include 'some'` + ) + expect( + getFileContentStub.calledWithExactly('test-cwd', 'file_another_content') + ).toBeTruthy() + }) + + test('file content should not contain', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher( + 'file (.+) content should (not )?(equal|contain|match)' + ) + + getFileContentStub.withArgs('test-cwd', 'file_some_content').resolves('some content') + getFileContentStub.withArgs('test-cwd', 'file_another_content').resolves('another content') + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileContent: getFileContentStub }, + } + + expect.assertions(3) + + await expect( + def.exec(world, 'file_some_content', 'not ', 'contain', 'some') + ).rejects.toThrow( + `Expected file 'file_some_content' to not contain 'some', but found 'some content' which does: expected 'some content' to not include 'some'` + ) + expect(getFileContentStub.calledWithExactly('test-cwd', 'file_some_content')).toBeTruthy() + + def.exec(world, 'file_another_content', 'not ', 'contain', 'some') + expect( + getFileContentStub.calledWithExactly('test-cwd', 'file_another_content') + ).toBeTruthy() + }) + + test('file content should match', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher( + 'file (.+) content should (not )?(equal|contain|match)' + ) + + getFileContentStub.withArgs('test-cwd', 'file_some_content').resolves('some content') + getFileContentStub + .withArgs('test-cwd', 'file_not_some_content') + .resolves('not some content') + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileContent: getFileContentStub }, + } + + expect.assertions(3) + def.exec(world, 'file_some_content', undefined, 'match', '^some.*$') + expect(getFileContentStub.calledWithExactly('test-cwd', 'file_some_content')).toBeTruthy() + + await expect( + def.exec(world, 'file_not_some_content', undefined, 'match', '^some.*$') + ).rejects.toThrow( + `Expected file 'file_not_some_content' to match '^some.*$', but found 'not some content' which does not: expected 'not some content' to match /^some.*$/` + ) + expect( + getFileContentStub.calledWithExactly('test-cwd', 'file_not_some_content') + ).toBeTruthy() + }) + + test('file content should not match', async () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher( + 'file (.+) content should (not )?(equal|contain|match)' + ) + + getFileContentStub.withArgs('test-cwd', 'file_some_content').resolves('some content') + getFileContentStub + .withArgs('test-cwd', 'file_not_some_content') + .resolves('not some content') + + const world = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileContent: getFileContentStub }, + } + + expect.assertions(3) + + await expect( + def.exec(world, 'file_some_content', 'not ', 'match', '^some.*$') + ).rejects.toThrow( + `Expected file 'file_some_content' to not match '^some.*$', but found 'some content' which does: expected 'some content' not to match /^some.*$/` + ) + expect(getFileContentStub.calledWithExactly('test-cwd', 'file_some_content')).toBeTruthy() + + def.exec(world, 'file_not_some_content', 'not ', 'match', '^some.*$') + expect( + getFileContentStub.calledWithExactly('test-cwd', 'file_not_some_content') + ).toBeTruthy() + }) +}) diff --git a/tests/extensions/fixtures/__mocks__/fixture.js b/tests/extensions/fixtures/__mocks__/fixture.js new file mode 100644 index 00000000..51dc1518 --- /dev/null +++ b/tests/extensions/fixtures/__mocks__/fixture.js @@ -0,0 +1 @@ +module.exports = () => ({ type: 'javascript module', testing: true }) diff --git a/tests/extensions/fixtures/__mocks__/fixtures/js_file.js b/tests/extensions/fixtures/__mocks__/fixtures/js_file.js new file mode 100644 index 00000000..6190107d --- /dev/null +++ b/tests/extensions/fixtures/__mocks__/fixtures/js_file.js @@ -0,0 +1,3 @@ +module.exports = () => ({ + data: 'fixture loaded from javascript module', +}) diff --git a/tests/extensions/fixtures/__mocks__/fixtures/json_file.json b/tests/extensions/fixtures/__mocks__/fixtures/json_file.json new file mode 100644 index 00000000..d9a6c132 --- /dev/null +++ b/tests/extensions/fixtures/__mocks__/fixtures/json_file.json @@ -0,0 +1,5 @@ +{ + "data": "fixture from JSON", + "testing": true, + "awesomeness": 100 +} diff --git a/tests/extensions/fixtures/__mocks__/fixtures/multi.json b/tests/extensions/fixtures/__mocks__/fixtures/multi.json new file mode 100644 index 00000000..d9a6c132 --- /dev/null +++ b/tests/extensions/fixtures/__mocks__/fixtures/multi.json @@ -0,0 +1,5 @@ +{ + "data": "fixture from JSON", + "testing": true, + "awesomeness": 100 +} diff --git a/tests/extensions/fixtures/__mocks__/fixtures/multi.yaml b/tests/extensions/fixtures/__mocks__/fixtures/multi.yaml new file mode 100644 index 00000000..038b55cf --- /dev/null +++ b/tests/extensions/fixtures/__mocks__/fixtures/multi.yaml @@ -0,0 +1,2 @@ +type: yaml +testing: true diff --git a/tests/extensions/fixtures/__mocks__/fixtures/text_file.txt b/tests/extensions/fixtures/__mocks__/fixtures/text_file.txt new file mode 100644 index 00000000..7b57bd29 --- /dev/null +++ b/tests/extensions/fixtures/__mocks__/fixtures/text_file.txt @@ -0,0 +1 @@ +some text diff --git a/tests/extensions/fixtures/__mocks__/fixtures/yaml_file.yaml b/tests/extensions/fixtures/__mocks__/fixtures/yaml_file.yaml new file mode 100644 index 00000000..038b55cf --- /dev/null +++ b/tests/extensions/fixtures/__mocks__/fixtures/yaml_file.yaml @@ -0,0 +1,2 @@ +type: yaml +testing: true diff --git a/tests/extensions/fixtures/__mocks__/no_default_function.js b/tests/extensions/fixtures/__mocks__/no_default_function.js new file mode 100644 index 00000000..54913fee --- /dev/null +++ b/tests/extensions/fixtures/__mocks__/no_default_function.js @@ -0,0 +1 @@ +exports.noDefaultFunction = () => ({ type: 'javascript module', testing: true }) diff --git a/tests/extensions/fixtures/__mocks__/test.feature b/tests/extensions/fixtures/__mocks__/test.feature new file mode 100644 index 00000000..e69de29b diff --git a/tests/extensions/fixtures/fixtures.test.ts b/tests/extensions/fixtures/fixtures.test.ts new file mode 100644 index 00000000..caf77f06 --- /dev/null +++ b/tests/extensions/fixtures/fixtures.test.ts @@ -0,0 +1,310 @@ +import yaml from 'js-yaml' +import { fixtures } from '../../../src/extensions/fixtures' +import { createSandbox, SinonStub, stub } from 'sinon' +import fs from 'fs' +import path from 'path' + +const yamlContent = { type: 'yaml', testing: true } +const textContent = 'This data were loaded from mocked text file' +const jsonContent = { type: 'json', testing: true } +const jsContent = { type: 'javascript module', testing: true } + +describe('extensions > fixtures > fixtures', () => { + afterEach(() => { + fixtures.reset() + }) + + describe('configure', () => { + it('should set new value for fixturesDir', () => { + fixtures.configure({ fixturesDir: 'other' }) + expect(fixtures.fixturesDir).toEqual('other') + }) + }) + + describe('setFeatureUri', () => { + it('should set new value for featureUrl', () => { + fixtures.setFeatureUri('feature/uri') + expect(fixtures.featureUri).toEqual('feature/uri') + }) + }) + + describe('loadYaml', () => { + const sandbox = createSandbox() + let loadTextStub: SinonStub, loadStub: SinonStub + beforeAll(() => { + loadTextStub = sandbox.stub(fixtures, 'loadText') + loadStub = sandbox.stub(yaml, 'load') + }) + + afterEach(() => sandbox.reset()) + afterAll(() => sandbox.restore()) + + it('should load valid .yaml fixture file', async () => { + loadTextStub.withArgs('fixture.yaml').resolves('content') + loadStub.withArgs('content').returns(yamlContent) + + const data = await fixtures.loadYaml('fixture.yaml') + expect(data).toEqual(yamlContent) + expect(loadTextStub.calledOnce).toBeTruthy() + expect(loadStub.calledOnce).toBeTruthy() + }) + + it('load empty .yaml fixture file', () => { + loadTextStub.withArgs('fixture.yaml.empty').resolves('content') + loadStub.withArgs('content').returns(undefined) + + fixtures + .loadYaml('fixture.yaml.empty') + .catch((err: { message: string }) => + expect(err.message).toMatch( + 'Fixture file is invalid, yaml parsing resulted in undefined data for file: fixture.yaml.empty' + ) + ) + expect(loadTextStub.calledOnce).toBeTruthy() + }) + + it('load invalid .yaml fixture file', async () => { + loadTextStub + .withArgs('fixture.yaml.invalid') + .rejects({ message: 'Unable to parse yaml fixture file: fixture.yaml.invalid' }) + await fixtures.loadYaml('fixture.yaml.invalid').catch((err: { message: string }) => { + expect(err.message).toMatch( + 'Unable to parse yaml fixture file: fixture.yaml.invalid' + ) + }) + expect(loadTextStub.calledOnce).toBeTruthy() + expect(loadStub.notCalled).toBeTruthy() + }) + }) + + describe('loadText', () => { + let readFileStub: SinonStub + beforeAll(() => { + readFileStub = stub(fs, 'readFile') + }) + + afterEach(() => readFileStub.reset()) + afterAll(() => readFileStub.restore()) + + it('load non existing .yaml fixture file', () => { + readFileStub.yields({ message: 'File does not exist (noent.yaml)' }, null) + + fixtures + .loadText('noent.yaml') + .catch((err: { message: string }) => + expect(err.message).toEqual('File does not exist (noent.yaml)') + ) + expect(readFileStub.calledOnce).toBeTruthy() + }) + + it('load valid .txt fixture file', async () => { + readFileStub.yields(null, textContent) + + await fixtures.loadText('fixture.txt').then((data) => { + expect(data).toEqual(textContent) + }) + expect(readFileStub.calledOnce).toBeTruthy() + }) + + it('load non existing .txt fixture file', async () => { + readFileStub.yields({ message: 'File does not exist (noent.txt)' }, null) + await fixtures + .loadText('noent.txt') + .catch((err: { message: string }) => + expect(err.message).toEqual('File does not exist (noent.txt)') + ) + expect(readFileStub.calledOnce).toBeTruthy() + }) + }) + + describe('load', () => { + const featureDir = 'tests/extensions/fixtures/__mocks__' + const featureUri = `${featureDir}/test.feature` + const fixturesDir = `${featureDir}/fixtures` + + const sandbox = createSandbox() + let loadYamlStub: SinonStub, + loadTextStub: SinonStub, + loadModuleStub: SinonStub, + loadJsonStub: SinonStub + + beforeAll(() => { + loadYamlStub = sandbox.stub(fixtures, 'loadYaml') + loadTextStub = sandbox.stub(fixtures, 'loadText') + loadModuleStub = sandbox.stub(fixtures, 'loadModule') + loadJsonStub = sandbox.stub(fixtures, 'loadJson') + }) + + beforeEach(() => { + fixtures.configure({ fixturesDir: 'fixtures' }) + fixtures.setFeatureUri(featureUri) + }) + + afterEach(() => { + sandbox.resetHistory() + }) + afterAll(() => sandbox.restore()) + + test('generic load of .yaml fixture file', async () => { + loadYamlStub.returns(yamlContent) + + await fixtures.load('yaml_file').then((data) => { + expect(data).toEqual(yamlContent) + }) + expect(loadYamlStub.calledWithExactly(`${fixturesDir}/yaml_file.yaml`)).toBeTruthy() + }) + + it('generic load of .txt fixture file', () => { + loadTextStub.returns(textContent) + return fixtures.load('text_file').then((data) => { + expect(data).toEqual(textContent) + expect(loadTextStub.calledWithExactly(`${fixturesDir}/text_file.txt`)).toBeTruthy() + }) + }) + + it('generic load of .js fixture file', () => { + loadModuleStub.returns(jsContent) + return fixtures.load('js_file').then((data) => { + expect(data).toEqual(jsContent) + expect(loadModuleStub.calledWithExactly(`${fixturesDir}/js_file.js`)).toBeTruthy() + }) + }) + + it('generic load of .json fixture file', () => { + loadJsonStub.returns(jsonContent) + return fixtures.load('json_file').then((data) => { + expect(data).toEqual(jsonContent) + expect(loadJsonStub.calledWithExactly(`${fixturesDir}/json_file.json`)).toBeTruthy() + }) + }) + + it('generic load with multiple matching fixture files', () => { + return fixtures.load('multi').catch((err: { message: string }) => { + expect(err.message).toEqual( + [ + `Found 2 matching fixture files, you should have only one matching 'multi', matches:`, + ' - tests/extensions/fixtures/__mocks__/fixtures/multi.json', + ' - tests/extensions/fixtures/__mocks__/fixtures/multi.yaml', + ].join('\n') + ) + }) + }) + + it('generic load without feature uri', () => { + fixtures.setFeatureUri(undefined) + + return fixtures.load('fixture').catch((err: { message: string }) => { + expect(err.message).toEqual('Cannot load fixture: fixture, no feature uri defined') + }) + }) + + it('generic load with no matching fixture file', () => { + fixtures.configure({ fixturesDir: 'none' }) + fixtures.setFeatureUri('none') + + return fixtures.load('fixture').catch((err: { message: string }) => { + expect(err.message).toEqual( + 'No fixture found for: fixture (./none/fixture.@(yaml|yml|js|json|txt))' + ) + }) + }) + }) + + describe('loadModule', () => { + let relativePathStub: SinonStub + + beforeAll(() => { + relativePathStub = stub(path, 'relative') + }) + + afterEach(() => relativePathStub.reset()) + afterAll(() => relativePathStub.restore()) + + it('load valid .js fixture file', () => { + relativePathStub.returns(`'../../../tests/extensions/fixtures/__mocks__/fixture.js`) + return fixtures.loadModule('fixture.js').then((data) => { + expect(data).toEqual(jsContent) + }) + }) + + it('load non existing .js fixture file', () => { + relativePathStub.returns('../../../tests/extensions/fixtures/__mocks__/noent.js') + return fixtures.loadModule('noent.js').catch((err: { message: string }) => { + expect(err.message).toEqual( + `An error occurred while loading fixture file: noent.js +error: Cannot find module '../../../tests/extensions/fixtures/__mocks__/noent.js' from 'src/extensions/fixtures/fixtures.ts'` + ) + }) + }) + + it('load .js without default exported function', () => { + relativePathStub.returns( + '../../../tests/extensions/fixtures/__mocks__/no_default_function.js' + ) + return fixtures + .loadModule('fixture.js.no_default_function') + .catch((err: { message: string }) => { + expect(err.message).toEqual( + [ + 'javascript fixture file should export default function.', + `Make sure you declared 'module.exports = ' in fixture.js.no_default_function`, + ].join('\n') + ) + }) + }) + }) + + describe('loadJson', () => { + let loadTextStub: SinonStub + beforeAll(() => { + loadTextStub = stub(fixtures, 'loadText') + }) + + afterEach(() => loadTextStub.reset()) + afterAll(() => { + loadTextStub.restore() + }) + + const content = JSON.stringify(jsonContent) + + it('load valid .json fixture file', async () => { + loadTextStub.withArgs('fixture.json').resolves(content) + expect(await fixtures.loadJson('fixture.json')).toEqual(jsonContent) + expect(loadTextStub.calledOnce).toBeTruthy() + }) + + it('load non existing .json fixture file', () => { + loadTextStub + .withArgs('noent.json') + .rejects(new Error('File does not exist (noent.json)')) + + return fixtures + .loadJson('noent.json') + .catch((err: { message: string }) => + expect(err.message).toEqual('File does not exist (noent.json)') + ) + }) + + it('load invalid .json fixture file', () => { + loadTextStub + .withArgs('fixture.json.invalid') + .rejects(new Error('Unable to parse json fixture file: fixture.json.invalid')) + return fixtures + .loadJson('fixture.json.invalid') + .catch((err: { message: string }) => + expect(err.message).toMatch( + 'Unable to parse json fixture file: fixture.json.invalid' + ) + ) + }) + }) + + describe('reset', () => { + it('should reset all values of fixtures', () => { + fixtures.setFeatureUri('something') + expect(fixtures.featureUri).toEqual('something') + fixtures.reset() + expect(fixtures.featureUri).toBeUndefined() + }) + }) +}) diff --git a/tests/extensions/fixtures/fixtures_loader.test.js b/tests/extensions/fixtures/fixtures_loader.test.js deleted file mode 100644 index 0810d347..00000000 --- a/tests/extensions/fixtures/fixtures_loader.test.js +++ /dev/null @@ -1,256 +0,0 @@ -'use strict' - -jest.mock('fs') - -const yamlContent = { type: 'yaml', testing: true } -const textContent = 'This data were loaded from mocked text file' -const jsonContent = { type: 'json', testing: true } -const jsContent = { type: 'javascript module', testing: true } - -jest.mock( - '../../../fixture.js', - () => { - // `jest.mock()` is not allowed to reference any out-of-scope variables - return () => ({ type: 'javascript module', testing: true }) - }, - { virtual: true } -) - -jest.mock('../../../fixture.js.no_default_function', () => ({}), { - virtual: true, -}) - -const yaml = require('js-yaml') -const FixturesLoader = require('../../../src/extensions/fixtures/fixtures_loader') -const fixturesLoader = FixturesLoader() - -const MOCK_FILES = { - 'fixture.yaml': yaml.dump(yamlContent), - 'fixture.yaml.empty': '', - 'fixture.yaml.invalid': ':\ninvalid', - 'fixture.txt': textContent, - 'fixture.json': JSON.stringify(jsonContent), - 'fixture.json.invalid': 'invalid', -} - -const MOCK_GLOBS = { - './yaml/fixture.@(yaml|yml|js|json|txt)': ['fixture.yaml'], - './txt/fixture.@(yaml|yml|js|json|txt)': ['fixture.txt'], - './js/fixture.@(yaml|yml|js|json|txt)': ['fixture.js'], - './json/fixture.@(yaml|yml|js|json|txt)': ['fixture.json'], - './multi/fixture.@(yaml|yml|js|json|txt)': ['fixture.json', 'fixture.yaml'], -} - -beforeEach(() => { - require('fs').__setMockFiles(MOCK_FILES) - require('glob')('__defineMocks', MOCK_GLOBS) -}) - -afterEach(() => { - fixturesLoader.reset() -}) - -test('configure', () => { - fixturesLoader.configure({ fixturesDir: 'other' }) -}) - -test('set feature uri', () => { - fixturesLoader.setFeatureUri('feature/uri') -}) - -test('load valid .yaml fixture file', () => { - expect.assertions(1) - - return fixturesLoader.loadYaml('fixture.yaml').then((data) => { - expect(data).toEqual(yamlContent) - }) -}) - -test('load non existing .yaml fixture file', () => { - expect.assertions(1) - - return fixturesLoader - .loadText('noent.yaml') - .catch((err) => expect(err.message).toEqual('File does not exist (noent.yaml)')) -}) - -test('load empty .yaml fixture file', () => { - expect.assertions(1) - - return fixturesLoader - .loadYaml('fixture.yaml.empty') - .catch((err) => - expect(err.message).toMatch( - 'Fixture file is invalid, yaml parsing resulted in undefined data for file: fixture.yaml.empty' - ) - ) -}) - -test('load invalid .yaml fixture file', () => { - expect.assertions(1) - - return fixturesLoader.loadYaml('fixture.yaml.invalid').catch((err) => { - expect(err.message).toMatch('Unable to parse yaml fixture file: fixture.yaml.invalid') - }) -}) - -test('generic load of .yaml fixture file', () => { - expect.assertions(1) - - fixturesLoader.configure({ fixturesDir: 'yaml' }) - fixturesLoader.setFeatureUri('yaml') - - return fixturesLoader.load('fixture').then((data) => { - expect(data).toEqual(yamlContent) - }) -}) - -test('load valid .txt fixture file', () => { - expect.assertions(1) - - return fixturesLoader.loadText('fixture.txt').then((data) => { - expect(data).toEqual(textContent) - }) -}) - -test('load non existing .txt fixture file', () => { - expect.assertions(1) - - return fixturesLoader - .loadText('noent.txt') - .catch((err) => expect(err.message).toEqual('File does not exist (noent.txt)')) -}) - -test('generic load of .txt fixture file', () => { - expect.assertions(1) - - fixturesLoader.configure({ fixturesDir: 'txt' }) - fixturesLoader.setFeatureUri('txt') - - return fixturesLoader.load('fixture').then((data) => { - expect(data).toEqual(textContent) - }) -}) - -test('load valid .js fixture file', () => { - expect.assertions(1) - - return fixturesLoader.loadModule('fixture.js').then((data) => { - expect(data).toEqual(jsContent) - }) -}) - -test('load non existing .js fixture file', () => { - expect.assertions(1) - - return fixturesLoader.loadModule('noent.js').catch((err) => { - expect(err.message).toMatch( - /An error occurred while loading fixture file: noent.js\nerror: Cannot find module '..\/..\/..\/noent\.js' from '.+src\/extensions\/fixtures'/ - ) - }) -}) - -test('load .js without default exported function', () => { - expect.assertions(1) - - return fixturesLoader.loadModule('fixture.js.no_default_function').catch((err) => { - expect(err.message).toEqual( - [ - 'javascript fixture file should export default function.', - `Make sure you declared 'module.exports = ' in fixture.js.no_default_function`, - ].join('\n') - ) - }) -}) - -test('generic load of .js fixture file', () => { - expect.assertions(1) - - fixturesLoader.configure({ fixturesDir: 'js' }) - fixturesLoader.setFeatureUri('js') - - return fixturesLoader.load('fixture').then((data) => { - expect(data).toEqual(jsContent) - }) -}) - -test('load valid .json fixture file', () => { - expect.assertions(1) - - return fixturesLoader.loadJson('fixture.json').then((data) => { - expect(data).toEqual(jsonContent) - }) -}) - -test('load non existing .json fixture file', () => { - expect.assertions(1) - - return fixturesLoader - .loadJson('noent.json') - .catch((err) => expect(err.message).toEqual('File does not exist (noent.json)')) -}) - -test('load invalid .json fixture file', () => { - expect.assertions(1) - - return fixturesLoader - .loadJson('fixture.json.invalid') - .catch((err) => - expect(err.message).toMatch('Unable to parse json fixture file: fixture.json.invalid') - ) -}) - -test('generic load of .json fixture file', () => { - expect.assertions(1) - - fixturesLoader.configure({ fixturesDir: 'json' }) - fixturesLoader.setFeatureUri('json') - - return fixturesLoader.load('fixture').then((data) => { - expect(data).toEqual(jsonContent) - }) -}) - -test('generic load without feature uri', () => { - expect.assertions(1) - - fixturesLoader.setFeatureUri(undefined) - - return fixturesLoader.load('fixture').catch((err) => { - expect(err.message).toEqual('Cannot load fixture: fixture, no feature uri defined') - }) -}) - -test('generic load with no matching fixture file', () => { - expect.assertions(1) - - fixturesLoader.configure({ fixturesDir: 'none' }) - fixturesLoader.setFeatureUri('none') - - return fixturesLoader.load('fixture').catch((err) => { - expect(err.message).toEqual( - 'No fixture found for: fixture (./none/fixture.@(yaml|yml|js|json|txt))' - ) - }) -}) - -test('generic load with multiple matching fixture files', () => { - expect.assertions(1) - - fixturesLoader.configure({ fixturesDir: 'multi' }) - fixturesLoader.setFeatureUri('multi') - - return fixturesLoader.load('fixture').catch((err) => { - expect(err.message).toEqual( - [ - `Found 2 matching fixture files, you should have only one matching 'fixture', matches:`, - ' - fixture.json', - ' - fixture.yaml', - ].join('\n') - ) - }) -}) - -test('reset fixtures', () => { - fixturesLoader.reset() -}) diff --git a/tests/extensions/http_api/definitions.test.js b/tests/extensions/http_api/definitions.test.js deleted file mode 100644 index 31de9b80..00000000 --- a/tests/extensions/http_api/definitions.test.js +++ /dev/null @@ -1,1148 +0,0 @@ -'use strict' - -const sinon = require('sinon') - -jest.mock('fs') -const fs = require('fs') - -const helper = require('../definitions_helper') -const definitions = require('../../../src/extensions/http_api/definitions') - -beforeEach(() => { - definitions.install() -}) - -afterEach(() => { - helper.clearContext() -}) - -test('set request headers', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('set request headers') - def.shouldMatch('I set request headers') - def.shouldMatch('set request headers') - - const clientMock = { - httpApiClient: { setHeaders: jest.fn() }, - state: { populateObject: (o) => o }, - } - const headers = { - Accept: 'application/json', - 'User-Agent': 'veggies/1.0', - } - def.exec(clientMock, { rowsHash: () => headers }) - expect(clientMock.httpApiClient.setHeaders).toHaveBeenCalledWith(headers) -}) - -test('assign request headers', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('assign request headers') - def.shouldMatch('I assign request headers') - def.shouldMatch('assign request headers') - - const clientMock = { - httpApiClient: { setHeader: jest.fn() }, - state: { populateObject: (o) => o }, - } - const headers = { - Accept: 'application/json', - 'User-Agent': 'veggies/1.0', - } - def.exec(clientMock, { rowsHash: () => headers }) - expect(clientMock.httpApiClient.setHeader).toHaveBeenCalledTimes(2) - expect(clientMock.httpApiClient.setHeader).toHaveBeenCalledWith('Accept', 'application/json') - expect(clientMock.httpApiClient.setHeader).toHaveBeenCalledWith('User-Agent', 'veggies/1.0') -}) - -test('set a single request header', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('request header to') - def.shouldNotMatch('I set Accept request header to ') - def.shouldMatch('I set Accept request header to test', ['Accept', 'test']) - def.shouldMatch('set Accept request header to test', ['Accept', 'test']) - - const clientMock = { - httpApiClient: { setHeader: jest.fn() }, - state: { populate: (v) => v }, - } - def.exec(clientMock, 'Accept', 'test') - expect(clientMock.httpApiClient.setHeader).toHaveBeenCalledWith('Accept', 'test') -}) - -test('set a single request header with a dash', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('request header to') - def.shouldMatch('I set X-Custom request header to test', ['X-Custom', 'test']) - def.shouldMatch('set X-Custom request header to test', ['X-Custom', 'test']) - - const clientMock = { - httpApiClient: { setHeader: jest.fn() }, - state: { populate: (v) => v }, - } - def.exec(clientMock, 'X-Custom', 'test') - expect(clientMock.httpApiClient.setHeader).toHaveBeenCalledWith('X-Custom', 'test') -}) - -test('set a single request header with an underscore', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('request header to') - def.shouldMatch('I set X_Custom request header to test', ['X_Custom', 'test']) - def.shouldMatch('set X_Custom request header to test', ['X_Custom', 'test']) - - const clientMock = { - httpApiClient: { setHeader: jest.fn() }, - state: { populate: (v) => v }, - } - def.exec(clientMock, 'X_Custom', 'test') - expect(clientMock.httpApiClient.setHeader).toHaveBeenCalledWith('X_Custom', 'test') -}) - -test('clear request headers', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('clear request headers') - def.shouldMatch('I clear request headers') - def.shouldMatch('clear request headers') - - const clientMock = { httpApiClient: { clearHeaders: jest.fn() } } - def.exec(clientMock) - expect(clientMock.httpApiClient.clearHeaders).toHaveBeenCalled() -}) - -test('set request json body', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('set request json body$') - def.shouldMatch('I set request json body') - def.shouldMatch('set request json body') -}) - -test('set request json body from fixture file', () => { - const context = helper.getContext() // Extension context - - expect.assertions(5) - - const def = context.getDefinitionByMatcher('set request json body from') - def.shouldNotMatch('I set request json body from ') - def.shouldMatch('I set request json body from fixture') - def.shouldMatch('set request json body from fixture') - - const fixture = { - is_active: 'true', - id: '2', - } - const worldMock = { - httpApiClient: { setJsonBody: jest.fn() }, - fixtures: { load: jest.fn(() => Promise.resolve(fixture)) }, - } - - return def.exec(worldMock, 'fixture').then(() => { - expect(worldMock.fixtures.load).toHaveBeenCalledWith('fixture') - expect(worldMock.httpApiClient.setJsonBody).toHaveBeenCalledWith(fixture) - }) -}) - -test('set request form body', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('set request form body$') - def.shouldMatch('I set request form body') - def.shouldMatch('set request form body') -}) - -test('set request form body from fixture file', () => { - const context = helper.getContext() // Extension context - - expect.assertions(5) - - const def = context.getDefinitionByMatcher('set request form body from') - def.shouldNotMatch('I set request form body from ') - def.shouldMatch('I set request form body from fixture') - def.shouldMatch('set request form body from fixture') - - const fixture = { - is_active: 'true', - id: '2', - } - const worldMock = { - httpApiClient: { setFormBody: jest.fn() }, - fixtures: { load: jest.fn(() => Promise.resolve(fixture)) }, - } - - return def.exec(worldMock, 'fixture').then(() => { - expect(worldMock.fixtures.load).toHaveBeenCalledWith('fixture') - expect(worldMock.httpApiClient.setFormBody).toHaveBeenCalledWith(fixture) - }) -}) - -test('set request multipart body from', () => { - const context = helper.getContext() // Extension context - - expect.assertions(5) - - const def = context.getDefinitionByMatcher('set request multipart body from') - def.shouldNotMatch('I set request multipart body from ') - def.shouldMatch('I set request multipart body from fixture') - def.shouldMatch('set request multipart body from fixture') - - const fixture = { - id: '2', - file: fs.createReadStream('path/to/file', {}), - } - const worldMock = { - httpApiClient: { setMultipartBody: jest.fn() }, - fixtures: { load: jest.fn(() => Promise.resolve(fixture)) }, - } - - return def.exec(worldMock, 'fixture').then(() => { - expect(worldMock.fixtures.load).toHaveBeenCalledWith('fixture') - expect(worldMock.httpApiClient.setMultipartBody).toHaveBeenCalledWith(fixture) - }) -}) - -test('clear request body', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('clear request body') - def.shouldMatch('I clear request body') - def.shouldMatch('clear request body') - - const clientMock = { httpApiClient: { clearBody: jest.fn() } } - def.exec(clientMock) - expect(clientMock.httpApiClient.clearBody).toHaveBeenCalled() -}) - -test('set request query', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('set request query') - def.shouldMatch('I set request query') - def.shouldMatch('set request query') - - const clientMock = { - httpApiClient: { setQuery: jest.fn() }, - state: { populateObject: (o) => o }, - } - const query = { - is_active: 'true', - id: '2', - } - def.exec(clientMock, { rowsHash: () => query }) - expect(clientMock.httpApiClient.setQuery).toHaveBeenCalledWith(query) -}) - -test('follow redirect', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('?follow redirect') - def.shouldMatch('I follow redirect') - def.shouldMatch('follow redirect') - - const clientMock = { - httpApiClient: { setFollowRedirect: jest.fn() }, - } - def.exec(clientMock) - expect(clientMock.httpApiClient.setFollowRedirect).toHaveBeenCalledWith(true) -}) - -test('do not follow redirect', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('do not follow redirect') - def.shouldMatch('I do not follow redirect') - def.shouldMatch('do not follow redirect') - - const clientMock = { - httpApiClient: { setFollowRedirect: jest.fn() }, - } - def.exec(clientMock) - expect(clientMock.httpApiClient.setFollowRedirect).toHaveBeenCalledWith(false) -}) - -test('pick response json property', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('pick response json') - def.shouldNotMatch('I pick response json as ') - def.shouldMatch('I pick response json key as value', ['key', 'value']) - def.shouldMatch('pick response json key as value', ['key', 'value']) -}) - -test('enable cookies', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('enable cookies') - def.shouldMatch('I enable cookies') - def.shouldMatch('enable cookies') - - const clientMock = { httpApiClient: { enableCookies: jest.fn() } } - def.exec(clientMock) - expect(clientMock.httpApiClient.enableCookies).toHaveBeenCalled() -}) - -test('disable cookies', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('disable cookies') - def.shouldMatch('I disable cookies') - def.shouldMatch('disable cookies') - - const clientMock = { httpApiClient: { disableCookies: jest.fn() } } - def.exec(clientMock) - expect(clientMock.httpApiClient.disableCookies).toHaveBeenCalled() -}) - -test('test cookie is present', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response should (not )?have an? (.+) cookie') - def.shouldNotMatch('response should have a cookie') - def.shouldNotMatch('response should have an cookie') - def.shouldMatch('response should have a test cookie', [undefined, 'test']) - def.shouldMatch('response should have an test cookie', [undefined, 'test']) - - const getCookie = sinon.stub() - getCookie.withArgs('cookie_exist').returns('some content') - getCookie.withArgs('cookie_dont_exist').returns(null) - - const clientMock = { httpApiClient: { getCookie } } - - expect(() => def.exec(clientMock, undefined, 'cookie_exist')).not.toThrow() - expect(() => def.exec(clientMock, undefined, 'cookie_dont_exist')).toThrow( - "No cookie found for key 'cookie_dont_exist': expected null not to be null" - ) -}) - -test('test cookie is absent', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response should (not )?have an? (.+) cookie') - def.shouldNotMatch('response should crap have a cookie') - def.shouldNotMatch('response should crap have an cookie') - def.shouldNotMatch('response should not have a cookie') - def.shouldNotMatch('response should not have an cookie') - def.shouldMatch('response should not have a test cookie', ['not ', 'test']) - def.shouldMatch('response should not have an test cookie', ['not ', 'test']) - - const getCookie = sinon.stub() - getCookie.withArgs('cookie_exist').returns('some content') - getCookie.withArgs('cookie_dont_exist').returns(null) - - const clientMock = { httpApiClient: { getCookie: getCookie } } - - expect(() => def.exec(clientMock, 'not ', 'cookie_exist')).toThrow( - "A cookie exists for key 'cookie_exist': expected 'some content' to be null" - ) - expect(() => def.exec(clientMock, 'not ', 'cookie_dont_exist')).not.toThrow() -}) - -test('test cookie is secure', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response (.+) cookie should (not )?be secure') - def.shouldNotMatch('response cookie should be secure') - def.shouldMatch('response test cookie should be secure', ['test', undefined]) - - const getCookie = sinon.stub() - getCookie.withArgs('secure_cookie').returns({ secure: true }) - getCookie.withArgs('not_secure_cookie').returns({ secure: false }) - const clientMock = { httpApiClient: { getCookie: getCookie } } - - expect(() => def.exec(clientMock, 'secure_cookie', undefined)).not.toThrow() - expect(() => def.exec(clientMock, 'not_secure_cookie', undefined)).toThrow( - "Cookie 'not_secure_cookie' is not secure: expected false to be true" - ) -}) - -test('test cookie is not secure', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response (.+) cookie should (not )?be secure') - def.shouldNotMatch('response cookie should not be secure') - def.shouldMatch('response test cookie should not be secure', ['test', 'not ']) - - const getCookie = sinon.stub() - getCookie.withArgs('secure_cookie').returns({ secure: true }) - getCookie.withArgs('not_secure_cookie').returns({ secure: false }) - const clientMock = { httpApiClient: { getCookie: getCookie } } - - expect(() => def.exec(clientMock, 'secure_cookie', 'not ')).toThrow( - "Cookie 'secure_cookie' is secure: expected true to be false" - ) - expect(() => def.exec(clientMock, 'not_secure_cookie', 'not ')).not.toThrow() -}) - -test('test cookie is http only', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response (.+) cookie should (not )?be http only') - def.shouldNotMatch('response cookie should be http only') - def.shouldMatch('response test cookie should be http only', ['test', undefined]) - - const getCookie = sinon.stub() - getCookie.withArgs('http_only').returns({ httpOnly: true }) - getCookie.withArgs('not_http_only').returns({ httpOnly: false }) - const clientMock = { httpApiClient: { getCookie: getCookie } } - - expect(() => def.exec(clientMock, 'http_only', undefined)).not.toThrow() - expect(() => def.exec(clientMock, 'not_http_only', undefined)).toThrow( - "Cookie 'not_http_only' is not http only: expected false to be true" - ) -}) - -test('test cookie is not http only', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response (.+) cookie should (not )?be http only') - def.shouldNotMatch('response cookie should not be http only') - def.shouldMatch('response test cookie should not be http only', ['test', 'not ']) - - const getCookie = sinon.stub() - getCookie.withArgs('http_only').returns({ httpOnly: true }) - getCookie.withArgs('not_http_only').returns({ httpOnly: false }) - const clientMock = { httpApiClient: { getCookie: getCookie } } - - expect(() => def.exec(clientMock, 'http_only', 'not ')).toThrow( - "Cookie 'http_only' is http only: expected true to be false" - ) - expect(() => def.exec(clientMock, 'not_http_only', 'not ')).not.toThrow() -}) - -test('test cookie domain equals given value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response (.+) cookie domain should (not )?be (.+)') - def.shouldNotMatch('response cookie domain should be domain') - def.shouldNotMatch('response test cookie domain should be ') - def.shouldMatch('response test cookie domain should be domain', ['test', undefined, 'domain']) - - const getCookie = sinon.stub() - getCookie.withArgs('domain1').returns({ domain: 'domain1' }) - getCookie.withArgs('domain2').returns({ domain: 'domain2' }) - const clientMock = { httpApiClient: { getCookie: getCookie } } - - expect(() => def.exec(clientMock, 'domain1', undefined, 'domain1')).not.toThrow() - expect(() => def.exec(clientMock, 'domain2', undefined, 'domain1')).toThrow( - `Expected cookie 'domain2' domain to be 'domain1', found 'domain2': expected 'domain2' to equal 'domain1'` - ) -}) - -test('test cookie domain does not equal given value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response (.+) cookie domain should (not )?be (.+)') - def.shouldNotMatch('response cookie domain should not be domain') - def.shouldNotMatch('response test cookie domain should not be ') - def.shouldMatch('response test cookie domain should not be domain', ['test', 'not ', 'domain']) - - const getCookie = sinon.stub() - getCookie.withArgs('domain1').returns({ domain: 'domain1' }) - getCookie.withArgs('domain2').returns({ domain: 'domain2' }) - const clientMock = { httpApiClient: { getCookie: getCookie } } - - expect(() => def.exec(clientMock, 'domain1', 'not ', 'domain1')).toThrow( - `Cookie 'domain1' domain is 'domain1': expected 'domain1' to not equal 'domain1'` - ) - expect(() => def.exec(clientMock, 'domain2', 'not ', 'domain1')).not.toThrow() -}) - -test('reset http client', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('reset http client') - def.shouldMatch('I reset http client') - def.shouldMatch('reset http client') - - const clientMock = { httpApiClient: { reset: jest.fn() } } - def.exec(clientMock) - expect(clientMock.httpApiClient.reset).toHaveBeenCalled() -}) - -test('perform a request', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('GET|POST|PUT|DELETE|PATCH') - def.shouldNotMatch('I GET ') - def.shouldMatch('I GET /', ['GET', '/']) - def.shouldMatch('I POST /create', ['POST', '/create']) - def.shouldMatch('I PUT /update', ['PUT', '/update']) - def.shouldMatch('I DELETE /delete', ['DELETE', '/delete']) - def.shouldMatch('I PATCH /updatePartial', ['PATCH', '/updatePartial']) - def.shouldMatch('GET /', ['GET', '/']) - def.shouldMatch('POST /create', ['POST', '/create']) - def.shouldMatch('PUT /update', ['PUT', '/update']) - def.shouldMatch('DELETE /delete', ['DELETE', '/delete']) - def.shouldMatch('PATCH /updatePartial', ['PATCH', '/updatePartial']) -}) - -test('dump response body', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('dump response body') - def.shouldMatch('I dump response body') - def.shouldMatch('dump response body') - - const clientMock = { - httpApiClient: { getResponse: jest.fn(() => ({ body: '' })) }, - } - def.exec(clientMock) - expect(clientMock.httpApiClient.getResponse).toHaveBeenCalled() -}) - -test('check response HTTP status code', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response status code should be') - def.shouldNotMatch('response status code should be ') - def.shouldNotMatch('response status code should be string') - def.shouldNotMatch('response status code should be 600') - def.shouldMatch('response status code should be 200', ['200']) - def.shouldMatch('response status code should be 404', ['404']) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ statusCode: 200 }) - getResponse.onSecondCall().returns({ statusCode: 400 }) - const clientMock = { httpApiClient: { getResponse: getResponse } } - - expect(() => def.exec(clientMock, '200')).not.toThrow() - expect(() => def.exec(clientMock, '200')).toThrow( - `Expected status code to be: 200, but found: 400: expected 400 to equal 200` - ) -}) - -test('check response HTTP status by message', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response status should be') - def.shouldNotMatch('response status should be ') - def.shouldMatch('response status should be ok', ['ok']) - def.shouldMatch('response status should be forbidden', ['forbidden']) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ statusCode: 200 }) - getResponse.onSecondCall().returns({ statusCode: 500 }) - const clientMock = { httpApiClient: { getResponse: getResponse } } - - expect(() => def.exec(clientMock, 'ok')).not.toThrow() - expect(() => def.exec(clientMock, 'ok')).toThrow( - `Expected status to be: 'ok', but found: 'internal server error': expected 500 to equal 200` - ) - expect(() => def.exec(clientMock, 'invalid')).toThrow(`'invalid' is not a valid status message`) -}) - -test('check json response', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - def.shouldMatch('json response should match', [undefined]) - def.shouldMatch('json response should fully match', ['fully ']) -}) - -test('check json response property is defined', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { name: 'john', gender: 'male' }, - }) - getResponse.onSecondCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { name: 'john' }, - }) - getResponse.onThirdCall().returns({ - headers: { 'content-type': 'application/json' }, - body: {}, - }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - const spec = [ - { - field: 'name', - matcher: 'defined', - }, - { - field: 'gender', - matcher: 'present', - }, - ] - - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'gender' is undefined: expected undefined not to be undefined` - ) - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'name' is undefined: expected undefined not to be undefined` - ) -}) - -test('check json response property equals expected value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { name: 'plouc' }, - }) - getResponse.onSecondCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { name: 'john' }, - }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - const spec = [ - { - field: 'name', - matcher: 'equals', - value: 'plouc', - }, - ] - - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Expected property 'name' to equal 'plouc', but found 'john': expected 'john' to deeply equal 'plouc'` - ) -}) - -test('check json response property contains value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'raphael', last_name: 'benn' }, - }) - getResponse.onSecondCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'raphael', last_name: 'be' }, - }) - getResponse.onThirdCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'rap', last_name: 'ben' }, - }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - const spec = [ - { - field: 'first_name', - matcher: 'contain', - value: 'raph', - }, - { - field: 'last_name', - matcher: 'contains', - value: 'ben', - }, - ] - - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'last_name' (be) does not contain 'ben': expected 'be' to include 'ben'` - ) - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'first_name' (rap) does not contain 'raph': expected 'rap' to include 'raph'` - ) -}) - -test('check json response property matches regexp', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'raphael', last_name: 'benn' }, - }) - getResponse.onSecondCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'raphael', last_name: 'be' }, - }) - getResponse.onThirdCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'rap', last_name: 'ben' }, - }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - const spec = [ - { - field: 'first_name', - matcher: 'matches', - value: 'raph', - }, - { - field: 'last_name', - matcher: 'match', - value: 'ben', - }, - ] - - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'last_name' (be) does not match 'ben': expected 'be' to match /ben/` - ) - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'first_name' (rap) does not match 'raph': expected 'rap' to match /raph/` - ) -}) - -test('check json response property matches expressions', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'Johnny' }, - }) - getResponse.onSecondCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'Bob' }, - }) - getResponse.onThirdCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'John' }, - }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - const spec = [ - { - expression: 'first_name ?', - }, - { - expression: 'first_name #= string', - }, - { - expression: 'first_name ^= Jo', - }, - { - expression: 'first_name *= hn', - }, - { - expression: 'first_name $= ny', - }, - { - expression: 'first_name ~= ^Johnny$', - }, - ] - - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'first_name' (Bob) does not start with 'Jo': expected 'Bob' to start with 'Jo'` - ) - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'first_name' (John) does not end with 'ny': expected 'John' to end with 'ny'` - ) -}) - -test('check json response property matches negated expressions', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'Bob' }, - }) - getResponse.onSecondCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'Johnny' }, - }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - const spec = [ - { - expression: 'last_name !?', - }, - { - expression: 'first_name !#= number', - }, - { - expression: 'first_name !^= Jo', - }, - { - expression: 'first_name !*= hn', - }, - { - expression: 'first_name !$= ny', - }, - { - expression: 'first_name !~= ^Johnny$', - }, - ] - - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( - `Property 'first_name' (Johnny) starts with 'Jo': expected 'Johnny' not to start with 'Jo'` - ) -}) - -test('check json response property matches padded expressions', () => { - const context = helper.getContext() - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'John', note: "I'm a geek!" }, - }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - const spec = [ - { - expression: 'first_name ? ', - }, - { - expression: 'first_name #= string', - }, - { - expression: 'first_name !~= ^Bob$', - }, - { - expression: "note = I'm a geek!", - }, - ] - - expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() -}) - -test('check json response fully matches spec', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('json response should (fully )?match') - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'Raphaël', last_name: 'ben' }, - }) - getResponse.onSecondCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'Raphaël', last_name: 'ben', gender: 'male' }, - }) - getResponse.onThirdCall().returns({ - headers: { 'content-type': 'application/json' }, - body: { first_name: 'Raphaël', last_name: 'be' }, - }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - const spec = [ - { - field: 'first_name', - matcher: 'equal', - value: 'Raphaël', - }, - { - field: 'last_name', - matcher: 'match', - value: 'ben', - }, - ] - - expect(() => def.exec(clientMock, 'fully ', { hashes: () => spec })).not.toThrow() - expect(() => def.exec(clientMock, 'fully ', { hashes: () => spec })).toThrow( - `Expected json response to fully match spec, but it does not: expected 3 to equal 2` - ) - expect(() => def.exec(clientMock, 'fully ', { hashes: () => spec })).toThrow( - `Property 'last_name' (be) does not match 'ben': expected 'be' to match /ben/` - ) -}) - -test('check json collection size for a given path', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('should receive a collection of') - def.shouldNotMatch('I should receive a collection of x items for path whatever') - def.shouldMatch('I should receive a collection of 1 item for path property', ['1', 'property']) - def.shouldMatch('I should receive a collection of 2 items for path property', ['2', 'property']) - def.shouldMatch('should receive a collection of 1 item for path property', ['1', 'property']) - def.shouldMatch('should receive a collection of 2 items', ['2', undefined]) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ body: { property: ['a', 'b', 'c'] } }) - getResponse.onSecondCall().returns({ body: { property: ['a', 'b'] } }) - const clientMock = { httpApiClient: { getResponse: getResponse } } - - expect(() => def.exec(clientMock, '3', 'property')).not.toThrow() - expect(() => def.exec(clientMock, '3', 'property')).toThrow(`expected 2 to equal 3`) -}) - -test('response match fixture', () => { - const context = helper.getContext() // Extension context - - expect.assertions(5) - - const def = context.getDefinitionByMatcher('should match fixture') - def.shouldNotMatch('response should match fixture ') - def.shouldMatch('response should match fixture fixture', ['fixture']) - - const snapshot = { testing: true } - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ statusCode: 200, body: snapshot }) - getResponse.onSecondCall().returns({ statusCode: 200, body: { app: true } }) - const worldMock = { - httpApiClient: { getResponse: getResponse }, - fixtures: { load: jest.fn(() => Promise.resolve(snapshot)) }, - } - - return Promise.all([ - expect(def.exec(worldMock, 'snapshot')).resolves.toBe(), - expect(def.exec(worldMock, 'snapshot')).rejects.toThrow( - `expected { app: true } to deeply equal { testing: true }` - ), - ]) -}) - -test('check response header value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher( - 'response header (.+) should (not )?(equal|contain|match)' - ) - def.shouldNotMatch('response header should match pattern ') - def.shouldNotMatch('response header Content-Type should match ') - def.shouldNotMatch('response header Content-Type should invalid thing') -}) - -test('check response header equals expected value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher( - 'response header (.+) should (not )?(equal|contain|match)' - ) - - def.shouldMatch('response header Content-Type should equal application/json', [ - 'Content-Type', - undefined, - 'equal', - 'application/json', - ]) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) - getResponse.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) - getResponse.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - expect(() => - def.exec(clientMock, 'Content-Type', undefined, 'equal', 'application/json') - ).not.toThrow() - expect(() => - def.exec(clientMock, 'Content-Type', undefined, 'equal', 'application/json') - ).toThrow( - `Expected header 'Content-Type' to equal 'application/json', but found 'application/xml' which does not: expected 'application/xml' to equal 'application/json'` - ) - expect(() => - def.exec(clientMock, 'Content-Type', undefined, 'equal', 'application/json') - ).toThrow(`Header 'Content-Type' does not exist: expected undefined not to be undefined`) -}) - -test('check response header does not equal expected value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher( - 'response header (.+) should (not )?(equal|contain|match)' - ) - - def.shouldMatch('response header Content-Type should not equal application/json', [ - 'Content-Type', - 'not ', - 'equal', - 'application/json', - ]) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) - getResponse.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) - getResponse.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - expect(() => def.exec(clientMock, 'Content-Type', 'not ', 'equal', 'application/json')).toThrow( - `Expected header 'Content-Type' to not equal 'application/json', but found 'application/json' which does: expected 'application/json' to not equal 'application/json'` - ) - expect(() => - def.exec(clientMock, 'Content-Type', 'not ', 'equal', 'application/json') - ).not.toThrow() - expect(() => def.exec(clientMock, 'Content-Type', 'not ', 'equal', 'application/json')).toThrow( - `Header 'Content-Type' does not exist: expected undefined not to be undefined` - ) -}) - -test('check response header contains value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher( - 'response header (.+) should (not )?(equal|contain|match)' - ) - - def.shouldMatch('response header Content-Type should contain application/json', [ - 'Content-Type', - undefined, - 'contain', - 'application/json', - ]) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) - getResponse.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) - getResponse.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - expect(() => def.exec(clientMock, 'Content-Type', undefined, 'contain', 'json')).not.toThrow() - expect(() => def.exec(clientMock, 'Content-Type', undefined, 'contain', 'json')).toThrow( - `Expected header 'Content-Type' to contain 'json', but found 'application/xml' which does not: expected 'application/xml' to include 'json'` - ) - expect(() => def.exec(clientMock, 'Content-Type', undefined, 'contain', 'json')).toThrow( - `Header 'Content-Type' does not exist: expected undefined not to be undefined` - ) -}) - -test('check response header does not contain value', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher( - 'response header (.+) should (not )?(equal|contain|match)' - ) - - def.shouldMatch('response header Content-Type should not contain application/json', [ - 'Content-Type', - 'not ', - 'contain', - 'application/json', - ]) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) - getResponse.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) - getResponse.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - expect(() => def.exec(clientMock, 'Content-Type', 'not ', 'contain', 'json')).toThrow( - `Expected header 'Content-Type' to not contain 'json', but found 'application/json' which does: expected 'application/json' to not include 'json'` - ) - expect(() => def.exec(clientMock, 'Content-Type', 'not ', 'contain', 'json')).not.toThrow() - expect(() => def.exec(clientMock, 'Content-Type', 'not ', 'contain', 'json')).toThrow( - `Header 'Content-Type' does not exist: expected undefined not to be undefined` - ) -}) - -test('check response header matches regexp', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher( - 'response header (.+) should (not )?(equal|contain|match)' - ) - - def.shouldMatch('response header Content-Type should match ^application/json$', [ - 'Content-Type', - undefined, - 'match', - '^application/json$', - ]) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) - getResponse.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) - getResponse.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - expect(() => - def.exec(clientMock, 'Content-Type', undefined, 'match', '^application/json$') - ).not.toThrow() - expect(() => - def.exec(clientMock, 'Content-Type', undefined, 'match', '^application/json$') - ).toThrow( - `Expected header 'Content-Type' to match '^application/json$', but found 'application/xml' which does not: expected 'application/xml' to match /^application\\/json$/` - ) - expect(() => - def.exec(clientMock, 'Content-Type', undefined, 'match', '^application/json$') - ).toThrow(`Header 'Content-Type' does not exist: expected undefined not to be undefined`) -}) - -test('check response header does not match regexp', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher( - 'response header (.+) should (not )?(equal|contain|match)' - ) - - def.shouldMatch('response header Content-Type should not match ^application/json$', [ - 'Content-Type', - 'not ', - 'match', - '^application/json$', - ]) - - const getResponse = sinon.stub() - getResponse.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) - getResponse.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) - getResponse.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) - const clientMock = { - state: { populate: (v) => v }, - httpApiClient: { getResponse: getResponse }, - } - - expect(() => - def.exec(clientMock, 'Content-Type', 'not ', 'match', '^application/json$') - ).toThrow( - `Expected header 'Content-Type' to not match '^application/json$', but found 'application/json' which does: expected 'application/json' not to match /^application\\/json$/` - ) - expect(() => - def.exec(clientMock, 'Content-Type', 'not ', 'match', '^application/json$') - ).not.toThrow() - expect(() => - def.exec(clientMock, 'Content-Type', 'not ', 'match', '^application/json$') - ).toThrow(`Header 'Content-Type' does not exist: expected undefined not to be undefined`) -}) diff --git a/tests/extensions/http_api/definitions.test.ts b/tests/extensions/http_api/definitions.test.ts new file mode 100644 index 00000000..a66a8dcd --- /dev/null +++ b/tests/extensions/http_api/definitions.test.ts @@ -0,0 +1,1204 @@ +import { createSandbox, SinonStub } from 'sinon' + +import * as helper from '../definitions_helper' +import * as definitions from '../../../src/extensions/http_api/definitions' +import { httpApiClient } from '../../../src/extensions/http_api' +import { fixtures } from '../../../src/extensions/fixtures' + +describe('extensions > http_api > definitions', () => { + const sandbox = createSandbox() + let setHeadersStub: SinonStub, + setHeaderStub: SinonStub, + clearHeadersStub: SinonStub, + setJsonBodyStub: SinonStub, + loadFixturesStub: SinonStub, + setFormBodyStub: SinonStub, + setMultipartBodyStub: SinonStub, + clearBodyStub: SinonStub, + setQueryStub: SinonStub, + setFollowRedirectStub: SinonStub, + enableCookiesStub: SinonStub, + disableCookiesStub: SinonStub, + getCookieStub: SinonStub, + resetStub: SinonStub, + getResponseStub: SinonStub + + beforeAll(() => { + setHeadersStub = sandbox.stub(httpApiClient, 'setHeaders') + setHeaderStub = sandbox.stub(httpApiClient, 'setHeader') + clearHeadersStub = sandbox.stub(httpApiClient, 'clearHeaders') + setJsonBodyStub = sandbox.stub(httpApiClient, 'setJsonBody') + loadFixturesStub = sandbox.stub(fixtures, 'load') + setFormBodyStub = sandbox.stub(httpApiClient, 'setFormBody') + setMultipartBodyStub = sandbox.stub(httpApiClient, 'setMultipartBody') + clearBodyStub = sandbox.stub(httpApiClient, 'clearBody') + setQueryStub = sandbox.stub(httpApiClient, 'setQuery') + setFollowRedirectStub = sandbox.stub(httpApiClient, 'setFollowRedirect') + enableCookiesStub = sandbox.stub(httpApiClient, 'enableCookies') + disableCookiesStub = sandbox.stub(httpApiClient, 'disableCookies') + getCookieStub = sandbox.stub(httpApiClient, 'getCookie') + resetStub = sandbox.stub(httpApiClient, 'reset') + getResponseStub = sandbox.stub(httpApiClient, 'getResponse') + }) + beforeEach(() => definitions.install()) + + afterEach(() => { + helper.clearContext() + sandbox.reset() + }) + afterAll(() => sandbox.restore()) + + test('set request headers', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('set request headers') + def.shouldMatch('I set request headers') + def.shouldMatch('set request headers') + + const clientMock = { + httpApiClient: { setHeaders: setHeadersStub }, + state: { populateObject: (o: string): string => o }, + } + const headers = { + Accept: 'application/json', + 'User-Agent': 'veggies/1.0', + } + def.exec(clientMock, { rowsHash: () => headers }) + expect(clientMock.httpApiClient.setHeaders.calledWithExactly(headers)).toBeTruthy() + }) + + test('assign request headers', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('assign request headers') + def.shouldMatch('I assign request headers') + def.shouldMatch('assign request headers') + + const clientMock = { + httpApiClient: { setHeader: setHeaderStub }, + state: { populateObject: (o: string): string => o }, + } + const headers = { + Accept: 'application/json', + 'User-Agent': 'veggies/1.0', + } + def.exec(clientMock, { rowsHash: () => headers }) + expect( + clientMock.httpApiClient.setHeader.calledWithExactly('Accept', 'application/json') + ).toBeTruthy() + expect( + clientMock.httpApiClient.setHeader.calledWithExactly('User-Agent', 'veggies/1.0') + ).toBeTruthy() + }) + + test('set a single request header', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('request header to') + def.shouldNotMatch('I set Accept request header to ') + def.shouldMatch('I set Accept request header to test', ['Accept', 'test']) + def.shouldMatch('set Accept request header to test', ['Accept', 'test']) + + const clientMock = { + httpApiClient: { setHeader: setHeaderStub }, + state: { populate: (v: string): string => v }, + } + def.exec(clientMock, 'Accept', 'test') + expect(clientMock.httpApiClient.setHeader.calledWithExactly('Accept', 'test')).toBeTruthy() + }) + + test('set a single request header with a dash', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('request header to') + def.shouldMatch('I set X-Custom request header to test', ['X-Custom', 'test']) + def.shouldMatch('set X-Custom request header to test', ['X-Custom', 'test']) + + const clientMock = { + httpApiClient: { setHeader: setHeaderStub }, + state: { populate: (v: string): string => v }, + } + def.exec(clientMock, 'X-Custom', 'test') + expect( + clientMock.httpApiClient.setHeader.calledWithExactly('X-Custom', 'test') + ).toBeTruthy() + }) + + test('set a single request header with an underscore', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('request header to') + def.shouldMatch('I set X_Custom request header to test', ['X_Custom', 'test']) + def.shouldMatch('set X_Custom request header to test', ['X_Custom', 'test']) + + const clientMock = { + httpApiClient: { setHeader: setHeaderStub }, + state: { populate: (v: string): string => v }, + } + def.exec(clientMock, 'X_Custom', 'test') + expect( + clientMock.httpApiClient.setHeader.calledWithExactly('X_Custom', 'test') + ).toBeTruthy() + }) + + test('clear request headers', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('clear request headers') + def.shouldMatch('I clear request headers') + def.shouldMatch('clear request headers') + + const clientMock = { httpApiClient: { clearHeaders: clearHeadersStub } } + def.exec(clientMock) + expect(clientMock.httpApiClient.clearHeaders.calledOnce).toBeTruthy() + }) + + test('set request json body', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('set request json body$') + def.shouldMatch('I set request json body') + def.shouldMatch('set request json body') + }) + + test('set request json body from fixture file', async () => { + const context = helper.getContext() // Extension context + + expect.assertions(5) + + const def = context.getDefinitionByMatcher('set request json body from') + def.shouldNotMatch('I set request json body from ') + def.shouldMatch('I set request json body from fixture') + def.shouldMatch('set request json body from fixture') + + const fixture = { + is_active: 'true', + id: '2', + } + const worldMock = { + httpApiClient: { setJsonBody: setJsonBodyStub }, + fixtures: { load: loadFixturesStub }, + } + loadFixturesStub.resolves(fixture) + + await def.exec(worldMock, 'fixture') + expect(worldMock.fixtures.load.calledWithExactly('fixture')).toBeTruthy() + expect(worldMock.httpApiClient.setJsonBody.calledOnceWithExactly(fixture)).toBeTruthy() + }) + + test('set request form body', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('set request form body$') + def.shouldMatch('I set request form body') + def.shouldMatch('set request form body') + }) + + test('set request form body from fixture file', async () => { + const context = helper.getContext() // Extension context + + expect.assertions(5) + + const def = context.getDefinitionByMatcher('set request form body from') + def.shouldNotMatch('I set request form body from ') + def.shouldMatch('I set request form body from fixture') + def.shouldMatch('set request form body from fixture') + + const fixture = { + is_active: 'true', + id: '2', + } + const worldMock = { + httpApiClient: { setFormBody: setFormBodyStub }, + fixtures: { load: loadFixturesStub }, + } + loadFixturesStub.resolves(fixture) + + await def.exec(worldMock, 'fixture') + expect(worldMock.fixtures.load.calledWithExactly('fixture')).toBeTruthy() + expect(worldMock.httpApiClient.setFormBody.calledWithExactly(fixture)).toBeTruthy() + }) + + test('set request multipart body from', async () => { + const context = helper.getContext() // Extension context + + expect.assertions(5) + + const def = context.getDefinitionByMatcher('set request multipart body from') + def.shouldNotMatch('I set request multipart body from ') + def.shouldMatch('I set request multipart body from fixture') + def.shouldMatch('set request multipart body from fixture') + + const fixture = { + id: '2', + file: 'some-file', + } + const worldMock = { + httpApiClient: { setMultipartBody: setMultipartBodyStub }, + fixtures: { load: loadFixturesStub }, + } + loadFixturesStub.resolves(fixture) + + await def.exec(worldMock, 'fixture') + expect(worldMock.fixtures.load.calledWithExactly('fixture')).toBeTruthy() + expect(worldMock.httpApiClient.setMultipartBody.calledWithExactly(fixture)).toBeTruthy() + }) + + test('clear request body', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('clear request body') + def.shouldMatch('I clear request body') + def.shouldMatch('clear request body') + + const clientMock = { httpApiClient: { clearBody: clearBodyStub } } + def.exec(clientMock) + expect(clientMock.httpApiClient.clearBody.calledOnce).toBeTruthy() + }) + + test('set request query', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('set request query') + def.shouldMatch('I set request query') + def.shouldMatch('set request query') + + const clientMock = { + httpApiClient: { setQuery: setQueryStub }, + state: { populateObject: (o: string): string => o }, + } + const query = { + is_active: 'true', + id: '2', + } + def.exec(clientMock, { rowsHash: () => query }) + expect(clientMock.httpApiClient.setQuery.calledOnce).toBeTruthy() + }) + + test('follow redirect', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('?follow redirect') + def.shouldMatch('I follow redirect') + def.shouldMatch('follow redirect') + + const clientMock = { + httpApiClient: { setFollowRedirect: setFollowRedirectStub }, + } + def.exec(clientMock) + expect(clientMock.httpApiClient.setFollowRedirect.calledWithExactly(true)).toBeTruthy() + }) + + test('do not follow redirect', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('do not follow redirect') + def.shouldMatch('I do not follow redirect') + def.shouldMatch('do not follow redirect') + + const clientMock = { + httpApiClient: { setFollowRedirect: setFollowRedirectStub }, + } + def.exec(clientMock) + expect(clientMock.httpApiClient.setFollowRedirect.calledWithExactly(false)).toBeTruthy() + }) + + test('pick response json property', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('pick response json') + def.shouldNotMatch('I pick response json as ') + def.shouldMatch('I pick response json key as value', ['key', 'value']) + def.shouldMatch('pick response json key as value', ['key', 'value']) + }) + + test('enable cookies', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('enable cookies') + def.shouldMatch('I enable cookies') + def.shouldMatch('enable cookies') + + const clientMock = { httpApiClient: { enableCookies: enableCookiesStub } } + def.exec(clientMock) + expect(clientMock.httpApiClient.enableCookies.calledOnce).toBeTruthy() + }) + + test('disable cookies', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('disable cookies') + def.shouldMatch('I disable cookies') + def.shouldMatch('disable cookies') + + const clientMock = { httpApiClient: { disableCookies: disableCookiesStub } } + def.exec(clientMock) + expect(clientMock.httpApiClient.disableCookies.calledOnce).toBeTruthy() + }) + + test('test cookie is present', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('response should (not )?have an? (.+) cookie') + def.shouldNotMatch('response should have a cookie') + def.shouldNotMatch('response should have an cookie') + def.shouldMatch('response should have a test cookie', [undefined, 'test']) + def.shouldMatch('response should have an test cookie', [undefined, 'test']) + + const clientMock = { httpApiClient: { getCookie: getCookieStub } } + getCookieStub.withArgs('cookie_exist').returns('some content') + getCookieStub.withArgs('cookie_dont_exist').returns(null) + + expect(() => def.exec(clientMock, undefined, 'cookie_exist')).not.toThrow() + expect(() => def.exec(clientMock, undefined, 'cookie_dont_exist')).toThrow( + "No cookie found for key 'cookie_dont_exist': expected null not to be null" + ) + expect(getCookieStub.calledTwice).toBeTruthy() + }) + + test('test cookie is absent', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('response should (not )?have an? (.+) cookie') + def.shouldNotMatch('response should crap have a cookie') + def.shouldNotMatch('response should crap have an cookie') + def.shouldNotMatch('response should not have a cookie') + def.shouldNotMatch('response should not have an cookie') + def.shouldMatch('response should not have a test cookie', ['not ', 'test']) + def.shouldMatch('response should not have an test cookie', ['not ', 'test']) + + getCookieStub.withArgs('cookie_exist').returns('some content') + getCookieStub.withArgs('cookie_dont_exist').returns(null) + + const clientMock = { httpApiClient: { getCookie: getCookieStub } } + + expect(() => def.exec(clientMock, 'not ', 'cookie_exist')).toThrow( + "A cookie exists for key 'cookie_exist': expected 'some content' to be null" + ) + expect(() => def.exec(clientMock, 'not ', 'cookie_dont_exist')).not.toThrow() + }) + + test('test cookie is secure', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('response (.+) cookie should (not )?be secure') + def.shouldNotMatch('response cookie should be secure') + def.shouldMatch('response test cookie should be secure', ['test', undefined]) + + getCookieStub.withArgs('secure_cookie').returns({ secure: true }) + getCookieStub.withArgs('not_secure_cookie').returns({ secure: false }) + const clientMock = { httpApiClient: { getCookie: getCookieStub } } + + expect(() => def.exec(clientMock, 'secure_cookie', undefined)).not.toThrow() + expect(() => def.exec(clientMock, 'not_secure_cookie', undefined)).toThrow( + "Cookie 'not_secure_cookie' is not secure: expected false to be true" + ) + }) + + test('test cookie is not secure', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('response (.+) cookie should (not )?be secure') + def.shouldNotMatch('response cookie should not be secure') + def.shouldMatch('response test cookie should not be secure', ['test', 'not ']) + + getCookieStub.withArgs('secure_cookie').returns({ secure: true }) + getCookieStub.withArgs('not_secure_cookie').returns({ secure: false }) + const clientMock = { httpApiClient: { getCookie: getCookieStub } } + + expect(() => def.exec(clientMock, 'secure_cookie', 'not ')).toThrow( + "Cookie 'secure_cookie' is secure: expected true to be false" + ) + expect(() => def.exec(clientMock, 'not_secure_cookie', 'not ')).not.toThrow() + }) + + test('test cookie is http only', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response (.+) cookie should (not )?be http only' + ) + def.shouldNotMatch('response cookie should be http only') + def.shouldMatch('response test cookie should be http only', ['test', undefined]) + + getCookieStub.withArgs('http_only').returns({ httpOnly: true }) + getCookieStub.withArgs('not_http_only').returns({ httpOnly: false }) + const clientMock = { httpApiClient: { getCookie: getCookieStub } } + + expect(() => def.exec(clientMock, 'http_only', undefined)).not.toThrow() + expect(() => def.exec(clientMock, 'not_http_only', undefined)).toThrow( + "Cookie 'not_http_only' is not http only: expected false to be true" + ) + }) + + test('test cookie is not http only', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response (.+) cookie should (not )?be http only' + ) + def.shouldNotMatch('response cookie should not be http only') + def.shouldMatch('response test cookie should not be http only', ['test', 'not ']) + + getCookieStub.withArgs('http_only').returns({ httpOnly: true }) + getCookieStub.withArgs('not_http_only').returns({ httpOnly: false }) + const clientMock = { httpApiClient: { getCookie: getCookieStub } } + + expect(() => def.exec(clientMock, 'http_only', 'not ')).toThrow( + "Cookie 'http_only' is http only: expected true to be false" + ) + expect(() => def.exec(clientMock, 'not_http_only', 'not ')).not.toThrow() + }) + + test('test cookie domain equals given value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response (.+) cookie domain should (not )?be (.+)' + ) + def.shouldNotMatch('response cookie domain should be domain') + def.shouldNotMatch('response test cookie domain should be ') + def.shouldMatch('response test cookie domain should be domain', [ + 'test', + undefined, + 'domain', + ]) + + getCookieStub.withArgs('domain1').returns({ domain: 'domain1' }) + getCookieStub.withArgs('domain2').returns({ domain: 'domain2' }) + const clientMock = { httpApiClient: { getCookie: getCookieStub } } + + expect(() => def.exec(clientMock, 'domain1', undefined, 'domain1')).not.toThrow() + expect(() => def.exec(clientMock, 'domain2', undefined, 'domain1')).toThrow( + `Expected cookie 'domain2' domain to be 'domain1', found 'domain2': expected 'domain2' to equal 'domain1'` + ) + }) + + test('test cookie domain does not equal given value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response (.+) cookie domain should (not )?be (.+)' + ) + def.shouldNotMatch('response cookie domain should not be domain') + def.shouldNotMatch('response test cookie domain should not be ') + def.shouldMatch('response test cookie domain should not be domain', [ + 'test', + 'not ', + 'domain', + ]) + + getCookieStub.withArgs('domain1').returns({ domain: 'domain1' }) + getCookieStub.withArgs('domain2').returns({ domain: 'domain2' }) + const clientMock = { httpApiClient: { getCookie: getCookieStub } } + + expect(() => def.exec(clientMock, 'domain1', 'not ', 'domain1')).toThrow( + `Cookie 'domain1' domain is 'domain1': expected 'domain1' to not equal 'domain1'` + ) + expect(() => def.exec(clientMock, 'domain2', 'not ', 'domain1')).not.toThrow() + }) + + test('reset http client', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('reset http client') + def.shouldMatch('I reset http client') + def.shouldMatch('reset http client') + + const clientMock = { httpApiClient: { reset: resetStub } } + def.exec(clientMock) + expect(clientMock.httpApiClient.reset.calledOnce).toBeTruthy() + }) + + test('perform a request', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('GET|POST|PUT|DELETE|PATCH') + def.shouldNotMatch('I GET ') + def.shouldMatch('I GET /', ['GET', '/']) + def.shouldMatch('I POST /create', ['POST', '/create']) + def.shouldMatch('I PUT /update', ['PUT', '/update']) + def.shouldMatch('I DELETE /delete', ['DELETE', '/delete']) + def.shouldMatch('I PATCH /updatePartial', ['PATCH', '/updatePartial']) + def.shouldMatch('GET /', ['GET', '/']) + def.shouldMatch('POST /create', ['POST', '/create']) + def.shouldMatch('PUT /update', ['PUT', '/update']) + def.shouldMatch('DELETE /delete', ['DELETE', '/delete']) + def.shouldMatch('PATCH /updatePartial', ['PATCH', '/updatePartial']) + }) + + test('dump response body', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('dump response body') + def.shouldMatch('I dump response body') + def.shouldMatch('dump response body') + + const clientMock = { + httpApiClient: { getResponse: getResponseStub }, + } + getResponseStub.returns({ body: '' }) + def.exec(clientMock) + expect(clientMock.httpApiClient.getResponse.calledOnce).toBeTruthy() + }) + + test('check response HTTP status code', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('response status code should be') + def.shouldNotMatch('response status code should be ') + def.shouldNotMatch('response status code should be string') + def.shouldNotMatch('response status code should be 600') + def.shouldMatch('response status code should be 200', ['200']) + def.shouldMatch('response status code should be 404', ['404']) + + getResponseStub.onFirstCall().returns({ statusCode: 200 }) + getResponseStub.onSecondCall().returns({ statusCode: 400 }) + const clientMock = { httpApiClient: { getResponse: getResponseStub } } + + expect(() => def.exec(clientMock, '200')).not.toThrow() + expect(() => def.exec(clientMock, '200')).toThrow( + `Expected status code to be: 200, but found: 400: expected 400 to equal 200` + ) + expect(getResponseStub.calledTwice).toBeTruthy() + }) + + test('check response HTTP status by message', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('response status should be') + def.shouldNotMatch('response status should be ') + def.shouldMatch('response status should be ok', ['ok']) + def.shouldMatch('response status should be forbidden', ['forbidden']) + + getResponseStub.onFirstCall().returns({ statusCode: 200 }) + getResponseStub.onSecondCall().returns({ statusCode: 500 }) + const clientMock = { httpApiClient: { getResponse: getResponseStub } } + + expect(() => def.exec(clientMock, 'ok')).not.toThrow() + expect(() => def.exec(clientMock, 'ok')).toThrow( + `Expected status to be: 'ok', but found: 'internal server error': expected 500 to equal 200` + ) + expect(() => def.exec(clientMock, 'invalid')).toThrow( + `'invalid' is not a valid status message` + ) + expect(getResponseStub.calledTwice).toBeTruthy() + }) + + test('check json response', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + def.shouldMatch('json response should match', [undefined]) + def.shouldMatch('json response should fully match', ['fully ']) + }) + + test('check json response property is defined', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + + getResponseStub.onFirstCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { name: 'john', gender: 'male' }, + }) + getResponseStub.onSecondCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { name: 'john' }, + }) + getResponseStub.onThirdCall().returns({ + headers: { 'content-type': 'application/json' }, + body: {}, + }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + const spec = [ + { + field: 'name', + matcher: 'defined', + }, + { + field: 'gender', + matcher: 'present', + }, + ] + + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'gender' is undefined: expected undefined not to be undefined` + ) + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'name' is undefined: expected undefined not to be undefined` + ) + expect(getResponseStub.calledThrice).toBeTruthy() + }) + + test('check json response property equals expected value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + + getResponseStub.onFirstCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { name: 'plouc' }, + }) + getResponseStub.onSecondCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { name: 'john' }, + }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + const spec = [ + { + field: 'name', + matcher: 'equals', + value: 'plouc', + }, + ] + + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Expected property 'name' to equal 'plouc', but found 'john': expected 'john' to deeply equal 'plouc'` + ) + expect(getResponseStub.calledTwice).toBeTruthy() + }) + + test('check json response property contains value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + + getResponseStub.onFirstCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'raphael', last_name: 'benn' }, + }) + getResponseStub.onSecondCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'raphael', last_name: 'be' }, + }) + getResponseStub.onThirdCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'rap', last_name: 'ben' }, + }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + const spec = [ + { + field: 'first_name', + matcher: 'contain', + value: 'raph', + }, + { + field: 'last_name', + matcher: 'contains', + value: 'ben', + }, + ] + + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'last_name' (be) does not contain 'ben': expected 'be' to include 'ben'` + ) + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'first_name' (rap) does not contain 'raph': expected 'rap' to include 'raph'` + ) + expect(getResponseStub.calledThrice).toBeTruthy() + }) + + test('check json response property matches regexp', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + + getResponseStub.onFirstCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'raphael', last_name: 'benn' }, + }) + getResponseStub.onSecondCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'raphael', last_name: 'be' }, + }) + getResponseStub.onThirdCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'rap', last_name: 'ben' }, + }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + const spec = [ + { + field: 'first_name', + matcher: 'matches', + value: 'raph', + }, + { + field: 'last_name', + matcher: 'match', + value: 'ben', + }, + ] + + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'last_name' (be) does not match 'ben': expected 'be' to match /ben/` + ) + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'first_name' (rap) does not match 'raph': expected 'rap' to match /raph/` + ) + expect(getResponseStub.calledThrice).toBeTruthy() + }) + + test('check json response property matches expressions', () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + + getResponseStub.onFirstCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'Johnny' }, + }) + getResponseStub.onSecondCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'Bob' }, + }) + getResponseStub.onThirdCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'John' }, + }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + const spec = [ + { + expression: 'first_name ?', + }, + { + expression: 'first_name #= string', + }, + { + expression: 'first_name ^= Jo', + }, + { + expression: 'first_name *= hn', + }, + { + expression: 'first_name $= ny', + }, + { + expression: 'first_name ~= ^Johnny$', + }, + ] + + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'first_name' (Bob) does not start with 'Jo': expected 'Bob' to start with 'Jo'` + ) + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'first_name' (John) does not end with 'ny': expected 'John' to end with 'ny'` + ) + expect(getResponseStub.calledThrice).toBeTruthy() + }) + + test('check json response property matches negated expressions', () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + + getResponseStub.onFirstCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'Bob' }, + }) + getResponseStub.onSecondCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'Johnny' }, + }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + const spec = [ + { + expression: 'last_name !?', + }, + { + expression: 'first_name !#= number', + }, + { + expression: 'first_name !^= Jo', + }, + { + expression: 'first_name !*= hn', + }, + { + expression: 'first_name !$= ny', + }, + { + expression: 'first_name !~= ^Johnny$', + }, + ] + + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).toThrow( + `Property 'first_name' (Johnny) starts with 'Jo': expected 'Johnny' not to start with 'Jo'` + ) + expect(getResponseStub.calledTwice).toBeTruthy() + }) + + test('check json response property matches padded expressions', () => { + const context = helper.getContext() + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + + getResponseStub.onFirstCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'John', note: "I'm a geek!" }, + }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + const spec = [ + { + expression: 'first_name ? ', + }, + { + expression: 'first_name #= string', + }, + { + expression: 'first_name !~= ^Bob$', + }, + { + expression: "note = I'm a geek!", + }, + ] + + expect(() => def.exec(clientMock, undefined, { hashes: () => spec })).not.toThrow() + expect(getResponseStub.calledOnce).toBeTruthy() + }) + + test('check json response fully matches spec', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('json response should (fully )?match') + + getResponseStub.onFirstCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'Raphaël', last_name: 'ben' }, + }) + getResponseStub.onSecondCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'Raphaël', last_name: 'ben', gender: 'male' }, + }) + getResponseStub.onThirdCall().returns({ + headers: { 'content-type': 'application/json' }, + body: { first_name: 'Raphaël', last_name: 'be' }, + }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + const spec = [ + { + field: 'first_name', + matcher: 'equal', + value: 'Raphaël', + }, + { + field: 'last_name', + matcher: 'match', + value: 'ben', + }, + ] + + expect(() => def.exec(clientMock, 'fully ', { hashes: () => spec })).not.toThrow() + expect(() => def.exec(clientMock, 'fully ', { hashes: () => spec })).toThrow( + `Expected json response to fully match spec, but it does not: expected 3 to equal 2` + ) + expect(() => def.exec(clientMock, 'fully ', { hashes: () => spec })).toThrow( + `Property 'last_name' (be) does not match 'ben': expected 'be' to match /ben/` + ) + expect(getResponseStub.calledThrice).toBeTruthy() + }) + + test('check json collection size for a given path', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('should receive a collection of') + def.shouldNotMatch('I should receive a collection of x items for path whatever') + def.shouldMatch('I should receive a collection of 1 item for path property', [ + '1', + 'property', + ]) + def.shouldMatch('I should receive a collection of 2 items for path property', [ + '2', + 'property', + ]) + def.shouldMatch('should receive a collection of 1 item for path property', [ + '1', + 'property', + ]) + def.shouldMatch('should receive a collection of 2 items', ['2', undefined]) + + getResponseStub.onFirstCall().returns({ body: { property: ['a', 'b', 'c'] } }) + getResponseStub.onSecondCall().returns({ body: { property: ['a', 'b'] } }) + const clientMock = { httpApiClient: { getResponse: getResponseStub } } + + expect(() => def.exec(clientMock, '3', 'property')).not.toThrow() + expect(() => def.exec(clientMock, '3', 'property')).toThrow(`expected 2 to equal 3`) + }) + + test('response match fixture', async () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('should match fixture') + def.shouldNotMatch('response should match fixture ') + def.shouldMatch('response should match fixture fixture', ['fixture']) + + const snapshot = { testing: true } + + getResponseStub.onFirstCall().returns({ statusCode: 200, body: snapshot }) + getResponseStub.onSecondCall().returns({ statusCode: 200, body: { app: true } }) + const worldMock = { + httpApiClient: { getResponse: getResponseStub }, + fixtures: { load: loadFixturesStub }, + } + loadFixturesStub.resolves(snapshot) + + await def.exec(worldMock, 'snapshot') + await expect(def.exec(worldMock, 'snapshot')).rejects.toThrow( + `expected { app: true } to deeply equal { testing: true }` + ) + expect(getResponseStub.calledTwice).toBeTruthy() + expect(loadFixturesStub.calledTwice).toBeTruthy() + }) + + test('check response header value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response header (.+) should (not )?(equal|contain|match)' + ) + def.shouldNotMatch('response header should match pattern ') + def.shouldNotMatch('response header Content-Type should match ') + def.shouldNotMatch('response header Content-Type should invalid thing') + }) + + test('check response header equals expected value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response header (.+) should (not )?(equal|contain|match)' + ) + + def.shouldMatch('response header Content-Type should equal application/json', [ + 'Content-Type', + undefined, + 'equal', + 'application/json', + ]) + + getResponseStub.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) + getResponseStub.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) + getResponseStub.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + expect(() => + def.exec(clientMock, 'Content-Type', undefined, 'equal', 'application/json') + ).not.toThrow() + expect(() => + def.exec(clientMock, 'Content-Type', undefined, 'equal', 'application/json') + ).toThrow( + `Expected header 'Content-Type' to equal 'application/json', but found 'application/xml' which does not: expected 'application/xml' to equal 'application/json'` + ) + expect(() => + def.exec(clientMock, 'Content-Type', undefined, 'equal', 'application/json') + ).toThrow(`Header 'Content-Type' does not exist: expected undefined not to be undefined`) + }) + + test('check response header does not equal expected value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response header (.+) should (not )?(equal|contain|match)' + ) + + def.shouldMatch('response header Content-Type should not equal application/json', [ + 'Content-Type', + 'not ', + 'equal', + 'application/json', + ]) + + getResponseStub.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) + getResponseStub.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) + getResponseStub.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + expect(() => + def.exec(clientMock, 'Content-Type', 'not ', 'equal', 'application/json') + ).toThrow( + `Expected header 'Content-Type' to not equal 'application/json', but found 'application/json' which does: expected 'application/json' to not equal 'application/json'` + ) + expect(() => + def.exec(clientMock, 'Content-Type', 'not ', 'equal', 'application/json') + ).not.toThrow() + expect(() => + def.exec(clientMock, 'Content-Type', 'not ', 'equal', 'application/json') + ).toThrow(`Header 'Content-Type' does not exist: expected undefined not to be undefined`) + }) + + test('check response header contains value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response header (.+) should (not )?(equal|contain|match)' + ) + + def.shouldMatch('response header Content-Type should contain application/json', [ + 'Content-Type', + undefined, + 'contain', + 'application/json', + ]) + + getResponseStub.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) + getResponseStub.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) + getResponseStub.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + expect(() => + def.exec(clientMock, 'Content-Type', undefined, 'contain', 'json') + ).not.toThrow() + expect(() => def.exec(clientMock, 'Content-Type', undefined, 'contain', 'json')).toThrow( + `Expected header 'Content-Type' to contain 'json', but found 'application/xml' which does not: expected 'application/xml' to include 'json'` + ) + expect(() => def.exec(clientMock, 'Content-Type', undefined, 'contain', 'json')).toThrow( + `Header 'Content-Type' does not exist: expected undefined not to be undefined` + ) + }) + + test('check response header does not contain value', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response header (.+) should (not )?(equal|contain|match)' + ) + + def.shouldMatch('response header Content-Type should not contain application/json', [ + 'Content-Type', + 'not ', + 'contain', + 'application/json', + ]) + + getResponseStub.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) + getResponseStub.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) + getResponseStub.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + expect(() => def.exec(clientMock, 'Content-Type', 'not ', 'contain', 'json')).toThrow( + `Expected header 'Content-Type' to not contain 'json', but found 'application/json' which does: expected 'application/json' to not include 'json'` + ) + expect(() => def.exec(clientMock, 'Content-Type', 'not ', 'contain', 'json')).not.toThrow() + expect(() => def.exec(clientMock, 'Content-Type', 'not ', 'contain', 'json')).toThrow( + `Header 'Content-Type' does not exist: expected undefined not to be undefined` + ) + }) + + test('check response header matches regexp', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response header (.+) should (not )?(equal|contain|match)' + ) + + def.shouldMatch('response header Content-Type should match ^application/json$', [ + 'Content-Type', + undefined, + 'match', + '^application/json$', + ]) + + getResponseStub.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) + getResponseStub.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) + getResponseStub.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + expect(() => + def.exec(clientMock, 'Content-Type', undefined, 'match', '^application/json$') + ).not.toThrow() + expect(() => + def.exec(clientMock, 'Content-Type', undefined, 'match', '^application/json$') + ).toThrow( + `Expected header 'Content-Type' to match '^application/json$', but found 'application/xml' which does not: expected 'application/xml' to match /^application\\/json$/` + ) + expect(() => + def.exec(clientMock, 'Content-Type', undefined, 'match', '^application/json$') + ).toThrow(`Header 'Content-Type' does not exist: expected undefined not to be undefined`) + }) + + test('check response header does not match regexp', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher( + 'response header (.+) should (not )?(equal|contain|match)' + ) + + def.shouldMatch('response header Content-Type should not match ^application/json$', [ + 'Content-Type', + 'not ', + 'match', + '^application/json$', + ]) + + getResponseStub.onFirstCall().returns({ headers: { 'content-type': 'application/json' } }) + getResponseStub.onSecondCall().returns({ headers: { 'content-type': 'application/xml' } }) + getResponseStub.onThirdCall().returns({ headers: { 'another-header': 'application/json' } }) + const clientMock = { + state: { populate: (v: string): string => v }, + httpApiClient: { getResponse: getResponseStub }, + } + + expect(() => + def.exec(clientMock, 'Content-Type', 'not ', 'match', '^application/json$') + ).toThrow( + `Expected header 'Content-Type' to not match '^application/json$', but found 'application/json' which does: expected 'application/json' not to match /^application\\/json$/` + ) + expect(() => + def.exec(clientMock, 'Content-Type', 'not ', 'match', '^application/json$') + ).not.toThrow() + expect(() => + def.exec(clientMock, 'Content-Type', 'not ', 'match', '^application/json$') + ).toThrow(`Header 'Content-Type' does not exist: expected undefined not to be undefined`) + }) +}) diff --git a/tests/extensions/http_api/utils.test.js b/tests/extensions/http_api/utils.test.js deleted file mode 100644 index 0f89a2ae..00000000 --- a/tests/extensions/http_api/utils.test.js +++ /dev/null @@ -1,25 +0,0 @@ -const { parseMatchExpression } = require('../../../src/extensions/http_api/utils') - -test('parseMatchExpression should throw when expression is undefined', () => { - expect(() => parseMatchExpression()).toThrow("'undefined' is not a valid expression") -}) - -test('parseMatchExpression should throw when expression does not match', () => { - expect(() => parseMatchExpression('does not match')).toThrow( - "'does not match' is not a valid expression" - ) -}) - -test('parseMatchExpression should return expected groups field, matcher, and value', () => { - const expression = 'foo.bar[0] ~= ^(s+)$' - - const expectedResult = { - field: 'foo.bar[0]', - matcher: '~=', - value: '^(s+)$', - } - - const result = parseMatchExpression(expression) - - expect(result).toEqual(expectedResult) -}) diff --git a/tests/extensions/http_api/utils.test.ts b/tests/extensions/http_api/utils.test.ts new file mode 100644 index 00000000..f1ffd647 --- /dev/null +++ b/tests/extensions/http_api/utils.test.ts @@ -0,0 +1,27 @@ +import { parseMatchExpression } from '../../../src/extensions/http_api/utils' + +describe('extensions > http_api', () => { + test('parseMatchExpression should throw when expression is undefined', () => { + expect(() => parseMatchExpression()).toThrow("'undefined' is not a valid expression") + }) + + test('parseMatchExpression should throw when expression does not match', () => { + expect(() => parseMatchExpression('does not match')).toThrow( + "'does not match' is not a valid expression" + ) + }) + + test('parseMatchExpression should return expected groups field, matcher, and value', () => { + const expression = 'foo.bar[0] ~= ^(s+)$' + + const expectedResult = { + field: 'foo.bar[0]', + matcher: '~=', + value: '^(s+)$', + } + + const result = parseMatchExpression(expression) + + expect(result).toEqual(expectedResult) + }) +}) diff --git a/tests/extensions/snapshot/clean.test.js b/tests/extensions/snapshot/clean.test.js deleted file mode 100644 index e73438eb..00000000 --- a/tests/extensions/snapshot/clean.test.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict' - -const clean = require('../../../src/extensions/snapshot/clean') -const snapshot = require('../../../src/extensions/snapshot/snapshot') - -beforeEach(() => { - clean.resetReferences() -}) - -test('referenceSnapshot should add snapshot file and name to internal list', () => { - const file = './test.js.snap' - const snapshotName = 'Scenario 1 1.1' - - clean.referenceSnapshot(file, snapshotName) - - expect(clean._snapshots).toEqual({ [file]: [snapshotName] }) -}) - -test('resetReferences should remove all entries', () => { - const file = './test.js.snap' - const snapshotName = 'Scenario 1 1.1' - - clean.referenceSnapshot(file, snapshotName) - clean.resetReferences() - - expect(clean._snapshots).toEqual({}) -}) - -test('cleanSnapshots should remove unreferenced snapshots from file', () => { - const file = './test.js.snap' - const snapshotName = 'Scenario 1 1.1' - const snapshot2Name = 'Scenario 2 1.1' - const snapshotContent = { [snapshotName]: 'some content', [snapshot2Name]: 'another content' } - const expectedContent = { [snapshotName]: 'some content' } - - clean.referenceSnapshot(file, snapshotName) - - snapshot.readSnapshotFile = jest.fn() - snapshot.writeSnapshotFile = jest.fn() - - snapshot.readSnapshotFile.mockReturnValueOnce(snapshotContent) - - clean.cleanSnapshots() - - expect(snapshot.readSnapshotFile.mock.calls.length).toBe(1) - expect(snapshot.readSnapshotFile).toHaveBeenCalledWith(file) - - expect(snapshot.writeSnapshotFile.mock.calls.length).toBe(1) - expect(snapshot.writeSnapshotFile).toHaveBeenCalledWith(file, expectedContent) -}) - -test('cleanSnapshots should remove unreferenced snapshots from multiple files', () => { - const file1 = './test1.js.snap' - const file2 = './test2.js.snap' - const snapshot1Name = 'Scenario 1 1.1' - const snapshot2Name = 'Scenario 2 1.1' - const snapshot1Content = { [snapshot1Name]: 'some content', [snapshot2Name]: 'another content' } - const snapshot2Content = { [snapshot1Name]: 'some content', [snapshot2Name]: 'another content' } - const expectedContent1 = { [snapshot1Name]: 'some content' } - const expectedContent2 = { [snapshot2Name]: 'another content' } - - clean.referenceSnapshot(file1, snapshot1Name) - clean.referenceSnapshot(file2, snapshot2Name) - - snapshot.readSnapshotFile = jest.fn() - snapshot.writeSnapshotFile = jest.fn() - - snapshot.readSnapshotFile.mockReturnValueOnce(snapshot1Content) - snapshot.readSnapshotFile.mockReturnValueOnce(snapshot2Content) - - clean.cleanSnapshots() - - expect(snapshot.readSnapshotFile.mock.calls.length).toBe(2) - expect(snapshot.readSnapshotFile).toHaveBeenCalledWith(file1) - expect(snapshot.readSnapshotFile).toHaveBeenCalledWith(file2) - - expect(snapshot.writeSnapshotFile.mock.calls.length).toBe(2) - expect(snapshot.writeSnapshotFile).toHaveBeenCalledWith(file1, expectedContent1) - expect(snapshot.writeSnapshotFile).toHaveBeenCalledWith(file2, expectedContent2) -}) diff --git a/tests/extensions/snapshot/clean.test.ts b/tests/extensions/snapshot/clean.test.ts new file mode 100644 index 00000000..6a5371a5 --- /dev/null +++ b/tests/extensions/snapshot/clean.test.ts @@ -0,0 +1,90 @@ +import * as clean from '../../../src/extensions/snapshot/clean' +import * as snapshotAction from '../../../src/extensions/snapshot/snapshot_actions' +import { createSandbox, SinonStub } from 'sinon' + +describe('extensions > snapshot', () => { + beforeEach(() => { + clean.resetReferences() + }) + + test('referenceSnapshot should add snapshot file and name to internal list', () => { + const file = './test.js.snap' + const snapshotName = 'Scenario 1 1.1' + + clean.referenceSnapshot(file, snapshotName) + + expect(clean._snapshots).toEqual({ [file]: [snapshotName] }) + }) + + test('resetReferences should remove all entries', () => { + const file = './test.js.snap' + const snapshotName = 'Scenario 1 1.1' + + clean.referenceSnapshot(file, snapshotName) + clean.resetReferences() + + expect(clean._snapshots).toEqual({}) + }) + + describe('cleanSnapshots', () => { + const sandbox = createSandbox() + let readSnapshotFileStub: SinonStub, writeSnapshotFileStub: SinonStub + + beforeAll(() => { + readSnapshotFileStub = sandbox.stub(snapshotAction, 'readSnapshotFile') + writeSnapshotFileStub = sandbox.stub(snapshotAction, 'writeSnapshotFile') + }) + + afterEach(() => sandbox.reset()) + afterAll(() => sandbox.restore()) + + it('should remove unreferenced snapshots from file', () => { + const file = './test.js.snap' + const snapshotName = 'Scenario 1 1.1' + const snapshot2Name = 'Scenario 2 1.1' + const snapshotContent = { + [snapshotName]: 'some content', + [snapshot2Name]: 'another content', + } + const expectedContent = { [snapshotName]: 'some content' } + + clean.referenceSnapshot(file, snapshotName) + + readSnapshotFileStub.withArgs(file).returns(snapshotContent) + + clean.cleanSnapshots() + + expect(readSnapshotFileStub.calledOnce).toBeTruthy() + expect(writeSnapshotFileStub.calledWithExactly(file, expectedContent)).toBeTruthy() + }) + + it('should remove unreferenced snapshots from multiple files', () => { + const file1 = './test1.js.snap' + const file2 = './test2.js.snap' + const snapshot1Name = 'Scenario 1 1.1' + const snapshot2Name = 'Scenario 2 1.1' + const snapshot1Content = { + [snapshot1Name]: 'some content', + [snapshot2Name]: 'another content', + } + const snapshot2Content = { + [snapshot1Name]: 'some content', + [snapshot2Name]: 'another content', + } + const expectedContent1 = { [snapshot1Name]: 'some content' } + const expectedContent2 = { [snapshot2Name]: 'another content' } + + clean.referenceSnapshot(file1, snapshot1Name) + clean.referenceSnapshot(file2, snapshot2Name) + + readSnapshotFileStub.withArgs(file1).returns(snapshot1Content) + readSnapshotFileStub.withArgs(file2).returns(snapshot2Content) + + clean.cleanSnapshots() + + expect(readSnapshotFileStub.calledTwice).toBeTruthy() + expect(writeSnapshotFileStub.calledWithExactly(file1, expectedContent1)).toBeTruthy() + expect(writeSnapshotFileStub.calledWithExactly(file2, expectedContent2)).toBeTruthy() + }) + }) +}) diff --git a/tests/extensions/snapshot/cmdOptions.test.js b/tests/extensions/snapshot/cmdOptions.test.js deleted file mode 100644 index 3d924ab0..00000000 --- a/tests/extensions/snapshot/cmdOptions.test.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict' - -beforeEach(() => { - delete require.cache[require.resolve('../../../src/extensions/snapshot/cmdOptions')] - jest.resetModules() -}) - -test('Snapshot cmdOptions updateSnapshots and cleanSnapshots set to false if no args passed', () => { - const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') - - expect(cmdOptions.updateSnapshots).toBeFalsy() - expect(cmdOptions.cleanSnapshots).toBeFalsy() -}) - -test("Snapshot cmdOptions updateSnapshots set to true if '-u' args passed", () => { - process.argv = ['yarn', 'run', 'cucumber-js', '-u'] - const cmdOptionss = require('../../../src/extensions/snapshot/cmdOptions') - - expect(cmdOptionss.updateSnapshots).toBeTruthy() - expect(cmdOptionss.cleanSnapshots).toBeFalsy() -}) - -test("Snapshot cmdOptions updateSnapshots set to true if '--updateSnapshots' args passed", () => { - process.argv = ['yarn', 'run', 'cucumber-js', '--updateSnapshots'] - const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') - - expect(cmdOptions.updateSnapshots).toBeTruthy() - expect(cmdOptions.cleanSnapshots).toBeFalsy() -}) - -test("Snapshot cmdOptions updateSnapshots set to true if '--updateSnapshots' and '-u' args passed", () => { - process.argv = ['yarn', 'run', 'cucumber-js', '--updateSnapshots', '-u'] - const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') - - expect(cmdOptions.updateSnapshots).toBeTruthy() - expect(cmdOptions.cleanSnapshots).toBeFalsy() -}) - -test("Snapshot cmdOptions cleanSnapshots set to true if '--cleanSnapshots' args passed", () => { - process.argv = ['yarn', 'run', 'cucumber-js', '--cleanSnapshots'] - const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') - - expect(cmdOptions.updateSnapshots).toBeFalsy() - expect(cmdOptions.cleanSnapshots).toBeTruthy() -}) - -test("Snapshot cmdOptions updateSnapshots and cleanSnapshots set to true if '--cleanSnapshots' and '--updateSnapshots' args passed", () => { - process.argv = ['yarn', 'run', 'cucumber-js', '--cleanSnapshots', '--updateSnapshots'] - const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') - - expect(cmdOptions.updateSnapshots).toBeTruthy() - expect(cmdOptions.cleanSnapshots).toBeTruthy() -}) - -test("Snapshot cmdOptions updateSnapshots and cleanSnapshots set to true if '--cleanSnapshots' and '-u' args passed", () => { - process.argv = ['yarn', 'run', 'cucumber-js', '--cleanSnapshots', '-u'] - const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') - - expect(cmdOptions.updateSnapshots).toBeTruthy() - expect(cmdOptions.cleanSnapshots).toBeTruthy() -}) - -test("Snapshot cmdOptions updateSnapshots and cleanSnapshots set to true if '--cleanSnapshots', 'updateSnapshots' and '-u' args passed", () => { - process.argv = ['yarn', 'run', 'cucumber-js', '--cleanSnapshots', '--updateSnapshots', '-u'] - const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') - - expect(cmdOptions.updateSnapshots).toBeTruthy() - expect(cmdOptions.cleanSnapshots).toBeTruthy() -}) diff --git a/tests/extensions/snapshot/cmd_options.test.ts b/tests/extensions/snapshot/cmd_options.test.ts new file mode 100644 index 00000000..9605cbf0 --- /dev/null +++ b/tests/extensions/snapshot/cmd_options.test.ts @@ -0,0 +1,77 @@ +/* eslint-disable +no-duplicate-imports, +import/no-duplicates, +@typescript-eslint/no-unsafe-member-access, +@typescript-eslint/ban-ts-comment, +@typescript-eslint/no-var-requires, +@typescript-eslint/no-unsafe-assignment +*/ + +describe('extensions > snapshot > cmd_options', () => { + beforeEach(() => { + delete require.cache[require.resolve('../../../src/extensions/snapshot/cmdOptions')] + jest.resetModules() + }) + + test('Snapshot cmdOptions updateSnapshots and cleanSnapshots set to false if no args passed', () => { + const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') + expect(cmdOptions.updateSnapshots).toBeFalsy() + expect(cmdOptions.cleanSnapshots).toBeFalsy() + }) + + test("Snapshot cmdOptions updateSnapshots set to true if '-u' args passed", () => { + process.argv = ['yarn', 'run', 'cucumber-js', '-u'] + + const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') + expect(cmdOptions.updateSnapshots).toBeTruthy() + expect(cmdOptions.cleanSnapshots).toBeFalsy() + }) + + test("Snapshot cmdOptions updateSnapshots set to true if '--updateSnapshots' args passed", () => { + process.argv = ['yarn', 'run', 'cucumber-js', '--updateSnapshots'] + + const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') + expect(cmdOptions.updateSnapshots).toBeTruthy() + expect(cmdOptions.cleanSnapshots).toBeFalsy() + }) + + test("Snapshot cmdOptions updateSnapshots set to true if '--updateSnapshots' and '-u' args passed", () => { + process.argv = ['yarn', 'run', 'cucumber-js', '--updateSnapshots', '-u'] + + const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') + expect(cmdOptions.updateSnapshots).toBeTruthy() + expect(cmdOptions.cleanSnapshots).toBeFalsy() + }) + + test("Snapshot cmdOptions cleanSnapshots set to true if '--cleanSnapshots' args passed", () => { + process.argv = ['yarn', 'run', 'cucumber-js', '--cleanSnapshots'] + + const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') + expect(cmdOptions.updateSnapshots).toBeFalsy() + expect(cmdOptions.cleanSnapshots).toBeTruthy() + }) + + test("Snapshot cmdOptions updateSnapshots and cleanSnapshots set to true if '--cleanSnapshots' and '--updateSnapshots' args passed", () => { + process.argv = ['yarn', 'run', 'cucumber-js', '--cleanSnapshots', '--updateSnapshots'] + + const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') + expect(cmdOptions.updateSnapshots).toBeTruthy() + expect(cmdOptions.cleanSnapshots).toBeTruthy() + }) + + test("Snapshot cmdOptions updateSnapshots and cleanSnapshots set to true if '--cleanSnapshots' and '-u' args passed", () => { + process.argv = ['yarn', 'run', 'cucumber-js', '--cleanSnapshots', '-u'] + + const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') + expect(cmdOptions.updateSnapshots).toBeTruthy() + expect(cmdOptions.cleanSnapshots).toBeTruthy() + }) + + test("Snapshot cmdOptions updateSnapshots and cleanSnapshots set to true if '--cleanSnapshots', 'updateSnapshots' and '-u' args passed", () => { + process.argv = ['yarn', 'run', 'cucumber-js', '--cleanSnapshots', '--updateSnapshots', '-u'] + + const cmdOptions = require('../../../src/extensions/snapshot/cmdOptions') + expect(cmdOptions.updateSnapshots).toBeTruthy() + expect(cmdOptions.cleanSnapshots).toBeTruthy() + }) +}) diff --git a/tests/extensions/snapshot/dedent.test.js b/tests/extensions/snapshot/dedent.test.js deleted file mode 100644 index 2a638f2a..00000000 --- a/tests/extensions/snapshot/dedent.test.js +++ /dev/null @@ -1,83 +0,0 @@ -'use strict' - -const dedent = require('../../../src/extensions/snapshot/dedent') - -test('dedent align to first character', () => { - const test = dedent(` - My text - Another line - Another line again - `) - - const expected = 'My text\n Another line\nAnother line again' - expect(test).toEqual(expected) -}) - -test('dedent align to first """', () => { - const test = dedent(` - """ - My text - Another line - Another line again - """ - `) - - const expected = ' My text\n Another line\n Another line again' - expect(test).toEqual(expected) -}) - -test('dedent align should also work with tabulation', () => { - const test = dedent(` - """ - \tMy text - Another line - Another line again - """ - `) - - const expected = ' \tMy text\n Another line\n Another line again' - expect(test).toEqual(expected) -}) - -test('dedent should not edit content if less than lines', () => { - const test = dedent(` - `) - - const expected = '\n ' - expect(test).toEqual(expected) -}) - -test('dedent align should ignore last and first lines', () => { - const test = dedent(`Some first content - My text - Another line - Another line again - Some last content`) - - const expected = 'My text\n Another line\nAnother line again' - expect(test).toEqual(expected) -}) - -test('dedent align with """ should ignore two last and two first lines', () => { - const test = dedent(`Some content - """Some other content - \tMy text - Another line - Another line again - """ Some end content - Some end other content`) - - const expected = ' \tMy text\n Another line\n Another line again' - expect(test).toEqual(expected) -}) - -test('dedent works without parenthesis', () => { - const test = dedent` - My text - Another line - Another line again - ` - - const expected = 'My text\n Another line\nAnother line again' - expect(test).toEqual(expected) -}) diff --git a/tests/extensions/snapshot/dedent.test.ts b/tests/extensions/snapshot/dedent.test.ts new file mode 100644 index 00000000..3d2ba1ed --- /dev/null +++ b/tests/extensions/snapshot/dedent.test.ts @@ -0,0 +1,83 @@ +import { dedent } from '../../../src/extensions/snapshot/dedent' + +describe('extensions > snapshot > dedent', () => { + test('dedent align to first character', () => { + const test = dedent(` + My text + Another line + Another line again + `) + + const expected = 'My text\n Another line\nAnother line again' + expect(test).toEqual(expected) + }) + + test('dedent align to first """', () => { + const test = dedent(` + """ + My text + Another line + Another line again + """ + `) + + const expected = ' My text\n Another line\n Another line again' + expect(test).toEqual(expected) + }) + + test('dedent align should also work with tabulation', () => { + const test = dedent(` + """ + \tMy text + Another line + Another line again + """ + `) + + const expected = ' \tMy text\n Another line\n Another line again' + expect(test).toEqual(expected) + }) + + test('dedent should not edit content if less than lines', () => { + const test = dedent(` + `) + + const expected = '\n ' + expect(test).toEqual(expected) + }) + + test('dedent align should ignore last and first lines', () => { + const test = dedent(`Some first content + My text + Another line + Another line again + Some last content`) + + const expected = 'My text\n Another line\nAnother line again' + expect(test).toEqual(expected) + }) + + test('dedent align with """ should ignore two last and two first lines', () => { + const test = dedent(`Some content + """Some other content + \tMy text + Another line + Another line again + """ Some end content + Some end other content`) + + const expected = ' \tMy text\n Another line\n Another line again' + expect(test).toEqual(expected) + }) + + test('dedent works without parenthesis', () => { + const test = dedent` + My text + Another line + Another line again + ` + + const expected = 'My text\n Another line\nAnother line again' + expect(test).toEqual(expected) + }) +}) diff --git a/tests/extensions/snapshot/definitions.test.js b/tests/extensions/snapshot/definitions.test.js deleted file mode 100644 index 5da4ade1..00000000 --- a/tests/extensions/snapshot/definitions.test.js +++ /dev/null @@ -1,155 +0,0 @@ -'use strict' - -const helper = require('../definitions_helper') -const definitions = require('../../../src/extensions/snapshot/definitions') - -beforeEach(() => { - definitions.install() -}) - -afterEach(() => { - helper.clearContext() -}) - -test('response match snapshot', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response body should match snapshot') - def.shouldMatch('response body should match snapshot') - - const content = 'test' - - const mock = { - httpApiClient: { - getResponse: jest.fn(() => { - return { body: content } - }), - }, - snapshot: { expectToMatch: jest.fn() }, - } - - def.exec(mock, {}) - expect(mock.snapshot.expectToMatch).toHaveBeenCalledWith(content) -}) - -test('json response match snapshot', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('response json body should match snapshot') - def.shouldMatch('response json body should match snapshot') - - const content = { key1: 'value1', key2: 'value2', key3: 3 } - - const mock = { - state: { populate: (v) => v }, - httpApiClient: { - getResponse: jest.fn(() => { - return { body: content } - }), - }, - snapshot: { expectToMatchJson: jest.fn() }, - } - - const spec = [ - { - field: 'key1', - matcher: 'equals', - value: 'value1', - }, - ] - - def.exec(mock, { hashes: () => spec }) - expect(mock.snapshot.expectToMatchJson).toHaveBeenCalledWith(content, spec) -}) - -test('stdout/stderr match snapshot', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(stderr|stdout) output should match snapshot') - def.shouldMatch('stdout output should match snapshot') - def.shouldMatch('stderr output should match snapshot') - - const content = 'test' - - const mock = { - cli: { getOutput: jest.fn(() => content) }, - snapshot: { expectToMatch: jest.fn() }, - } - - def.exec(mock, {}) - expect(mock.snapshot.expectToMatch).toHaveBeenCalledWith(content) -}) - -test('stdout/stderr with json output match snapshot', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('(stderr|stdout) json output should match snapshot') - def.shouldMatch('stdout json output should match snapshot') - def.shouldMatch('stderr json output should match snapshot') - - const content = { key1: 'value1', key2: 'value2', key3: 3 } - const content_as_string = JSON.stringify(content) - - const mock = { - state: { populate: (v) => v }, - cli: { getOutput: jest.fn(() => content_as_string) }, - snapshot: { expectToMatchJson: jest.fn() }, - } - - const spec = [ - { - field: 'key1', - matcher: 'equals', - value: 'value1', - }, - ] - - def.exec(mock, 'stdout', { hashes: () => spec }) - expect(mock.snapshot.expectToMatchJson).toHaveBeenCalledWith(content, spec) -}) - -test('file match snapshot', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('file (.+) should match snapshot') - def.shouldMatch('file somefile.txt should match snapshot') - def.shouldNotMatch('file should match snapshot') - - const content = 'test' - - const mock = { - cli: { getCwd: jest.fn(() => '') }, - fileSystem: { getFileContent: jest.fn(() => Promise.resolve(content)) }, - snapshot: { expectToMatch: jest.fn() }, - } - - return def.exec(mock, {}).then(() => { - expect(mock.snapshot.expectToMatch).toHaveBeenCalledWith(content) - }) -}) - -test('json file match snapshot', () => { - const context = helper.getContext() // Extension context - const def = context.getDefinitionByMatcher('json file (.+) content should match snapshot') - def.shouldMatch('json file somefile.txt content should match snapshot') - def.shouldNotMatch('json file content should match snapshot') - - const content = { key1: 'value1', key2: 'value2', key3: 3 } - const content_as_string = JSON.stringify(content) - - const mock = { - state: { populate: (v) => v }, - cli: { getCwd: jest.fn(() => '') }, - fileSystem: { getFileContent: jest.fn(() => Promise.resolve(content_as_string)) }, - snapshot: { expectToMatchJson: jest.fn() }, - } - - const spec = [ - { - field: 'key1', - matcher: 'equals', - value: 'value1', - }, - ] - - return def.exec(mock, {}, { hashes: () => spec }).then(() => { - expect(mock.snapshot.expectToMatchJson).toHaveBeenCalledWith(content, spec) - }) -}) diff --git a/tests/extensions/snapshot/definitions.test.ts b/tests/extensions/snapshot/definitions.test.ts new file mode 100644 index 00000000..e9400c91 --- /dev/null +++ b/tests/extensions/snapshot/definitions.test.ts @@ -0,0 +1,184 @@ +import * as helper from '../definitions_helper' +import * as definitions from '../../../src/extensions/snapshot/definitions' +import { createSandbox, SinonStub } from 'sinon' +import { snapshot } from '../../../src/extensions/snapshot' +import { httpApiClient } from '../../../src/extensions/http_api' +import * as fileSystem from '../../../src/extensions/file_system/file_system' +import { cli } from '../../../src/extensions/cli' + +describe('extensions > snapshot > definitions', () => { + const sandbox = createSandbox() + let expectToMatchStub: SinonStub, + expectToMatchJsonStub: SinonStub, + getResponseStub: SinonStub, + getFileContentStub: SinonStub, + getCwdStub: SinonStub, + getOutputStub: SinonStub + + beforeAll(() => { + expectToMatchStub = sandbox.stub(snapshot, 'expectToMatch') + expectToMatchJsonStub = sandbox.stub(snapshot, 'expectToMatchJson') + getResponseStub = sandbox.stub(httpApiClient, 'getResponse') + getFileContentStub = sandbox.stub(fileSystem, 'getFileContent') + getCwdStub = sandbox.stub(cli, 'getCwd') + getOutputStub = sandbox.stub(cli, 'getOutput') + }) + beforeEach(() => { + definitions.install() + }) + + afterEach(() => { + helper.clearContext() + sandbox.reset() + }) + afterAll(() => sandbox.restore()) + + test('response match snapshot', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('response body should match snapshot') + def.shouldMatch('response body should match snapshot') + + const content = 'test' + + const mock = { + httpApiClient: { + getResponse: getResponseStub, + }, + snapshot: { expectToMatch: expectToMatchStub }, + } + getResponseStub.returns({ body: content }) + + def.exec(mock, {}) + expect(mock.snapshot.expectToMatch.calledWithExactly(content)).toBeTruthy() + }) + + test('json response match snapshot', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('response json body should match snapshot') + def.shouldMatch('response json body should match snapshot') + + const content = { key1: 'value1', key2: 'value2', key3: 3 } + + const mock = { + state: { populate: (v: string): string => v }, + httpApiClient: { + getResponse: getResponseStub, + }, + snapshot: { expectToMatchJson: expectToMatchJsonStub }, + } + getResponseStub.returns({ body: content }) + + const spec = [ + { + field: 'key1', + matcher: 'equals', + value: 'value1', + }, + ] + + def.exec(mock, { hashes: () => spec }) + expect(mock.snapshot.expectToMatchJson.calledWithExactly(content, spec)).toBeTruthy() + }) + + test('stdout/stderr match snapshot', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('(stderr|stdout) output should match snapshot') + def.shouldMatch('stdout output should match snapshot') + def.shouldMatch('stderr output should match snapshot') + + const content = 'test' + + const mock = { + cli: { getOutput: getOutputStub }, + snapshot: { expectToMatch: expectToMatchStub }, + } + getOutputStub.returns(content) + + def.exec(mock, {}) + expect(mock.snapshot.expectToMatch.calledWithExactly(content)).toBeTruthy() + }) + + test('stdout/stderr with json output match snapshot', () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher( + '(stderr|stdout) json output should match snapshot' + ) + def.shouldMatch('stdout json output should match snapshot') + def.shouldMatch('stderr json output should match snapshot') + + const content = { key1: 'value1', key2: 'value2', key3: 3 } + const content_as_string = JSON.stringify(content) + + const mock = { + state: { populate: (v: string): string => v }, + cli: { getOutput: getOutputStub }, + snapshot: { expectToMatchJson: expectToMatchJsonStub }, + } + getOutputStub.returns(content_as_string) + + const spec = [ + { + field: 'key1', + matcher: 'equals', + value: 'value1', + }, + ] + + def.exec(mock, 'stdout', { hashes: () => spec }) + expect(mock.snapshot.expectToMatchJson.calledWithExactly(content, spec)).toBeTruthy() + }) + + test('file match snapshot', async () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('file (.+) should match snapshot') + def.shouldMatch('file somefile.txt should match snapshot') + def.shouldNotMatch('file should match snapshot') + + const content = 'test' + + const mock = { + cli: { getCwd: getCwdStub }, + fileSystem: { getFileContent: getFileContentStub }, + snapshot: { expectToMatch: expectToMatchStub }, + } + getFileContentStub.resolves(content) + getCwdStub.returns('') + + await def.exec(mock, {}) + expect(mock.snapshot.expectToMatch.calledWithExactly(content)).toBeTruthy() + }) + + test('json file match snapshot', async () => { + const context = helper.getContext() // Extension context + const def = context.getDefinitionByMatcher('json file (.+) content should match snapshot') + def.shouldMatch('json file somefile.txt content should match snapshot') + def.shouldNotMatch('json file content should match snapshot') + + const content = { key1: 'value1', key2: 'value2', key3: 3 } + const content_as_string = JSON.stringify(content) + + const mock = { + state: { populate: (v: string): string => v }, + cli: { getCwd: getCwdStub }, + fileSystem: { + getFileContent: getFileContentStub, + }, + snapshot: { expectToMatchJson: expectToMatchJsonStub }, + } + getCwdStub.returns('') + getFileContentStub.resolves(content_as_string) + + const spec = [ + { + field: 'key1', + matcher: 'equals', + value: 'value1', + }, + ] + + await def.exec(mock, {}, { hashes: () => spec }) + expect(mock.snapshot.expectToMatchJson.calledWithExactly(content, spec)).toBeTruthy() + }) +}) diff --git a/tests/extensions/snapshot/extension.test.js b/tests/extensions/snapshot/extension.test.js index 3e5fb598..bbb3ec18 100644 --- a/tests/extensions/snapshot/extension.test.js +++ b/tests/extensions/snapshot/extension.test.js @@ -1,274 +1,267 @@ -'use strict' +let fs = require('fs-extra') -const fs = require('fs-extra') - -const Snapshot = require('../../../src/extensions/snapshot/extension') +const { snapshot, Snapshot } = require('../../../src/extensions/snapshot') const clean = require('../../../src/extensions/snapshot/clean') const fixtures = require('./fixtures') -beforeAll(() => { - // - - fs.statSync = jest.fn() - fs.readFileSync = jest.fn() - fs.writeFileSync = jest.fn() - fs.mkdirsSync = jest.fn() - - fs.readFileSync.mockImplementation((file) => { - if (file === fixtures.featureFile1) return fixtures.featureFileContent1 - if (file === fixtures.featureFile1WithPropertyMatchers) return fixtures.featureFileContent1 - if (file === fixtures.featureFile1NotExists) return fixtures.featureFileContent1 - if (file === fixtures.featureFile1And2) return fixtures.featureFileContent1 - if (file === fixtures.featureFile1With2SnapshotsInAScenario) - return fixtures.featureFileContent1 - if (file === fixtures.featureFile1With3SnapshotsInAScenario) - return fixtures.featureFileContent1 - if (file === fixtures.featureFileMultilineString) return fixtures.featureFileContent1 - if (file === fixtures.snapshotFile1) return fixtures.snapshotFileContent1 - if (file === fixtures.snapshotFile1WithPropertyMatchers) - return fixtures.snapshotFileContent1WithPropertyMatchers - if (file === fixtures.snapshotFile1And2) return fixtures.snapshotFileContent1And2 - if (file === fixtures.snapshotFile1With2SnapshotsInAScenario) - return fixtures.snapshotFileContent1 - if (file === fixtures.snapshotFile1With3SnapshotsInAScenario) - return fixtures.snapshotFileContent1With3SnapshotsInAScenario - if (file === fixtures.snapshotFileMultilineString) - return fixtures.snapshotFileContentMultilineString - throw new Error(`Unexpected call to readFileSync with file ${file}`) - }) - - fs.writeFileSync.mockImplementation(() => {}) - - fs.statSync.mockImplementation((file) => { - if (file === fixtures.snapshotFile1) return {} - if (file === fixtures.snapshotFile1WithPropertyMatchers) return {} - if (file === fixtures.snapshotFile1NotExists) return null - if (file === fixtures.snapshotFile1And2) return {} - if (file === fixtures.snapshotFile1With2SnapshotsInAScenario) return {} - if (file === fixtures.snapshotFile1With3SnapshotsInAScenario) return {} - if (file === fixtures.snapshotFileMultilineString) return {} - throw new Error(`Unexpected call to statSync with file ${file}`) +jest.mock('fs-extra') +describe('extensions > snapshot > extension', () => { + beforeAll(() => { + fs.statSync = jest.fn() + fs.readFileSync = jest.fn() + fs.writeFileSync = jest.fn() + fs.mkdirsSync = jest.fn() + + fs.readFileSync.mockImplementation((file) => { + if (file === fixtures.featureFile1) return fixtures.featureFileContent1 + if (file === fixtures.featureFile1WithPropertyMatchers) + return fixtures.featureFileContent1 + if (file === fixtures.featureFile1NotExists) return fixtures.featureFileContent1 + if (file === fixtures.featureFile1And2) return fixtures.featureFileContent1 + if (file === fixtures.featureFile1With2SnapshotsInAScenario) + return fixtures.featureFileContent1 + if (file === fixtures.featureFile1With3SnapshotsInAScenario) + return fixtures.featureFileContent1 + if (file === fixtures.featureFileMultilineString) return fixtures.featureFileContent1 + if (file === fixtures.snapshotFile1) return fixtures.snapshotFileContent1 + if (file === fixtures.snapshotFile1WithPropertyMatchers) + return fixtures.snapshotFileContent1WithPropertyMatchers + if (file === fixtures.snapshotFile1And2) return fixtures.snapshotFileContent1And2 + if (file === fixtures.snapshotFile1With2SnapshotsInAScenario) + return fixtures.snapshotFileContent1 + if (file === fixtures.snapshotFile1With3SnapshotsInAScenario) + return fixtures.snapshotFileContent1With3SnapshotsInAScenario + if (file === fixtures.snapshotFileMultilineString) + return fixtures.snapshotFileContentMultilineString + throw new Error(`Unexpected call to readFileSync with file ${file}`) + }) + + fs.writeFileSync.mockImplementation(() => {}) + + fs.statSync.mockImplementation((file) => { + if (file === fixtures.snapshotFile1) return {} + if (file === fixtures.snapshotFile1WithPropertyMatchers) return {} + if (file === fixtures.snapshotFile1NotExists) return null + if (file === fixtures.snapshotFile1And2) return {} + if (file === fixtures.snapshotFile1With2SnapshotsInAScenario) return {} + if (file === fixtures.snapshotFile1With3SnapshotsInAScenario) return {} + if (file === fixtures.snapshotFileMultilineString) return {} + throw new Error(`Unexpected call to statSync with file ${file}`) + }) }) -}) - -afterEach(() => { - fs.statSync.mockClear() - fs.readFileSync.mockClear() - fs.writeFileSync.mockClear() - fs.mkdirsSync.mockClear() - clean.resetReferences() -}) + afterEach(() => { + fs.statSync.mockClear() + fs.readFileSync.mockClear() + fs.writeFileSync.mockClear() + fs.mkdirsSync.mockClear() -test("expectToMatch shouldn't throw an error if snapshot matches", () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1 - snapshot.scenarioLine = 3 + clean.resetReferences() + snapshot.reset() + }) + afterAll(() => { + jest.restoreAllMocks() + }) - snapshot.expectToMatch(fixtures.value1) + test("expectToMatch shouldn't throw an error if snapshot matches", () => { + snapshot.featureFile = fixtures.featureFile1 + snapshot.scenarioLine = 3 - expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.featureFile1) - expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.snapshotFile1) - expect(fs.statSync).toHaveBeenCalledWith(fixtures.snapshotFile1) + snapshot.expectToMatch(fixtures.value1) - expect(fs.readFileSync.mock.calls.length).toBe(2) - expect(fs.writeFileSync.mock.calls.length).toBe(0) - expect(fs.statSync.mock.calls.length).toBe(1) -}) + expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.featureFile1) + expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.snapshotFile1) + expect(fs.statSync).toHaveBeenCalledWith(fixtures.snapshotFile1) -test('expectToMatch should throw an error if scenario not found in file', () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1 - snapshot.scenarioLine = 4 + expect(fs.readFileSync.mock.calls.length).toBe(2) + expect(fs.writeFileSync.mock.calls.length).toBe(0) + expect(fs.statSync.mock.calls.length).toBe(1) + }) - expect(() => snapshot.expectToMatch(fixtures.value2)).toThrow( - 'Can not do a snapshot. Scenario not found in file ./snapshot1.feature on line 4' - ) - expect(fs.readFileSync.mock.calls.length).toBe(1) -}) + test('expectToMatch should throw an error if scenario not found in file', () => { + snapshot.featureFile = fixtures.featureFile1 + snapshot.scenarioLine = 4 -test("expectToMatch should throw an error if snapshot doesn't match", () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1 - snapshot.scenarioLine = 3 + expect(() => snapshot.expectToMatch(fixtures.value2)).toThrow( + 'Can not do a snapshot. Scenario not found in file ./snapshot1.feature on line 4' + ) + expect(fs.readFileSync.mock.calls.length).toBe(1) + }) - expect(() => snapshot.expectToMatch(fixtures.value2)).toThrow(fixtures.diffErrorValue1VsValue2) - expect(fs.readFileSync.mock.calls.length).toBe(2) -}) + test("expectToMatch should throw an error if snapshot doesn't match", () => { + snapshot.featureFile = fixtures.featureFile1 + snapshot.scenarioLine = 3 -test("expectToMatch should write a snapshot if it doesn't exists", () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1 - snapshot.scenarioLine = 6 - - snapshot.expectToMatch(fixtures.value2) - expect(fs.writeFileSync).toHaveBeenCalledWith( - fixtures.snapshotFile1, - fixtures.snapshotFileContent1And2 - ) - expect(fs.writeFileSync.mock.calls.length).toBe(1) -}) + expect(() => snapshot.expectToMatch(fixtures.value2)).toThrow( + fixtures.diffErrorValue1VsValue2 + ) + expect(fs.readFileSync.mock.calls.length).toBe(2) + }) -test('expectToMatch should not write a snapshot and throw a diff error when preventSnapshotsCreation is true', () => { - const snapshot = Snapshot({ preventSnapshotsCreation: true }) - snapshot.featureFile = fixtures.featureFile1 - snapshot.scenarioLine = 6 + test("expectToMatch should write a snapshot if it doesn't exists", () => { + snapshot.featureFile = fixtures.featureFile1 + snapshot.scenarioLine = 6 - expect(() => snapshot.expectToMatch(fixtures.value2)).toThrow( - "The snapshot does not exist and won't be created." - ) - expect(fs.writeFileSync).not.toHaveBeenCalled() -}) + snapshot.expectToMatch(fixtures.value2) + expect(fs.writeFileSync).toHaveBeenCalledWith( + fixtures.snapshotFile1, + fixtures.snapshotFileContent1And2 + ) + expect(fs.writeFileSync.mock.calls.length).toBe(1) + }) -test("expectToMatch should write a snapshot file if it doesn't exists", () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1NotExists - snapshot.scenarioLine = 3 - - snapshot.expectToMatch(fixtures.value1) - expect(fs.writeFileSync).toHaveBeenCalledWith( - fixtures.snapshotFile1NotExists, - fixtures.snapshotFileContent1 - ) - expect(fs.writeFileSync.mock.calls.length).toBe(1) -}) + test('expectToMatch should not write a snapshot and throw a diff error when preventSnapshotsCreation is true', () => { + const snapshot = new Snapshot({ preventSnapshotsCreation: true }) + snapshot.featureFile = fixtures.featureFile1 + snapshot.scenarioLine = 6 -test('expectToMatch should work even if two scenarios have the same name', () => { - const snapshot = Snapshot() + expect(() => snapshot.expectToMatch(fixtures.value2)).toThrow( + "The snapshot does not exist and won't be created." + ) + expect(fs.writeFileSync).not.toHaveBeenCalled() + }) - snapshot.featureFile = fixtures.featureFile1And2 - snapshot.scenarioLine = 9 - snapshot.expectToMatch(fixtures.value3) + test("expectToMatch should write a snapshot file if it doesn't exists", () => { + snapshot.featureFile = fixtures.featureFile1NotExists + snapshot.scenarioLine = 3 - expect(fs.writeFileSync).toHaveBeenCalledWith( - fixtures.snapshotFile1And2, - fixtures.snapshotFileContent1And2And3 - ) - expect(fs.writeFileSync.mock.calls.length).toBe(1) -}) + snapshot.expectToMatch(fixtures.value1) + expect(fs.writeFileSync).toHaveBeenCalledWith( + fixtures.snapshotFile1NotExists, + fixtures.snapshotFileContent1 + ) + expect(fs.writeFileSync.mock.calls.length).toBe(1) + }) -test('expectToMatch should work even if there is multiple snapshots in a scenario', () => { - const snapshot = Snapshot() + test('expectToMatch should work even if two scenarios have the same name', () => { + snapshot.featureFile = fixtures.featureFile1And2 + snapshot.scenarioLine = 9 + snapshot.expectToMatch(fixtures.value3) - snapshot.featureFile = fixtures.featureFile1With2SnapshotsInAScenario - snapshot.scenarioLine = 3 - snapshot.expectToMatch(fixtures.value1) - snapshot.expectToMatch(fixtures.value2) + expect(fs.writeFileSync).toHaveBeenCalledWith( + fixtures.snapshotFile1And2, + fixtures.snapshotFileContent1And2And3 + ) + expect(fs.writeFileSync.mock.calls.length).toBe(1) + }) - expect(fs.writeFileSync).toHaveBeenCalledWith( - fixtures.snapshotFile1With2SnapshotsInAScenario, - fixtures.snapshotFileContent1With2SnapshotsInAScenario - ) - expect(fs.writeFileSync.mock.calls.length).toBe(1) -}) + test('expectToMatch should work even if there is multiple snapshots in a scenario', () => { + snapshot.featureFile = fixtures.featureFile1With2SnapshotsInAScenario + snapshot.scenarioLine = 3 + snapshot.expectToMatch(fixtures.value1) + snapshot.expectToMatch(fixtures.value2) + + expect(fs.writeFileSync).toHaveBeenCalledWith( + fixtures.snapshotFile1With2SnapshotsInAScenario, + fixtures.snapshotFileContent1With2SnapshotsInAScenario + ) + expect(fs.writeFileSync.mock.calls.length).toBe(1) + }) -test('expectToMatch should update snapshot if given update option', () => { - const snapshot = Snapshot({ updateSnapshots: true }) + test('expectToMatch should update snapshot if given update option', () => { + const snapshot = new Snapshot({ updateSnapshots: true }) - snapshot.featureFile = fixtures.featureFile1 - snapshot.scenarioLine = 3 - snapshot.expectToMatch(fixtures.value2) + snapshot.featureFile = fixtures.featureFile1 + snapshot.scenarioLine = 3 + snapshot.expectToMatch(fixtures.value2) - expect(fs.writeFileSync).toHaveBeenCalledWith( - fixtures.snapshotFile1, - fixtures.snapshotFileContent1WithValue2 - ) - expect(fs.writeFileSync.mock.calls.length).toBe(1) -}) + expect(fs.writeFileSync).toHaveBeenCalledWith( + fixtures.snapshotFile1, + fixtures.snapshotFileContent1WithValue2 + ) + expect(fs.writeFileSync.mock.calls.length).toBe(1) + }) -test("expectToMatch should notify played scenarios snapshots so they don't get removed", () => { - const snapshot = Snapshot({ cleanSnapshots: true }) + test("expectToMatch should notify played scenarios snapshots so they don't get removed", () => { + const snapshot = new Snapshot({ cleanSnapshots: true }) - snapshot.featureFile = fixtures.featureFile1And2 - snapshot.scenarioLine = 3 - snapshot.expectToMatch(fixtures.value1) - clean.cleanSnapshots() + snapshot.featureFile = fixtures.featureFile1And2 + snapshot.scenarioLine = 3 + snapshot.expectToMatch(fixtures.value1) + clean.cleanSnapshots() - expect(fs.writeFileSync).toHaveBeenCalledWith( - fixtures.snapshotFile1And2, - fixtures.snapshotFileContent1 - ) - expect(fs.writeFileSync.mock.calls.length).toBe(1) -}) + expect(fs.writeFileSync).toHaveBeenCalledWith( + fixtures.snapshotFile1And2, + fixtures.snapshotFileContent1 + ) + expect(fs.writeFileSync.mock.calls.length).toBe(1) + }) -test("expectToMatch should notify all played snapshots in scenarios so they don't get removed", () => { - const snapshot = Snapshot({ cleanSnapshots: true }) + test("expectToMatch should notify all played snapshots in scenarios so they don't get removed", () => { + const snapshot = new Snapshot({ cleanSnapshots: true }) - snapshot.featureFile = fixtures.featureFile1With3SnapshotsInAScenario - snapshot.scenarioLine = 3 - snapshot.expectToMatch(fixtures.value1) - snapshot.expectToMatch(fixtures.value2) - clean.cleanSnapshots() + snapshot.featureFile = fixtures.featureFile1With3SnapshotsInAScenario + snapshot.scenarioLine = 3 + snapshot.expectToMatch(fixtures.value1) + snapshot.expectToMatch(fixtures.value2) + clean.cleanSnapshots() - expect(fs.writeFileSync).toHaveBeenCalledWith( - fixtures.snapshotFile1With3SnapshotsInAScenario, - fixtures.snapshotFileContent1With2SnapshotsInAScenario - ) - expect(fs.writeFileSync.mock.calls.length).toBe(1) -}) + expect(fs.writeFileSync).toHaveBeenCalledWith( + fixtures.snapshotFile1With3SnapshotsInAScenario, + fixtures.snapshotFileContent1With2SnapshotsInAScenario + ) + expect(fs.writeFileSync.mock.calls.length).toBe(1) + }) -test('expectToMatchJson should success if there are no json field specified for matchers', () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1 - snapshot.scenarioLine = 3 + test('expectToMatchJson should success if there are no json field specified for matchers', () => { + snapshot.featureFile = fixtures.featureFile1 + snapshot.scenarioLine = 3 - snapshot.expectToMatchJson(fixtures.value1, []) + snapshot.expectToMatchJson(fixtures.value1, []) - expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.featureFile1) - expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.snapshotFile1) - expect(fs.statSync).toHaveBeenCalledWith(fixtures.snapshotFile1) + expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.featureFile1) + expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.snapshotFile1) + expect(fs.statSync).toHaveBeenCalledWith(fixtures.snapshotFile1) - expect(fs.readFileSync.mock.calls.length).toBe(2) - expect(fs.writeFileSync.mock.calls.length).toBe(0) - expect(fs.statSync.mock.calls.length).toBe(1) -}) + expect(fs.readFileSync.mock.calls.length).toBe(2) + expect(fs.writeFileSync.mock.calls.length).toBe(0) + expect(fs.statSync.mock.calls.length).toBe(1) + }) -test('expectToMatchJson should success if there is a json field specified and field matches', () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1WithPropertyMatchers - snapshot.scenarioLine = 3 + test('expectToMatchJson should success if there is a json field specified and field matches', () => { + snapshot.featureFile = fixtures.featureFile1WithPropertyMatchers + snapshot.scenarioLine = 3 - const propertiesMatchers = [{ field: 'key2', matcher: 'type', value: 'string' }] - snapshot.expectToMatchJson(fixtures.value1, propertiesMatchers) + const propertiesMatchers = [{ field: 'key2', matcher: 'type', value: 'string' }] + snapshot.expectToMatchJson(fixtures.value1, propertiesMatchers) - expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.featureFile1WithPropertyMatchers) - expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.snapshotFile1WithPropertyMatchers) - expect(fs.statSync).toHaveBeenCalledWith(fixtures.snapshotFile1WithPropertyMatchers) + expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.featureFile1WithPropertyMatchers) + expect(fs.readFileSync).toHaveBeenCalledWith(fixtures.snapshotFile1WithPropertyMatchers) + expect(fs.statSync).toHaveBeenCalledWith(fixtures.snapshotFile1WithPropertyMatchers) - expect(fs.readFileSync.mock.calls.length).toBe(2) - expect(fs.writeFileSync.mock.calls.length).toBe(0) - expect(fs.statSync.mock.calls.length).toBe(1) -}) + expect(fs.readFileSync.mock.calls.length).toBe(2) + expect(fs.writeFileSync.mock.calls.length).toBe(0) + expect(fs.statSync.mock.calls.length).toBe(1) + }) -test("expectToMatchJson should throw an error if a field doesn't match it's matcher", () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1WithPropertyMatchers - snapshot.scenarioLine = 3 + test("expectToMatchJson should throw an error if a field doesn't match it's matcher", () => { + snapshot.featureFile = fixtures.featureFile1WithPropertyMatchers + snapshot.scenarioLine = 3 - const propertiesMatchers = [{ field: 'key2', matcher: 'type', value: 'string' }] + const propertiesMatchers = [{ field: 'key2', matcher: 'type', value: 'string' }] - expect(() => - snapshot.expectToMatchJson(fixtures.value1WithError, propertiesMatchers) - ).toThrowError("Property 'key2' (2) type is not 'string': expected 2 to be a string") -}) + expect(() => + snapshot.expectToMatchJson(fixtures.value1WithError, propertiesMatchers) + ).toThrowError("Property 'key2' (2) type is not 'string': expected 2 to be a string") + }) -test('expectToMatchJson should throw an error if a property matcher changes', () => { - const snapshot = Snapshot() - snapshot.featureFile = fixtures.featureFile1WithPropertyMatchers - snapshot.scenarioLine = 3 + test('expectToMatchJson should throw an error if a property matcher changes', () => { + snapshot.featureFile = fixtures.featureFile1WithPropertyMatchers + snapshot.scenarioLine = 3 - const propertiesMatchers = [{ field: 'key2', matcher: 'type', value: 'number' }] + const propertiesMatchers = [{ field: 'key2', matcher: 'type', value: 'number' }] - expect(() => snapshot.expectToMatchJson(fixtures.value1WithError, propertiesMatchers)).toThrow( - fixtures.diffErrorFile1WithPropertyMatchers - ) -}) + expect(() => + snapshot.expectToMatchJson(fixtures.value1WithError, propertiesMatchers) + ).toThrow(fixtures.diffErrorFile1WithPropertyMatchers) + }) -test('expectToMatch should handle multiline content correctly', () => { - const snapshot = Snapshot({ cleanSnapshots: true }) + test('expectToMatch should handle multiline content correctly', () => { + const snapshot = new Snapshot({ cleanSnapshots: true }) - snapshot.featureFile = fixtures.featureFileMultilineString - snapshot.scenarioLine = 3 - expect(snapshot.expectToMatch(fixtures.multilineValue)).toBeUndefined() - clean.cleanSnapshots() + snapshot.featureFile = fixtures.featureFileMultilineString + snapshot.scenarioLine = 3 + expect(snapshot.expectToMatch(fixtures.multilineValue)).toBeUndefined() + clean.cleanSnapshots() + }) }) diff --git a/tests/extensions/snapshot/file_system.test.ts b/tests/extensions/snapshot/file_system.test.ts new file mode 100644 index 00000000..7f0e1fc9 --- /dev/null +++ b/tests/extensions/snapshot/file_system.test.ts @@ -0,0 +1,91 @@ +import * as fileSystem from '../../../src/extensions/snapshot/file_system' +import fs from 'fs-extra' +import { createSandbox, SinonStub, stub } from 'sinon' +import path from 'path' + +describe('extensions > snapshot > file_system', () => { + describe('getFileContent', () => { + let readFileSyncStub: SinonStub + + beforeAll(() => { + readFileSyncStub = stub(fs, 'readFileSync') + }) + afterAll(() => readFileSyncStub.restore()) + + it('getFileContent read and decode a file sync', () => { + const filename = 'test.json' + const content = 'é~se' + + readFileSyncStub.withArgs(filename).returns(Buffer.from(content, 'utf8')) + + expect(fileSystem.getFileContent(filename)).toBe(content) + expect(readFileSyncStub.calledOnce).toBeTruthy() + }) + }) + + describe('writeFileContent', () => { + const sandbox = createSandbox() + const folder = 'folder1/folder2' + let createDirectoryStub: SinonStub, dirnameStub: SinonStub, writeFileSyncStub: SinonStub + + beforeAll(() => { + createDirectoryStub = sandbox.stub(fileSystem, 'createDirectory') + dirnameStub = sandbox.stub(path, 'dirname') + writeFileSyncStub = sandbox.stub(fs, 'writeFileSync') + }) + + afterEach(() => sandbox.reset()) + afterAll(() => sandbox.restore()) + + it("create directory if it doesn't exists", () => { + const file = 'folder1/folder2/test.json' + const content = 'test' + + dirnameStub.returns(folder) + + fileSystem.writeFileContent(file, content) + expect(dirnameStub.calledWithExactly(file)).toBeTruthy() + expect(createDirectoryStub.calledWithExactly(folder)).toBeTruthy() + expect(writeFileSyncStub.calledWithExactly(file, content)).toBeTruthy() + }) + + it("don't create directory if explicitly not asked to", () => { + const file = 'folder1/folder2/test.json' + const content = 'test' + + fileSystem.writeFileContent(file, content, { createDir: false }) + expect(dirnameStub.notCalled).toBeTruthy() + expect(createDirectoryStub.notCalled).toBeTruthy() + expect(writeFileSyncStub.calledWithExactly(file, content)).toBeTruthy() + }) + }) + + describe('getFileInfo', () => { + let statSyncStub: SinonStub + + beforeAll(() => { + statSyncStub = stub(fs, 'statSync') + }) + afterEach(() => statSyncStub.reset()) + afterAll(() => statSyncStub.restore()) + + it("getFileInfo returns null if file doesn't exists", () => { + const file = './dontexist.file' + + statSyncStub.returns(null) + + expect(fileSystem.getFileInfo(file)).toBe(null) + expect(statSyncStub.calledWithExactly(file)).toBeTruthy() + }) + + it('getFileInfo returns file infos if it exists', () => { + const file = './exist.file' + const infos = { key1: 'value1' } + + statSyncStub.returns(infos) + + expect(fileSystem.getFileInfo(file)).toBe(infos) + expect(statSyncStub.calledWithExactly(file)).toBeTruthy() + }) + }) +}) diff --git a/tests/extensions/snapshot/fixtures.js b/tests/extensions/snapshot/fixtures.js index 95d1a3b2..c25cd609 100644 --- a/tests/extensions/snapshot/fixtures.js +++ b/tests/extensions/snapshot/fixtures.js @@ -1,4 +1,4 @@ -const dedent = require('../../../src/extensions/snapshot/dedent') +const { dedent } = require('../../../src/extensions/snapshot/dedent') exports.featureFileContent1 = dedent` """ @@ -150,7 +150,13 @@ exports.diffErrorFile1WithPropertyMatchers = dedent` \u001b[2m }\u001b[22m ` -exports.value1 = { key1: 'value1', key2: 'value2', key3: 'value3', key4: 'value4', key5: 'value5' } +exports.value1 = { + key1: 'value1', + key2: 'value2', + key3: 'value3', + key4: 'value4', + key5: 'value5', +} exports.value1WithError = { key1: 'value1', key2: 2, @@ -158,10 +164,25 @@ exports.value1WithError = { key4: 'value4', key5: 'value5', } -exports.value2 = { key1: 'value1', key2: 'value2', key3: 'value8', key4: 'value4', key5: 'value5' } -exports.value3 = { key1: 'value1', key2: 'value2', key3: 'value9', key4: 'value4', key5: 'value5' } +exports.value2 = { + key1: 'value1', + key2: 'value2', + key3: 'value8', + key4: 'value4', + key5: 'value5', +} +exports.value3 = { + key1: 'value1', + key2: 'value2', + key3: 'value9', + key4: 'value4', + key5: 'value5', +} exports.multilineValue = { - content: { text: 'i am a text', 'long text': 'I\r\n am \r\n a\r\n long\r\n text' }, + content: { + text: 'i am a text', + 'long text': 'I\r\n am \r\n a\r\n long\r\n text', + }, } exports.featureFile1 = './snapshot1.feature' diff --git a/tests/extensions/snapshot/fs.test.js b/tests/extensions/snapshot/fs.test.js deleted file mode 100644 index 9e30800f..00000000 --- a/tests/extensions/snapshot/fs.test.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict' - -const fileSystem = require('../../../src/extensions/snapshot/fs') -const fs = require('fs-extra') - -test('getFileContent read and decode a file sync', () => { - const filename = 'test.json' - const content = 'é~se' - - fs.readFileSync = jest.fn() - fs.readFileSync.mockReturnValueOnce(Buffer.from(content, 'utf8')) - - expect(fileSystem.getFileContent(filename)).toBe(content) - expect(fs.readFileSync.mock.calls.length).toBe(1) - expect(fs.readFileSync).toHaveBeenCalledWith(filename) -}) - -test("writeFileContent create directory if it doesn't exists", () => { - const file = 'folder1/folder2/test.json' - const folder = 'folder1/folder2' - const content = 'test' - - fileSystem.createDirectory = jest.fn() - fs.writeFileSync = jest.fn() - - fileSystem.writeFileContent(file, content) - expect(fileSystem.createDirectory.mock.calls.length).toBe(1) - expect(fileSystem.createDirectory).toHaveBeenCalledWith(folder) - - expect(fs.writeFileSync.mock.calls.length).toBe(1) - expect(fs.writeFileSync).toHaveBeenCalledWith(file, content) -}) - -test("writeFileContent don't create directory if explicitly not asked to", () => { - const file = 'folder1/folder2/test.json' - const content = 'test' - - fileSystem.createDirectory = jest.fn() - fs.writeFileSync = jest.fn() - - fileSystem.writeFileContent(file, content, { createDir: false }) - expect(fileSystem.createDirectory.mock.calls.length).toBe(0) - - expect(fs.writeFileSync.mock.calls.length).toBe(1) - expect(fs.writeFileSync).toHaveBeenCalledWith(file, content) -}) - -test("getFileInfo returns null if file doesn't exists", () => { - const file = './dontexist.file' - - const statSync = fs.statSync - fs.statSync = jest.fn() - fs.statSync.mockImplementationOnce(statSync) - - expect(fileSystem.getFileInfo(file)).toBe(null) - expect(fs.statSync.mock.calls.length).toBe(1) - expect(fs.statSync).toHaveBeenCalledWith(file) -}) - -test('getFileInfo returns file infos if it exists', () => { - const file = './exist.file' - const infos = { key1: 'value1' } - - fs.statSync = jest.fn() - fs.statSync.mockReturnValueOnce(infos) - - expect(fileSystem.getFileInfo(file)).toBe(infos) - expect(fs.statSync.mock.calls.length).toBe(1) - expect(fs.statSync).toHaveBeenCalledWith(file) -}) diff --git a/tests/extensions/snapshot/snapshot.test.js b/tests/extensions/snapshot/snapshot.test.js deleted file mode 100644 index f6b79cf7..00000000 --- a/tests/extensions/snapshot/snapshot.test.js +++ /dev/null @@ -1,412 +0,0 @@ -'use strict' - -const snapshot = require('../../../src/extensions/snapshot/snapshot') -const fileSystem = require('../../../src/extensions/snapshot/fs') -const dedent = require('../../../src/extensions/snapshot/dedent') - -const { diff } = require('jest-diff') - -jest.mock('jest-diff', () => ({ diff: jest.fn() })) - -test('parseSnapshotFile should parse snapshot file content', () => { - const content = dedent` - """ - \n - exports[\`scenario 1 1.1\`] = \`some content\`; - - exports[\`scenario 2 1.1\`] = \`another content\`;\n - """ - ` - - const expected = { - ['scenario 1 1.1']: 'some content', - ['scenario 2 1.1']: 'another content', - } - - expect(snapshot.parseSnapshotFile(content)).toEqual(expected) -}) - -test('formatSnapshotFile should format snapshot file content', () => { - const content = { - ['scenario 1 1.1']: 'some content', - ['scenario 2 1.1']: 'another content', - } - - const expected = dedent` - """ - - - exports[\`scenario 1 1.1\`] = \`some content\`; - - exports[\`scenario 2 1.1\`] = \`another content\`; - - """ - ` - - expect(snapshot.formatSnapshotFile(content)).toEqual(expected) -}) - -test('formatSnapshotFile should format snapshot file content, sort by keys and escape back ticks', () => { - const content = { - ['scenario` 1 1.1']: 'some` content`', - ['scenario 2 1.1']: 'another content', - } - - const expected = dedent` - """ - - - exports[\`scenario 2 1.1\`] = \`another content\`; - - exports[\`scenario\\\` 1 1.1\`] = \`some\\\` content\\\`\`; - - """ - ` - - expect(snapshot.formatSnapshotFile(content)).toEqual(expected) -}) - -test('formatSnapshotFile should normalize new lines', () => { - const content = { - ['scenario 1 1.1']: 'some content\rnewline content\r\n', - ['scenario 2 1.1']: 'another content', - } - - const expected = dedent` - """ - - - exports[\`scenario 1 1.1\`] = \`some content - newline content - \`; - - exports[\`scenario 2 1.1\`] = \`another content\`; - - """ - ` - - expect(snapshot.formatSnapshotFile(content)).toEqual(expected) -}) - -describe('diff', () => { - afterEach(() => { - diff.mockReset() - }) - - afterAll(() => { - diff.mockRestore() - }) - - const diffOptions = { - expand: false, - colors: true, - aAnnotation: 'Snapshot', - bAnnotation: 'Received', - } - - test('return null when the diff message contains the NO_DIFF_MESSAGE value', () => { - const expectedContent = ` - Object { - "key1": "value1", - } - ` - - const snapshotContent = ` - Object { - "key1": "value1", - } - ` - - const diffResult = `This - has - nothing to do with the - result! - -> Compared values have no visual difference. ¯\\_(ツ)_/¯ - ` - - diff.mockReturnValue(diffResult) - - const diffMessage = snapshot.diff(snapshotContent, expectedContent) - - expect(diffMessage).toBeNull() - - expect(diff).toHaveBeenNthCalledWith(1, snapshotContent, expectedContent, diffOptions) - }) - - test('return a custom diff message when the diff message is not defined', () => { - const expectedContent = 'a' - - const snapshotContent = 'b' - - diff.mockReturnValue(undefined) - - const diffMessage = snapshot.diff(snapshotContent, expectedContent) - - const expectedDiffMessage = '\n\u001b[32m- a\u001b[39m \n \u001b[31m+ b\u001b[39m' - - expect(diffMessage).toEqual(expectedDiffMessage) - }) - - test('return the diff message', () => { - const snapshotContent = ` - Object { - "key1": "value1", - "key2": "value2", - "key3": "value3", - "key4": "value4", - "key5": "value5", - } - ` - - const expectedContent = ` - Object { - "key1": "value1", - "key2": "value6", - "key3": "value3", - "key4": "value7", - "key5": "value5", - } - ` - - const diffResult = ` - """ - - \u001b[32m- Snapshot\u001b[39m - \u001b[31m+ Received\u001b[39m - - - \u001b[2m Object {\u001b[22m - \u001b[2m "key1": "value1",\u001b[22m - \u001b[32m- "key2": "value2",\u001b[39m - \u001b[31m+ "key2": "value6",\u001b[39m - \u001b[2m "key3": "value3",\u001b[22m - \u001b[32m- "key4": "value4",\u001b[39m - \u001b[31m+ "key4": "value7",\u001b[39m - \u001b[2m "key5": "value5",\u001b[22m - \u001b[2m }\u001b[22m - \u001b[2m \u001b[22m - """ - ` - - diff.mockReturnValue(diffResult) - - const expectedDiffMessage = `\n${diffResult}` - - const diffMessage = snapshot.diff(snapshotContent, expectedContent) - - expect(diffMessage).toEqual(expectedDiffMessage) - }) -}) - -test('snapshotsPath returns snapshot path', () => { - const featurePath = 'myfolder/featurefile.feature' - const expectedPath = 'myfolder/__snapshots__/featurefile.feature.snap' - const options = {} - expect(snapshot.snapshotsPath(featurePath, options)).toEqual(expectedPath) -}) - -test('snapshotsPath returns snapshot path with overrided folder and extension', () => { - const featurePath = 'myfolder/featurefile.feature' - const expectedPath = 'myfolder/testsnap/featurefile.feature.sna' - const options = { - snaphotsDirname: 'testsnap', - snapshotsFileExtension: 'sna', - } - expect(snapshot.snapshotsPath(featurePath, options)).toEqual(expectedPath) -}) - -test('writeSnapshotFile should format and write snapshot file', () => { - const file = 'folder1/feature1.feature' - - const scenario1Snapshot = dedent` - """ - Object { - "key1": "value1", - "key2": "value2", - "key3": "value3", - "key4": "value4", - "key5": "value5", - } - """ - ` - const contentToWrite = { 'scenario 1 1.1': scenario1Snapshot } - - const expectedWrite = dedent` - """ - - - exports[\`scenario 1 1.1\`] = \`Object { - "key1": "value1", - "key2": "value2", - "key3": "value3", - "key4": "value4", - "key5": "value5", - }\`; - - """ - ` - - fileSystem.writeFileContent = jest.fn() - - snapshot.writeSnapshotFile(file, contentToWrite) - - expect(fileSystem.writeFileContent.mock.calls.length).toBe(1) - expect(fileSystem.writeFileContent).toHaveBeenCalledWith(file, expectedWrite) -}) - -test('readSnapshotFile should read and parse snapshot file', () => { - const file = 'folder1/feature1.feature' - - const snapshotContent = dedent` - """ - Object { - "key1": "value1", - "key2": "value2", - "key3": "value3", - "key4": "value4", - "key5": "value5", - } - """ - ` - - const fileContent = dedent` - """ - - - exports[\`scenario 1 1.1\`] = \`Object { - "key1": "value1", - "key2": "value2", - "key3": "value3", - "key4": "value4", - "key5": "value5", - }\`; - - """ - ` - - const expectedContent = { 'scenario 1 1.1': snapshotContent } - - fileSystem.getFileInfo = jest.fn() - fileSystem.getFileContent = jest.fn() - - fileSystem.getFileInfo.mockImplementationOnce(() => { - return {} - }) - fileSystem.getFileContent.mockImplementationOnce(() => { - return fileContent - }) - - expect(snapshot.readSnapshotFile(file)).toEqual(expectedContent) - - expect(fileSystem.getFileInfo.mock.calls.length).toBe(1) - expect(fileSystem.getFileInfo).toHaveBeenCalledWith(file) - - expect(fileSystem.getFileContent.mock.calls.length).toBe(1) - expect(fileSystem.getFileContent).toHaveBeenCalledWith(file) -}) - -test("readSnapshotFile should give an empty object if file doesn't exists", () => { - const file = 'folder1/feature1.feature' - - const expectedContent = {} - - fileSystem.getFileInfo = jest.fn() - fileSystem.getFileContent = jest.fn() - - fileSystem.getFileInfo.mockImplementationOnce(() => { - null - }) - - expect(snapshot.readSnapshotFile(file)).toEqual(expectedContent) - - expect(fileSystem.getFileInfo.mock.calls.length).toBe(1) - expect(fileSystem.getFileInfo).toHaveBeenCalledWith(file) - - expect(fileSystem.getFileContent.mock.calls.length).toBe(0) -}) - -test('readSnapshotFile throw an error if no file', () => { - expect(snapshot.readSnapshotFile).toThrowError( - /Missing snapshot file undefined to read snapshots/ - ) -}) - -test('prefixSnapshots give a prefix per scenario name', () => { - const scenarios = [ - { name: 'Scenario 1', line: 10 }, - { name: 'Scenario 2', line: 20 }, - ] - - const expectedResult = { - 10: { name: 'Scenario 1', line: 10, prefix: 'Scenario 1 1' }, - 20: { name: 'Scenario 2', line: 20, prefix: 'Scenario 2 1' }, - } - - expect(snapshot.prefixSnapshots(scenarios)).toEqual(expectedResult) -}) - -test('prefixSnapshots works with duplicate scenarios names', () => { - const scenarios = [ - { name: 'Scenario 1', line: 10 }, - { name: 'Scenario 2', line: 20 }, - { name: 'Scenario 1', line: 30 }, - ] - - const expectedResult = { - 10: { name: 'Scenario 1', line: 10, prefix: 'Scenario 1 1' }, - 20: { name: 'Scenario 2', line: 20, prefix: 'Scenario 2 1' }, - 30: { name: 'Scenario 1', line: 30, prefix: 'Scenario 1 2' }, - } - - expect(snapshot.prefixSnapshots(scenarios)).toEqual(expectedResult) -}) - -test('prefixSnapshots throw an error if no scenarios object', () => { - expect(snapshot.prefixSnapshots).toThrowError(/Scenarios are required to prefix snapshots/) -}) - -test('extractScenarios read scenarios names from a file', () => { - const file = 'folder1/feature1.feature' - - const fileContent = ` - @cli @offline - Feature: yarn CLI - - Scenario: Running an invalid command - When I run command yarn invalid - Then exit code should be 1 - And stderr should contain Command "invalid" not found. - - Scenario: Getting info about installed yarn version - When I run command yarn --version - Then exit code should be 0 - And stdout should match ^[0-9]{1}.[0-9]{1,3}.[0-9]{1,3} - And stderr should be empty - - Scenario: Running an invalid command - When I run command yarn invalid - Then exit code should be 1 - And stderr should contain Command "invalid" not found. - ` - - const expectedContent = [ - { name: 'Running an invalid command', line: 5 }, - { name: 'Getting info about installed yarn version', line: 10 }, - { name: 'Running an invalid command', line: 16 }, - ] - - fileSystem.getFileContent = jest.fn() - fileSystem.getFileContent.mockImplementationOnce(() => { - return fileContent - }) - - expect(snapshot.extractScenarios(file)).toEqual(expectedContent) - - expect(fileSystem.getFileContent.mock.calls.length).toBe(1) - expect(fileSystem.getFileContent).toHaveBeenCalledWith(file) -}) - -test('extractScenarios throw an error if no file', () => { - expect(snapshot.extractScenarios).toThrowError(/Invalid feature file undefined/) - expect(snapshot.extractScenarios).toThrowError(TypeError) -}) diff --git a/tests/extensions/snapshot/snapshot.test.ts b/tests/extensions/snapshot/snapshot.test.ts new file mode 100644 index 00000000..83fc40d0 --- /dev/null +++ b/tests/extensions/snapshot/snapshot.test.ts @@ -0,0 +1,419 @@ +import * as snapshotActions from '../../../src/extensions/snapshot/snapshot_actions' +import * as fileSystem from '../../../src/extensions/snapshot/file_system' +import { dedent } from '../../../src/extensions/snapshot/dedent' + +import { diff } from 'jest-diff' +import { createSandbox, SinonStub, stub } from 'sinon' + +jest.mock('jest-diff') +const diffMock = >(diff) + +describe('extensions > snapshot > snapshot', () => { + test('parseSnapshotFile should parse snapshot file content', () => { + const content = dedent` + """ + \n + exports[\`scenario 1 1.1\`] = \`some content\`; + + exports[\`scenario 2 1.1\`] = \`another content\`;\n + """ + ` + + const expected = { + ['scenario 1 1.1']: 'some content', + ['scenario 2 1.1']: 'another content', + } + + expect(snapshotActions.parseSnapshotFile(content)).toEqual(expected) + }) + + test('formatSnapshotFile should format snapshot file content', () => { + const content = { + ['scenario 1 1.1']: 'some content', + ['scenario 2 1.1']: 'another content', + } + + const expected = dedent` + """ + + + exports[\`scenario 1 1.1\`] = \`some content\`; + + exports[\`scenario 2 1.1\`] = \`another content\`; + + """ + ` + + expect(snapshotActions.formatSnapshotFile(content)).toEqual(expected) + }) + + test('formatSnapshotFile should format snapshot file content, sort by keys and escape back ticks', () => { + const content = { + ['scenario` 1 1.1']: 'some` content`', + ['scenario 2 1.1']: 'another content', + } + + const expected = dedent` + """ + + + exports[\`scenario 2 1.1\`] = \`another content\`; + + exports[\`scenario\\\` 1 1.1\`] = \`some\\\` content\\\`\`; + + """ + ` + + expect(snapshotActions.formatSnapshotFile(content)).toEqual(expected) + }) + + test('formatSnapshotFile should normalize new lines', () => { + const content = { + ['scenario 1 1.1']: 'some content\rnewline content\r\n', + ['scenario 2 1.1']: 'another content', + } + + const expected = dedent` + """ + + + exports[\`scenario 1 1.1\`] = \`some content + newline content + \`; + + exports[\`scenario 2 1.1\`] = \`another content\`; + + """ + ` + + expect(snapshotActions.formatSnapshotFile(content)).toEqual(expected) + }) + + describe('diff', () => { + afterEach(() => { + diffMock.mockReset() + }) + + afterAll(() => { + diffMock.mockRestore() + }) + + const diffOptions = { + expand: false, + aAnnotation: 'Snapshot', + bAnnotation: 'Received', + } + + test('return null when the diff message contains the NO_DIFF_MESSAGE value', () => { + const expectedContent = ` + Object { + "key1": "value1", + } + ` + + const snapshotContent = ` + Object { + "key1": "value1", + } + ` + + const diffResult = `This + has + nothing to do with the + result! + -> Compared values have no visual difference. ¯\\_(ツ)_/¯ + ` + + // @ts-ignore + diffMock.mockReturnValue(diffResult) + + const diffMessage = snapshotActions.diff(snapshotContent, expectedContent) + + expect(diffMessage).toBeNull() + + expect(diff).toHaveBeenNthCalledWith(1, snapshotContent, expectedContent, diffOptions) + }) + + test('return a custom diff message when the diff message is not defined', () => { + const expectedContent = 'a' + + const snapshotContent = 'b' + + // @ts-ignore + diffMock.mockReturnValue(undefined) + + const diffMessage = snapshotActions.diff(snapshotContent, expectedContent) + + const expectedDiffMessage = '\n\u001b[32m- a\u001b[39m \n \u001b[31m+ b\u001b[39m' + + expect(diffMessage).toEqual(expectedDiffMessage) + }) + + test('return the diff message', () => { + const snapshotContent = ` + Object { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + } + ` + + const expectedContent = ` + Object { + "key1": "value1", + "key2": "value6", + "key3": "value3", + "key4": "value7", + "key5": "value5", + } + ` + + const diffResult = ` + """ + + \u001b[32m- Snapshot\u001b[39m + \u001b[31m+ Received\u001b[39m + + + \u001b[2m Object {\u001b[22m + \u001b[2m "key1": "value1",\u001b[22m + \u001b[32m- "key2": "value2",\u001b[39m + \u001b[31m+ "key2": "value6",\u001b[39m + \u001b[2m "key3": "value3",\u001b[22m + \u001b[32m- "key4": "value4",\u001b[39m + \u001b[31m+ "key4": "value7",\u001b[39m + \u001b[2m "key5": "value5",\u001b[22m + \u001b[2m }\u001b[22m + \u001b[2m \u001b[22m + """ + ` + + // @ts-ignore + diffMock.mockReturnValue(diffResult) + + const expectedDiffMessage = `\n${diffResult}` + + const diffMessage = snapshotActions.diff(snapshotContent, expectedContent) + + expect(diffMessage).toEqual(expectedDiffMessage) + }) + }) + + test('snapshotsPath returns snapshot path', () => { + const featurePath = 'myfolder/featurefile.feature' + const expectedPath = 'myfolder/__snapshots__/featurefile.feature.snap' + const options = {} + expect(snapshotActions.snapshotsPath(featurePath, options)).toEqual(expectedPath) + }) + + test('snapshotsPath returns snapshot path with overrided folder and extension', () => { + const featurePath = 'myfolder/featurefile.feature' + const expectedPath = 'myfolder/testsnap/featurefile.feature.sna' + const options = { + snapshotsDirname: 'testsnap', + snapshotsFileExtension: 'sna', + } + expect(snapshotActions.snapshotsPath(featurePath, options)).toEqual(expectedPath) + }) + + describe('writeSnapshotFile', () => { + let writeFileContentStub: SinonStub + beforeAll(() => { + writeFileContentStub = stub(fileSystem, 'writeFileContent') + }) + + afterAll(() => writeFileContentStub.restore()) + + it('should format and write snapshot file', () => { + const file = 'folder1/feature1.feature' + + const scenario1Snapshot = dedent` + """ + Object { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + } + """ + ` + const contentToWrite = { 'scenario 1 1.1': scenario1Snapshot } + + const expectedWrite = dedent` + """ + + + exports[\`scenario 1 1.1\`] = \`Object { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + }\`; + + """ + ` + + snapshotActions.writeSnapshotFile(file, contentToWrite) + + expect(writeFileContentStub.calledWithExactly(file, expectedWrite)).toBeTruthy() + }) + }) + + describe('readSnapshotFile', () => { + const sandbox = createSandbox() + let getFileInfoStub: SinonStub, getFileContentStub: SinonStub + + beforeAll(() => { + getFileInfoStub = sandbox.stub(fileSystem, 'getFileInfo') + getFileContentStub = sandbox.stub(fileSystem, 'getFileContent') + }) + + afterEach(() => sandbox.reset()) + afterAll(() => sandbox.restore()) + + it('should read and parse snapshot file', () => { + const file = 'folder1/feature1.feature' + + const snapshotContent = dedent` + """ + Object { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + } + """ + ` + + const fileContent = dedent` + """ + + + exports[\`scenario 1 1.1\`] = \`Object { + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + }\`; + + """ + ` + + const expectedContent = { 'scenario 1 1.1': snapshotContent } + + getFileInfoStub.returns({}) + getFileContentStub.returns(fileContent) + + expect(snapshotActions.readSnapshotFile(file)).toEqual(expectedContent) + + expect(getFileInfoStub.calledWithExactly(file)).toBeTruthy() + expect(getFileContentStub.calledWithExactly(file)).toBeTruthy() + }) + + it("should give an empty object if file doesn't exists", () => { + const file = 'folder1/feature1.feature' + + const expectedContent = {} + getFileInfoStub.returns(null) + + expect(snapshotActions.readSnapshotFile(file)).toEqual(expectedContent) + + expect(getFileInfoStub.calledWithExactly(file)).toBeTruthy() + expect(getFileContentStub.notCalled).toBeTruthy() + }) + }) + + test('readSnapshotFile throw an error if no file', () => { + expect(snapshotActions.readSnapshotFile).toThrowError( + /Missing snapshot file undefined to read snapshots/ + ) + }) + + test('prefixSnapshots give a prefix per scenario name', () => { + const scenarios = [ + { name: 'Scenario 1', line: 10 }, + { name: 'Scenario 2', line: 20 }, + ] + + const expectedResult = { + 10: { name: 'Scenario 1', line: 10, prefix: 'Scenario 1 1' }, + 20: { name: 'Scenario 2', line: 20, prefix: 'Scenario 2 1' }, + } + + expect(snapshotActions.prefixSnapshots(scenarios)).toEqual(expectedResult) + }) + + test('prefixSnapshots works with duplicate scenarios names', () => { + const scenarios = [ + { name: 'Scenario 1', line: 10 }, + { name: 'Scenario 2', line: 20 }, + { name: 'Scenario 1', line: 30 }, + ] + + const expectedResult = { + 10: { name: 'Scenario 1', line: 10, prefix: 'Scenario 1 1' }, + 20: { name: 'Scenario 2', line: 20, prefix: 'Scenario 2 1' }, + 30: { name: 'Scenario 1', line: 30, prefix: 'Scenario 1 2' }, + } + + expect(snapshotActions.prefixSnapshots(scenarios)).toEqual(expectedResult) + }) + + test('prefixSnapshots throw an error if no scenarios object', () => { + expect(snapshotActions.prefixSnapshots).toThrowError( + /Scenarios are required to prefix snapshots/ + ) + }) + + // test('extractScenarios read scenarios names from a file', () => { + // const file = 'folder1/feature1.feature' + // + // const fileContent = ` + // @cli @offline + // Feature: yarn CLI + // + // Scenario: Running an invalid command + // When I run command yarn invalid + // Then exit code should be 1 + // And stderr should contain Command "invalid" not found. + // + // Scenario: Getting info about installed yarn version + // When I run command yarn --version + // Then exit code should be 0 + // And stdout should match ^[0-9]{1}.[0-9]{1,3}.[0-9]{1,3} + // And stderr should be empty + // + // Scenario: Running an invalid command + // When I run command yarn invalid + // Then exit code should be 1 + // And stderr should contain Command "invalid" not found. + // ` + // + // const expectedContent = [ + // { name: 'Running an invalid command', line: 5 }, + // { name: 'Getting info about installed yarn version', line: 10 }, + // { name: 'Running an invalid command', line: 16 }, + // ] + // + // fileSystem.getFileContent = jest.fn() + // fileSystem.getFileContent.mockImplementationOnce(() => { + // return fileContent + // }) + // + // expect(snapshotActions.extractScenarios(file)).toEqual(expectedContent) + // + // expect(fileSystem.getFileContent.mock.calls.length).toBe(1) + // expect(fileSystem.getFileContent).toHaveBeenCalledWith(file) + // }) + + test('extractScenarios throw an error if no file', () => { + expect(snapshotActions.extractScenarios).toThrowError(/Invalid feature file undefined/) + expect(snapshotActions.extractScenarios).toThrowError(TypeError) + }) +}) diff --git a/tests/extensions/snapshot/statistics.test.js b/tests/extensions/snapshot/statistics.test.js deleted file mode 100644 index 8b84f326..00000000 --- a/tests/extensions/snapshot/statistics.test.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' - -const statistics = require('../../../src/extensions/snapshot/statistics') - -let logSpy -beforeEach(() => { - statistics.created = [] - statistics.removed = [] - statistics.updated = [] - logSpy = jest.spyOn(console, 'log').mockImplementation() -}) - -afterEach(() => { - logSpy.mockRestore() -}) - -test('should not print to console snapshot statistics with empty datas', () => { - statistics.printReport() - expect(logSpy).toHaveBeenCalledTimes(0) -}) - -test('should print to console snapshot statistics with data in created', () => { - statistics.created.push('item') - statistics.printReport() - expect(logSpy).toHaveBeenCalledTimes(1) - expect(logSpy).toHaveBeenLastCalledWith( - '`\n\nSnapshots: \u001b[32m1 created, \u001b[39m1 total\n' - ) -}) - -test('should print to console snapshot statistics with data in updated', () => { - statistics.updated.push('item') - statistics.printReport() - expect(logSpy).toHaveBeenCalledTimes(1) - expect(logSpy).toHaveBeenLastCalledWith( - '`\n\nSnapshots: \u001b[33m1 updated, \u001b[39m1 total\n' - ) -}) - -test('should print to console snapshot statistics with data in removed', () => { - statistics.removed.push('item') - statistics.printReport() - expect(logSpy).toHaveBeenCalledTimes(1) - expect(logSpy).toHaveBeenLastCalledWith( - '`\n\nSnapshots: \u001b[31m1 removed, \u001b[39m1 total\n' - ) -}) diff --git a/tests/extensions/snapshot/statistics.test.ts b/tests/extensions/snapshot/statistics.test.ts new file mode 100644 index 00000000..7eeab162 --- /dev/null +++ b/tests/extensions/snapshot/statistics.test.ts @@ -0,0 +1,54 @@ +import * as statistics from '../../../src/extensions/snapshot/statistics' +import { SinonStub, stub } from 'sinon' + +describe('extensions > snapshot > statistics', () => { + let logStub: SinonStub + beforeAll(() => { + logStub = stub(console, 'log') + }) + + afterEach(() => { + logStub.reset() + statistics.reset() + }) + afterAll(() => logStub.restore()) + + test('should not print to console snapshot statistics with empty datas', () => { + statistics.printReport() + expect(logStub.notCalled).toBeTruthy() + }) + + test('should print to console snapshot statistics with data in created', () => { + statistics.created.push({ file: 'file', name: 'item' }) + statistics.printReport() + expect( + logStub.calledWithExactly('`\n\nSnapshots: \u001b[32m1 created, \u001b[39m1 total\n') + ).toBeTruthy() + }) + + test('should print to console snapshot statistics with data in updated', () => { + statistics.updated.push({ file: 'file', name: 'item' }) + statistics.printReport() + expect( + logStub.calledWithExactly('`\n\nSnapshots: \u001b[33m1 updated, \u001b[39m1 total\n') + ).toBeTruthy() + }) + + test('should print to console snapshot statistics with data in removed', () => { + statistics.removed.push({ file: 'file', name: 'item' }) + statistics.printReport() + expect( + logStub.calledWithExactly('`\n\nSnapshots: \u001b[31m1 removed, \u001b[39m1 total\n') + ).toBeTruthy() + }) + + test('should reset to empty value', () => { + statistics.created.push({ file: 'file', name: 'item' }) + statistics.updated.push({ file: 'file', name: 'item' }) + statistics.removed.push({ file: 'file', name: 'item' }) + statistics.reset() + expect(statistics.created).toEqual([]) + expect(statistics.updated).toEqual([]) + expect(statistics.removed).toEqual([]) + }) +}) diff --git a/tests/extensions/state/definitions.test.js b/tests/extensions/state/definitions.test.js deleted file mode 100644 index eff69bed..00000000 --- a/tests/extensions/state/definitions.test.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict' - -const helper = require('../definitions_helper') -const definitions = require('../../../src/extensions/state/definitions') - -beforeEach(() => { - definitions.install() -}) - -afterEach(() => { - helper.clearContext() -}) - -test('set state property', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('set state (.+) to') - def.shouldNotMatch('I set state property to ') - def.shouldMatch('I set state property to value', ['property', 'value']) - def.shouldMatch('set state property to value', ['property', 'value']) - - const stateMock = { state: { set: jest.fn() } } - def.exec(stateMock, 'property', 'value') - expect(stateMock.state.set).toHaveBeenCalledWith('property', 'value') -}) - -test('clear state', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('clear state') - def.shouldMatch('I clear state') - def.shouldMatch('clear state') - - const stateMock = { state: { clear: jest.fn() } } - def.exec(stateMock) - expect(stateMock.state.clear).toHaveBeenCalled() -}) - -test('dump current state', () => { - const context = helper.getContext() // Extension context - - const def = context.getDefinitionByMatcher('dump state') - def.shouldMatch('I dump state') - def.shouldMatch('dump state') - - const stateMock = { state: { dump: jest.fn(() => 'state') } } - def.exec(stateMock) - expect(stateMock.state.dump).toHaveBeenCalled() -}) diff --git a/tests/extensions/state/definitions.test.ts b/tests/extensions/state/definitions.test.ts new file mode 100644 index 00000000..bd182f28 --- /dev/null +++ b/tests/extensions/state/definitions.test.ts @@ -0,0 +1,61 @@ +import * as helper from '../definitions_helper' +import { install } from '../../../src/extensions/state/definitions' +import { createSandbox, SinonStub } from 'sinon' +import { state } from '../../../src/extensions/state' + +describe('extensions > state > definitions', () => { + const sandbox = createSandbox() + let setStateStub: SinonStub, clearStateStub: SinonStub, dumpStateStub: SinonStub + + beforeAll(() => { + setStateStub = sandbox.stub(state, 'set') + clearStateStub = sandbox.stub(state, 'clear') + dumpStateStub = sandbox.stub(state, 'dump') + sandbox.stub(console, 'log') + }) + beforeEach(() => install()) + + afterEach(() => { + sandbox.reset() + helper.clearContext() + }) + afterAll(() => sandbox.restore()) + + test('set state property', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('set state (.+) to (.+)') + def.shouldNotMatch('I set state property to ') + def.shouldMatch('I set state property to value', ['property', 'value']) + def.shouldMatch('set state property to value', ['property', 'value']) + + const stateMock = { state: { set: setStateStub } } + def.exec(stateMock, 'property', 'value') + expect(stateMock.state.set.calledWithExactly('property', 'value')).toBeTruthy() + }) + + test('clear state', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('clear state') + def.shouldMatch('I clear state') + def.shouldMatch('clear state') + + const stateMock = { state: { clear: clearStateStub } } + def.exec(stateMock) + expect(stateMock.state.clear.calledOnce).toBeTruthy() + }) + + test('dump current state', () => { + const context = helper.getContext() // Extension context + + const def = context.getDefinitionByMatcher('dump state') + def.shouldMatch('I dump state') + def.shouldMatch('dump state') + + const stateMock = { state: { dump: dumpStateStub } } + dumpStateStub.returns('state') + def.exec(stateMock) + expect(stateMock.state.dump.calledOnce).toBeTruthy() + }) +}) diff --git a/tests/extensions/state/state.test.js b/tests/extensions/state/state.test.js deleted file mode 100644 index 6e20298b..00000000 --- a/tests/extensions/state/state.test.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict' - -const State = require('../../../src/extensions/state/state') - -const state = State() - -test('should return null value', () => { - expect(state.populate('((null))')).toBe('((null))') -}) - -test('should return undefined value', () => { - expect(state.populate('((undefined))')).toBe('((undefined))') -}) - -test('should populate value with state data', () => { - state.clear() - state.set('key1', '1') - expect(state.populate('{{key1}}')).toBe('1') - expect(state.populate('{{key1}}((number))')).toBe('1((number))') -}) - -test('should returned complete object populated with the state data', () => { - state.clear() - state.set('key1', 'value1') - state.set('key2', '2') - - const object = { - field1: '{{key1}}', - object2: { - field1: '1((number))', - field2: '{{key2}}((number))', - }, - } - - const expectedObject = { - field1: 'value1', - object2: { - field1: '1((number))', - field2: '2((number))', - }, - } - - expect(state.populateObject(object)).toEqual(expectedObject) -}) - -test('should dump value all data from state', () => { - state.clear() - state.set('key1', '1') - expect(state.dump()).toStrictEqual({ key1: '1' }) -}) - -test('should get value from state', () => { - state.clear() - state.set('key1', '1') - expect(state.get('key1')).toBe('1') -}) diff --git a/tests/extensions/state/state.test.ts b/tests/extensions/state/state.test.ts new file mode 100644 index 00000000..9cf50f4d --- /dev/null +++ b/tests/extensions/state/state.test.ts @@ -0,0 +1,48 @@ +import { state } from '../../../src/extensions/state' + +describe('extensions > state > state', () => { + test('should return null value', () => { + expect(state.populate('((null))')).toBe('((null))') + }) + + test('should return undefined value', () => { + expect(state.populate('((undefined))')).toBe('((undefined))') + }) + + test('should populate value with state data', () => { + state.clear() + state.set('key1', '1') + expect(state.populate('{{key1}}')).toBe('1') + expect(state.populate('{{key1}}((number))')).toBe('1((number))') + }) + + test('should returned complete object populated with the state data', () => { + state.clear() + state.set('key1', 'value1') + state.set('key2', '2') + + const object = { + field1: '{{key1}}', + object2: '1((number))', + } + + const expectedObject = { + field1: 'value1', + object2: '1((number))', + } + + expect(state.populateObject(object)).toEqual(expectedObject) + }) + + test('should dump value all data from state', () => { + state.clear() + state.set('key1', '1') + expect(state.dump()).toStrictEqual({ key1: '1' }) + }) + + test('should get value from state', () => { + state.clear() + state.set('key1', '1') + expect(state.get('key1')).toBe('1') + }) +}) diff --git a/tests/utils/commandLine.test.js b/tests/utils/commandLine.test.js deleted file mode 100644 index 41b2ea5d..00000000 --- a/tests/utils/commandLine.test.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict' - -const sinon = require('sinon') - -const { hasArg, hasOneArgOf } = require('../../src/utils/commandLine') - -const argvStub = sinon.stub(process, 'argv') - -beforeAll(() => { - argvStub.value(['--argOne', '-t', '--three']) -}) - -afterEach(() => { - argvStub.resetHistory() -}) - -afterAll(() => { - argvStub.restore() -}) - -test('hasArg should return true when the arg is found', () => { - expect(hasArg('-t')).toBe(true) -}) - -test('hasArg should return false when the arg is not found', () => { - expect(hasArg('--missing')).toBe(false) -}) - -test('hasOneArgOf should return true when at least an arg is found', () => { - expect(hasOneArgOf(['--argOne', '--notFound'])).toBe(true) -}) - -test('hasOneArgOf should return false when no args are found', () => { - expect(hasOneArgOf(['--missing', '--notFound'])).toBe(false) -}) diff --git a/tests/utils/command_line.test.ts b/tests/utils/command_line.test.ts new file mode 100644 index 00000000..dec6e3d5 --- /dev/null +++ b/tests/utils/command_line.test.ts @@ -0,0 +1,29 @@ +import { stub } from 'sinon' +import { hasArg, hasOneArgOf } from '../../src/utils/command_line' + +const argvStub = stub(process, 'argv') + +describe('utils > commandLine', () => { + beforeAll(() => { + argvStub.value(['--argOne', '-t', '--three']) + }) + + afterEach(() => argvStub.resetHistory()) + afterAll(() => argvStub.restore()) + + test('hasArg should return true when the arg is found', () => { + expect(hasArg('-t')).toBe(true) + }) + + test('hasArg should return false when the arg is not found', () => { + expect(hasArg('--missing')).toBe(false) + }) + + test('hasOneArgOf should return true when at least an arg is found', () => { + expect(hasOneArgOf(['--argOne', '--notFound'])).toBe(true) + }) + + test('hasOneArgOf should return false when no args are found', () => { + expect(hasOneArgOf(['--missing', '--notFound'])).toBe(false) + }) +}) diff --git a/tsconfig.json b/tsconfig.json index acd31be9..9e9ea64d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ - - { "compilerOptions": { + "typeRoots": ["./node_modules/@types", "src/@types"], + "types": ["node","jest","chai"], "incremental": true, "noImplicitAny": true, "outDir": "./build", @@ -20,6 +20,7 @@ "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, }, - "includes": ["**/*"], - "exclude": ["./node_modules", "./build", "./coverage"] + "include": ["src","tests","bin","scripts"], + "exclude": ["./node_modules", "./build", "./coverage","examples"], + "files": ["src/@types/chai.d.ts"] } diff --git a/yarn.lock b/yarn.lock index a21a72d2..1257af1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,334 +5,162 @@ __metadata: version: 5 cacheKey: 8 -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/code-frame@npm:7.10.4" - dependencies: - "@babel/highlight": ^7.10.4 - checksum: feb4543c8a509fe30f0f6e8d7aa84f82b41148b963b826cd330e34986f649a85cb63b2f13dd4effdf434ac555d16f14940b8ea5f4433297c2f5ff85486ded019 - languageName: node - linkType: hard - -"@babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/code-frame@npm:7.14.5" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/code-frame@npm:7.16.0" dependencies: - "@babel/highlight": ^7.14.5 - checksum: 0adbe4f8d91586f764f524e57631f582ab988b2ef504391a5d89db29bfaaf7c67c237798ed4a249b6a2d7135852cf94d3d07ce6b9739dd1df1f271d5ed069565 + "@babel/highlight": ^7.16.0 + checksum: 8961d0302ec6b8c2e9751a11e06a17617425359fd1645e4dae56a90a03464c68a0916115100fbcd030961870313f21865d0b85858360a2c68aabdda744393607 languageName: node linkType: hard -"@babel/compat-data@npm:^7.14.5": - version: 7.14.7 - resolution: "@babel/compat-data@npm:7.14.7" - checksum: dcf7a72cb650206857a98cce1ab0973e67689f19afc3b30cabff6dbddf563f188d54d3b3f92a70c6bc1feb9049d8b2e601540e1d435b6866c77bffad0a441c9f - languageName: node - linkType: hard - -"@babel/core@npm:^7.1.0, @babel/core@npm:^7.7.5": - version: 7.11.1 - resolution: "@babel/core@npm:7.11.1" - dependencies: - "@babel/code-frame": ^7.10.4 - "@babel/generator": ^7.11.0 - "@babel/helper-module-transforms": ^7.11.0 - "@babel/helpers": ^7.10.4 - "@babel/parser": ^7.11.1 - "@babel/template": ^7.10.4 - "@babel/traverse": ^7.11.0 - "@babel/types": ^7.11.0 - convert-source-map: ^1.7.0 - debug: ^4.1.0 - gensync: ^1.0.0-beta.1 - json5: ^2.1.2 - lodash: ^4.17.19 - resolve: ^1.3.2 - semver: ^5.4.1 - source-map: ^0.5.0 - checksum: 37cf924a21c5be89a3e9d5106875f06dcaec3d3e2fba7de19babb3fa619ef517120b4f8c48ff982f04b2b9c88eed803e122a143580214c1a60372954eee7ae4e +"@babel/compat-data@npm:^7.16.0": + version: 7.16.4 + resolution: "@babel/compat-data@npm:7.16.4" + checksum: 4949ce54eafc4b38d5623696a872acaaced1a523605708d81c2c483253941917d90dae0de40fc01e152ae56075dadd89c23014da5a632b09c001a716fa689cae languageName: node linkType: hard -"@babel/core@npm:^7.7.2": - version: 7.14.6 - resolution: "@babel/core@npm:7.14.6" +"@babel/core@npm:^7.1.0, @babel/core@npm:^7.12.3, @babel/core@npm:^7.7.2, @babel/core@npm:^7.7.5": + version: 7.16.5 + resolution: "@babel/core@npm:7.16.5" dependencies: - "@babel/code-frame": ^7.14.5 - "@babel/generator": ^7.14.5 - "@babel/helper-compilation-targets": ^7.14.5 - "@babel/helper-module-transforms": ^7.14.5 - "@babel/helpers": ^7.14.6 - "@babel/parser": ^7.14.6 - "@babel/template": ^7.14.5 - "@babel/traverse": ^7.14.5 - "@babel/types": ^7.14.5 + "@babel/code-frame": ^7.16.0 + "@babel/generator": ^7.16.5 + "@babel/helper-compilation-targets": ^7.16.3 + "@babel/helper-module-transforms": ^7.16.5 + "@babel/helpers": ^7.16.5 + "@babel/parser": ^7.16.5 + "@babel/template": ^7.16.0 + "@babel/traverse": ^7.16.5 + "@babel/types": ^7.16.0 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.1.2 semver: ^6.3.0 source-map: ^0.5.0 - checksum: 6ede604d8de7a103c087b96a58548a3d27efb9e53de6ecc84f4b4ca947cd91f02b0289fc04557b04eb6e31243dbeabdcdb8fd520a1780f284333f56eb1b58913 + checksum: e5b76c6be95ab56a441772173463a56f824b39eba5fd3efe4b9784863922a1cb8abde6331d894854ed563b5ffe4be76d52524ecd07963660bb146f49a3cb3556 languageName: node linkType: hard -"@babel/generator@npm:^7.11.0": - version: 7.11.0 - resolution: "@babel/generator@npm:7.11.0" +"@babel/generator@npm:^7.16.5, @babel/generator@npm:^7.7.2": + version: 7.16.5 + resolution: "@babel/generator@npm:7.16.5" dependencies: - "@babel/types": ^7.11.0 + "@babel/types": ^7.16.0 jsesc: ^2.5.1 source-map: ^0.5.0 - checksum: 2daf1274c40beb45c6506f996c14d96fd244008b27d26365baca00c6720be6f4b44f9bf748fa0cd55be4777280b36a8a9f69f7f3dd077b59c0c7bfb922443d2d + checksum: 621fa2da21a5397a4739f03af1eda76140f0da9f962071640a479c0cf1859edc576aa8881b5771be9274238f048bf9024c94d826003659f64eee29c48f2fe470 languageName: node linkType: hard -"@babel/generator@npm:^7.14.5, @babel/generator@npm:^7.7.2": - version: 7.14.5 - resolution: "@babel/generator@npm:7.14.5" - dependencies: - "@babel/types": ^7.14.5 - jsesc: ^2.5.1 - source-map: ^0.5.0 - checksum: 7fcfeaf17e8e76ea91c66dc86c776d2112f52ce0315d3f4ca6a74b6eada0be1592d1ea6286d7241d3f634b63717ceef5d180d041a0b3dca9d071ba2e5fa7c77b - languageName: node - linkType: hard - -"@babel/helper-compilation-targets@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-compilation-targets@npm:7.14.5" +"@babel/helper-compilation-targets@npm:^7.16.3": + version: 7.16.3 + resolution: "@babel/helper-compilation-targets@npm:7.16.3" dependencies: - "@babel/compat-data": ^7.14.5 + "@babel/compat-data": ^7.16.0 "@babel/helper-validator-option": ^7.14.5 - browserslist: ^4.16.6 + browserslist: ^4.17.5 semver: ^6.3.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 02df2c6d1bc5f2336f380945aa266a3a65d057c5eff6be667235a8005048b21f69e4aaebc8e43ccfc2fb406688383ae8e572f257413febf244772e5e7af5fd7f - languageName: node - linkType: hard - -"@babel/helper-function-name@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helper-function-name@npm:7.10.4" - dependencies: - "@babel/helper-get-function-arity": ^7.10.4 - "@babel/template": ^7.10.4 - "@babel/types": ^7.10.4 - checksum: eb9226d1c768b974f30a20fafd809353a2dbc359f66d6d27e4dd917fb471df9a9c2b771e0f1a838b21aa195b3cbba8a472d95327b80b3bd0e12edf407a3c0d53 - languageName: node - linkType: hard - -"@babel/helper-function-name@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-function-name@npm:7.14.5" - dependencies: - "@babel/helper-get-function-arity": ^7.14.5 - "@babel/template": ^7.14.5 - "@babel/types": ^7.14.5 - checksum: fd8ffa82f7622b6e9a6294fb3b98b42e743ab2a8e3c329367667a960b5b98b48bc5ebf8be7308981f1985b9f3c69e1a3b4a91c8944ae97c31803240da92fb3c8 + checksum: 038bcd43ac914371c51bf6e72b5cedcae432f0d359285d74a9133c6a839bd625a7d5412d7471d50aa78a3e1c79b0a692b50a8d6a1299ebf69733b512ff199323 languageName: node linkType: hard -"@babel/helper-get-function-arity@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helper-get-function-arity@npm:7.10.4" +"@babel/helper-environment-visitor@npm:^7.16.5": + version: 7.16.5 + resolution: "@babel/helper-environment-visitor@npm:7.16.5" dependencies: - "@babel/types": ^7.10.4 - checksum: 798e2eb6cd5d2ff91a6cc3904ad626fca366fb33e631cb214477f100207ef26acdf78280a31f8adf59a988f020221165834902d5e201a8b5bbefab361d502daf + "@babel/types": ^7.16.0 + checksum: f57da613f2fb9ca0b85cb4a9131cb688555e78ba8b0047ac0e73551b247eb71bf8fa075e6408064e8ab71ec230f24b4e06367efc9ccd1dcfcea0efe0086f02f3 languageName: node linkType: hard -"@babel/helper-get-function-arity@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-get-function-arity@npm:7.14.5" +"@babel/helper-function-name@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-function-name@npm:7.16.0" dependencies: - "@babel/types": ^7.14.5 - checksum: a60779918b677a35e177bb4f46babfd54e9790587b6a4f076092a9eff2a940cbeacdeb10c94331b26abfe838769554d72293d16df897246cfccd1444e5e27cb7 + "@babel/helper-get-function-arity": ^7.16.0 + "@babel/template": ^7.16.0 + "@babel/types": ^7.16.0 + checksum: 8c02371d28678f3bb492e69d4635b2fe6b1c5a93ce129bf883f1fafde2005f4dbc0e643f52103ca558b698c0774bfb84a93f188d71db1c077f754b6220629b92 languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-hoist-variables@npm:7.14.5" +"@babel/helper-get-function-arity@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-get-function-arity@npm:7.16.0" dependencies: - "@babel/types": ^7.14.5 - checksum: 35af58eebffca10988de7003e044ce2d27212aea72ac6d2c4604137da7f1e193cc694d8d60805d0d0beaf3d990f6f2dcc2622c52e3d3148e37017a29cacf2e56 + "@babel/types": ^7.16.0 + checksum: 1a68322c7b5fdffb1b51df32f7a53b1ff2268b5b99d698f0a1a426dcb355482a44ef3dae982a507907ba975314638dabb6d77ac1778098bdbe99707e6c29cae8 languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.10.4": - version: 7.11.0 - resolution: "@babel/helper-member-expression-to-functions@npm:7.11.0" +"@babel/helper-hoist-variables@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-hoist-variables@npm:7.16.0" dependencies: - "@babel/types": ^7.11.0 - checksum: 79c50984ad0cea556628e28d68abdc1dc6546398484bdf5477521c6f38fa7c065a9de227bdf7c48405535542a3c52850f49b0ec3475c254bd56ad47c6730d0f5 + "@babel/types": ^7.16.0 + checksum: 2ee5b400c267c209a53c90eea406a8f09c30d4d7a2b13e304289d858a2e34a99272c062cfad6dad63705662943951c42ff20042ef539b2d3c4f8743183a28954 languageName: node linkType: hard -"@babel/helper-member-expression-to-functions@npm:^7.14.5": - version: 7.14.7 - resolution: "@babel/helper-member-expression-to-functions@npm:7.14.7" +"@babel/helper-module-imports@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-module-imports@npm:7.16.0" dependencies: - "@babel/types": ^7.14.5 - checksum: 1768b849224002d7a8553226ad73e1e957fb6184b68234d5df7a45cf8e4453ed1208967c1cace1a4d973b223ddc881d105e372945ec688f09485dff0e8ed6180 + "@babel/types": ^7.16.0 + checksum: 8e1eb9ac39440e52080b87c78d8d318e7c93658bdd0f3ce0019c908de88cbddafdc241f392898c0b0ba81fc52c8c6d2f9cc1b163ac5ed2a474d49b11646b7516 languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helper-module-imports@npm:7.10.4" +"@babel/helper-module-transforms@npm:^7.16.5": + version: 7.16.5 + resolution: "@babel/helper-module-transforms@npm:7.16.5" dependencies: - "@babel/types": ^7.10.4 - checksum: 051e75c8052881316c654b6d58a9f9743317c63f9230badec565095c9102448620bc3038461d2853dace841d1a271c84acb958dd7c39d74af1b2dda4f649b2b0 + "@babel/helper-environment-visitor": ^7.16.5 + "@babel/helper-module-imports": ^7.16.0 + "@babel/helper-simple-access": ^7.16.0 + "@babel/helper-split-export-declaration": ^7.16.0 + "@babel/helper-validator-identifier": ^7.15.7 + "@babel/template": ^7.16.0 + "@babel/traverse": ^7.16.5 + "@babel/types": ^7.16.0 + checksum: 0463e7198e5540cbb90981f769c89ec302001b211c33df1a6790a1eaee678ec418cee40ef3cf0fe159d40787214fbba129582f6b07e79244dc8cbcd5e791dd18 languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-module-imports@npm:7.14.5" - dependencies: - "@babel/types": ^7.14.5 - checksum: b98279908698a50a22634e683924cb25eb93edf1bf28ac65691dfa82d7a1a4dae4e6b12b8ef9f9a50171ca484620bce544f270873c53505d8a45364c5b665c0c +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.5, @babel/helper-plugin-utils@npm:^7.8.0": + version: 7.16.5 + resolution: "@babel/helper-plugin-utils@npm:7.16.5" + checksum: 3ff605f879a9ed287952b538a8334bb16e6cf7cf441f205713b1cf8043b047a965773b66e50575018504f349e16368acfe4702a2f376e16263733e2c7c6c3e39 languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.11.0": - version: 7.11.0 - resolution: "@babel/helper-module-transforms@npm:7.11.0" +"@babel/helper-simple-access@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-simple-access@npm:7.16.0" dependencies: - "@babel/helper-module-imports": ^7.10.4 - "@babel/helper-replace-supers": ^7.10.4 - "@babel/helper-simple-access": ^7.10.4 - "@babel/helper-split-export-declaration": ^7.11.0 - "@babel/template": ^7.10.4 - "@babel/types": ^7.11.0 - lodash: ^4.17.19 - checksum: e784af9d4143b1e72026f6a63935b0586e20b0cb1739ae5d022611e80b8b028b19ade9ac4aa19b0304c547dde8e03058dad4ac232b05ec55f8e45e1adecb802b + "@babel/types": ^7.16.0 + checksum: 2d7155f318411788b42d2f4a3d406de12952ad620d0bd411a0f3b5803389692ad61d9e7fab5f93b23ad3d8a09db4a75ca9722b9873a606470f468bc301944af6 languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-module-transforms@npm:7.14.5" +"@babel/helper-split-export-declaration@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/helper-split-export-declaration@npm:7.16.0" dependencies: - "@babel/helper-module-imports": ^7.14.5 - "@babel/helper-replace-supers": ^7.14.5 - "@babel/helper-simple-access": ^7.14.5 - "@babel/helper-split-export-declaration": ^7.14.5 - "@babel/helper-validator-identifier": ^7.14.5 - "@babel/template": ^7.14.5 - "@babel/traverse": ^7.14.5 - "@babel/types": ^7.14.5 - checksum: f5d64c0242ec8949ee09069a634d28ae750ab22f9533ea90eab9eaf3405032a33b0b329a63fac0a7901482efb8a388a06279f7544225a0bc3c1b92b306ab2b6e + "@babel/types": ^7.16.0 + checksum: 8bd87b5ea2046b145f0f55bc75cbdb6df69eaeb32919ee3c1c758757025aebca03e567a4d48389eb4f16a55021adb6ed8fa58aa771e164b15fa5e0a0722f771d languageName: node linkType: hard -"@babel/helper-optimise-call-expression@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helper-optimise-call-expression@npm:7.10.4" - dependencies: - "@babel/types": ^7.10.4 - checksum: 358b904a5067c19d3d09e8e9a8ba1bdfb8dad71bb6fa3777d64f04e78d8425bad0b8ea7969bbcdf14bad0a7815d3575fc3323a085cbea6c36c47063e3aee4b00 - languageName: node - linkType: hard - -"@babel/helper-optimise-call-expression@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-optimise-call-expression@npm:7.14.5" - dependencies: - "@babel/types": ^7.14.5 - checksum: c7af558c63eb5449bf2249f1236d892ed54a400cb6c721756cde573b996c12c64dee6b57fa18ad1a0025d152e6f689444f7ea32997a1d56e1af66c3eda18843d - languageName: node - linkType: hard - -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.8.0": - version: 7.10.4 - resolution: "@babel/helper-plugin-utils@npm:7.10.4" - checksum: 639ed8fc462b97a83226cee6bb081b1d77e7f73e8b033d2592ed107ee41d96601e321e5ea53a33e47469c7f1146b250a3dcda5ab873c7de162ab62120c341a41 - languageName: node - linkType: hard - -"@babel/helper-plugin-utils@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-plugin-utils@npm:7.14.5" - checksum: fe20e90a24d02770a60ebe80ab9f0dfd7258503cea8006c71709ac9af1aa3e47b0de569499673f11ea6c99597f8c0e4880ae1d505986e61101b69716820972fe - languageName: node - linkType: hard - -"@babel/helper-replace-supers@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helper-replace-supers@npm:7.10.4" - dependencies: - "@babel/helper-member-expression-to-functions": ^7.10.4 - "@babel/helper-optimise-call-expression": ^7.10.4 - "@babel/traverse": ^7.10.4 - "@babel/types": ^7.10.4 - checksum: de4d52d5442dc7e549fbc59b287d189fac02e98ef0b7ca788448f8e8fc6e351d63523b3a3f844105f701ff35985266021f75360aedfd1686cc7e7324e1dfd1dd - languageName: node - linkType: hard - -"@babel/helper-replace-supers@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-replace-supers@npm:7.14.5" - dependencies: - "@babel/helper-member-expression-to-functions": ^7.14.5 - "@babel/helper-optimise-call-expression": ^7.14.5 - "@babel/traverse": ^7.14.5 - "@babel/types": ^7.14.5 - checksum: 35d33cfe473f9fb5cc1110ee259686179ecd07e00e07d9eb03de998e47f49d59fc2e183cf6be0793fd6bec24510b893415e52ace93ae940f94663c4a02c6fbd0 - languageName: node - linkType: hard - -"@babel/helper-simple-access@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helper-simple-access@npm:7.10.4" - dependencies: - "@babel/template": ^7.10.4 - "@babel/types": ^7.10.4 - checksum: 4a2c9073c3a864528f0bee135b2b1f07ac0fd0ef60c43e1a67de4ca6bdd3cc73cc32393637ab8fd61bafc6ed9742b8104a75577268d4568440f9551cd256f9b9 - languageName: node - linkType: hard - -"@babel/helper-simple-access@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-simple-access@npm:7.14.5" - dependencies: - "@babel/types": ^7.14.5 - checksum: cd795416bd10dd2f1bdebb36f1af08bf263024fdbf789cfda5dd1fbf4fea1fd0375e21d0bcb910a7d49b09b7480340797dcdfc888fbc895aeae45c145358ad75 - languageName: node - linkType: hard - -"@babel/helper-split-export-declaration@npm:^7.11.0": - version: 7.11.0 - resolution: "@babel/helper-split-export-declaration@npm:7.11.0" - dependencies: - "@babel/types": ^7.11.0 - checksum: eb03088c44e70ba3039b4608b0d108dcb1659f951b976044a487961c725b7c18e3d14b30f78180b8375c4bdbd0410494de56f716d30bc9ae6493e53c17047ec1 - languageName: node - linkType: hard - -"@babel/helper-split-export-declaration@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-split-export-declaration@npm:7.14.5" - dependencies: - "@babel/types": ^7.14.5 - checksum: 93437025a33747bfd37d6d5a9cdac8f4b6b3e5c0c53c0e24c5444575e731ea64fd5471a51a039fd74ff3378f916ea2d69d9f10274d253ed6f832952be2fd65f0 - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helper-validator-identifier@npm:7.10.4" - checksum: 3cbfdff0efea8f3bca050cfe408a156604293d3313c7279c8c02d916a0b3ef82617f28f7729877a94c0e8e922d4b7623c4f0a108ae2853bf762d933e101a5f8c - languageName: node - linkType: hard - -"@babel/helper-validator-identifier@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/helper-validator-identifier@npm:7.14.5" - checksum: 6366bceab4498785defc083a1bd96344f788d90a1aa7a6f18d6813c1d3d134640bfc05690453c0b79bbfc820472cf5b29110dfddaca1f8e2763dfe1bd5df0b88 +"@babel/helper-validator-identifier@npm:^7.15.7": + version: 7.15.7 + resolution: "@babel/helper-validator-identifier@npm:7.15.7" + checksum: f041c28c531d1add5cc345b25d5df3c29c62bce3205b4d4a93dcd164ccf630350acba252d374fad8f5d8ea526995a215829f27183ba7ce7ce141843bf23068a6 languageName: node linkType: hard @@ -343,65 +171,34 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helpers@npm:7.10.4" - dependencies: - "@babel/template": ^7.10.4 - "@babel/traverse": ^7.10.4 - "@babel/types": ^7.10.4 - checksum: c6bad75fa5136b7dc06c3dbedc38abb46820d1fb6227cdf511d62a169de57b5a5c56da718eaa200bb4f9de9ed13ae94f839dcdda4e6f8d4bb8117ffc8409fef5 - languageName: node - linkType: hard - -"@babel/helpers@npm:^7.14.6": - version: 7.14.6 - resolution: "@babel/helpers@npm:7.14.6" - dependencies: - "@babel/template": ^7.14.5 - "@babel/traverse": ^7.14.5 - "@babel/types": ^7.14.5 - checksum: fe4e73975b062a8b8b95f499f4ac1064c9a53d4ee83cc273c2420250f6a46b59f1f5e35050d41ebe04efd7885a28ceea6f4f16d8eb091e24622f2a4a5eb20f23 - languageName: node - linkType: hard - -"@babel/highlight@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/highlight@npm:7.10.4" +"@babel/helpers@npm:^7.16.5": + version: 7.16.5 + resolution: "@babel/helpers@npm:7.16.5" dependencies: - "@babel/helper-validator-identifier": ^7.10.4 - chalk: ^2.0.0 - js-tokens: ^4.0.0 - checksum: 6fab4679162562907942acc3647bc8c405b955f3bef7c654ef160491d0801ebdc12651c2051144dc0e22b69044fe3059d630151d5d7fb84b10ed4093da707707 + "@babel/template": ^7.16.0 + "@babel/traverse": ^7.16.5 + "@babel/types": ^7.16.0 + checksum: 960d938a4359b7f9ff7b753e33b6f600e269aec0ef6030c8026ac37525103da8cde5f1c04ce7de1ad6fc37707aa6178eae938d6fc82544aa25c9fd602c62e0a8 languageName: node linkType: hard -"@babel/highlight@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/highlight@npm:7.14.5" +"@babel/highlight@npm:^7.16.0": + version: 7.16.0 + resolution: "@babel/highlight@npm:7.16.0" dependencies: - "@babel/helper-validator-identifier": ^7.14.5 + "@babel/helper-validator-identifier": ^7.15.7 chalk: ^2.0.0 js-tokens: ^4.0.0 - checksum: 4e4b22fb886c939551d73307de16232c186fdb4d8ec8f514541b058feaecdba5234788a0740ca5bcd28777f4108596c39ac4b7463684c63b3812f6071e3fb88f - languageName: node - linkType: hard - -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.10.4, @babel/parser@npm:^7.11.0, @babel/parser@npm:^7.11.1, @babel/parser@npm:^7.9.4": - version: 7.11.3 - resolution: "@babel/parser@npm:7.11.3" - bin: - parser: ./bin/babel-parser.js - checksum: cc0ca7a97b1fa0abbef7af9df5e7ee1a5e0bef0e664ecd482a53ad60decd56a5d03c63fb6d7e5cf39f299d68bda0a544746ad51ccbe36e8f730df476d773d6f4 + checksum: abf244c48fcff20ec87830e8b99c776f4dcdd9138e63decc195719a94148da35339639e0d8045eb9d1f3e67a39ab90a9c3f5ce2d579fb1a0368d911ddf29b4e5 languageName: node linkType: hard -"@babel/parser@npm:^7.14.5, @babel/parser@npm:^7.14.6, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.7.2": - version: 7.14.7 - resolution: "@babel/parser@npm:7.14.7" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.0, @babel/parser@npm:^7.16.5, @babel/parser@npm:^7.7.2, @babel/parser@npm:^7.9.4": + version: 7.16.6 + resolution: "@babel/parser@npm:7.16.6" bin: parser: ./bin/babel-parser.js - checksum: 0d7acc8cf9c19ccd0e80ab0608953f32f4375f3867c080211270e7bb4bb94c551fd1fc3f49b3cc92a4eec356cf507801f5c93c4c72996968bdc4c28815fe0550 + checksum: 5cbb01a7b2ba5d609945099bfadb01f54e11ef85201e1e0bf47010ee1b35c257eca6ff91606c6ce8adba82a95e180b583183e4dc076f4a70e706152075dd98ca languageName: node linkType: hard @@ -428,13 +225,13 @@ __metadata: linkType: hard "@babel/plugin-syntax-class-properties@npm:^7.8.3": - version: 7.10.4 - resolution: "@babel/plugin-syntax-class-properties@npm:7.10.4" + version: 7.12.13 + resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" dependencies: - "@babel/helper-plugin-utils": ^7.10.4 + "@babel/helper-plugin-utils": ^7.12.13 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 454db1392c5415abf0681a619e71c667df94f7c7587767016b804c4b84180ed4fc00cefcae677850fa356c991085ab61ae524c4d4bab95445cb4aa0a3af96017 + checksum: 24f34b196d6342f28d4bad303612d7ff566ab0a013ce89e775d98d6f832969462e7235f3e7eaf17678a533d4be0ba45d3ae34ab4e5a9dcbda5d98d49e5efa2fc languageName: node linkType: hard @@ -538,90 +335,52 @@ __metadata: linkType: hard "@babel/plugin-syntax-typescript@npm:^7.7.2": - version: 7.14.5 - resolution: "@babel/plugin-syntax-typescript@npm:7.14.5" + version: 7.16.5 + resolution: "@babel/plugin-syntax-typescript@npm:7.16.5" dependencies: - "@babel/helper-plugin-utils": ^7.14.5 + "@babel/helper-plugin-utils": ^7.16.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 5447d13b31aeeeaa5c2b945e60a598642dedca480f11d3232b0927aeb6a6bb8201a0025f509bc23851da4bf126f69b0522790edbd58f4560f0a4984cabd0d126 + checksum: 73454e8e9d5be92304d60d457203b43e04a9d331c4234eefad390a3a4d36a30d75b211ba9e98205e0b322a6c178e46b5852da35889eef9183549d6589d04a01e languageName: node linkType: hard -"@babel/template@npm:^7.10.4, @babel/template@npm:^7.3.3": - version: 7.10.4 - resolution: "@babel/template@npm:7.10.4" - dependencies: - "@babel/code-frame": ^7.10.4 - "@babel/parser": ^7.10.4 - "@babel/types": ^7.10.4 - checksum: 174a1fbfa19ed68141c9a047ff02972ebd3e8c7a98a00ffa79d4958d0f31bcfe17766987c2064d7fae851a277e1c499a05a527df346b3821d9aa9f730979cea9 - languageName: node - linkType: hard - -"@babel/template@npm:^7.14.5": - version: 7.14.5 - resolution: "@babel/template@npm:7.14.5" +"@babel/template@npm:^7.16.0, @babel/template@npm:^7.3.3": + version: 7.16.0 + resolution: "@babel/template@npm:7.16.0" dependencies: - "@babel/code-frame": ^7.14.5 - "@babel/parser": ^7.14.5 - "@babel/types": ^7.14.5 - checksum: 4939199c5b1ca8940e14c87f30f4fab5f35c909bef88447131075349027546927b4e3e08e50db5c2db2024f2c6585a4fe571c739c835ac980f7a4ada2dd8a623 + "@babel/code-frame": ^7.16.0 + "@babel/parser": ^7.16.0 + "@babel/types": ^7.16.0 + checksum: 940f105cc6a6aee638cd8cfae80b8b80811e0ddd53b6a11f3a68431ebb998564815fb26511b5d9cb4cff66ea67130ba7498555ee015375d32f5f89ceaa6662ea languageName: node linkType: hard -"@babel/traverse@npm:^7.1.0, @babel/traverse@npm:^7.10.4, @babel/traverse@npm:^7.11.0": - version: 7.11.0 - resolution: "@babel/traverse@npm:7.11.0" +"@babel/traverse@npm:^7.1.0, @babel/traverse@npm:^7.16.5, @babel/traverse@npm:^7.7.2": + version: 7.16.5 + resolution: "@babel/traverse@npm:7.16.5" dependencies: - "@babel/code-frame": ^7.10.4 - "@babel/generator": ^7.11.0 - "@babel/helper-function-name": ^7.10.4 - "@babel/helper-split-export-declaration": ^7.11.0 - "@babel/parser": ^7.11.0 - "@babel/types": ^7.11.0 + "@babel/code-frame": ^7.16.0 + "@babel/generator": ^7.16.5 + "@babel/helper-environment-visitor": ^7.16.5 + "@babel/helper-function-name": ^7.16.0 + "@babel/helper-hoist-variables": ^7.16.0 + "@babel/helper-split-export-declaration": ^7.16.0 + "@babel/parser": ^7.16.5 + "@babel/types": ^7.16.0 debug: ^4.1.0 globals: ^11.1.0 - lodash: ^4.17.19 - checksum: 6cf1b5a361b77669c41ac1a5e13cae63e7efbb34fc9684e226eab35d3f4246aa621f0997dce01bc42334964723cf032f43a366c9f245e8e263190e106d9fbdd1 + checksum: 6bc31311b641ac0a1c6c854cad3faa172f54d987f9a28d7d75ed64ecbcc74983f60acd51bdd792f77e451fd5385c10ce9955f9d1d60162bd32748cc42dc7eef9 languageName: node linkType: hard -"@babel/traverse@npm:^7.14.5, @babel/traverse@npm:^7.7.2": - version: 7.14.7 - resolution: "@babel/traverse@npm:7.14.7" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.16.0, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": + version: 7.16.0 + resolution: "@babel/types@npm:7.16.0" dependencies: - "@babel/code-frame": ^7.14.5 - "@babel/generator": ^7.14.5 - "@babel/helper-function-name": ^7.14.5 - "@babel/helper-hoist-variables": ^7.14.5 - "@babel/helper-split-export-declaration": ^7.14.5 - "@babel/parser": ^7.14.7 - "@babel/types": ^7.14.5 - debug: ^4.1.0 - globals: ^11.1.0 - checksum: 11e9162e46bdd6daef8691facbf5c47838f6e312ac775be35c40353c77887338d1b9ce497211d2ae96628a9230551f03eb3df49b4ca53b6f668082f2c157d1a0 - languageName: node - linkType: hard - -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.10.4, @babel/types@npm:^7.11.0, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3": - version: 7.11.0 - resolution: "@babel/types@npm:7.11.0" - dependencies: - "@babel/helper-validator-identifier": ^7.10.4 - lodash: ^4.17.19 + "@babel/helper-validator-identifier": ^7.15.7 to-fast-properties: ^2.0.0 - checksum: a87b9ebfce4d84d01e1299a8ab2d4489773295301b4b9fe47edc43f2534b45ae3cb0994a96d5fb5e757fb51fbe9216723a3e87f0e7f4922efa3403d1c82b2c51 - languageName: node - linkType: hard - -"@babel/types@npm:^7.14.5, @babel/types@npm:^7.8.3": - version: 7.14.5 - resolution: "@babel/types@npm:7.14.5" - dependencies: - "@babel/helper-validator-identifier": ^7.14.5 - to-fast-properties: ^2.0.0 - checksum: 7c1ab6e8bdf438d44236034cab10f7d0f1971179bc405dca26733a9b89dd87dd692dc49a238a7495075bc41a9a17fb6f08b4d1da45ea6ddcce1e5c8593574aea + checksum: 5b483da5c6e6f2394fba7ee1da8787a0c9cddd33491271c4da702e49e6faf95ce41d7c8bf9a4ee47f2ef06bdb35096f4d0f6ae4b5bea35ebefe16309d22344b7 languageName: node linkType: hard @@ -806,6 +565,22 @@ __metadata: languageName: node linkType: hard +"@cspotcode/source-map-consumer@npm:0.8.0": + version: 0.8.0 + resolution: "@cspotcode/source-map-consumer@npm:0.8.0" + checksum: c0c16ca3d2f58898f1bd74c4f41a189dbcc202e642e60e489cbcc2e52419c4e89bdead02c886a12fb13ea37798ede9e562b2321df997ebc210ae9bd881561b4e + languageName: node + linkType: hard + +"@cspotcode/source-map-support@npm:0.7.0": + version: 0.7.0 + resolution: "@cspotcode/source-map-support@npm:0.7.0" + dependencies: + "@cspotcode/source-map-consumer": 0.8.0 + checksum: 9faddda7757cd778b5fd6812137b2cc265810043680d6399acc20441668fafcdc874053be9dccd0d9110087287bfad27eb3bf342f72bceca9aa9059f5d0c4be8 + languageName: node + linkType: hard + "@cucumber/create-meta@npm:^5.0.0": version: 5.0.0 resolution: "@cucumber/create-meta@npm:5.0.0" @@ -816,17 +591,17 @@ __metadata: linkType: hard "@cucumber/cucumber-expressions@npm:^12.1.1": - version: 12.1.1 - resolution: "@cucumber/cucumber-expressions@npm:12.1.1" + version: 12.1.3 + resolution: "@cucumber/cucumber-expressions@npm:12.1.3" dependencies: - becke-ch--regex--s0-0-v1--base--pl--lib: ^1.4.0 - checksum: cff6d3016686331d144070c9d075b95727dd49fbc392d051fa7007d42b4f68fab242d57f6952afb9fee8dc939d5df199bb98edf0cf6dd8f88a44c74976b171e1 + regexp-match-indices: 1.0.2 + checksum: be96b56f793332efdb815d7e171239f6e2618970ef32e60ba354fa20ec04cf5b16f533f254782ad9ecc1c523612e5237971451e924bf180e1f41b25c98bb5d5f languageName: node linkType: hard "@cucumber/cucumber@npm:7.x": - version: 7.3.0 - resolution: "@cucumber/cucumber@npm:7.3.0" + version: 7.3.1 + resolution: "@cucumber/cucumber@npm:7.3.1" dependencies: "@cucumber/create-meta": ^5.0.0 "@cucumber/cucumber-expressions": ^12.1.1 @@ -863,7 +638,7 @@ __metadata: verror: ^1.10.0 bin: cucumber-js: bin/cucumber-js - checksum: 03cabba57f397d5a77ccaf8ed565a230cfb6ba8c5294b34de10225bd119958672c10b2150674a388e90ef2c9cd06d64499902e0fea02066a331ad335a71ed5c7 + checksum: b2f712592a4c8ce72792259581235ce5e4614212e56981768488e1d68eece938a214af2e54382841d8d53fecbe3daba7e814659cac87d7e8a3b95c18539c7f39 languageName: node linkType: hard @@ -939,8 +714,23 @@ __metadata: dependencies: "@commitlint/config-conventional": 15.x "@cucumber/cucumber": 7.x + "@types/babylon": 6.x + "@types/chai": 4.x + "@types/fs-extra": 9.x + "@types/glob": 7.x "@types/jest": 27.x + "@types/js-yaml": 4.x + "@types/lodash": 4.x + "@types/moment-timezone": 0.5.x + "@types/mustache": 4.x + "@types/natural-compare": 1.x "@types/node": 16.x + "@types/proxyquire": ^1.3.28 + "@types/request": 2.x + "@types/sinon": 10.x + "@types/tough-cookie": 4.x + "@typescript-eslint/eslint-plugin": 5.x + "@typescript-eslint/parser": 5.x arg: 5.0.1 babylon: 6.x chai: 4.3 @@ -949,10 +739,13 @@ __metadata: conventional-changelog-cli: 2.x coveralls: 3.x eslint: 8.x + eslint-config-prettier: 8.x + eslint-plugin-import: 2.x + eslint-plugin-prettier: 4.x fs-extra: 10.0.0 gh-pages: 3.x glob: 7.2.0 - husky: ^7.0.4 + husky: 7.0.4 jest: 27.x jest-diff: 27.4.2 js-yaml: 4.1.0 @@ -966,15 +759,17 @@ __metadata: nock: 13.x prettier: 2.x pretty-format: 27.4.2 + proxyquire: ^2.1.3 request: 2.88.2 sinon: 12.x tough-cookie: 4.0.0 ts-jest: 27.x + ts-node: 10.x typescript: 4.5.x peerDependencies: "@cucumber/cucumber": ">=7.0.0" bin: - veggies: ./bin/veggies.js + veggies: build/bin/veggies.js languageName: unknown linkType: soft @@ -1009,6 +804,13 @@ __metadata: languageName: node linkType: hard +"@gar/promisify@npm:^1.0.1": + version: 1.1.2 + resolution: "@gar/promisify@npm:1.1.2" + checksum: d05081e0887a49c178b75ee3067bd6ee086f73c154d121b854fb2e044e8a89cb1cbb6de3a0dd93a519b80f0531fda68b099dd7256205f7fbb3490324342f2217 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.9.2": version: 0.9.2 resolution: "@humanwhocodes/config-array@npm:0.9.2" @@ -1048,9 +850,9 @@ __metadata: linkType: hard "@istanbuljs/schema@npm:^0.1.2": - version: 0.1.2 - resolution: "@istanbuljs/schema@npm:0.1.2" - checksum: 5ce9facf2f0e3f4a93e56853cdfd78456e22d2c210c677530046e9c634ddc323dd62423ac711cd3554b5be06052c87fb8e0c266aa9010726940654c357290e78 + version: 0.1.3 + resolution: "@istanbuljs/schema@npm:0.1.3" + checksum: 5282759d961d61350f33d9118d16bcaed914ebf8061a52f4fa474b2cb08720c9c81d165e13b82f2e5a8a212cc5af482f0c6fc1ac27b9e067e5394c9a6ed186c9 languageName: node linkType: hard @@ -1068,14 +870,14 @@ __metadata: languageName: node linkType: hard -"@jest/core@npm:^27.4.4": - version: 27.4.4 - resolution: "@jest/core@npm:27.4.4" +"@jest/core@npm:^27.4.5": + version: 27.4.5 + resolution: "@jest/core@npm:27.4.5" dependencies: "@jest/console": ^27.4.2 - "@jest/reporters": ^27.4.4 + "@jest/reporters": ^27.4.5 "@jest/test-result": ^27.4.2 - "@jest/transform": ^27.4.4 + "@jest/transform": ^27.4.5 "@jest/types": ^27.4.2 "@types/node": "*" ansi-escapes: ^4.2.1 @@ -1084,15 +886,15 @@ __metadata: exit: ^0.1.2 graceful-fs: ^4.2.4 jest-changed-files: ^27.4.2 - jest-config: ^27.4.4 - jest-haste-map: ^27.4.4 + jest-config: ^27.4.5 + jest-haste-map: ^27.4.5 jest-message-util: ^27.4.2 jest-regex-util: ^27.4.0 - jest-resolve: ^27.4.4 - jest-resolve-dependencies: ^27.4.4 - jest-runner: ^27.4.4 - jest-runtime: ^27.4.4 - jest-snapshot: ^27.4.4 + jest-resolve: ^27.4.5 + jest-resolve-dependencies: ^27.4.5 + jest-runner: ^27.4.5 + jest-runtime: ^27.4.5 + jest-snapshot: ^27.4.5 jest-util: ^27.4.2 jest-validate: ^27.4.2 jest-watcher: ^27.4.2 @@ -1105,7 +907,7 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 2eac488307b19ba6751a03a8b07a1464913aacc4b4a5fcb28cea84555d76dcb77c6831574bc7aa477f396915813f303441b6f9392fe0c229d93634a1fb6d41a8 + checksum: d9332952196018abfc0b5cbbc9062f71872859bbe7a55b98788fc7b2f30fec1286d2dd882d8aa75fa14f5aeea8401a3eaacfed88dc86b159934dc35e06a2cadd languageName: node linkType: hard @@ -1146,14 +948,14 @@ __metadata: languageName: node linkType: hard -"@jest/reporters@npm:^27.4.4": - version: 27.4.4 - resolution: "@jest/reporters@npm:27.4.4" +"@jest/reporters@npm:^27.4.5": + version: 27.4.5 + resolution: "@jest/reporters@npm:27.4.5" dependencies: "@bcoe/v8-coverage": ^0.2.3 "@jest/console": ^27.4.2 "@jest/test-result": ^27.4.2 - "@jest/transform": ^27.4.4 + "@jest/transform": ^27.4.5 "@jest/types": ^27.4.2 "@types/node": "*" chalk: ^4.0.0 @@ -1166,10 +968,10 @@ __metadata: istanbul-lib-report: ^3.0.0 istanbul-lib-source-maps: ^4.0.0 istanbul-reports: ^3.0.2 - jest-haste-map: ^27.4.4 - jest-resolve: ^27.4.4 + jest-haste-map: ^27.4.5 + jest-resolve: ^27.4.5 jest-util: ^27.4.2 - jest-worker: ^27.4.4 + jest-worker: ^27.4.5 slash: ^3.0.0 source-map: ^0.6.0 string-length: ^4.0.1 @@ -1180,7 +982,7 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 67a6bf9ec4ac9a8ea68e3a1d0702ec3cb6daf5f51c95f17563e98bab9f49b016a61d5b2e2c483aac690fe63b33038a12944a3551c35cdb1ae81ecadffa64cb36 + checksum: d053edae6906171f29c50c6129a600dd10d00320adf6df57938efc651ddd98aecdf7e3f82c3778e77311e8358e57e337d21c391aa867c9c289366e7bd4d6cf2b languageName: node linkType: hard @@ -1207,21 +1009,21 @@ __metadata: languageName: node linkType: hard -"@jest/test-sequencer@npm:^27.4.4": - version: 27.4.4 - resolution: "@jest/test-sequencer@npm:27.4.4" +"@jest/test-sequencer@npm:^27.4.5": + version: 27.4.5 + resolution: "@jest/test-sequencer@npm:27.4.5" dependencies: "@jest/test-result": ^27.4.2 graceful-fs: ^4.2.4 - jest-haste-map: ^27.4.4 - jest-runtime: ^27.4.4 - checksum: 37e3df089fe15e7f7c2070c915267d89aae1dfe72f97ea210ee974f48a2797637276c46fa5481a09614c8107529578aea9e498f54e2b3ed3aa9f888d0f9e3460 + jest-haste-map: ^27.4.5 + jest-runtime: ^27.4.5 + checksum: b78376fe4b964f2fd7e71083c220e5f0a8f59f079dc88783c60fce969b09ea38eebabc32c50a4637c20679a8bfa8220abb814cd232d241ee385d4df3d93f7d21 languageName: node linkType: hard -"@jest/transform@npm:^27.4.4": - version: 27.4.4 - resolution: "@jest/transform@npm:27.4.4" +"@jest/transform@npm:^27.4.5": + version: 27.4.5 + resolution: "@jest/transform@npm:27.4.5" dependencies: "@babel/core": ^7.1.0 "@jest/types": ^27.4.2 @@ -1230,7 +1032,7 @@ __metadata: convert-source-map: ^1.4.0 fast-json-stable-stringify: ^2.0.0 graceful-fs: ^4.2.4 - jest-haste-map: ^27.4.4 + jest-haste-map: ^27.4.5 jest-regex-util: ^27.4.0 jest-util: ^27.4.2 micromatch: ^4.0.4 @@ -1238,7 +1040,7 @@ __metadata: slash: ^3.0.0 source-map: ^0.6.1 write-file-atomic: ^3.0.0 - checksum: 931e323b7dc390ab6205f97b69d0e35e5efeec44a55a536bc5e4d2dab4a07955e9d2f6f6bce7b54112d83e4659c81b1c680602642fcf32dce405f26f3bb1a1b3 + checksum: f7a479545969d327a253ff1963c20260cffdee50cbc1345205f06e206df09871dd3f62dd4ba5358a087587ef5fa320b2e32efe1166192d8da835065e99d6bce7 languageName: node linkType: hard @@ -1255,16 +1057,54 @@ __metadata: languageName: node linkType: hard -"@sinonjs/commons@npm:^1.6.0, @sinonjs/commons@npm:^1.7.0": - version: 1.8.1 - resolution: "@sinonjs/commons@npm:1.8.1" +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" dependencies: - type-detect: 4.0.8 - checksum: 8f258c039275d217b654c94731409208a3530c56f64ebc2a1bddaa82045800c7f9dbd09806d6dc451ee12cb79c7a5d509a58ad28179a83336da266ba5c231912 + "@nodelib/fs.stat": 2.0.5 + run-parallel: ^1.1.9 + checksum: a970d595bd23c66c880e0ef1817791432dbb7acbb8d44b7e7d0e7a22f4521260d4a83f7f9fd61d44fda4610105577f8f58a60718105fb38352baed612fd79e59 + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": 2.1.5 + fastq: ^1.6.0 + checksum: 190c643f156d8f8f277bf2a6078af1ffde1fd43f498f187c2db24d35b4b4b5785c02c7dc52e356497b9a1b65b13edc996de08de0b961c32844364da02986dc53 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^1.0.0": + version: 1.1.0 + resolution: "@npmcli/fs@npm:1.1.0" + dependencies: + "@gar/promisify": ^1.0.1 + semver: ^7.3.5 + checksum: e435b883b4f8da8c95a820f458cabb7d86582406eed5ad79fc689000d3e2df17e1f475c4903627272c001357cabc70d8b4c62520cbdae8cfab1dfdd51949f408 + languageName: node + linkType: hard + +"@npmcli/move-file@npm:^1.0.1": + version: 1.1.2 + resolution: "@npmcli/move-file@npm:1.1.2" + dependencies: + mkdirp: ^1.0.4 + rimraf: ^3.0.2 + checksum: c96381d4a37448ea280951e46233f7e541058cf57a57d4094dd4bdcaae43fa5872b5f2eb6bfb004591a68e29c5877abe3cdc210cb3588cbf20ab2877f31a7de7 languageName: node linkType: hard -"@sinonjs/commons@npm:^1.8.3": +"@sinonjs/commons@npm:^1.6.0, @sinonjs/commons@npm:^1.7.0, @sinonjs/commons@npm:^1.8.3": version: 1.8.3 resolution: "@sinonjs/commons@npm:1.8.3" dependencies: @@ -1273,7 +1113,7 @@ __metadata: languageName: node linkType: hard -"@sinonjs/fake-timers@npm:^7.0.4": +"@sinonjs/fake-timers@npm:^7.0.4, @sinonjs/fake-timers@npm:^7.1.0": version: 7.1.2 resolution: "@sinonjs/fake-timers@npm:7.1.2" dependencies: @@ -1316,82 +1156,130 @@ __metadata: languageName: node linkType: hard -"@types/babel__core@npm:^7.0.0": - version: 7.1.9 - resolution: "@types/babel__core@npm:7.1.9" - dependencies: - "@babel/parser": ^7.1.0 - "@babel/types": ^7.0.0 - "@types/babel__generator": "*" - "@types/babel__template": "*" - "@types/babel__traverse": "*" - checksum: d92c530efc3e50147f7956fc2aa49162508d2c5abffea7818051dfa9a9c9bc263b7d3da7d6fa826669ff8c68d733bbaced2dfdd486282b8bd013ccc3a0a3b7f6 +"@tsconfig/node10@npm:^1.0.7": + version: 1.0.8 + resolution: "@tsconfig/node10@npm:1.0.8" + checksum: b8d5fffbc6b17ef64ef74f7fdbccee02a809a063ade785c3648dae59406bc207f70ea2c4296f92749b33019fa36a5ae716e42e49cc7f1bbf0fd147be0d6b970a languageName: node linkType: hard -"@types/babel__core@npm:^7.1.14": - version: 7.1.15 - resolution: "@types/babel__core@npm:7.1.15" +"@tsconfig/node12@npm:^1.0.7": + version: 1.0.9 + resolution: "@tsconfig/node12@npm:1.0.9" + checksum: a01b2400ab3582b86b589c6d31dcd0c0656f333adecde85d6d7d4086adb059808b82692380bb169546d189bf771ae21d02544a75b57bd6da4a5dd95f8567bec9 + languageName: node + linkType: hard + +"@tsconfig/node14@npm:^1.0.0": + version: 1.0.1 + resolution: "@tsconfig/node14@npm:1.0.1" + checksum: 976345e896c0f059867f94f8d0f6ddb8b1844fb62bf36b727de8a9a68f024857e5db97ed51d3325e23e0616a5e48c034ff51a8d595b3fe7e955f3587540489be + languageName: node + linkType: hard + +"@tsconfig/node16@npm:^1.0.2": + version: 1.0.2 + resolution: "@tsconfig/node16@npm:1.0.2" + checksum: ca94d3639714672bbfd55f03521d3f56bb6a25479bd425da81faf21f13e1e9d15f40f97377dedbbf477a5841c5b0c8f4cd1b391f33553d750b9202c54c2c07aa + languageName: node + linkType: hard + +"@types/babel-types@npm:*": + version: 7.0.11 + resolution: "@types/babel-types@npm:7.0.11" + checksum: 9b02719c7cf5062c9ab656a3b4efe3dd79e7a4ab49b5a2ce18d4ec1cb326588a1b40fc03863c24c9dec962f56762c53ed06cdc2a1c241c84e76e54a37a6195fc + languageName: node + linkType: hard + +"@types/babel__core@npm:^7.0.0, @types/babel__core@npm:^7.1.14": + version: 7.1.17 + resolution: "@types/babel__core@npm:7.1.17" dependencies: "@babel/parser": ^7.1.0 "@babel/types": ^7.0.0 "@types/babel__generator": "*" "@types/babel__template": "*" "@types/babel__traverse": "*" - checksum: 3ea016369666a013564f8d3119ae987b3a3f1bdf31cc90e0d58714eea10d6b89a9fb1f6146290ee239ecc285800b246f18be930625c1d83e79d074842e43ab7d + checksum: 0108efab8acb6a8e0aab6f8113d5ef1fc4b58d40737aa70a3ee83112959e0880e5548374e7edb562e4e837cde4ae47265348b04eb7e684283b0dea418d013420 languageName: node linkType: hard "@types/babel__generator@npm:*": - version: 7.6.1 - resolution: "@types/babel__generator@npm:7.6.1" + version: 7.6.3 + resolution: "@types/babel__generator@npm:7.6.3" dependencies: "@babel/types": ^7.0.0 - checksum: d4b1f967fdf3fcc9648bce12ed3de5492abe645c643be36a3ce348051a7da405f2fea504821468a99bfb2089e622faa1fa85688ceb482bb6c8c4af85db31a752 + checksum: 0aa1881c47e3e471cabb9183ae42176591b168a6fe4714d205aec33a7e480d65a8a1ba7fcd9678337aadc34059dc5baa04841e5adfbbe67ae33bad79e7633b8e languageName: node linkType: hard "@types/babel__template@npm:*": - version: 7.0.2 - resolution: "@types/babel__template@npm:7.0.2" + version: 7.4.1 + resolution: "@types/babel__template@npm:7.4.1" dependencies: "@babel/parser": ^7.1.0 "@babel/types": ^7.0.0 - checksum: ce04f0ab702d7d4c753c09e08db3e61e5fc69375ea70f5c991110511b7286124070ca70e260e8074614f8a339424de7e387c08033eaf0a9f5c81a93e350965a8 + checksum: 649fe8b42c2876be1fd28c6ed9b276f78152d5904ec290b6c861d9ef324206e0a5c242e8305c421ac52ecf6358fa7e32ab7a692f55370484825c1df29b1596ee languageName: node linkType: hard -"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": - version: 7.0.13 - resolution: "@types/babel__traverse@npm:7.0.13" +"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.4, @types/babel__traverse@npm:^7.0.6": + version: 7.14.2 + resolution: "@types/babel__traverse@npm:7.14.2" dependencies: "@babel/types": ^7.3.0 - checksum: 93ce3fe15a52b33ad99181536dc55b959bd5457a6871ff105b7d619149c6bc85257294ae853fa226ba37bb68f63290fa82b1a172a746e81032fb84b0a0f4221a + checksum: a797ea09c72307569e3ee08aa3900ca744ce3091114084f2dc59b67a45ee7d01df7865252790dbfa787a7915ce892cdc820c9b920f3683292765fc656b08dc63 languageName: node linkType: hard -"@types/babel__traverse@npm:^7.0.4": - version: 7.14.2 - resolution: "@types/babel__traverse@npm:7.14.2" +"@types/babylon@npm:6.x": + version: 6.16.6 + resolution: "@types/babylon@npm:6.16.6" dependencies: - "@babel/types": ^7.3.0 - checksum: a797ea09c72307569e3ee08aa3900ca744ce3091114084f2dc59b67a45ee7d01df7865252790dbfa787a7915ce892cdc820c9b920f3683292765fc656b08dc63 + "@types/babel-types": "*" + checksum: c011e028bc09457b9a488e5ad8bc081b43c0c7919419343eaef95aeda5e5bb825dc93c323af8e60d97373702d94ae006016e7c641a9be24fef0d9f5ecc2e54c0 languageName: node linkType: hard -"@types/color-name@npm:^1.1.1": - version: 1.1.1 - resolution: "@types/color-name@npm:1.1.1" - checksum: b71fcad728cc68abcba1d405742134410c8f8eb3c2ef18113b047afca158ad23a4f2c229bcf71a38f4a818dead375c45b20db121d0e69259c2d81e97a740daa6 +"@types/caseless@npm:*": + version: 0.12.2 + resolution: "@types/caseless@npm:0.12.2" + checksum: 430d15911184ad11e0a8aa21d1ec15fcc93b90b63570c37bf16ebd34457482bfc8de3f5eb6771e0ef986ce183270d4297823b0f492c346255967e78f7292388b + languageName: node + linkType: hard + +"@types/chai@npm:4.x": + version: 4.3.0 + resolution: "@types/chai@npm:4.3.0" + checksum: 3e393e094263db65df28a0123dc13f342937c1bab6cd173eae913d593c5b9a16b555713a08c34863a1fbf079aa7222b96197c70380a5c130549d6b2f6845a989 + languageName: node + linkType: hard + +"@types/fs-extra@npm:9.x": + version: 9.0.13 + resolution: "@types/fs-extra@npm:9.0.13" + dependencies: + "@types/node": "*" + checksum: add79e212acd5ac76b97b9045834e03a7996aef60a814185e0459088fd290519a3c1620865d588fa36c4498bf614210d2a703af5cf80aa1dbc125db78f6edac3 + languageName: node + linkType: hard + +"@types/glob@npm:7.x": + version: 7.2.0 + resolution: "@types/glob@npm:7.2.0" + dependencies: + "@types/minimatch": "*" + "@types/node": "*" + checksum: 6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19 languageName: node linkType: hard "@types/graceful-fs@npm:^4.1.2": - version: 4.1.3 - resolution: "@types/graceful-fs@npm:4.1.3" + version: 4.1.5 + resolution: "@types/graceful-fs@npm:4.1.5" dependencies: "@types/node": "*" - checksum: f13a1b177e44ceafd430f1d5f63b7e7fd2a430c47d5dd1a20d10b88c1c5cdb033d96d539a084de79663b8735f72d31e8aa574f10daa622b1df16285f6a057383 + checksum: d076bb61f45d0fc42dee496ef8b1c2f8742e15d5e47e90e20d0243386e426c04d4efd408a48875ab432f7960b4ce3414db20ed0fbbfc7bcc89d84e574f6e045a languageName: node linkType: hard @@ -1412,11 +1300,11 @@ __metadata: linkType: hard "@types/istanbul-reports@npm:^3.0.0": - version: 3.0.0 - resolution: "@types/istanbul-reports@npm:3.0.0" + version: 3.0.1 + resolution: "@types/istanbul-reports@npm:3.0.1" dependencies: "@types/istanbul-lib-report": "*" - checksum: 286a18cff19c4dac4321b9ea406a3560faf577fb2a4df5abf9d577fa81ba831c9baa7d40d03f1daf7fe613d468546b731c00b844b72fad9834c583311a35bb7b + checksum: f1ad54bc68f37f60b30c7915886b92f86b847033e597f9b34f2415acdbe5ed742fa559a0a40050d74cdba3b6a63c342cac1f3a64dba5b68b66a6941f4abd7903 languageName: node linkType: hard @@ -1430,31 +1318,89 @@ __metadata: languageName: node linkType: hard +"@types/js-yaml@npm:4.x": + version: 4.0.5 + resolution: "@types/js-yaml@npm:4.0.5" + checksum: 7dcac8c50fec31643cc9d6444b5503239a861414cdfaa7ae9a38bc22597c4d850c4b8cec3d82d73b3fbca408348ce223b0408d598b32e094470dfffc6d486b4d + languageName: node + linkType: hard + +"@types/json-schema@npm:^7.0.9": + version: 7.0.9 + resolution: "@types/json-schema@npm:7.0.9" + checksum: 259d0e25f11a21ba5c708f7ea47196bd396e379fddb79c76f9f4f62c945879dc21657904914313ec2754e443c5018ea8372362f323f30e0792897fdb2098a705 + languageName: node + linkType: hard + +"@types/json5@npm:^0.0.29": + version: 0.0.29 + resolution: "@types/json5@npm:0.0.29" + checksum: e60b153664572116dfea673c5bda7778dbff150498f44f998e34b5886d8afc47f16799280e4b6e241c0472aef1bc36add771c569c68fc5125fc2ae519a3eb9ac + languageName: node + linkType: hard + +"@types/lodash@npm:4.x": + version: 4.14.178 + resolution: "@types/lodash@npm:4.14.178" + checksum: a69a04a60bfc5257c3130a554b4efa0c383f0141b7b3db8ab7cf07ad2a46ea085fce66d0242da41da7e5647b133d5dfb2c15add9cbed8d7fef955e4a1e5b3128 + languageName: node + linkType: hard + +"@types/minimatch@npm:*": + version: 3.0.5 + resolution: "@types/minimatch@npm:3.0.5" + checksum: c41d136f67231c3131cf1d4ca0b06687f4a322918a3a5adddc87ce90ed9dbd175a3610adee36b106ae68c0b92c637c35e02b58c8a56c424f71d30993ea220b92 + languageName: node + linkType: hard + "@types/minimist@npm:^1.2.0": - version: 1.2.0 - resolution: "@types/minimist@npm:1.2.0" - checksum: 30cbd9acd7ddb60bc3729adcc43a9da4940c90180fa0f08228f1da95ec6c00db2e3fd3af5280fc5345e3fa2637253bb5cf6625f30d571ef9bc3820a531febb7e + version: 1.2.2 + resolution: "@types/minimist@npm:1.2.2" + checksum: b8da83c66eb4aac0440e64674b19564d9d86c80ae273144db9681e5eeff66f238ade9515f5006ffbfa955ceff8b89ad2bd8ec577d7caee74ba101431fb07045d + languageName: node + linkType: hard + +"@types/moment-timezone@npm:0.5.x": + version: 0.5.13 + resolution: "@types/moment-timezone@npm:0.5.13" + dependencies: + moment: ">=2.14.0" + checksum: 35bd6414b790663e4879e38b4a344526ebe87b3f795929f9bc38f91f7caa373c65dd19a0e836182319f929d0f7fcff9b629efc6dbcef23d386bf0c87597f5b49 + languageName: node + linkType: hard + +"@types/mustache@npm:4.x": + version: 4.1.2 + resolution: "@types/mustache@npm:4.1.2" + checksum: 1f37849aae39b3188dbeac71734393586db80faa798795ea730db8e40d393c1d4d6a65ad8735a5ccf6dfc8b920d0792af1e6ef13e3ed2e02d204259678545240 + languageName: node + linkType: hard + +"@types/natural-compare@npm:1.x": + version: 1.4.1 + resolution: "@types/natural-compare@npm:1.4.1" + checksum: 30c6fc7a99b64f113c085bfea1ab98142e47240d3869d222048b597ea17e44e532634eb38adbea8850e69415f7e7ccc90161f247ea87521d9f804dc8389f3824 languageName: node linkType: hard "@types/node@npm:*": - version: 14.6.0 - resolution: "@types/node@npm:14.6.0" - checksum: 8c5a3a404932dd303c0ebad82d632fd83ecdd2dc900b198a256f650ee44c456f5d560ec0923cd66e2fe51b2ec08c5a7eb10df68b4b9cb66b8371c01da8d5d73c + version: 17.0.1 + resolution: "@types/node@npm:17.0.1" + checksum: 16a5aa2778e1e49443d272f0da20af66c143337ddb14d9f36d5e7eb1e11ebbf195769d3d495ec5456ec94fe3d0e703c5df1ac279a05c7dc2a1220474467c1103 languageName: node linkType: hard "@types/node@npm:16.x": - version: 16.11.12 - resolution: "@types/node@npm:16.11.12" - checksum: a3feb346d61a56f5a137c29bb8c63cfa3cc02e184b9dffdc18ef1528dcce55596e570575215a2e39e6ce69343eeb2a5ba71c271938f1dc8db4cc393902855412 + version: 16.11.17 + resolution: "@types/node@npm:16.11.17" + checksum: 86ed84f79450f6aba1a6ef09f8407c10076966c3cc7cc4eb3d35b8ae4f47817e525641396ef0a667fa0a88fcdf484f6182812c074601403083bdf3b5e1ac0313 languageName: node linkType: hard "@types/normalize-package-data@npm:^2.4.0": - version: 2.4.0 - resolution: "@types/normalize-package-data@npm:2.4.0" - checksum: fd22ba86a186a033dbe173840fd2ad091032be6d48163198869d058821acca7373d9f39cfd0caf42f3b92bc737723814fe1b4e9e90eacaa913836610aa197d3b + version: 2.4.1 + resolution: "@types/normalize-package-data@npm:2.4.1" + checksum: e87bccbf11f95035c89a132b52b79ce69a1e3652fe55962363063c9c0dae0fe2477ebc585e03a9652adc6f381d24ba5589cc5e51849df4ced3d3e004a7d40ed5 languageName: node linkType: hard @@ -1466,9 +1412,37 @@ __metadata: linkType: hard "@types/prettier@npm:^2.1.5": - version: 2.3.2 - resolution: "@types/prettier@npm:2.3.2" - checksum: c4313e16650811f47b07a0fa7ac0742e966f61283a7292eb667fd4626d760bf3b7d896be3eaabb3354ad45fdbe3a340299b018dd3bcce1ff753d030a8cd2479c + version: 2.4.2 + resolution: "@types/prettier@npm:2.4.2" + checksum: 76e230b2d11028af11fe12e09b2d5b10b03738e9abf819ae6ebb0f78cac13d39f860755ce05ac3855b608222518d956628f5d00322dc206cc6d1f2d8d1519f1e + languageName: node + linkType: hard + +"@types/proxyquire@npm:^1.3.28": + version: 1.3.28 + resolution: "@types/proxyquire@npm:1.3.28" + checksum: 9f0267b4516c2686a2e99a6d4dbd2ed660133180aad775a4000f3e103d8b2fe6b1bfe5b36003edf7adfb02542aed6c2fa6049fc217daea73ebced2942a762f27 + languageName: node + linkType: hard + +"@types/request@npm:2.x": + version: 2.48.7 + resolution: "@types/request@npm:2.48.7" + dependencies: + "@types/caseless": "*" + "@types/node": "*" + "@types/tough-cookie": "*" + form-data: ^2.5.0 + checksum: 83c2b682b19a74c7a507f5295a80b3ee85f0b64bc078cdc692ea9c1748ca9452cc739050889d2161f141f7ddac1b01790944ad05e08a6d4d9367fe7473cc6a87 + languageName: node + linkType: hard + +"@types/sinon@npm:10.x": + version: 10.0.6 + resolution: "@types/sinon@npm:10.0.6" + dependencies: + "@sinonjs/fake-timers": ^7.1.0 + checksum: 1c2ae7daa822014a558d513c1ae341aed676bfe678b9e48cf13a0ccc0eabc429f211371e8f10495d5eb156c0aedfeb3ad5253ebfe026fc14a5b77c461a6cea2a languageName: node linkType: hard @@ -1479,6 +1453,13 @@ __metadata: languageName: node linkType: hard +"@types/tough-cookie@npm:*, @types/tough-cookie@npm:4.x": + version: 4.0.1 + resolution: "@types/tough-cookie@npm:4.0.1" + checksum: 7570c1c2d74201f4ead3512cf8e4c99e97d92ab8a02ae2fb987fd720ced0ca1a2baf250c98a861a170b86762606c9bf6d32207675f13dffc5ab75c08c96578d2 + languageName: node + linkType: hard + "@types/uuid@npm:8.3.0": version: 8.3.0 resolution: "@types/uuid@npm:8.3.0" @@ -1487,9 +1468,9 @@ __metadata: linkType: hard "@types/yargs-parser@npm:*": - version: 15.0.0 - resolution: "@types/yargs-parser@npm:15.0.0" - checksum: 333ab73a1f9c82c64b2fac2441558e58f062fbe7affc35bb53b8e755b62cdd32b1bbc6f4da23773887a2189bf04395e2a8c710df344df4cd578993aeefe98053 + version: 20.2.1 + resolution: "@types/yargs-parser@npm:20.2.1" + checksum: 1d039e64494a7a61ddd278349a3dc60b19f99ff0517425696e796f794e4252452b9d62178e69755ad03f439f9dc0c8c3d7b3a1201b3a24e134bac1a09fa11eaa languageName: node linkType: hard @@ -1502,6 +1483,106 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/eslint-plugin@npm:5.x": + version: 5.8.1 + resolution: "@typescript-eslint/eslint-plugin@npm:5.8.1" + dependencies: + "@typescript-eslint/experimental-utils": 5.8.1 + "@typescript-eslint/scope-manager": 5.8.1 + debug: ^4.3.2 + functional-red-black-tree: ^1.0.1 + ignore: ^5.1.8 + regexpp: ^3.2.0 + semver: ^7.3.5 + tsutils: ^3.21.0 + peerDependencies: + "@typescript-eslint/parser": ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 9e5b5c1e22563fc0a31f1b916cea8b059b6dd218ccbf809b7453e4563065781e4544a6d5ce4cbf60b40394f2604e925d10cafd468a4dd0f490e75775267839a0 + languageName: node + linkType: hard + +"@typescript-eslint/experimental-utils@npm:5.8.1": + version: 5.8.1 + resolution: "@typescript-eslint/experimental-utils@npm:5.8.1" + dependencies: + "@types/json-schema": ^7.0.9 + "@typescript-eslint/scope-manager": 5.8.1 + "@typescript-eslint/types": 5.8.1 + "@typescript-eslint/typescript-estree": 5.8.1 + eslint-scope: ^5.1.1 + eslint-utils: ^3.0.0 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 15c17a7b7a45a9e1ebf537e6d6221e423c8f5114c0a517265698745b9a4ae965487ef7856a0b1ee64cbda8db641a9204270fda88398ab1d7013256e0ccbd3e75 + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:5.x": + version: 5.8.1 + resolution: "@typescript-eslint/parser@npm:5.8.1" + dependencies: + "@typescript-eslint/scope-manager": 5.8.1 + "@typescript-eslint/types": 5.8.1 + "@typescript-eslint/typescript-estree": 5.8.1 + debug: ^4.3.2 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: bb1702851ff0ade16a50789c517155557ad7e9b8e5e9c4553aad52fedbc8f94acaade1dc5ba12a96b54a13a68dfea13955ab885aad97cf3c526a8b90880bd8a3 + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:5.8.1": + version: 5.8.1 + resolution: "@typescript-eslint/scope-manager@npm:5.8.1" + dependencies: + "@typescript-eslint/types": 5.8.1 + "@typescript-eslint/visitor-keys": 5.8.1 + checksum: d9254018d723aff32fc512b7292737b154367198ab58e0faf814b4ce77d4de20552ed1678f2639b35e480eb5594eb9d5f1d34360885f5e4d80ca8e5a9ccf666c + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:5.8.1": + version: 5.8.1 + resolution: "@typescript-eslint/types@npm:5.8.1" + checksum: f9809c2c0f523841adeeb66410911f10492d3df7a912bc3d72304f4edbc5b5cb1a3f5f2a6ded20e8b524cc18e92d2a735fb8b96570e75df669061182932200ef + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:5.8.1": + version: 5.8.1 + resolution: "@typescript-eslint/typescript-estree@npm:5.8.1" + dependencies: + "@typescript-eslint/types": 5.8.1 + "@typescript-eslint/visitor-keys": 5.8.1 + debug: ^4.3.2 + globby: ^11.0.4 + is-glob: ^4.0.3 + semver: ^7.3.5 + tsutils: ^3.21.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: e3cfbd088f1e0104b5b38fcc6e400a0d0e72395694406357e478369c4df532aa2accfe2ee77c71854ca9a04e0e3cddbed86388334805c91ca4241b032cbb6d20 + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:5.8.1": + version: 5.8.1 + resolution: "@typescript-eslint/visitor-keys@npm:5.8.1" + dependencies: + "@typescript-eslint/types": 5.8.1 + eslint-visitor-keys: ^3.0.0 + checksum: 46567678718a227b34a255a3606e1a2c5190a470dc9493d4c175f57566d2c16b88780fb273ca44f22cab06d45d87b25371215e93b88ac10a475877bd64bdfece + languageName: node + linkType: hard + "JSONStream@npm:^1.0.4": version: 1.3.5 resolution: "JSONStream@npm:1.3.5" @@ -1514,14 +1595,7 @@ __metadata: languageName: node linkType: hard -"abab@npm:^2.0.3": - version: 2.0.4 - resolution: "abab@npm:2.0.4" - checksum: 2aab16527f1ff727ab416ab0d9b62d5fd79341b972fcd2408253bec2b83585295dae8e4228f2a564da0bee9ad6c82d6aaa14f4d9988d0dfa6eabdaba362765c0 - languageName: node - linkType: hard - -"abab@npm:^2.0.5": +"abab@npm:^2.0.3, abab@npm:^2.0.5": version: 2.0.5 resolution: "abab@npm:2.0.5" checksum: 0ec951b46d5418c2c2f923021ec193eaebdb4e802ffd5506286781b454be722a13a8430f98085cd3e204918401d9130ec6cc8f5ae19be315b3a0e857d83196e1 @@ -1561,25 +1635,23 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^7.1.1": - version: 7.4.0 - resolution: "acorn@npm:7.4.0" - bin: - acorn: bin/acorn - checksum: 1cbf7cae01f8fdc9ee2c65294b7f0a741a67760b22fee4ea3bbbffd0102fc76b07cd7437494221df7f7e51e75fdff3dae4bf11763d29e310e779fc61d3378ad5 +"acorn-walk@npm:^8.1.1": + version: 8.2.0 + resolution: "acorn-walk@npm:8.2.0" + checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 languageName: node linkType: hard -"acorn@npm:^8.2.4": - version: 8.4.1 - resolution: "acorn@npm:8.4.1" +"acorn@npm:^7.1.1": + version: 7.4.1 + resolution: "acorn@npm:7.4.1" bin: acorn: bin/acorn - checksum: 0a8fd264349285aa36194b26a5a9d70c3641e78ad459ec44b9a9a5738e0ce6d86ec120ca2c0f04477165cee912fdeb158f62d6582697185c82278bdbf71187f8 + checksum: 1860f23c2107c910c6177b7b7be71be350db9e1080d814493fae143ae37605189504152d1ba8743ba3178d0b37269ce1ffc42b101547fdc1827078f82671e407 languageName: node linkType: hard -"acorn@npm:^8.6.0": +"acorn@npm:^8.2.4, acorn@npm:^8.6.0": version: 8.6.0 resolution: "acorn@npm:8.6.0" bin: @@ -1588,6 +1660,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.4.1": + version: 8.7.0 + resolution: "acorn@npm:8.7.0" + bin: + acorn: bin/acorn + checksum: e0f79409d68923fbf1aa6d4166f3eedc47955320d25c89a20cc822e6ba7c48c5963d5bc657bc242d68f7a4ac9faf96eef033e8f73656da6c640d4219935fdfd0 + languageName: node + linkType: hard + "add-stream@npm:^1.0.0": version: 1.0.0 resolution: "add-stream@npm:1.0.0" @@ -1595,7 +1676,7 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:6": +"agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" dependencies: @@ -1604,29 +1685,28 @@ __metadata: languageName: node linkType: hard -"aggregate-error@npm:^3.0.0": - version: 3.0.1 - resolution: "aggregate-error@npm:3.0.1" +"agentkeepalive@npm:^4.1.3": + version: 4.1.4 + resolution: "agentkeepalive@npm:4.1.4" dependencies: - clean-stack: ^2.0.0 - indent-string: ^4.0.0 - checksum: 1f922d00cc51cf9f7f6f729c0b925689ed5a464aefc1fac8309924f622000ee3741d314d864b2d776f9627236ea79daf5a83d093f6b72edc52160571160eff82 + debug: ^4.1.0 + depd: ^1.1.2 + humanize-ms: ^1.2.1 + checksum: d49c24d4b333e9507119385895a583872f4f53d62764a89be165926e824056a126955bae4a6d3c6f7cd26f4089621a40f7b27675f7868214d82118f744b9e82d languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.12.3": - version: 6.12.4 - resolution: "ajv@npm:6.12.4" +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" dependencies: - fast-deep-equal: ^3.1.1 - fast-json-stable-stringify: ^2.0.0 - json-schema-traverse: ^0.4.1 - uri-js: ^4.2.2 - checksum: 8713e39b0e29fe0e1d1002438990e78e6804f965218b34bde9651c2c731a44007dae7f7c7136072ca4da4e7ab5ce03c08b806c674d3ad436d808ff363d9c2f9f + clean-stack: ^2.0.0 + indent-string: ^4.0.0 + checksum: 1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 languageName: node linkType: hard -"ajv@npm:^6.12.4": +"ajv@npm:^6.10.0, ajv@npm:^6.12.3, ajv@npm:^6.12.4": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -1646,32 +1726,11 @@ __metadata: linkType: hard "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": - version: 4.3.1 - resolution: "ansi-escapes@npm:4.3.1" + version: 4.3.2 + resolution: "ansi-escapes@npm:4.3.2" dependencies: - type-fest: ^0.11.0 - checksum: c4962c1791cc4e29efb9976680bad7b23f322ca039e588406680fffc8b6bc6e223721193eb481dab076309d9a7371bbfc4e835efe5fe267e3395ffa047da239d - languageName: node - linkType: hard - -"ansi-regex@npm:^2.0.0": - version: 2.1.1 - resolution: "ansi-regex@npm:2.1.1" - checksum: 190abd03e4ff86794f338a31795d262c1dfe8c91f7e01d04f13f646f1dcb16c5800818f886047876f1272f065570ab86b24b99089f8b68a0e11ff19aed4ca8f1 - languageName: node - linkType: hard - -"ansi-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "ansi-regex@npm:3.0.0" - checksum: 2ad11c416f81c39f5c65eafc88cf1d71aa91d76a2f766e75e457c2a3c43e8a003aadbf2966b61c497aa6a6940a36412486c975b3270cdfc3f413b69826189ec3 - languageName: node - linkType: hard - -"ansi-regex@npm:^5.0.0": - version: 5.0.0 - resolution: "ansi-regex@npm:5.0.0" - checksum: b1bb4e992a5d96327bb4f72eaba9f8047f1d808d273ad19d399e266bfcc7fb19a4d1a127a32f7bc61fe46f1a94a4d04ec4c424e3fbe184929aa866323d8ed4ce + type-fest: ^0.21.3 + checksum: 93111c42189c0a6bed9cdb4d7f2829548e943827ee8479c74d6e0b22ee127b2a21d3f8b5ca57723b8ef78ce011fbfc2784350eb2bde3ccfccf2f575fa8489815 languageName: node linkType: hard @@ -1699,12 +1758,11 @@ __metadata: linkType: hard "ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": - version: 4.2.1 - resolution: "ansi-styles@npm:4.2.1" + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" dependencies: - "@types/color-name": ^1.1.1 color-convert: ^2.0.1 - checksum: 7c74dbc7ec912b9e45dacbfaa7e2513bea6aa24d5357a0cd3255e7f83ecfc62e1454c77ab150a8df60de700c83c17fbbf040e7c204b4b6fc7aa250c8afcb865f + checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 languageName: node linkType: hard @@ -1730,29 +1788,29 @@ __metadata: linkType: hard "anymatch@npm:^3.0.3": - version: 3.1.1 - resolution: "anymatch@npm:3.1.1" + version: 3.1.2 + resolution: "anymatch@npm:3.1.2" dependencies: normalize-path: ^3.0.0 picomatch: ^2.0.4 - checksum: c951385862bf114807d594bdffccb769bd7219ddc14f24fc135cde075ad2477a97991567b8bb5032d4f279f96897f0c2af6468a350a6c674ac0a5ee3b62a26d6 + checksum: 985163db2292fac9e5a1e072bf99f1b5baccf196e4de25a0b0b81865ebddeb3b3eb4480734ef0a2ac8c002845396b91aa89121f5b84f93981a4658164a9ec6e9 languageName: node linkType: hard -"aproba@npm:^1.0.3": - version: 1.2.0 - resolution: "aproba@npm:1.2.0" - checksum: 0fca141966559d195072ed047658b6e6c4fe92428c385dd38e288eacfc55807e7b4989322f030faff32c0f46bb0bc10f1e0ac32ec22d25315a1e5bbc0ebb76dc +"aproba@npm:^1.0.3 || ^2.0.0": + version: 2.0.0 + resolution: "aproba@npm:2.0.0" + checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 languageName: node linkType: hard -"are-we-there-yet@npm:~1.1.2": - version: 1.1.5 - resolution: "are-we-there-yet@npm:1.1.5" +"are-we-there-yet@npm:^2.0.0": + version: 2.0.0 + resolution: "are-we-there-yet@npm:2.0.0" dependencies: delegates: ^1.0.0 - readable-stream: ^2.0.6 - checksum: 9a746b1dbce4122f44002b0c39fbba5b2c6f52c00e88b6ccba6fc68652323f8a1355a20e8ab94846995626d8de3bf67669a3b4a037dff0885db14607168f2b15 + readable-stream: ^3.6.0 + checksum: 6c80b4fd04ecee6ba6e737e0b72a4b41bdc64b7d279edfc998678567ff583c8df27e27523bc789f2c99be603ffa9eaa612803da1d886962d2086e7ff6fa90c7c languageName: node linkType: hard @@ -1793,6 +1851,19 @@ __metadata: languageName: node linkType: hard +"array-includes@npm:^3.1.4": + version: 3.1.4 + resolution: "array-includes@npm:3.1.4" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + es-abstract: ^1.19.1 + get-intrinsic: ^1.1.1 + is-string: ^1.0.7 + checksum: 69967c38c52698f84b50a7aed5554aadc89c6ac6399b6d92ad061a5952f8423b4bba054c51d40963f791dfa294d7247cdd7988b6b1f2c5861477031c6386e1c0 + languageName: node + linkType: hard + "array-union@npm:^1.0.1": version: 1.0.2 resolution: "array-union@npm:1.0.2" @@ -1802,6 +1873,13 @@ __metadata: languageName: node linkType: hard +"array-union@npm:^2.1.0": + version: 2.1.0 + resolution: "array-union@npm:2.1.0" + checksum: 5bee12395cba82da674931df6d0fea23c4aa4660cb3b338ced9f828782a65caa232573e6bf3968f23e0c5eb301764a382cef2f128b170a9dc59de0e36c39f98d + languageName: node + linkType: hard + "array-uniq@npm:^1.0.1": version: 1.0.3 resolution: "array-uniq@npm:1.0.3" @@ -1809,6 +1887,17 @@ __metadata: languageName: node linkType: hard +"array.prototype.flat@npm:^1.2.5": + version: 1.2.5 + resolution: "array.prototype.flat@npm:1.2.5" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + es-abstract: ^1.19.0 + checksum: 9cc6414b111abfc7717e39546e4887b1e5ec74df8f1618d83425deaa95752bf05d475d1d241253b4d88d4a01f8e1bc84845ad5b7cc2047f8db2f614512acd40e + languageName: node + linkType: hard + "arrify@npm:^1.0.1": version: 1.0.1 resolution: "arrify@npm:1.0.1" @@ -1817,11 +1906,11 @@ __metadata: linkType: hard "asn1@npm:~0.2.3": - version: 0.2.4 - resolution: "asn1@npm:0.2.4" + version: 0.2.6 + resolution: "asn1@npm:0.2.6" dependencies: safer-buffer: ~2.1.0 - checksum: aa5d6f77b1e0597df53824c68cfe82d1d89ce41cb3520148611f025fbb3101b2d25dd6a40ad34e4fac10f6b19ed5e8628cd4b7d212261e80e83f02b39ee5663c + checksum: 39f2ae343b03c15ad4f238ba561e626602a3de8d94ae536c46a4a93e69578826305366dc09fbb9b56aec39b4982a463682f259c38e59f6fa380cd72cd61e493d languageName: node linkType: hard @@ -1881,17 +1970,17 @@ __metadata: linkType: hard "aws4@npm:^1.8.0": - version: 1.10.1 - resolution: "aws4@npm:1.10.1" - checksum: 290a22fc1168d32bbd924d8b6eef71510a2ab983f4d6edaaa211c696229bcc774574d0091943db62f7e8b2c497daf1895c95392866e5c5307c3435ab3fcaedf8 + version: 1.11.0 + resolution: "aws4@npm:1.11.0" + checksum: 5a00d045fd0385926d20ebebcfba5ec79d4482fe706f63c27b324d489a04c68edb0db99ed991e19eda09cb8c97dc2452059a34d97545cebf591d7a2b5a10999f languageName: node linkType: hard -"babel-jest@npm:^27.4.4": - version: 27.4.4 - resolution: "babel-jest@npm:27.4.4" +"babel-jest@npm:^27.4.5": + version: 27.4.5 + resolution: "babel-jest@npm:27.4.5" dependencies: - "@jest/transform": ^27.4.4 + "@jest/transform": ^27.4.5 "@jest/types": ^27.4.2 "@types/babel__core": ^7.1.14 babel-plugin-istanbul: ^6.0.0 @@ -1901,20 +1990,20 @@ __metadata: slash: ^3.0.0 peerDependencies: "@babel/core": ^7.8.0 - checksum: 2d9de4f050f5c976a88f2afe5f7c9c53a5a1420320c70e46186d05dfe53eddccbcded80ef4ade80f4a1cacd940f6c934357e229b1f9666b00776c0d358d95fa6 + checksum: 986601fd143e6bdd9b9c176ade5c1f93a63e38beba511527183fec5f1041920f1262fcb3f87e8660c85fc6cc731d5d49570b35d54c31427644c6849caa137d89 languageName: node linkType: hard "babel-plugin-istanbul@npm:^6.0.0": - version: 6.0.0 - resolution: "babel-plugin-istanbul@npm:6.0.0" + version: 6.1.1 + resolution: "babel-plugin-istanbul@npm:6.1.1" dependencies: "@babel/helper-plugin-utils": ^7.0.0 "@istanbuljs/load-nyc-config": ^1.0.0 "@istanbuljs/schema": ^0.1.2 - istanbul-lib-instrument: ^4.0.0 + istanbul-lib-instrument: ^5.0.4 test-exclude: ^6.0.0 - checksum: bc586cf088ec471a98a474ef0e9361ace61947da2a3e54162f1e1ab712a1a81a88007639e8aff7db2fc8678ae7c671e696e6edd6ccf72db8e6af86f0628d5a08 + checksum: cb4fd95738219f232f0aece1116628cccff16db891713c4ccb501cddbbf9272951a5df81f2f2658dfdf4b3e7b236a9d5cbcf04d5d8c07dd5077297339598061a languageName: node linkType: hard @@ -1974,9 +2063,9 @@ __metadata: linkType: hard "balanced-match@npm:^1.0.0": - version: 1.0.0 - resolution: "balanced-match@npm:1.0.0" - checksum: 9b67bfe558772f40cf743a3469b48b286aecec2ea9fe80c48d74845e53aab1cef524fafedf123a63019b49ac397760573ef5f173f539423061f7217cbb5fbd40 + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 languageName: node linkType: hard @@ -1989,13 +2078,6 @@ __metadata: languageName: node linkType: hard -"becke-ch--regex--s0-0-v1--base--pl--lib@npm:^1.4.0": - version: 1.4.0 - resolution: "becke-ch--regex--s0-0-v1--base--pl--lib@npm:1.4.0" - checksum: 45d23b80ab851623b427af95e87a23126a8c168f1ba73bdd5209169297ebefb893d8c6210ff1a51c76174eb0c5112c4ce0bbf3883d90907e3ccb9287b77b3f91 - languageName: node - linkType: hard - "bluebird@npm:^3.7.2": version: 3.7.2 resolution: "bluebird@npm:3.7.2" @@ -2029,18 +2111,18 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.16.6": - version: 4.16.6 - resolution: "browserslist@npm:4.16.6" +"browserslist@npm:^4.17.5": + version: 4.19.1 + resolution: "browserslist@npm:4.19.1" dependencies: - caniuse-lite: ^1.0.30001219 - colorette: ^1.2.2 - electron-to-chromium: ^1.3.723 + caniuse-lite: ^1.0.30001286 + electron-to-chromium: ^1.4.17 escalade: ^3.1.1 - node-releases: ^1.1.71 + node-releases: ^2.0.1 + picocolors: ^1.0.0 bin: browserslist: cli.js - checksum: 3dffc86892d2dcfcfc66b52519b7e5698ae070b4fc92ab047e760efc4cae0474e9e70bbe10d769c8d3491b655ef3a2a885b88e7196c83cc5dc0a46dfdba8b70c + checksum: c0777fd483691638fd6801e16c9d809e1d65f6d2b06db2e806654be51045cbab1452a89841a2c5caea2cbe19d621b4f1d391cffbb24512aa33280039ab345875 languageName: node linkType: hard @@ -2063,9 +2145,45 @@ __metadata: linkType: hard "buffer-from@npm:^1.0.0": - version: 1.1.1 - resolution: "buffer-from@npm:1.1.1" - checksum: ccc53b69736008bff764497367c4d24879ba7122bc619ee499ff47eef3a5b885ca496e87272e7ebffa0bec3804c83f84041c616f6e3318f40624e27c1d80f045 + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 0448524a562b37d4d7ed9efd91685a5b77a50672c556ea254ac9a6d30e3403a517d8981f10e565db24e8339413b43c97ca2951f10e399c6125a0d8911f5679bb + languageName: node + linkType: hard + +"cacache@npm:^15.2.0": + version: 15.3.0 + resolution: "cacache@npm:15.3.0" + dependencies: + "@npmcli/fs": ^1.0.0 + "@npmcli/move-file": ^1.0.1 + chownr: ^2.0.0 + fs-minipass: ^2.0.0 + glob: ^7.1.4 + infer-owner: ^1.0.4 + lru-cache: ^6.0.0 + minipass: ^3.1.1 + minipass-collect: ^1.0.2 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.2 + mkdirp: ^1.0.3 + p-map: ^4.0.0 + promise-inflight: ^1.0.1 + rimraf: ^3.0.2 + ssri: ^8.0.1 + tar: ^6.0.2 + unique-filename: ^1.1.1 + checksum: a07327c27a4152c04eb0a831c63c00390d90f94d51bb80624a66f4e14a6b6360bbf02a84421267bd4d00ca73ac9773287d8d7169e8d2eafe378d2ce140579db8 + languageName: node + linkType: hard + +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind@npm:1.0.2" + dependencies: + function-bind: ^1.1.1 + get-intrinsic: ^1.0.2 + checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 languageName: node linkType: hard @@ -2087,7 +2205,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": +"camelcase@npm:^5.3.1": version: 5.3.1 resolution: "camelcase@npm:5.3.1" checksum: e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b @@ -2095,16 +2213,16 @@ __metadata: linkType: hard "camelcase@npm:^6.2.0": - version: 6.2.0 - resolution: "camelcase@npm:6.2.0" - checksum: 8335cfd0ecc472eae685896a42afd8c9dacd193a91f569120b931c87deb053a1ba82102031b9b48a4dbc1d18066caeacf2e4ace8c3c7f0d02936d348dc0b5a87 + version: 6.2.1 + resolution: "camelcase@npm:6.2.1" + checksum: d876272ef76391ebf8442fb7ea1d77e80ae179ce1339e021a8731b4895fd190dc19e148e045469cff5825d4c089089f3fff34d804d3f49115d55af97dd6ac0af languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001219": - version: 1.0.30001243 - resolution: "caniuse-lite@npm:1.0.30001243" - checksum: 912cf885036847a8b6972cd65170d9f355a08b08cfd6b06bcaa8de965755ee5b35a4bbe4eb73844aac313a42e2038ae0971a5b4c9b94d631db2f073c906ce1d6 +"caniuse-lite@npm:^1.0.30001286": + version: 1.0.30001291 + resolution: "caniuse-lite@npm:1.0.30001291" + checksum: ae24be79227036564ccd2ab8a0be8793a2e650941607a9f3e68a967db08d90cf17ded0382c2ce87063051b7200e618ec83bdb12f423ed60665922dc4d8eb8f78 languageName: node linkType: hard @@ -2149,7 +2267,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:4.x": +"chalk@npm:4.x, chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -2170,16 +2288,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0": - version: 4.1.0 - resolution: "chalk@npm:4.1.0" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: 5561c7b4c063badee3e16d04bce50bd033e1be1bf4c6948639275683ffa7a1993c44639b43c22b1c505f0f813a24b1889037eb182546b48946f9fe7cdd0e7d13 - languageName: node - linkType: hard - "char-regex@npm:^1.0.2": version: 1.0.2 resolution: "char-regex@npm:1.0.2" @@ -2209,9 +2317,9 @@ __metadata: linkType: hard "cjs-module-lexer@npm:^1.0.0": - version: 1.2.1 - resolution: "cjs-module-lexer@npm:1.2.1" - checksum: 9e9905e3f5b8b1f262d10ebb0d33407d25a48d0341acd3215ed402f9284188183f14d577340a171f75fd137b7654a780bcb6008dee9e9bd12957174f6c0e4661 + version: 1.2.2 + resolution: "cjs-module-lexer@npm:1.2.2" + checksum: 977f3f042bd4f08e368c890d91eecfbc4f91da0bc009a3c557bc4dfbf32022ad1141244ac1178d44de70fc9f3dea7add7cd9a658a34b9fae98a55d8f92331ce5 languageName: node linkType: hard @@ -2290,13 +2398,6 @@ __metadata: languageName: node linkType: hard -"code-point-at@npm:^1.0.0": - version: 1.1.0 - resolution: "code-point-at@npm:1.1.0" - checksum: 17d5666611f9b16d64fdf48176d9b7fb1c7d1c1607a189f7e600040a11a6616982876af148230336adb7d8fe728a559f743a4e29db3747e3b1a32fa7f4529681 - languageName: node - linkType: hard - "collect-v8-coverage@npm:^1.0.0": version: 1.0.1 resolution: "collect-v8-coverage@npm:1.0.1" @@ -2336,10 +2437,12 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^1.2.2": - version: 1.2.2 - resolution: "colorette@npm:1.2.2" - checksum: 69fec14ddaedd0f5b00e4bae40dc4bc61f7050ebdc82983a595d6fd64e650b9dc3c033fff378775683138e992e0ddd8717ac7c7cec4d089679dcfbe3cd921b04 +"color-support@npm:^1.1.2": + version: 1.1.3 + resolution: "color-support@npm:1.1.3" + bin: + color-support: bin.js + checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b languageName: node linkType: hard @@ -2423,24 +2526,14 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.0.0, console-control-strings@npm:~1.1.0": +"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed languageName: node linkType: hard -"conventional-changelog-angular@npm:^5.0.11": - version: 5.0.11 - resolution: "conventional-changelog-angular@npm:5.0.11" - dependencies: - compare-func: ^2.0.0 - q: ^1.5.1 - checksum: 40e479430e6a66a28ff3177736df535ae9e80ff4867c5accab3ec2f8bfb5330f1fa7053db09540694698195821077eab437920dd45c218f74afbf43dab7b7b3c - languageName: node - linkType: hard - -"conventional-changelog-angular@npm:^5.0.12": +"conventional-changelog-angular@npm:^5.0.11, conventional-changelog-angular@npm:^5.0.12": version: 5.0.13 resolution: "conventional-changelog-angular@npm:5.0.13" dependencies: @@ -2483,18 +2576,7 @@ __metadata: languageName: node linkType: hard -"conventional-changelog-conventionalcommits@npm:^4.3.1": - version: 4.6.0 - resolution: "conventional-changelog-conventionalcommits@npm:4.6.0" - dependencies: - compare-func: ^2.0.0 - lodash: ^4.17.15 - q: ^1.5.1 - checksum: b7dccba3e91ee9122f5b5e214e891f9dc516cd7432894fef8e3bf60d76bd8618bb252621c23fe628ec0eda2032ba2e253e0177317aa133452ef62b9703b84778 - languageName: node - linkType: hard - -"conventional-changelog-conventionalcommits@npm:^4.5.0": +"conventional-changelog-conventionalcommits@npm:^4.3.1, conventional-changelog-conventionalcommits@npm:^4.5.0": version: 4.6.1 resolution: "conventional-changelog-conventionalcommits@npm:4.6.1" dependencies: @@ -2645,31 +2727,38 @@ __metadata: linkType: hard "convert-source-map@npm:^1.4.0, convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0": - version: 1.7.0 - resolution: "convert-source-map@npm:1.7.0" + version: 1.8.0 + resolution: "convert-source-map@npm:1.8.0" dependencies: safe-buffer: ~5.1.1 - checksum: bcd2e3ea7d37f96b85a6e362c8a89402ccc73757256e3ee53aa2c22fe915adb854c66b1f81111be815a3a6a6ce3c58e8001858e883c9d5b4fe08a853fa865967 + checksum: 985d974a2d33e1a2543ada51c93e1ba2f73eaed608dc39f229afc78f71dcc4c8b7d7c684aa647e3c6a3a204027444d69e53e169ce94e8d1fa8d7dee80c9c8fed languageName: node linkType: hard -"core-util-is@npm:1.0.2, core-util-is@npm:~1.0.0": +"core-util-is@npm:1.0.2": version: 1.0.2 resolution: "core-util-is@npm:1.0.2" checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab languageName: node linkType: hard +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 9de8597363a8e9b9952491ebe18167e3b36e7707569eed0ebf14f8bba773611376466ae34575bca8cfe3c767890c859c74056084738f09d4e4a6f902b2ad7d99 + languageName: node + linkType: hard + "cosmiconfig@npm:^7.0.0": - version: 7.0.0 - resolution: "cosmiconfig@npm:7.0.0" + version: 7.0.1 + resolution: "cosmiconfig@npm:7.0.1" dependencies: "@types/parse-json": ^4.0.0 import-fresh: ^3.2.1 parse-json: ^5.0.0 path-type: ^4.0.0 yaml: ^1.10.0 - checksum: 6801feaa0249e9b9fdde5b3d70dc33b4f9c69095bec94d67e3fe08b66eac24dc7e2099f053597cfbc94b743de269aa5d2cfa7da3fde765433423b06bd122941a + checksum: 4be63e7117955fd88333d7460e4c466a90f556df6ef34efd59034d2463484e339666c41f02b523d574a797ec61f4a91918c5b89a316db2ea2f834e0d2d09465b languageName: node linkType: hard @@ -2773,36 +2862,33 @@ __metadata: languageName: node linkType: hard -"debug@npm:4": - version: 4.3.2 - resolution: "debug@npm:4.3.2" +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3": + version: 4.3.3 + resolution: "debug@npm:4.3.3" dependencies: ms: 2.1.2 peerDependenciesMeta: supports-color: optional: true - checksum: 820ea160e267e23c953c9ed87e7ad93494d8cda2f7349af5e7e3bb236d23707ee3022f477d5a7d2ee86ef2bf7d60aa9ab22d1f58080d7deb9dccd073585e1e43 + checksum: 14472d56fe4a94dbcfaa6dbed2dd3849f1d72ba78104a1a328047bb564643ca49df0224c3a17fa63533fd11dd3d4c8636cd861191232a2c6735af00cc2d4de16 languageName: node linkType: hard -"debug@npm:^4.1.0, debug@npm:^4.1.1": - version: 4.1.1 - resolution: "debug@npm:4.1.1" +"debug@npm:^2.6.9": + version: 2.6.9 + resolution: "debug@npm:2.6.9" dependencies: - ms: ^2.1.1 - checksum: 1e681f5cce94ba10f8dde74b20b42e4d8cf0d2a6700f4c165bb3bb6885565ef5ca5885bf07e704974a835f2415ff095a63164f539988a1f07e8a69fe8b1d65ad + ms: 2.0.0 + checksum: d2f51589ca66df60bf36e1fa6e4386b318c3f1e06772280eea5b1ae9fd3d05e9c2b7fd8a7d862457d00853c75b00451aa2d7459b924629ee385287a650f58fe6 languageName: node linkType: hard -"debug@npm:^4.3.2": - version: 4.3.3 - resolution: "debug@npm:4.3.3" +"debug@npm:^3.2.7": + version: 3.2.7 + resolution: "debug@npm:3.2.7" dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 14472d56fe4a94dbcfaa6dbed2dd3849f1d72ba78104a1a328047bb564643ca49df0224c3a17fa63533fd11dd3d4c8636cd861191232a2c6735af00cc2d4de16 + ms: ^2.1.1 + checksum: b3d8c5940799914d30314b7c3304a43305fd0715581a919dacb8b3176d024a782062368405b47491516d2091d6462d4d11f2f4974a405048094f8bfebfa3071c languageName: node linkType: hard @@ -2816,7 +2902,7 @@ __metadata: languageName: node linkType: hard -"decamelize@npm:^1.1.0, decamelize@npm:^1.2.0": +"decamelize@npm:^1.1.0": version: 1.2.0 resolution: "decamelize@npm:1.2.0" checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa @@ -2847,9 +2933,9 @@ __metadata: linkType: hard "deep-is@npm:^0.1.3, deep-is@npm:~0.1.3": - version: 0.1.3 - resolution: "deep-is@npm:0.1.3" - checksum: c15b04c3848a89880c94e25b077c19b47d9a30dd99048e70e5f95d943e7b246bee1da0c1376b56b01bc045be2cae7d9b1c856e68e47e9805634327de7c6cb6d5 + version: 0.1.4 + resolution: "deep-is@npm:0.1.4" + checksum: edb65dd0d7d1b9c40b2f50219aef30e116cedd6fc79290e740972c132c09106d2e80aa0bc8826673dd5a00222d4179c84b36a790eef63a4c4bca75a37ef90804 languageName: node linkType: hard @@ -2860,6 +2946,15 @@ __metadata: languageName: node linkType: hard +"define-properties@npm:^1.1.3": + version: 1.1.3 + resolution: "define-properties@npm:1.1.3" + dependencies: + object-keys: ^1.0.12 + checksum: da80dba55d0cd76a5a7ab71ef6ea0ebcb7b941f803793e4e0257b384cb772038faa0c31659d244e82c4342edef841c1a1212580006a05a5068ee48223d787317 + languageName: node + linkType: hard + "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -2874,6 +2969,13 @@ __metadata: languageName: node linkType: hard +"depd@npm:^1.1.2": + version: 1.1.2 + resolution: "depd@npm:1.1.2" + checksum: 6b406620d269619852885ce15965272b829df6f409724415e0002c8632ab6a8c0a08ec1f0bd2add05dc7bd7507606f7e2cc034fa24224ab829580040b835ecd9 + languageName: node + linkType: hard + "detect-newline@npm:^3.0.0": version: 3.1.0 resolution: "detect-newline@npm:3.1.0" @@ -2902,6 +3004,24 @@ __metadata: languageName: node linkType: hard +"dir-glob@npm:^3.0.1": + version: 3.0.1 + resolution: "dir-glob@npm:3.0.1" + dependencies: + path-type: ^4.0.0 + checksum: fa05e18324510d7283f55862f3161c6759a3f2f8dbce491a2fc14c8324c498286c54282c1f0e933cb930da8419b30679389499b919122952a4f8592362ef4615 + languageName: node + linkType: hard + +"doctrine@npm:^2.1.0": + version: 2.1.0 + resolution: "doctrine@npm:2.1.0" + dependencies: + esutils: ^2.0.2 + checksum: a45e277f7feaed309fe658ace1ff286c6e2002ac515af0aaf37145b8baa96e49899638c7cd47dccf84c3d32abfc113246625b3ac8f552d1046072adee13b0dc8 + languageName: node + linkType: hard + "doctrine@npm:^3.0.0": version: 3.0.0 resolution: "doctrine@npm:3.0.0" @@ -2921,11 +3041,11 @@ __metadata: linkType: hard "dot-prop@npm:^5.1.0": - version: 5.2.0 - resolution: "dot-prop@npm:5.2.0" + version: 5.3.0 + resolution: "dot-prop@npm:5.3.0" dependencies: is-obj: ^2.0.0 - checksum: 709a8208bff4fc4d5a11e357957a9e59ed625d7db909d14ea1e0dbeb30d26c25325a6e64ea27ed96fb17978cc13c7e38cf30bac17bb81eb9b5a740a4fe909a87 + checksum: d5775790093c234ef4bfd5fbe40884ff7e6c87573e5339432870616331189f7f5d86575c5b5af2dcf0f61172990f4f734d07844b1f23482fff09e3c4bead05ea languageName: node linkType: hard @@ -2956,10 +3076,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.3.723": - version: 1.3.771 - resolution: "electron-to-chromium@npm:1.3.771" - checksum: 7f49245e5d1a61f1b78069c5af7ac970498ea78afeaee9ba7cc264eb2350f3b647de86ca8a55339b10b15a34c166c03396291661faa2bf8e50461ddbe397d8b3 +"electron-to-chromium@npm:^1.4.17": + version: 1.4.24 + resolution: "electron-to-chromium@npm:1.4.24" + checksum: 71c065ae9cb74bc7e44abeea20c1639a2057bee4927f09ccc2a6d492fa5c7a31da63ad5d5d8659f7d04db680225543aae54579793f269c9d2ecc1c1541018688 languageName: node linkType: hard @@ -2991,7 +3111,16 @@ __metadata: languageName: node linkType: hard -"enquirer@npm:^2.3.5, enquirer@npm:^2.3.6": +"encoding@npm:^0.1.12": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: ^0.6.2 + checksum: bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f + languageName: node + linkType: hard + +"enquirer@npm:^2.3.5": version: 2.3.6 resolution: "enquirer@npm:2.3.6" dependencies: @@ -3008,9 +3137,16 @@ __metadata: linkType: hard "env-paths@npm:^2.2.0": - version: 2.2.0 - resolution: "env-paths@npm:2.2.0" - checksum: ba2aea38301aafd69086be1f8cb453b92946e4840cb0de9d1c88a67e6f43a6174dcddb60b218ec36db8720b12de46b0d93c2f97ad9bbec6a267b479ab37debb6 + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 8b7b1be20d2de12d2255c0bc2ca638b7af5171142693299416e6a9339bd7d88fc8d7707d913d78e0993176005405a236b066b45666b27b797252c771156ace54 languageName: node linkType: hard @@ -3032,6 +3168,45 @@ __metadata: languageName: node linkType: hard +"es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.1": + version: 1.19.1 + resolution: "es-abstract@npm:1.19.1" + dependencies: + call-bind: ^1.0.2 + es-to-primitive: ^1.2.1 + function-bind: ^1.1.1 + get-intrinsic: ^1.1.1 + get-symbol-description: ^1.0.0 + has: ^1.0.3 + has-symbols: ^1.0.2 + internal-slot: ^1.0.3 + is-callable: ^1.2.4 + is-negative-zero: ^2.0.1 + is-regex: ^1.1.4 + is-shared-array-buffer: ^1.0.1 + is-string: ^1.0.7 + is-weakref: ^1.0.1 + object-inspect: ^1.11.0 + object-keys: ^1.1.1 + object.assign: ^4.1.2 + string.prototype.trimend: ^1.0.4 + string.prototype.trimstart: ^1.0.4 + unbox-primitive: ^1.0.1 + checksum: b6be8410672c5364db3fb01eb786e30c7b4bb32b4af63d381c08840f4382c4a168e7855cd338bf59d4f1a1a1138f4d748d1fd40ec65aaa071876f9e9fbfed949 + languageName: node + linkType: hard + +"es-to-primitive@npm:^1.2.1": + version: 1.2.1 + resolution: "es-to-primitive@npm:1.2.1" + dependencies: + is-callable: ^1.1.4 + is-date-object: ^1.0.1 + is-symbol: ^1.0.2 + checksum: 4ead6671a2c1402619bdd77f3503991232ca15e17e46222b0a41a5d81aebc8740a77822f5b3c965008e631153e9ef0580540007744521e72de8e33599fca2eed + languageName: node + linkType: hard + "es5-ext@npm:^0.10.35, es5-ext@npm:^0.10.50, es5-ext@npm:~0.10.46": version: 0.10.53 resolution: "es5-ext@npm:0.10.53" @@ -3111,6 +3286,86 @@ __metadata: languageName: node linkType: hard +"eslint-config-prettier@npm:8.x": + version: 8.3.0 + resolution: "eslint-config-prettier@npm:8.3.0" + peerDependencies: + eslint: ">=7.0.0" + bin: + eslint-config-prettier: bin/cli.js + checksum: df4cea3032671995bb5ab07e016169072f7fa59f44a53251664d9ca60951b66cdc872683b5c6a3729c91497c11490ca44a79654b395dd6756beb0c3903a37196 + languageName: node + linkType: hard + +"eslint-import-resolver-node@npm:^0.3.6": + version: 0.3.6 + resolution: "eslint-import-resolver-node@npm:0.3.6" + dependencies: + debug: ^3.2.7 + resolve: ^1.20.0 + checksum: 6266733af1e112970e855a5bcc2d2058fb5ae16ad2a6d400705a86b29552b36131ffc5581b744c23d550de844206fb55e9193691619ee4dbf225c4bde526b1c8 + languageName: node + linkType: hard + +"eslint-module-utils@npm:^2.7.1": + version: 2.7.1 + resolution: "eslint-module-utils@npm:2.7.1" + dependencies: + debug: ^3.2.7 + find-up: ^2.1.0 + pkg-dir: ^2.0.0 + checksum: c30dfa125aafe65e5f6a30a31c26932106fcf09934a2f47d7f8a393ed9106da7b07416f2337b55c85f9db0175c873ee0827be5429a24ec381b49940f342b9ac3 + languageName: node + linkType: hard + +"eslint-plugin-import@npm:2.x": + version: 2.25.3 + resolution: "eslint-plugin-import@npm:2.25.3" + dependencies: + array-includes: ^3.1.4 + array.prototype.flat: ^1.2.5 + debug: ^2.6.9 + doctrine: ^2.1.0 + eslint-import-resolver-node: ^0.3.6 + eslint-module-utils: ^2.7.1 + has: ^1.0.3 + is-core-module: ^2.8.0 + is-glob: ^4.0.3 + minimatch: ^3.0.4 + object.values: ^1.1.5 + resolve: ^1.20.0 + tsconfig-paths: ^3.11.0 + peerDependencies: + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + checksum: 8bdf4b1fafb0e5c8f57a1673f72d84307d32c06a23942990d198c8b32a85a5ae0098872d1ef5bf80d7dfe8ec542f6a671e3c5e706731a80b493c9015f7a147f5 + languageName: node + linkType: hard + +"eslint-plugin-prettier@npm:4.x": + version: 4.0.0 + resolution: "eslint-plugin-prettier@npm:4.0.0" + dependencies: + prettier-linter-helpers: ^1.0.0 + peerDependencies: + eslint: ">=7.28.0" + prettier: ">=2.0.0" + peerDependenciesMeta: + eslint-config-prettier: + optional: true + checksum: 03d69177a3c21fa2229c7e427ce604429f0b20ab7f411e2e824912f572a207c7f5a41fd1f0a95b9b8afe121e291c1b1f1dc1d44c7aad4b0837487f9c19f5210d + languageName: node + linkType: hard + +"eslint-scope@npm:^5.1.1": + version: 5.1.1 + resolution: "eslint-scope@npm:5.1.1" + dependencies: + esrecurse: ^4.3.0 + estraverse: ^4.1.1 + checksum: 47e4b6a3f0cc29c7feedee6c67b225a2da7e155802c6ea13bbef4ac6b9e10c66cd2dcb987867ef176292bf4e64eccc680a49e35e9e9c669f4a02bac17e86abdb + languageName: node + linkType: hard + "eslint-scope@npm:^7.1.0": version: 7.1.0 resolution: "eslint-scope@npm:7.1.0" @@ -3139,7 +3394,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.1.0": +"eslint-visitor-keys@npm:^3.0.0, eslint-visitor-keys@npm:^3.1.0": version: 3.1.0 resolution: "eslint-visitor-keys@npm:3.1.0" checksum: fd2d613bb315bc549068ca97771d868437fb60c8f13ef8d6d54669773ff53f814b759fa9e57966f15e4c50a5f5e11c6ba47060b8f201f9776311f6c5d5c11b70 @@ -3147,8 +3402,8 @@ __metadata: linkType: hard "eslint@npm:8.x": - version: 8.4.1 - resolution: "eslint@npm:8.4.1" + version: 8.5.0 + resolution: "eslint@npm:8.5.0" dependencies: "@eslint/eslintrc": ^1.0.5 "@humanwhocodes/config-array": ^0.9.2 @@ -3190,7 +3445,7 @@ __metadata: v8-compile-cache: ^2.0.3 bin: eslint: bin/eslint.js - checksum: d962cd7cd0f68ddc2412f47154b8992ad3af987cf47fa6e60e51a2b7d32a91f934388f7d29e2c45b16b7ac69f0d220d0a483189ec6ba43a8a480110c34f158f9 + checksum: c1a9e26070520a308cc30b62ba0d37d5b115ed23987a93219819537bdea9398e6ebe57c27d97be36ecc83b5162c72e82ecb0a9e5b44b7992980f9be90eb5c4b3 languageName: node linkType: hard @@ -3233,10 +3488,17 @@ __metadata: languageName: node linkType: hard +"estraverse@npm:^4.1.1": + version: 4.3.0 + resolution: "estraverse@npm:4.3.0" + checksum: a6299491f9940bb246124a8d44b7b7a413a8336f5436f9837aaa9330209bd9ee8af7e91a654a3545aee9c54b3308e78ee360cef1d777d37cfef77d2fa33b5827 + languageName: node + linkType: hard + "estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": - version: 5.2.0 - resolution: "estraverse@npm:5.2.0" - checksum: ec11b70d946bf5d7f76f91db38ef6f08109ac1b36cda293a26e678e58df4719f57f67b9ec87042afdd1f0267cee91865be3aa48d2161765a93defab5431be7b8 + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 072780882dc8416ad144f8fe199628d2b3e7bbc9989d9ed43795d2c90309a2047e6bc5979d7e2322a341163d22cfad9e21f4110597fe487519697389497e4e2b languageName: node linkType: hard @@ -3286,11 +3548,11 @@ __metadata: linkType: hard "ext@npm:^1.1.2": - version: 1.4.0 - resolution: "ext@npm:1.4.0" + version: 1.6.0 + resolution: "ext@npm:1.6.0" dependencies: - type: ^2.0.0 - checksum: 70acfb68763ad888b34a1c8f2fd9ae5e7265c2470a58a7204645fea07fdbb802512944ea3820db5e643369a9364a98f01732c72e3f2ee577bc2582c3e7e370e3 + type: ^2.5.0 + checksum: ca3ef4619e838f441a92238a98b77ac873da2175ace746c64303ffe2c3208e79a3acf3bf7004e40b720f3c2a83bf0143e6dd4a7cdfae6e73f54a3bfc7a14b5c2 languageName: node linkType: hard @@ -3309,9 +3571,9 @@ __metadata: linkType: hard "extsprintf@npm:^1.2.0": - version: 1.4.0 - resolution: "extsprintf@npm:1.4.0" - checksum: 184dc8a413eb4b1ff16bdce797340e7ded4d28511d56a1c9afa5a95bcff6ace154063823eaf0206dbbb0d14059d74f382a15c34b7c0636fa74a7e681295eb67e + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 languageName: node linkType: hard @@ -3322,6 +3584,26 @@ __metadata: languageName: node linkType: hard +"fast-diff@npm:^1.1.2": + version: 1.2.0 + resolution: "fast-diff@npm:1.2.0" + checksum: 1b5306eaa9e826564d9e5ffcd6ebd881eb5f770b3f977fcbf38f05c824e42172b53c79920e8429c54eb742ce15a0caf268b0fdd5b38f6de52234c4a8368131ae + languageName: node + linkType: hard + +"fast-glob@npm:^3.1.1": + version: 3.2.7 + resolution: "fast-glob@npm:3.2.7" + dependencies: + "@nodelib/fs.stat": ^2.0.2 + "@nodelib/fs.walk": ^1.2.3 + glob-parent: ^5.1.2 + merge2: ^1.3.0 + micromatch: ^4.0.4 + checksum: 2f4708ff112d2b451888129fdd9a0938db88b105b0ddfd043c064e3c4d3e20eed8d7c7615f7565fee660db34ddcf08a2db1bf0ab3c00b87608e4719694642d78 + languageName: node + linkType: hard + "fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" @@ -3336,6 +3618,15 @@ __metadata: languageName: node linkType: hard +"fastq@npm:^1.6.0": + version: 1.13.0 + resolution: "fastq@npm:1.13.0" + dependencies: + reusify: ^1.0.4 + checksum: 32cf15c29afe622af187d12fc9cd93e160a0cb7c31a3bb6ace86b7dea3b28e7b72acde89c882663f307b2184e14782c6c664fa315973c03626c7d4bff070bb0b + languageName: node + linkType: hard + "fb-watchman@npm:^2.0.0": version: 2.0.1 resolution: "fb-watchman@npm:2.0.1" @@ -3381,6 +3672,16 @@ __metadata: languageName: node linkType: hard +"fill-keys@npm:^1.0.2": + version: 1.0.2 + resolution: "fill-keys@npm:1.0.2" + dependencies: + is-object: ~1.0.1 + merge-descriptors: ~1.0.0 + checksum: 6ac5ff60ff08f2f44d19e919c9ca579f4efaaa8c88232b4aab5a5b5522aeb8ec91501956e780cb2b44574fe4a4a337e9b43187829267d0b66a6bfedbafae893f + languageName: node + linkType: hard + "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -3391,17 +3692,17 @@ __metadata: linkType: hard "find-cache-dir@npm:^3.3.1": - version: 3.3.1 - resolution: "find-cache-dir@npm:3.3.1" + version: 3.3.2 + resolution: "find-cache-dir@npm:3.3.2" dependencies: commondir: ^1.0.1 make-dir: ^3.0.2 pkg-dir: ^4.1.0 - checksum: 0f7c22b65e07f9b486b4560227d014fab1e79ffbbfbafb87d113a2e878510bd620ef6fdff090e5248bb2846d28851d19e42bfdc7c50687966acc106328e7abf1 + checksum: 1e61c2e64f5c0b1c535bd85939ae73b0e5773142713273818cc0b393ee3555fb0fd44e1a5b161b8b6c3e03e98c2fcc9c227d784850a13a90a8ab576869576817 languageName: node linkType: hard -"find-up@npm:^2.0.0": +"find-up@npm:^2.0.0, find-up@npm:^2.1.0": version: 2.1.0 resolution: "find-up@npm:2.1.0" dependencies: @@ -3454,6 +3755,17 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^2.5.0": + version: 2.5.1 + resolution: "form-data@npm:2.5.1" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.6 + mime-types: ^2.1.12 + checksum: 5134ada56cc246b293a1ac7678dba6830000603a3979cf83ff7b2f21f2e3725202237cfb89e32bcb38a1d35727efbd3c3a22e65b42321e8ade8eec01ce755d08 + languageName: node + linkType: hard + "form-data@npm:^3.0.0": version: 3.0.1 resolution: "form-data@npm:3.0.1" @@ -3514,7 +3826,7 @@ __metadata: languageName: node linkType: hard -fsevents@^2.3.2: +"fsevents@npm:^2.3.2": version: 2.3.2 resolution: "fsevents@npm:2.3.2" dependencies: @@ -3547,26 +3859,20 @@ fsevents@^2.3.2: languageName: node linkType: hard -"gauge@npm:~2.7.3": - version: 2.7.4 - resolution: "gauge@npm:2.7.4" +"gauge@npm:^4.0.0": + version: 4.0.0 + resolution: "gauge@npm:4.0.0" dependencies: - aproba: ^1.0.3 + ansi-regex: ^5.0.1 + aproba: ^1.0.3 || ^2.0.0 + color-support: ^1.1.2 console-control-strings: ^1.0.0 - has-unicode: ^2.0.0 - object-assign: ^4.1.0 + has-unicode: ^2.0.1 signal-exit: ^3.0.0 - string-width: ^1.0.1 - strip-ansi: ^3.0.1 - wide-align: ^1.1.0 - checksum: a89b53cee65579b46832e050b5f3a79a832cc422c190de79c6b8e2e15296ab92faddde6ddf2d376875cbba2b043efa99b9e1ed8124e7365f61b04e3cee9d40ee - languageName: node - linkType: hard - -"gensync@npm:^1.0.0-beta.1": - version: 1.0.0-beta.1 - resolution: "gensync@npm:1.0.0-beta.1" - checksum: 92686a5445740fb505f68d66318df5ff04fd803d31385c1ea7b432d860d3e098eb2bc03c8c820356e6f71d86abc0a213ba48bec98b9befafb380b302bfa9e0c1 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + wide-align: ^1.1.2 + checksum: 637b34c84f518defa89319dbef68211a24e9302182ad2a619e3be1be5b7dcf2a962c8359e889294af667440f4722e7e6e61671859e00bd8ec280a136ded89b25 languageName: node linkType: hard @@ -3591,6 +3897,17 @@ fsevents@^2.3.2: languageName: node linkType: hard +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1": + version: 1.1.1 + resolution: "get-intrinsic@npm:1.1.1" + dependencies: + function-bind: ^1.1.1 + has: ^1.0.3 + has-symbols: ^1.0.1 + checksum: a9fe2ca8fa3f07f9b0d30fb202bcd01f3d9b9b6b732452e79c48e79f7d6d8d003af3f9e38514250e3553fdc83c61650851cb6870832ac89deaaceb08e3721a17 + languageName: node + linkType: hard + "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -3619,6 +3936,16 @@ fsevents@^2.3.2: languageName: node linkType: hard +"get-symbol-description@npm:^1.0.0": + version: 1.0.0 + resolution: "get-symbol-description@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.1 + checksum: 9ceff8fe968f9270a37a1f73bf3f1f7bda69ca80f4f80850670e0e7b9444ff99323f7ac52f96567f8b5f5fbe7ac717a0d81d3407c7313e82810c6199446a5247 + languageName: node + linkType: hard + "getpass@npm:^0.1.1": version: 0.1.7 resolution: "getpass@npm:0.1.7" @@ -3646,22 +3973,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"git-raw-commits@npm:^2.0.0": - version: 2.0.7 - resolution: "git-raw-commits@npm:2.0.7" - dependencies: - dargs: ^7.0.0 - lodash.template: ^4.0.2 - meow: ^7.0.0 - split2: ^2.0.0 - through2: ^3.0.0 - bin: - git-raw-commits: cli.js - checksum: 331ded71df36bdfe4df9f66ecd0eba5cd72b6783c4779a38cee01890f291b0360ee434fa63d6ec84ccc77670ec1aa0d1792cec3902959d1bef7a89c2e9ce7872 - languageName: node - linkType: hard - -"git-raw-commits@npm:^2.0.8": +"git-raw-commits@npm:^2.0.0, git-raw-commits@npm:^2.0.8": version: 2.0.10 resolution: "git-raw-commits@npm:2.0.10" dependencies: @@ -3707,6 +4019,15 @@ fsevents@^2.3.2: languageName: node linkType: hard +"glob-parent@npm:^5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: ^4.0.1 + checksum: f4f2bfe2425296e8a47e36864e4f42be38a996db40420fe434565e4480e3322f18eb37589617a98640c5dc8fdec1a387007ee18dbb1f3f5553409c34d17f425e + languageName: node + linkType: hard + "glob-parent@npm:^6.0.1": version: 6.0.2 resolution: "glob-parent@npm:6.0.2" @@ -3716,7 +4037,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"glob@npm:7.2.0": +"glob@npm:7.2.0, glob@npm:^7.0.3, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": version: 7.2.0 resolution: "glob@npm:7.2.0" dependencies: @@ -3730,20 +4051,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"glob@npm:^7.0.3, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": - version: 7.1.6 - resolution: "glob@npm:7.1.6" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.0.4 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 351d549dd90553b87c2d3f90ce11aed9e1093c74130440e7ae0592e11bbcd2ce7f0ebb8ba6bfe63aaf9b62166a7f4c80cb84490ae5d78408bb2572bf7d4ee0a6 - languageName: node - linkType: hard - "global-dirs@npm:^0.1.1": version: 0.1.1 resolution: "global-dirs@npm:0.1.1" @@ -3769,6 +4076,20 @@ fsevents@^2.3.2: languageName: node linkType: hard +"globby@npm:^11.0.4": + version: 11.0.4 + resolution: "globby@npm:11.0.4" + dependencies: + array-union: ^2.1.0 + dir-glob: ^3.0.1 + fast-glob: ^3.1.1 + ignore: ^5.1.4 + merge2: ^1.3.0 + slash: ^3.0.0 + checksum: d3e02d5e459e02ffa578b45f040381c33e3c0538ed99b958f0809230c423337999867d7b0dbf752ce93c46157d3bbf154d3fff988a93ccaeb627df8e1841775b + languageName: node + linkType: hard + "globby@npm:^6.1.0": version: 6.1.0 resolution: "globby@npm:6.1.0" @@ -3782,16 +4103,16 @@ fsevents@^2.3.2: languageName: node linkType: hard -"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.3, graceful-fs@npm:^4.2.4": - version: 4.2.4 - resolution: "graceful-fs@npm:4.2.4" - checksum: 9d58c444eb4f391ce30b451aae8a8af2bd675d9f6f624719e97306f571ab89b2bd2b5f9025199bc63a2edfe2e53e7701554012f32a708148d53aa689163728cc +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": + version: 4.2.8 + resolution: "graceful-fs@npm:4.2.8" + checksum: 5d224c8969ad0581d551dfabdb06882706b31af2561bd5e2034b4097e67cc27d05232849b8643866585fd0a41c7af152950f8776f4dd5579e9853733f31461c6 languageName: node linkType: hard "handlebars@npm:^4.7.6": - version: 4.7.6 - resolution: "handlebars@npm:4.7.6" + version: 4.7.7 + resolution: "handlebars@npm:4.7.7" dependencies: minimist: ^1.2.5 neo-async: ^2.6.0 @@ -3803,7 +4124,7 @@ fsevents@^2.3.2: optional: true bin: handlebars: bin/handlebars - checksum: 6fb9ba14c703cb6e77d5a1dcb37631126d25ae1503834b9b4f151d5324b52192a618d1944badc351588271cc28a8144093f85dbabc8c72278cfa2dbd09c70124 + checksum: 1e79a43f5e18d15742977cb987923eab3e2a8f44f2d9d340982bcb69e1735ed049226e534d7c1074eaddaf37e4fb4f471a8adb71cddd5bc8cf3f894241df5cee languageName: node linkType: hard @@ -3831,6 +4152,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"has-bigints@npm:^1.0.1": + version: 1.0.1 + resolution: "has-bigints@npm:1.0.1" + checksum: 44ab55868174470065d2e0f8f6def1c990d12b82162a8803c679699fa8a39f966e336f2a33c185092fe8aea7e8bf2e85f1c26add5f29d98f2318bd270096b183 + languageName: node + linkType: hard + "has-flag@npm:^3.0.0": version: 3.0.0 resolution: "has-flag@npm:3.0.0" @@ -3845,7 +4173,23 @@ fsevents@^2.3.2: languageName: node linkType: hard -"has-unicode@npm:^2.0.0": +"has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2": + version: 1.0.2 + resolution: "has-symbols@npm:1.0.2" + checksum: 2309c426071731be792b5be43b3da6fb4ed7cbe8a9a6bcfca1862587709f01b33d575ce8f5c264c1eaad09fca2f9a8208c0a2be156232629daa2dd0c0740976b + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.0": + version: 1.0.0 + resolution: "has-tostringtag@npm:1.0.0" + dependencies: + has-symbols: ^1.0.2 + checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c + languageName: node + linkType: hard + +"has-unicode@npm:^2.0.1": version: 2.0.1 resolution: "has-unicode@npm:2.0.1" checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 @@ -3862,9 +4206,9 @@ fsevents@^2.3.2: linkType: hard "hosted-git-info@npm:^2.1.4": - version: 2.8.8 - resolution: "hosted-git-info@npm:2.8.8" - checksum: fc5bdbd1ce2597c7fe43cf905ae18c7f96a8e042a46340af4cc4e5a0497d4a0669e2ac5ebc16bc0fef98eb8fe5d55b9b467d3aa97b97f0a87d7673644af31c74 + version: 2.8.9 + resolution: "hosted-git-info@npm:2.8.9" + checksum: c955394bdab888a1e9bb10eb33029e0f7ce5a2ac7b3f158099dc8c486c99e73809dca609f5694b223920ca2174db33d32b12f9a2a47141dc59607c29da5a62dd languageName: node linkType: hard @@ -3893,6 +4237,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"http-cache-semantics@npm:^4.1.0": + version: 4.1.0 + resolution: "http-cache-semantics@npm:4.1.0" + checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 + languageName: node + linkType: hard + "http-proxy-agent@npm:^4.0.1": version: 4.0.1 resolution: "http-proxy-agent@npm:4.0.1" @@ -3932,7 +4283,16 @@ fsevents@^2.3.2: languageName: node linkType: hard -"husky@npm:^7.0.4": +"humanize-ms@npm:^1.2.1": + version: 1.2.1 + resolution: "humanize-ms@npm:1.2.1" + dependencies: + ms: ^2.0.0 + checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 + languageName: node + linkType: hard + +"husky@npm:7.0.4": version: 7.0.4 resolution: "husky@npm:7.0.4" bin: @@ -3950,6 +4310,15 @@ fsevents@^2.3.2: languageName: node linkType: hard +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: ">= 2.1.2 < 3.0.0" + checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf + languageName: node + linkType: hard + "ignore@npm:^4.0.6": version: 4.0.6 resolution: "ignore@npm:4.0.6" @@ -3957,17 +4326,14 @@ fsevents@^2.3.2: languageName: node linkType: hard -"import-fresh@npm:^3.0.0": - version: 3.2.1 - resolution: "import-fresh@npm:3.2.1" - dependencies: - parent-module: ^1.0.0 - resolve-from: ^4.0.0 - checksum: caef42418a087c3951fb676943a7f21ba8971aa07f9b622dff4af7edcef4160e1b172dccd85a88d7eb109cf41406a4592f70259e6b3b33aeafd042bb61f81d96 +"ignore@npm:^5.1.4, ignore@npm:^5.1.8": + version: 5.2.0 + resolution: "ignore@npm:5.2.0" + checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77 languageName: node linkType: hard -"import-fresh@npm:^3.2.1": +"import-fresh@npm:^3.0.0, import-fresh@npm:^3.2.1": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" dependencies: @@ -3978,14 +4344,14 @@ fsevents@^2.3.2: linkType: hard "import-local@npm:^3.0.2": - version: 3.0.2 - resolution: "import-local@npm:3.0.2" + version: 3.0.3 + resolution: "import-local@npm:3.0.3" dependencies: pkg-dir: ^4.2.0 resolve-cwd: ^3.0.0 bin: import-local-fixture: fixtures/cli.js - checksum: c74d9f9484c878cda1de3434613c7ff72d5dadcf20e5482542232d7c2575b713ff88701d6675fcf09a3684cb23fb407c8b333b9cbc59438712723d058d8e976c + checksum: 38ae57d35e7fd5f63b55895050c798d4dd590e4e2337e9ffa882fb3ea7a7716f3162c7300e382e0a733ca5d07b389fadff652c00fa7b072d5cb6ea34ca06b179 languageName: node linkType: hard @@ -4003,6 +4369,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"infer-owner@npm:^1.0.4": + version: 1.0.4 + resolution: "infer-owner@npm:1.0.4" + checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 + languageName: node + linkType: hard + "inflight@npm:^1.0.4": version: 1.0.6 resolution: "inflight@npm:1.0.6" @@ -4013,7 +4386,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": +"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -4021,9 +4394,27 @@ fsevents@^2.3.2: linkType: hard "ini@npm:^1.3.2, ini@npm:^1.3.4": - version: 1.3.5 - resolution: "ini@npm:1.3.5" - checksum: a4c1652f481a7770f6c4d223dbc0ea3cbbe253f7af8ddc8276e22e1185ab8252404dd0ca2ba625e4829a507b3e8e1ec3df38243d0cc4b20dbe915a22118d3f98 + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: dfd98b0ca3a4fc1e323e38a6c8eb8936e31a97a918d3b377649ea15bdb15d481207a0dda1021efbd86b464cae29a0d33c1d7dcaf6c5672bee17fa849bc50a1b3 + languageName: node + linkType: hard + +"internal-slot@npm:^1.0.3": + version: 1.0.3 + resolution: "internal-slot@npm:1.0.3" + dependencies: + get-intrinsic: ^1.1.0 + has: ^1.0.3 + side-channel: ^1.0.4 + checksum: 1944f92e981e47aebc98a88ff0db579fd90543d937806104d0b96557b10c1f170c51fb777b97740a8b6ddeec585fca8c39ae99fd08a8e058dfc8ab70937238bf + languageName: node + linkType: hard + +"ip@npm:^1.1.5": + version: 1.1.5 + resolution: "ip@npm:1.1.5" + checksum: 30133981f082a060a32644f6a7746e9ba7ac9e2bc07ecc8bbdda3ee8ca9bec1190724c390e45a1ee7695e7edfd2a8f7dda2c104ec5f7ac5068c00648504c7e5a languageName: node linkType: hard @@ -4034,16 +4425,33 @@ fsevents@^2.3.2: languageName: node linkType: hard -"is-core-module@npm:^2.2.0": - version: 2.4.0 - resolution: "is-core-module@npm:2.4.0" +"is-bigint@npm:^1.0.1": + version: 1.0.4 + resolution: "is-bigint@npm:1.0.4" dependencies: - has: ^1.0.3 - checksum: c498902d4c4d0e8eba3a2e8293ccd442158cfe49a71d7cfad136ccf9902b6a41de34ddaa86cdc95c8b7c22f872e59572d8a5d994cbec04c8ecf27ffe75137119 + has-bigints: ^1.0.1 + checksum: c56edfe09b1154f8668e53ebe8252b6f185ee852a50f9b41e8d921cb2bed425652049fbe438723f6cb48a63ca1aa051e948e7e401e093477c99c84eba244f666 + languageName: node + linkType: hard + +"is-boolean-object@npm:^1.1.0": + version: 1.1.2 + resolution: "is-boolean-object@npm:1.1.2" + dependencies: + call-bind: ^1.0.2 + has-tostringtag: ^1.0.0 + checksum: c03b23dbaacadc18940defb12c1c0e3aaece7553ef58b162a0f6bba0c2a7e1551b59f365b91e00d2dbac0522392d576ef322628cb1d036a0fe51eb466db67222 + languageName: node + linkType: hard + +"is-callable@npm:^1.1.4, is-callable@npm:^1.2.4": + version: 1.2.4 + resolution: "is-callable@npm:1.2.4" + checksum: 1a28d57dc435797dae04b173b65d6d1e77d4f16276e9eff973f994eadcfdc30a017e6a597f092752a083c1103cceb56c91e3dadc6692fedb9898dfaba701575f languageName: node linkType: hard -"is-core-module@npm:^2.5.0": +"is-core-module@npm:^2.2.0, is-core-module@npm:^2.5.0, is-core-module@npm:^2.8.0": version: 2.8.0 resolution: "is-core-module@npm:2.8.0" dependencies: @@ -4052,26 +4460,19 @@ fsevents@^2.3.2: languageName: node linkType: hard -"is-extglob@npm:^2.1.1": - version: 2.1.1 - resolution: "is-extglob@npm:2.1.1" - checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 - languageName: node - linkType: hard - -"is-fullwidth-code-point@npm:^1.0.0": - version: 1.0.0 - resolution: "is-fullwidth-code-point@npm:1.0.0" +"is-date-object@npm:^1.0.1": + version: 1.0.5 + resolution: "is-date-object@npm:1.0.5" dependencies: - number-is-nan: ^1.0.0 - checksum: 4d46a7465a66a8aebcc5340d3b63a56602133874af576a9ca42c6f0f4bd787a743605771c5f246db77da96605fefeffb65fc1dbe862dcc7328f4b4d03edf5a57 + has-tostringtag: ^1.0.0 + checksum: baa9077cdf15eb7b58c79398604ca57379b2fc4cf9aa7a9b9e295278648f628c9b201400c01c5e0f7afae56507d741185730307cbe7cad3b9f90a77e5ee342fc languageName: node linkType: hard -"is-fullwidth-code-point@npm:^2.0.0": - version: 2.0.0 - resolution: "is-fullwidth-code-point@npm:2.0.0" - checksum: eef9c6e15f68085fec19ff6a978a6f1b8f48018fd1265035552078ee945573594933b09bbd6f562553e2a241561439f1ef5339276eba68d272001343084cfab8 +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 languageName: node linkType: hard @@ -4103,21 +4504,35 @@ fsevents@^2.3.2: languageName: node linkType: hard -"is-glob@npm:^4.0.0": - version: 4.0.1 - resolution: "is-glob@npm:4.0.1" - dependencies: - is-extglob: ^2.1.1 - checksum: 84627cad11b4e745f5db5a163f32c47b711585a5ff6e14f8f8d026db87f4cdd3e2c95f6fa1f94ad22e469f36d819ae2814f03f9c668b164422ac3354a94672d3 +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: ^2.1.1 + checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4 + languageName: node + linkType: hard + +"is-lambda@npm:^1.0.1": + version: 1.0.1 + resolution: "is-lambda@npm:1.0.1" + checksum: 93a32f01940220532e5948538699ad610d5924ac86093fcee83022252b363eb0cc99ba53ab084a04e4fb62bf7b5731f55496257a4c38adf87af9c4d352c71c35 + languageName: node + linkType: hard + +"is-negative-zero@npm:^2.0.1": + version: 2.0.2 + resolution: "is-negative-zero@npm:2.0.2" + checksum: f3232194c47a549da60c3d509c9a09be442507616b69454716692e37ae9f37c4dea264fb208ad0c9f3efd15a796a46b79df07c7e53c6227c32170608b809149a languageName: node linkType: hard -"is-glob@npm:^4.0.3": - version: 4.0.3 - resolution: "is-glob@npm:4.0.3" +"is-number-object@npm:^1.0.4": + version: 1.0.6 + resolution: "is-number-object@npm:1.0.6" dependencies: - is-extglob: ^2.1.1 - checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4 + has-tostringtag: ^1.0.0 + checksum: c697704e8fc2027fc41cb81d29805de4e8b6dc9c3efee93741dbf126a8ecc8443fef85adbc581415ae7e55d325e51d0a942324ae35c829131748cce39cba55f3 languageName: node linkType: hard @@ -4135,6 +4550,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"is-object@npm:~1.0.1": + version: 1.0.2 + resolution: "is-object@npm:1.0.2" + checksum: 971219c4b1985b9751f65e4c8296d3104f0457b0e8a70849e848a4a2208bc47317d73b3b85d4a369619cb2df8284dc22584cb2695a7d99aca5e8d0aa64fc075a + languageName: node + linkType: hard + "is-plain-obj@npm:^1.1.0": version: 1.1.0 resolution: "is-plain-obj@npm:1.1.0" @@ -4149,10 +4571,45 @@ fsevents@^2.3.2: languageName: node linkType: hard +"is-regex@npm:^1.1.4": + version: 1.1.4 + resolution: "is-regex@npm:1.1.4" + dependencies: + call-bind: ^1.0.2 + has-tostringtag: ^1.0.0 + checksum: 362399b33535bc8f386d96c45c9feb04cf7f8b41c182f54174c1a45c9abbbe5e31290bbad09a458583ff6bf3b2048672cdb1881b13289569a7c548370856a652 + languageName: node + linkType: hard + +"is-shared-array-buffer@npm:^1.0.1": + version: 1.0.1 + resolution: "is-shared-array-buffer@npm:1.0.1" + checksum: 2ffb92533e64e2876e6cfe6906871d28400b6f1a53130fe652ec8007bc0e5044d05e7af8e31bdc992fbba520bd92938cfbeedd0f286be92f250c7c76191c4d90 + languageName: node + linkType: hard + "is-stream@npm:^2.0.0": - version: 2.0.0 - resolution: "is-stream@npm:2.0.0" - checksum: 4dc47738e26bc4f1b3be9070b6b9e39631144f204fc6f87db56961220add87c10a999ba26cf81699f9ef9610426f69cb08a4713feff8deb7d8cadac907826935 + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: b8e05ccdf96ac330ea83c12450304d4a591f9958c11fd17bed240af8d5ffe08aedafa4c0f4cfccd4d28dc9d4d129daca1023633d5c11601a6cbc77521f6fae66 + languageName: node + linkType: hard + +"is-string@npm:^1.0.5, is-string@npm:^1.0.7": + version: 1.0.7 + resolution: "is-string@npm:1.0.7" + dependencies: + has-tostringtag: ^1.0.0 + checksum: 323b3d04622f78d45077cf89aab783b2f49d24dc641aa89b5ad1a72114cfeff2585efc8c12ef42466dff32bde93d839ad321b26884cf75e5a7892a938b089989 + languageName: node + linkType: hard + +"is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": + version: 1.0.4 + resolution: "is-symbol@npm:1.0.4" + dependencies: + has-symbols: ^1.0.2 + checksum: 92805812ef590738d9de49d677cd17dfd486794773fb6fa0032d16452af46e9b91bb43ffe82c983570f015b37136f4b53b28b8523bfb10b0ece7a66c31a54510 languageName: node linkType: hard @@ -4172,6 +4629,15 @@ fsevents@^2.3.2: languageName: node linkType: hard +"is-weakref@npm:^1.0.1": + version: 1.0.2 + resolution: "is-weakref@npm:1.0.2" + dependencies: + call-bind: ^1.0.2 + checksum: 95bd9a57cdcb58c63b1c401c60a474b0f45b94719c30f548c891860f051bc2231575c290a6b420c6bc6e7ed99459d424c652bd5bf9a1d5259505dc35b4bf83de + languageName: node + linkType: hard + "isarray@npm:0.0.1": version: 0.0.1 resolution: "isarray@npm:0.0.1" @@ -4200,14 +4666,14 @@ fsevents@^2.3.2: languageName: node linkType: hard -"istanbul-lib-coverage@npm:^3.0.0": - version: 3.0.0 - resolution: "istanbul-lib-coverage@npm:3.0.0" - checksum: ea57c2428858cc5d1e04c0e28b362950bbf6415e8ba1235cdd6f4c8dc3c57cb950db8b4e8a4f7e33abc240aa1eb816dba0d7285bdb8b70bda22bb2082492dbfc +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": + version: 3.2.0 + resolution: "istanbul-lib-coverage@npm:3.2.0" + checksum: a2a545033b9d56da04a8571ed05c8120bf10e9bce01cf8633a3a2b0d1d83dff4ac4fe78d6d5673c27fc29b7f21a41d75f83a36be09f82a61c367b56aa73c1ff9 languageName: node linkType: hard -"istanbul-lib-instrument@npm:^4.0.0, istanbul-lib-instrument@npm:^4.0.3": +"istanbul-lib-instrument@npm:^4.0.3": version: 4.0.3 resolution: "istanbul-lib-instrument@npm:4.0.3" dependencies: @@ -4219,6 +4685,19 @@ fsevents@^2.3.2: languageName: node linkType: hard +"istanbul-lib-instrument@npm:^5.0.4": + version: 5.1.0 + resolution: "istanbul-lib-instrument@npm:5.1.0" + dependencies: + "@babel/core": ^7.12.3 + "@babel/parser": ^7.14.7 + "@istanbuljs/schema": ^0.1.2 + istanbul-lib-coverage: ^3.2.0 + semver: ^6.3.0 + checksum: 8b82e733c69fe9f94d2e21f3e5760c9bedb110329aa75df4bd40df95f1cac3bf38767e43f35b125cc547ceca7376b72ce7d95cc5238b7e9088345c7b589233d3 + languageName: node + linkType: hard + "istanbul-lib-report@npm:^3.0.0": version: 3.0.0 resolution: "istanbul-lib-report@npm:3.0.0" @@ -4231,23 +4710,23 @@ fsevents@^2.3.2: linkType: hard "istanbul-lib-source-maps@npm:^4.0.0": - version: 4.0.0 - resolution: "istanbul-lib-source-maps@npm:4.0.0" + version: 4.0.1 + resolution: "istanbul-lib-source-maps@npm:4.0.1" dependencies: debug: ^4.1.1 istanbul-lib-coverage: ^3.0.0 source-map: ^0.6.1 - checksum: 292bfb4083e5f8783cdf829a7686b1a377d0c6c2119d4343c8478e948b38146c4827cddc7eee9f57605acd63c291376d67e4a84163d37c5fc78ad0f27f7e2621 + checksum: 21ad3df45db4b81852b662b8d4161f6446cd250c1ddc70ef96a585e2e85c26ed7cd9c2a396a71533cfb981d1a645508bc9618cae431e55d01a0628e7dec62ef2 languageName: node linkType: hard "istanbul-reports@npm:^3.0.2": - version: 3.0.2 - resolution: "istanbul-reports@npm:3.0.2" + version: 3.1.1 + resolution: "istanbul-reports@npm:3.1.1" dependencies: html-escaper: ^2.0.0 istanbul-lib-report: ^3.0.0 - checksum: c5da63f1f4610f47f3015c525a3bc2fb4c87a8791ae452ee3983546d7a2873f0cf5d5fff7c3735ac52943c5b3506f49c294c92f1837df6ec03312625ccd176d7 + checksum: a9940767ee960fd21d4c9b24c417c15d38725be2f3517a72070e962e088fdf7b813f50985f660cd48436690237fdc5640bab10a1a91e0e94b0e400c212c85f3c languageName: node linkType: hard @@ -4262,9 +4741,9 @@ fsevents@^2.3.2: languageName: node linkType: hard -"jest-circus@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-circus@npm:27.4.4" +"jest-circus@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-circus@npm:27.4.5" dependencies: "@jest/environment": ^27.4.4 "@jest/test-result": ^27.4.2 @@ -4278,29 +4757,29 @@ fsevents@^2.3.2: jest-each: ^27.4.2 jest-matcher-utils: ^27.4.2 jest-message-util: ^27.4.2 - jest-runtime: ^27.4.4 - jest-snapshot: ^27.4.4 + jest-runtime: ^27.4.5 + jest-snapshot: ^27.4.5 jest-util: ^27.4.2 pretty-format: ^27.4.2 slash: ^3.0.0 stack-utils: ^2.0.3 throat: ^6.0.1 - checksum: e85698bedc0650deacd6f3a03022f25e786b6dafd0285311303afb933b48ac9c54039138ddcd540513035e335c136695b71cfff9fb4a95116e173e0221edaaf9 + checksum: 0d9ba909fb73ab17d127208a44e0cd1064ed3fcce3208b7c181b684b00e3504f1edc84119cd14d9c4c8df8957904875bf68e3151303bd06e42345a8635112eb0 languageName: node linkType: hard -"jest-cli@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-cli@npm:27.4.4" +"jest-cli@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-cli@npm:27.4.5" dependencies: - "@jest/core": ^27.4.4 + "@jest/core": ^27.4.5 "@jest/test-result": ^27.4.2 "@jest/types": ^27.4.2 chalk: ^4.0.0 exit: ^0.1.2 graceful-fs: ^4.2.4 import-local: ^3.0.2 - jest-config: ^27.4.4 + jest-config: ^27.4.5 jest-util: ^27.4.2 jest-validate: ^27.4.2 prompts: ^2.0.1 @@ -4312,31 +4791,31 @@ fsevents@^2.3.2: optional: true bin: jest: bin/jest.js - checksum: bb19871a076fa23d1a3e198b7537f7a817b57ed8664202ec02a5f6cfab7c2d9cb7a84ba000406e931c29ffea2374d91d88b96f8b1bbf4fefdd51f31ee688f6f3 + checksum: 8c430614ab058fd612eae402620c784e583477520598aa4f68e9115d5f475a50d6897cdad4c832777ec8964446c5a9f02047cf74bed7e0f090220758eac1cc41 languageName: node linkType: hard -"jest-config@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-config@npm:27.4.4" +"jest-config@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-config@npm:27.4.5" dependencies: "@babel/core": ^7.1.0 - "@jest/test-sequencer": ^27.4.4 + "@jest/test-sequencer": ^27.4.5 "@jest/types": ^27.4.2 - babel-jest: ^27.4.4 + babel-jest: ^27.4.5 chalk: ^4.0.0 ci-info: ^3.2.0 deepmerge: ^4.2.2 glob: ^7.1.1 graceful-fs: ^4.2.4 - jest-circus: ^27.4.4 + jest-circus: ^27.4.5 jest-environment-jsdom: ^27.4.4 jest-environment-node: ^27.4.4 jest-get-type: ^27.4.0 - jest-jasmine2: ^27.4.4 + jest-jasmine2: ^27.4.5 jest-regex-util: ^27.4.0 - jest-resolve: ^27.4.4 - jest-runner: ^27.4.4 + jest-resolve: ^27.4.5 + jest-runner: ^27.4.5 jest-util: ^27.4.2 jest-validate: ^27.4.2 micromatch: ^4.0.4 @@ -4347,7 +4826,7 @@ fsevents@^2.3.2: peerDependenciesMeta: ts-node: optional: true - checksum: 4460edc2e42530722030edd883db5955a6899c1e9e6df9de74bd684b2a945c97a978d82dc406acc8fc026103c870c884f1d0e460cffceab00df444c54ae134c8 + checksum: 8b166404959d368c49573b8d3e9ff5537557413a96aa41e05824f01147db1525168489ae3f1f028525a587bd724f718f9c77f1256351c48cf0e3c766a86292cb languageName: node linkType: hard @@ -4421,9 +4900,9 @@ fsevents@^2.3.2: languageName: node linkType: hard -"jest-haste-map@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-haste-map@npm:27.4.4" +"jest-haste-map@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-haste-map@npm:27.4.5" dependencies: "@jest/types": ^27.4.2 "@types/graceful-fs": ^4.1.2 @@ -4435,19 +4914,19 @@ fsevents@^2.3.2: jest-regex-util: ^27.4.0 jest-serializer: ^27.4.0 jest-util: ^27.4.2 - jest-worker: ^27.4.4 + jest-worker: ^27.4.5 micromatch: ^4.0.4 walker: ^1.0.7 dependenciesMeta: fsevents: optional: true - checksum: ebd8e00672aadeb7a42ca5c2070f0240f7892b87c769ba203fe09528b7ca0892e04a60252131ccbdb8746476e07fed67f95c144961ff8ad2cc2dd708cf07e39c + checksum: acd593ec33b028169c7bf753a5c92eabdb05f87ba9f14e33fe24a4adc1e0a1ff4be0c4757a57a82413263ebbb6b567708b4f3019cb4df899d2d07fcec64bd75a languageName: node linkType: hard -"jest-jasmine2@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-jasmine2@npm:27.4.4" +"jest-jasmine2@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-jasmine2@npm:27.4.5" dependencies: "@babel/traverse": ^7.1.0 "@jest/environment": ^27.4.4 @@ -4462,12 +4941,12 @@ fsevents@^2.3.2: jest-each: ^27.4.2 jest-matcher-utils: ^27.4.2 jest-message-util: ^27.4.2 - jest-runtime: ^27.4.4 - jest-snapshot: ^27.4.4 + jest-runtime: ^27.4.5 + jest-snapshot: ^27.4.5 jest-util: ^27.4.2 pretty-format: ^27.4.2 throat: ^6.0.1 - checksum: ddd9edf6ac0b0fcc40b2dbcc29f58fed47310fea2a31094592567e1c06eaee6d2680049775b6eb26152e514149d898b5a3c19e0a30d671cf0ae3d8f1fb3f0088 + checksum: 9759e865f39390f71c83a3cabb3196c2655df2bf3771b71d9c2f2db400cec96ab7eff1b44e8b582280c07db985538bacb408dd6a42aff83984b0a27b2968fa36 languageName: node linkType: hard @@ -4539,43 +5018,43 @@ fsevents@^2.3.2: languageName: node linkType: hard -"jest-resolve-dependencies@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-resolve-dependencies@npm:27.4.4" +"jest-resolve-dependencies@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-resolve-dependencies@npm:27.4.5" dependencies: "@jest/types": ^27.4.2 jest-regex-util: ^27.4.0 - jest-snapshot: ^27.4.4 - checksum: be76bb7d85e8a5f8c44e13798a916240b9d5b5ff3ace56acbac45c2f90d3314ea5171c0b9a6ed1b71e593578930a6bdcfe5035de18d392b095b46d31e971c8fd + jest-snapshot: ^27.4.5 + checksum: 1fc16cb7c8df130420732184cd87a2c8ae6bf6cbb37d61dd69fddf69ab5ab2be50774962ce4b477b915fa1cc3dc69cb1830b6a18bd1b33c3c1a9c40e43cb11ce languageName: node linkType: hard -"jest-resolve@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-resolve@npm:27.4.4" +"jest-resolve@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-resolve@npm:27.4.5" dependencies: "@jest/types": ^27.4.2 chalk: ^4.0.0 graceful-fs: ^4.2.4 - jest-haste-map: ^27.4.4 + jest-haste-map: ^27.4.5 jest-pnp-resolver: ^1.2.2 jest-util: ^27.4.2 jest-validate: ^27.4.2 resolve: ^1.20.0 resolve.exports: ^1.1.0 slash: ^3.0.0 - checksum: 2dd3b27cc018adb754ac059c0f90eae5d8a92b3b2e0a24f16a22c448456c2f025cebd324f2408a7db229f251da76526592bde81c5cf4a408f1403571a592b7b0 + checksum: 57d619ed1ab4ba5d1b079f9ca3e93c7d9bcc9faa195b617fda6155cbce6eb48c234a957f41f7feee43740b4a5b50ebec8aea61023f766ac4b2eb6ff946c76025 languageName: node linkType: hard -"jest-runner@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-runner@npm:27.4.4" +"jest-runner@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-runner@npm:27.4.5" dependencies: "@jest/console": ^27.4.2 "@jest/environment": ^27.4.4 "@jest/test-result": ^27.4.2 - "@jest/transform": ^27.4.4 + "@jest/transform": ^27.4.5 "@jest/types": ^27.4.2 "@types/node": "*" chalk: ^4.0.0 @@ -4585,29 +5064,29 @@ fsevents@^2.3.2: jest-docblock: ^27.4.0 jest-environment-jsdom: ^27.4.4 jest-environment-node: ^27.4.4 - jest-haste-map: ^27.4.4 + jest-haste-map: ^27.4.5 jest-leak-detector: ^27.4.2 jest-message-util: ^27.4.2 - jest-resolve: ^27.4.4 - jest-runtime: ^27.4.4 + jest-resolve: ^27.4.5 + jest-runtime: ^27.4.5 jest-util: ^27.4.2 - jest-worker: ^27.4.4 + jest-worker: ^27.4.5 source-map-support: ^0.5.6 throat: ^6.0.1 - checksum: fe9764e43e02e79d600446cca01664cc4c9957993c7c4ee8795af251f765b378f0da8748fae190cce0916eedd408b9601c452df666c18aece1ac70c5d6fd4019 + checksum: 456f5e3c55dfd0fdad21703a26aa2ff729bbcea173a4ac6a6a99f65d77c564ace13a0e53c33b074020d3594dbff831b7f6424f27d99485120c691ee129a6b6f4 languageName: node linkType: hard -"jest-runtime@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-runtime@npm:27.4.4" +"jest-runtime@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-runtime@npm:27.4.5" dependencies: "@jest/console": ^27.4.2 "@jest/environment": ^27.4.4 "@jest/globals": ^27.4.4 "@jest/source-map": ^27.4.0 "@jest/test-result": ^27.4.2 - "@jest/transform": ^27.4.4 + "@jest/transform": ^27.4.5 "@jest/types": ^27.4.2 "@types/yargs": ^16.0.0 chalk: ^4.0.0 @@ -4617,18 +5096,18 @@ fsevents@^2.3.2: exit: ^0.1.2 glob: ^7.1.3 graceful-fs: ^4.2.4 - jest-haste-map: ^27.4.4 + jest-haste-map: ^27.4.5 jest-message-util: ^27.4.2 jest-mock: ^27.4.2 jest-regex-util: ^27.4.0 - jest-resolve: ^27.4.4 - jest-snapshot: ^27.4.4 + jest-resolve: ^27.4.5 + jest-snapshot: ^27.4.5 jest-util: ^27.4.2 jest-validate: ^27.4.2 slash: ^3.0.0 strip-bom: ^4.0.0 yargs: ^16.2.0 - checksum: f12cfcd7db91d1f36613a884493e68f255fff529504c25e77af0c60fea71fa526ebe401f27aaf05b8e563e365339829f6c39b2196159be4da6ea3445408d5d7b + checksum: 3fddd950504e2eee83f13237d8e2321c91237881a04e71cfd5457064eb970a91de3b8560b15ed6dbfc8843aa06151907510842f5f2f8e93b5a172a1d282ae26e languageName: node linkType: hard @@ -4642,9 +5121,9 @@ fsevents@^2.3.2: languageName: node linkType: hard -"jest-snapshot@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-snapshot@npm:27.4.4" +"jest-snapshot@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-snapshot@npm:27.4.5" dependencies: "@babel/core": ^7.7.2 "@babel/generator": ^7.7.2 @@ -4652,7 +5131,7 @@ fsevents@^2.3.2: "@babel/plugin-syntax-typescript": ^7.7.2 "@babel/traverse": ^7.7.2 "@babel/types": ^7.0.0 - "@jest/transform": ^27.4.4 + "@jest/transform": ^27.4.5 "@jest/types": ^27.4.2 "@types/babel__traverse": ^7.0.4 "@types/prettier": ^2.1.5 @@ -4662,15 +5141,15 @@ fsevents@^2.3.2: graceful-fs: ^4.2.4 jest-diff: ^27.4.2 jest-get-type: ^27.4.0 - jest-haste-map: ^27.4.4 + jest-haste-map: ^27.4.5 jest-matcher-utils: ^27.4.2 jest-message-util: ^27.4.2 - jest-resolve: ^27.4.4 + jest-resolve: ^27.4.5 jest-util: ^27.4.2 natural-compare: ^1.4.0 pretty-format: ^27.4.2 semver: ^7.3.2 - checksum: 4339cfa7725a0eb0b368af7e348dba07a03fc418c931d127843f8a933287c01cc880c5e32272a736c4c5ad1ba53fd96b789c30f9809258f515c77f3c140f91de + checksum: c5dcb1ccb95feb8773fc64b6d21d28fc8e8d2cf53bfde74247b3d34a83936a9b92492416d447d4e559e7b2ce39e442e4ee4a266d2f54c9ab8ab686eb16d1c8f4 languageName: node linkType: hard @@ -4717,24 +5196,24 @@ fsevents@^2.3.2: languageName: node linkType: hard -"jest-worker@npm:^27.4.4": - version: 27.4.4 - resolution: "jest-worker@npm:27.4.4" +"jest-worker@npm:^27.4.5": + version: 27.4.5 + resolution: "jest-worker@npm:27.4.5" dependencies: "@types/node": "*" merge-stream: ^2.0.0 supports-color: ^8.0.0 - checksum: e4a383d587f9e0fbe247a6c2e3a0dbe7b5cc0f9fe8583d6ffdc9257591563e62ecd6ef4c987dc44ca43a97d430395e36221427325bc839312a7ea62a4b1baedd + checksum: eb0b6be412103299c3d8643ad26daf862826ca841bd2a3ff47d2d931804ab7d7f0db2fcdea7dbf47ce8eacb7742b3f2586c2d6ebdaa8d0ac77c65f7b698e7683 languageName: node linkType: hard "jest@npm:27.x": - version: 27.4.4 - resolution: "jest@npm:27.4.4" + version: 27.4.5 + resolution: "jest@npm:27.4.5" dependencies: - "@jest/core": ^27.4.4 + "@jest/core": ^27.4.5 import-local: ^3.0.2 - jest-cli: ^27.4.4 + jest-cli: ^27.4.5 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -4742,7 +5221,7 @@ fsevents@^2.3.2: optional: true bin: jest: bin/jest.js - checksum: 0af09bd42c1ecef555dbc4a8cdd2b77b4bfff9ab3d7d2ee01e343067f443eb5743c7acdf2f28e8d2d268c8e058b129c79b1eda99d7c8b06a6a0e9e1fd04cb741 + checksum: 57ee4be68650dd1f89e077cca48813d824779a07626e84178c672727ace1ef3cd489f124a27dc02b88601774413330e6d35080b11919efa6460ee61d378c6610 languageName: node linkType: hard @@ -4765,23 +5244,23 @@ fsevents@^2.3.2: linkType: hard "js-yaml@npm:^3.13.1": - version: 3.14.0 - resolution: "js-yaml@npm:3.14.0" + version: 3.14.1 + resolution: "js-yaml@npm:3.14.1" dependencies: argparse: ^1.0.7 esprima: ^4.0.0 bin: js-yaml: bin/js-yaml.js - checksum: a1a47c912ba20956f96cb0998dea2e74c7f7129d831fe33d3c5a16f3f83712ce405172a8dd1c26bf2b3ad74b54016d432ff727928670ae5a50a57a677c387949 + checksum: bef146085f472d44dee30ec34e5cf36bf89164f5d585435a3d3da89e52622dff0b188a580e4ad091c3341889e14cb88cac6e4deb16dc5b1e9623bb0601fc255c languageName: node linkType: hard "js2xmlparser@npm:^4.0.1": - version: 4.0.1 - resolution: "js2xmlparser@npm:4.0.1" + version: 4.0.2 + resolution: "js2xmlparser@npm:4.0.2" dependencies: - xmlcreate: ^2.0.3 - checksum: 0cf9471c9b293a0afb9004064c93bdae410293991ec90f7e0a05c93e13071043d277d4cb6f2c8b6956321d7d85a4b18a5aee658b353b3321862fdbc4c7c99ac2 + xmlcreate: ^2.0.4 + checksum: 55e3af71dc0104941dfc3e85452230db42ff3870a5777d1ea26bc0c68743f49113a517a7b305421a932b29f10058a012a7da8f5ba07860a05a1dce9fe5b62962 languageName: node linkType: hard @@ -4817,8 +5296,8 @@ fsevents@^2.3.2: linkType: hard "jsdom@npm:^16.6.0": - version: 16.6.0 - resolution: "jsdom@npm:16.6.0" + version: 16.7.0 + resolution: "jsdom@npm:16.7.0" dependencies: abab: ^2.0.5 acorn: ^8.2.4 @@ -4845,14 +5324,14 @@ fsevents@^2.3.2: whatwg-encoding: ^1.0.5 whatwg-mimetype: ^2.3.0 whatwg-url: ^8.5.0 - ws: ^7.4.5 + ws: ^7.4.6 xml-name-validator: ^3.0.0 peerDependencies: canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 4abf126bba167f1cf123601232ceb3be0696a4370c8fa484a1a99d93926f251c372d84233b74aeede55909c3f30c350c646d27409f41353ea733c52e0243f49c + checksum: 454b83371857000763ed31130a049acd1b113e3b927e6dcd75c67ddc30cdd242d7ebcac5c2294b7a1a6428155cb1398709c573b3c6d809218692ea68edd93370 languageName: node linkType: hard @@ -4872,6 +5351,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"json-parse-even-better-errors@npm:^2.3.0": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 798ed4cf3354a2d9ccd78e86d2169515a0097a5c133337807cdf7f1fc32e1391d207ccfc276518cc1d7d8d4db93288b8a50ba4293d212ad1336e52a8ec0a941f + languageName: node + linkType: hard + "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -4879,10 +5365,10 @@ fsevents@^2.3.2: languageName: node linkType: hard -"json-schema@npm:0.2.3": - version: 0.2.3 - resolution: "json-schema@npm:0.2.3" - checksum: bbc2070988fb5f2a2266a31b956f1b5660e03ea7eaa95b33402901274f625feb586ae0c485e1df854fde40a7f0dc679f3b3ca8e5b8d31f8ea07a0d834de785c7 +"json-schema@npm:0.4.0": + version: 0.4.0 + resolution: "json-schema@npm:0.4.0" + checksum: 66389434c3469e698da0df2e7ac5a3281bcff75e797a5c127db7c5b56270e01ae13d9afa3c03344f76e32e81678337a8c912bdbb75101c62e487dc3778461d72 languageName: node linkType: hard @@ -4900,7 +5386,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"json5@npm:2.x": +"json5@npm:2.x, json5@npm:^2.1.2": version: 2.2.0 resolution: "json5@npm:2.2.0" dependencies: @@ -4911,14 +5397,14 @@ fsevents@^2.3.2: languageName: node linkType: hard -"json5@npm:^2.1.2": - version: 2.1.3 - resolution: "json5@npm:2.1.3" +"json5@npm:^1.0.1": + version: 1.0.1 + resolution: "json5@npm:1.0.1" dependencies: - minimist: ^1.2.5 + minimist: ^1.2.0 bin: json5: lib/cli.js - checksum: b2de57a66520eca0fbb6c5ef59249b8308efb93fe89a8c75f5a6846e4f5f7d99a5a6f2e4db4d7a1c7047802dd816ed602a052d147a415d0e6b7f834885b62bc3 + checksum: e76ea23dbb8fc1348c143da628134a98adf4c5a4e8ea2adaa74a80c455fc2cdf0e2e13e6398ef819bfe92306b610ebb2002668ed9fc1af386d593691ef346fc3 languageName: node linkType: hard @@ -4935,15 +5421,15 @@ fsevents@^2.3.2: linkType: hard "jsonfile@npm:^6.0.1": - version: 6.0.1 - resolution: "jsonfile@npm:6.0.1" + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" dependencies: graceful-fs: ^4.1.6 - universalify: ^1.0.0 + universalify: ^2.0.0 dependenciesMeta: graceful-fs: optional: true - checksum: d37b3732c6a44d2839338d4580f6092d569a1dc6b1895b8f0b02ece5c39420b4b8b89cf3540caa4a7da9986c5844d2f6d39753651ddd785959e628b9d87ba216 + checksum: 7af3b8e1ac8fe7f1eccc6263c6ca14e1966fcbc74b618d3c78a0a2075579487547b94f72b7a1114e844a1e15bb00d440e5d1720bfc4612d790a6f285d5ea8354 languageName: node linkType: hard @@ -4955,21 +5441,21 @@ fsevents@^2.3.2: linkType: hard "jsprim@npm:^1.2.2": - version: 1.4.1 - resolution: "jsprim@npm:1.4.1" + version: 1.4.2 + resolution: "jsprim@npm:1.4.2" dependencies: assert-plus: 1.0.0 extsprintf: 1.3.0 - json-schema: 0.2.3 + json-schema: 0.4.0 verror: 1.10.0 - checksum: 6bcb20ec265ae18bb48e540a6da2c65f9c844f7522712d6dfcb01039527a49414816f4869000493363f1e1ea96cbad00e46188d5ecc78257a19f152467587373 + checksum: 2ad1b9fdcccae8b3d580fa6ced25de930eaa1ad154db21bbf8478a4d30bbbec7925b5f5ff29b933fba9412b16a17bd484a8da4fdb3663b5e27af95dd693bab2a languageName: node linkType: hard "just-extend@npm:^4.0.2": - version: 4.1.0 - resolution: "just-extend@npm:4.1.0" - checksum: 47d93278674b55b1e1765dde868d034231b5f457d3d8e75e67006412d0dad56fc942a6814643fdc589bcaa455e21c47b9c84fd92f4aa777f827c30ea279a2ed9 + version: 4.2.1 + resolution: "just-extend@npm:4.2.1" + checksum: ff9fdede240fad313efeeeb68a660b942e5586d99c0058064c78884894a2690dc09bba44c994ad4e077e45d913fef01a9240c14a72c657b53687ac58de53b39c languageName: node linkType: hard @@ -5049,9 +5535,9 @@ fsevents@^2.3.2: linkType: hard "lines-and-columns@npm:^1.1.6": - version: 1.1.6 - resolution: "lines-and-columns@npm:1.1.6" - checksum: 198a5436b1fa5cf703bae719c01c686b076f0ad7e1aafd95a58d626cabff302dc0414822126f2f80b58a8c3d66cda8a7b6da064f27130f87e1d3506d6dfd0d68 + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 0c37f9f7fa212b38912b7145e1cd16a5f3cd34d782441c3e6ca653485d326f58b3caccda66efce1c5812bde4961bbde3374fae4b0d11bf1226152337f3894aa5 languageName: node linkType: hard @@ -5065,30 +5551,29 @@ fsevents@^2.3.2: linkType: hard "lint-staged@npm:12.x": - version: 12.1.2 - resolution: "lint-staged@npm:12.1.2" + version: 12.1.3 + resolution: "lint-staged@npm:12.1.3" dependencies: cli-truncate: ^3.1.0 colorette: ^2.0.16 commander: ^8.3.0 - debug: ^4.3.2 - enquirer: ^2.3.6 + debug: ^4.3.3 execa: ^5.1.1 lilconfig: 2.0.4 - listr2: ^3.13.3 + listr2: ^3.13.5 micromatch: ^4.0.4 normalize-path: ^3.0.0 - object-inspect: ^1.11.0 + object-inspect: ^1.11.1 string-argv: ^0.3.1 - supports-color: ^9.0.2 + supports-color: ^9.2.1 yaml: ^1.10.2 bin: lint-staged: bin/lint-staged.js - checksum: ec84ce4f74d0d8fe314bd0b62fb5e0be64af692d1876f68c457116faef61b4b3b79d72df7819c248bfbb20c46e88fbe994128e7351e84bec7048263e00199925 + checksum: f42fea248f690478ddd3d15ed9dd2302c0744da6af3032d1afe6e9f3c5ff99a7f4af85bf73425d027ff3c53b8c2a4a8f2227ab3e2e1367af786d301debea4bce languageName: node linkType: hard -"listr2@npm:^3.13.3": +"listr2@npm:^3.13.5": version: 3.13.5 resolution: "listr2@npm:3.13.5" dependencies: @@ -5149,13 +5634,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"lodash._reinterpolate@npm:^3.0.0": - version: 3.0.0 - resolution: "lodash._reinterpolate@npm:3.0.0" - checksum: 06d2d5f33169604fa5e9f27b6067ed9fb85d51a84202a656901e5ffb63b426781a601508466f039c720af111b0c685d12f1a5c14ff8df5d5f27e491e562784b2 - languageName: node - linkType: hard - "lodash.get@npm:^4, lodash.get@npm:^4.4.2": version: 4.4.2 resolution: "lodash.get@npm:4.4.2" @@ -5191,46 +5669,13 @@ fsevents@^2.3.2: languageName: node linkType: hard -"lodash.sortby@npm:^4.7.0": - version: 4.7.0 - resolution: "lodash.sortby@npm:4.7.0" - checksum: db170c9396d29d11fe9a9f25668c4993e0c1331bcb941ddbd48fb76f492e732add7f2a47cfdf8e9d740fa59ac41bbfaf931d268bc72aab3ab49e9f89354d718c - languageName: node - linkType: hard - -"lodash.template@npm:^4.0.2": - version: 4.5.0 - resolution: "lodash.template@npm:4.5.0" - dependencies: - lodash._reinterpolate: ^3.0.0 - lodash.templatesettings: ^4.0.0 - checksum: ca64e5f07b6646c9d3dbc0fe3aaa995cb227c4918abd1cef7a9024cd9c924f2fa389a0ec4296aa6634667e029bc81d4bbdb8efbfde11df76d66085e6c529b450 - languageName: node - linkType: hard - -"lodash.templatesettings@npm:^4.0.0": - version: 4.2.0 - resolution: "lodash.templatesettings@npm:4.2.0" - dependencies: - lodash._reinterpolate: ^3.0.0 - checksum: 863e025478b092997e11a04e9d9e735875eeff1ffcd6c61742aa8272e3c2cddc89ce795eb9726c4e74cef5991f722897ff37df7738a125895f23fc7d12a7bb59 - languageName: node - linkType: hard - -"lodash@npm:4.17.21, lodash@npm:^4.17.21, lodash@npm:^4.7.0": +"lodash@npm:4.17.21, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.21, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 languageName: node linkType: hard -"lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19": - version: 4.17.20 - resolution: "lodash@npm:4.17.20" - checksum: b31afa09739b7292a88ec49ffdb2fcaeb41f690def010f7a067eeedffece32da6b6847bfe4d38a77e6f41778b9b2bca75eeab91209936518173271f0b69376ea - languageName: node - linkType: hard - "log-driver@npm:^1.2.7": version: 1.2.7 resolution: "log-driver@npm:1.2.7" @@ -5284,12 +5729,36 @@ fsevents@^2.3.2: languageName: node linkType: hard -"makeerror@npm:1.0.x": - version: 1.0.11 - resolution: "makeerror@npm:1.0.11" +"make-fetch-happen@npm:^9.1.0": + version: 9.1.0 + resolution: "make-fetch-happen@npm:9.1.0" + dependencies: + agentkeepalive: ^4.1.3 + cacache: ^15.2.0 + http-cache-semantics: ^4.1.0 + http-proxy-agent: ^4.0.1 + https-proxy-agent: ^5.0.0 + is-lambda: ^1.0.1 + lru-cache: ^6.0.0 + minipass: ^3.1.3 + minipass-collect: ^1.0.2 + minipass-fetch: ^1.3.2 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + negotiator: ^0.6.2 + promise-retry: ^2.0.1 + socks-proxy-agent: ^6.0.0 + ssri: ^8.0.0 + checksum: 0eb371c85fdd0b1584fcfdf3dc3c62395761b3c14658be02620c310305a9a7ecf1617a5e6fb30c1d081c5c8aaf177fa133ee225024313afabb7aa6a10f1e3d04 + languageName: node + linkType: hard + +"makeerror@npm:1.0.12": + version: 1.0.12 + resolution: "makeerror@npm:1.0.12" dependencies: - tmpl: 1.0.x - checksum: 9a62ec2d9648c5329fdc4bc7d779a7305f32b1e55422a4f14244bc890bb43287fe013eb8d965e92a0cf4c443f3e59265b1fc3125eaedb0c2361e28b1a8de565d + tmpl: 1.0.5 + checksum: b38a025a12c8146d6eeea5a7f2bf27d51d8ad6064da8ca9405fcf7bf9b54acd43e3b30ddd7abb9b1bfa4ddb266019133313482570ddb207de568f71ecfcf6060 languageName: node linkType: hard @@ -5301,9 +5770,9 @@ fsevents@^2.3.2: linkType: hard "map-obj@npm:^4.0.0": - version: 4.1.0 - resolution: "map-obj@npm:4.1.0" - checksum: c62b22f23e58d742a093a0935fa904c92cc788d56132b75666160ac0c5704d3c677d28794594c7adf7ed0c177a96579e781dbf06e0a1b5d574c992a5c13877a3 + version: 4.3.0 + resolution: "map-obj@npm:4.3.0" + checksum: fbc554934d1a27a1910e842bc87b177b1a556609dd803747c85ece420692380827c6ae94a95cce4407c054fa0964be3bf8226f7f2cb2e9eeee432c7c1985684e languageName: node linkType: hard @@ -5347,25 +5816,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"meow@npm:^7.0.0": - version: 7.1.0 - resolution: "meow@npm:7.1.0" - dependencies: - "@types/minimist": ^1.2.0 - camelcase-keys: ^6.2.2 - decamelize-keys: ^1.1.0 - hard-rejection: ^2.1.0 - minimist-options: 4.1.0 - normalize-package-data: ^2.5.0 - read-pkg-up: ^7.0.1 - redent: ^3.0.0 - trim-newlines: ^3.0.0 - type-fest: ^0.13.1 - yargs-parser: ^18.1.3 - checksum: f9678c35e755989a0567d642bd0c9c301f21d4390a180defa5bec70f2d1b8f6fe161ec8bd52795c593f71d685393bbf3fae464b8af78991d1ef5564a53ab67a8 - languageName: node - linkType: hard - "meow@npm:^8.0.0": version: 8.1.2 resolution: "meow@npm:8.1.2" @@ -5385,6 +5835,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"merge-descriptors@npm:~1.0.0": + version: 1.0.1 + resolution: "merge-descriptors@npm:1.0.1" + checksum: 5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26 + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -5392,6 +5849,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"merge2@npm:^1.3.0": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 7268db63ed5169466540b6fb947aec313200bcf6d40c5ab722c22e242f651994619bcd85601602972d3c85bd2cc45a358a4c61937e9f11a061919a1da569b0c2 + languageName: node + linkType: hard + "micromatch@npm:^4.0.4": version: 4.0.4 resolution: "micromatch@npm:4.0.4" @@ -5402,19 +5866,19 @@ fsevents@^2.3.2: languageName: node linkType: hard -"mime-db@npm:1.44.0": - version: 1.44.0 - resolution: "mime-db@npm:1.44.0" - checksum: b2613996804d690adc4ca6744479b8ef08b04db7e99f84ab7e1274e0c2503a446d22296016ae0ea1a1d159858866445601c1f43d46c8d71d52f72842b1780c15 +"mime-db@npm:1.51.0": + version: 1.51.0 + resolution: "mime-db@npm:1.51.0" + checksum: 613b1ac9d6e725cc24444600b124a7f1ce6c60b1baa654f39a3e260d0995a6dffc5693190217e271af7e2a5612dae19f2a73f3e316707d797a7391165f7ef423 languageName: node linkType: hard "mime-types@npm:^2.1.12, mime-types@npm:~2.1.19": - version: 2.1.27 - resolution: "mime-types@npm:2.1.27" + version: 2.1.34 + resolution: "mime-types@npm:2.1.34" dependencies: - mime-db: 1.44.0 - checksum: 4c1f596c6ddfc1a9c37356e91f471ae6e72401288197de31ef3604cf02ef14c6ac661adce55cece1f1c626a96d780ffd47435619606c103cb967fb007729eefb + mime-db: 1.51.0 + checksum: 67013de9e9d6799bde6d669d18785b7e18bcd212e710d3e04a4727f92f67a8ad4e74aee24be28b685adb794944814bde649119b58ee3282ffdbee58f9278d9f3 languageName: node linkType: hard @@ -5459,23 +5923,74 @@ fsevents@^2.3.2: languageName: node linkType: hard -"minimist@npm:^1.2.5": +"minimist@npm:^1.2.0, minimist@npm:^1.2.5": version: 1.2.5 resolution: "minimist@npm:1.2.5" checksum: 86706ce5b36c16bfc35c5fe3dbb01d5acdc9a22f2b6cc810b6680656a1d2c0e44a0159c9a3ba51fb072bb5c203e49e10b51dcd0eec39c481f4c42086719bae52 languageName: node linkType: hard -"minipass@npm:^3.0.0": - version: 3.1.3 - resolution: "minipass@npm:3.1.3" +"minipass-collect@npm:^1.0.2": + version: 1.0.2 + resolution: "minipass-collect@npm:1.0.2" + dependencies: + minipass: ^3.0.0 + checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 + languageName: node + linkType: hard + +"minipass-fetch@npm:^1.3.2": + version: 1.4.1 + resolution: "minipass-fetch@npm:1.4.1" + dependencies: + encoding: ^0.1.12 + minipass: ^3.1.0 + minipass-sized: ^1.0.3 + minizlib: ^2.0.0 + dependenciesMeta: + encoding: + optional: true + checksum: ec93697bdb62129c4e6c0104138e681e30efef8c15d9429dd172f776f83898471bc76521b539ff913248cc2aa6d2b37b652c993504a51cc53282563640f29216 + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: ^3.0.0 + checksum: 56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.2, minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: ^3.0.0 + checksum: b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: ^3.0.0 + checksum: 79076749fcacf21b5d16dd596d32c3b6bf4d6e62abb43868fac21674078505c8b15eaca4e47ed844985a4514854f917d78f588fcd029693709417d8f98b2bd60 + languageName: node + linkType: hard + +"minipass@npm:^3.0.0, minipass@npm:^3.1.0, minipass@npm:^3.1.1, minipass@npm:^3.1.3": + version: 3.1.6 + resolution: "minipass@npm:3.1.6" dependencies: yallist: ^4.0.0 - checksum: 74b623c1f996caafa66772301b66a1b634b20270f0d1a731ef86195d5a1a5f9984a773a1e88a6cecfd264d6c471c4c0fc8574cd96488f01c8f74c0b600021e55 + checksum: 57a04041413a3531a65062452cb5175f93383ef245d6f4a2961d34386eb9aa8ac11ac7f16f791f5e8bbaf1dfb1ef01596870c88e8822215db57aa591a5bb0a77 languageName: node linkType: hard -"minizlib@npm:^2.1.1": +"minizlib@npm:^2.0.0, minizlib@npm:^2.1.1": version: 2.1.2 resolution: "minizlib@npm:2.1.2" dependencies: @@ -5501,6 +6016,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"module-not-found-error@npm:^1.0.1": + version: 1.0.1 + resolution: "module-not-found-error@npm:1.0.1" + checksum: ebd65339d4d5980dd55cd32dbf112ec02b8e33f30866312b94caeee4783322259f18cf2270e9d2e600df3bd1876c35612b87f5c2525c21885fb1f83e85a9b9b0 + languageName: node + linkType: hard + "moment-timezone@npm:0.5.34": version: 0.5.34 resolution: "moment-timezone@npm:0.5.34" @@ -5510,20 +6032,34 @@ fsevents@^2.3.2: languageName: node linkType: hard -"moment@npm:>= 2.9.0": - version: 2.27.0 - resolution: "moment@npm:2.27.0" - checksum: 2674e94f9ae58ac28ca0d27c90c6b7eb4ad90a4d347fa7312d97c66f2e186cbf0ad0dd8981eb4ea8b1edfa70bcb0c21ed7e0dbe9f21b1c801c3d6f1f2c0390f1 +"moment@npm:>= 2.9.0, moment@npm:>=2.14.0": + version: 2.29.1 + resolution: "moment@npm:2.29.1" + checksum: 1e14d5f422a2687996be11dd2d50c8de3bd577c4a4ca79ba5d02c397242a933e5b941655de6c8cb90ac18f01cc4127e55b4a12ae3c527a6c0a274e455979345e + languageName: node + linkType: hard + +"ms@npm:2.0.0": + version: 2.0.0 + resolution: "ms@npm:2.0.0" + checksum: 0e6a22b8b746d2e0b65a430519934fefd41b6db0682e3477c10f60c76e947c4c0ad06f63ffdf1d78d335f83edee8c0aa928aa66a36c7cd95b69b26f468d527f4 languageName: node linkType: hard -"ms@npm:2.1.2, ms@npm:^2.1.1": +"ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f languageName: node linkType: hard +"ms@npm:^2.0.0, ms@npm:^2.1.1": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d + languageName: node + linkType: hard + "mustache@npm:4.x": version: 4.2.0 resolution: "mustache@npm:4.2.0" @@ -5551,6 +6087,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"negotiator@npm:^0.6.2": + version: 0.6.2 + resolution: "negotiator@npm:0.6.2" + checksum: dfddaff6c06792f1c4c3809e29a427b8daef8cd437c83b08dd51d7ee11bbd1c29d9512d66b801144d6c98e910ffd8723f2432e0cbf8b18d41d2a09599c975ab3 + languageName: node + linkType: hard + "neo-async@npm:^2.6.0": version: 2.6.2 resolution: "neo-async@npm:2.6.2" @@ -5601,22 +6144,22 @@ fsevents@^2.3.2: linkType: hard "node-gyp@npm:latest": - version: 7.1.0 - resolution: "node-gyp@npm:7.1.0" + version: 8.4.1 + resolution: "node-gyp@npm:8.4.1" dependencies: env-paths: ^2.2.0 glob: ^7.1.4 - graceful-fs: ^4.2.3 - nopt: ^4.0.3 - npmlog: ^4.1.2 - request: ^2.88.2 - rimraf: ^2.6.3 - semver: ^7.3.2 - tar: ^6.0.1 + graceful-fs: ^4.2.6 + make-fetch-happen: ^9.1.0 + nopt: ^5.0.0 + npmlog: ^6.0.0 + rimraf: ^3.0.2 + semver: ^7.3.5 + tar: ^6.1.2 which: ^2.0.2 bin: node-gyp: bin/node-gyp.js - checksum: d3dde89d9b5b1217af63a5751337c49b0e20833309c4dcdf000e099c5f668541d82f7ce32ae80246b97bda2a7fd5b04ee1811dcda5cf3844c530f6a0fb67637f + checksum: 341710b5da39d3660e6a886b37e210d33f8282047405c2e62c277bcc744c7552c5b8b972ebc3a7d5c2813794e60cc48c3ebd142c46d6e0321db4db6c92dd0355 languageName: node linkType: hard @@ -5627,29 +6170,21 @@ fsevents@^2.3.2: languageName: node linkType: hard -"node-modules-regexp@npm:^1.0.0": - version: 1.0.0 - resolution: "node-modules-regexp@npm:1.0.0" - checksum: 99541903536c5ce552786f0fca7f06b88df595e62e423c21fa86a1674ee2363dad1f7482d1bec20b4bd9fa5f262f88e6e5cb788fc56411113f2fe2e97783a3a7 - languageName: node - linkType: hard - -"node-releases@npm:^1.1.71": - version: 1.1.73 - resolution: "node-releases@npm:1.1.73" - checksum: 44a6caec3330538a669c156fa84833725ae92b317585b106e08ab292c14da09f30cb913c10f1a7402180a51b10074832d4e045b6c3512d74c37d86b41a69e63b +"node-releases@npm:^2.0.1": + version: 2.0.1 + resolution: "node-releases@npm:2.0.1" + checksum: b20dd8d4bced11f75060f0387e05e76b9dc4a0451f7bb3516eade6f50499ea7768ba95d8a60d520c193402df1e58cb3fe301510cc1c1ad68949c3d57b5149866 languageName: node linkType: hard -"nopt@npm:^4.0.3": - version: 4.0.3 - resolution: "nopt@npm:4.0.3" +"nopt@npm:^5.0.0": + version: 5.0.0 + resolution: "nopt@npm:5.0.0" dependencies: abbrev: 1 - osenv: ^0.1.4 bin: nopt: bin/nopt.js - checksum: 66cd3b6021fc8130fc201236bc3dce614fc86988b78faa91377538b09d57aad9ba4300b5d6a01dc93d6c6f2c170f81cc893063d496d108150b65191beb4a50a4 + checksum: d35fdec187269503843924e0114c0c6533fb54bbf1620d0f28b4b60ba01712d6687f62565c55cc20a504eff0fbe5c63e22340c3fad549ad40469ffb611b04f2f languageName: node linkType: hard @@ -5693,22 +6228,15 @@ fsevents@^2.3.2: languageName: node linkType: hard -"npmlog@npm:^4.1.2": - version: 4.1.2 - resolution: "npmlog@npm:4.1.2" +"npmlog@npm:^6.0.0": + version: 6.0.0 + resolution: "npmlog@npm:6.0.0" dependencies: - are-we-there-yet: ~1.1.2 - console-control-strings: ~1.1.0 - gauge: ~2.7.3 - set-blocking: ~2.0.0 - checksum: edbda9f95ec20957a892de1839afc6fb735054c3accf6fbefe767bac9a639fd5cea2baeac6bd2bcd50a85cb54924d57d9886c81c7fbc2332c2ddd19227504192 - languageName: node - linkType: hard - -"number-is-nan@npm:^1.0.0": - version: 1.0.1 - resolution: "number-is-nan@npm:1.0.1" - checksum: 13656bc9aa771b96cef209ffca31c31a03b507ca6862ba7c3f638a283560620d723d52e626d57892c7fff475f4c36ac07f0600f14544692ff595abff214b9ffb + are-we-there-yet: ^2.0.0 + console-control-strings: ^1.1.0 + gauge: ^4.0.0 + set-blocking: ^2.0.0 + checksum: 33d8a7fe3d63bf83b16655b6588ae7ba10b5f37b067a661e7cab6508660d7c3204ae716ee2c5ce4eb9626fd1489cf2fa7645d789bc3b704f8c3ccb04a532a50b languageName: node linkType: hard @@ -5733,10 +6261,40 @@ fsevents@^2.3.2: languageName: node linkType: hard -"object-inspect@npm:^1.11.0": - version: 1.11.1 - resolution: "object-inspect@npm:1.11.1" - checksum: 98bc8e1e108b193cfb5d9bfb71b79f0e19d187aca4f9a3f28ea0e946c0011a74f9fc2ada83ecf2216b3e69fe6bf697fda8230ed84a6ca5680887e7bb73cf34ad +"object-inspect@npm:^1.11.0, object-inspect@npm:^1.11.1, object-inspect@npm:^1.9.0": + version: 1.12.0 + resolution: "object-inspect@npm:1.12.0" + checksum: 2b36d4001a9c921c6b342e2965734519c9c58c355822243c3207fbf0aac271f8d44d30d2d570d450b2cc6f0f00b72bcdba515c37827d2560e5f22b1899a31cf4 + languageName: node + linkType: hard + +"object-keys@npm:^1.0.12, object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a + languageName: node + linkType: hard + +"object.assign@npm:^4.1.2": + version: 4.1.2 + resolution: "object.assign@npm:4.1.2" + dependencies: + call-bind: ^1.0.0 + define-properties: ^1.1.3 + has-symbols: ^1.0.1 + object-keys: ^1.1.1 + checksum: d621d832ed7b16ac74027adb87196804a500d80d9aca536fccb7ba48d33a7e9306a75f94c1d29cbfa324bc091bfc530bc24789568efdaee6a47fcfa298993814 + languageName: node + linkType: hard + +"object.values@npm:^1.1.5": + version: 1.1.5 + resolution: "object.values@npm:1.1.5" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + es-abstract: ^1.19.1 + checksum: 0f17e99741ebfbd0fa55ce942f6184743d3070c61bd39221afc929c8422c4907618c8da694c6915bc04a83ab3224260c779ba37fc07bb668bdc5f33b66a902a4 languageName: node linkType: hard @@ -5786,30 +6344,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"os-homedir@npm:^1.0.0": - version: 1.0.2 - resolution: "os-homedir@npm:1.0.2" - checksum: af609f5a7ab72de2f6ca9be6d6b91a599777afc122ac5cad47e126c1f67c176fe9b52516b9eeca1ff6ca0ab8587fe66208bc85e40a3940125f03cdb91408e9d2 - languageName: node - linkType: hard - -"os-tmpdir@npm:^1.0.0": - version: 1.0.2 - resolution: "os-tmpdir@npm:1.0.2" - checksum: 5666560f7b9f10182548bf7013883265be33620b1c1b4a4d405c25be2636f970c5488ff3e6c48de75b55d02bde037249fe5dbfbb4c0fb7714953d56aed062e6d - languageName: node - linkType: hard - -"osenv@npm:^0.1.4": - version: 0.1.5 - resolution: "osenv@npm:0.1.5" - dependencies: - os-homedir: ^1.0.0 - os-tmpdir: ^1.0.0 - checksum: 779d261920f2a13e5e18cf02446484f12747d3f2ff82280912f52b213162d43d312647a40c332373cbccd5e3fb8126915d3bfea8dde4827f70f82da76e52d359 - languageName: node - linkType: hard - "p-limit@npm:^1.1.0": version: 1.3.0 resolution: "p-limit@npm:1.3.0" @@ -5916,14 +6450,14 @@ fsevents@^2.3.2: linkType: hard "parse-json@npm:^5.0.0": - version: 5.0.1 - resolution: "parse-json@npm:5.0.1" + version: 5.2.0 + resolution: "parse-json@npm:5.2.0" dependencies: "@babel/code-frame": ^7.0.0 error-ex: ^1.3.1 - json-parse-better-errors: ^1.0.1 + json-parse-even-better-errors: ^2.3.0 lines-and-columns: ^1.1.6 - checksum: 63063f966e81d97adb385f0594001e7e4b77b261dd41527929d199819d15a70dcb6b6891be5403097b3b90c374a8f31948bfee43a3e672650d5f2c9fe1c87296 + checksum: 62085b17d64da57f40f6afc2ac1f4d95def18c4323577e1eced571db75d9ab59b297d1d10582920f84b15985cbfc6b6d450ccbf317644cfa176f3ed982ad87e2 languageName: node linkType: hard @@ -5962,10 +6496,10 @@ fsevents@^2.3.2: languageName: node linkType: hard -"path-parse@npm:^1.0.6": - version: 1.0.6 - resolution: "path-parse@npm:1.0.6" - checksum: 962a85dd384d68d469ec5ba4010df8f8f9b7e936ce603bbe3211476c5615feb3c2b1ca61211a78445fadc833f0b1a86ea6484c861035ec4ac93011ba9aff9a11 +"path-parse@npm:^1.0.6, path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a languageName: node linkType: hard @@ -6008,14 +6542,14 @@ fsevents@^2.3.2: languageName: node linkType: hard -"picomatch@npm:^2.0.4": - version: 2.2.2 - resolution: "picomatch@npm:2.2.2" - checksum: 897a589f94665b4fd93e075fa94893936afe3f7bbef44250f0e878a8d9d001972a79589cac2856c24f6f5aa3b0abc9c8ba00c98fae4dc22bc0117188864d4181 +"picocolors@npm:^1.0.0": + version: 1.0.0 + resolution: "picocolors@npm:1.0.0" + checksum: a2e8092dd86c8396bdba9f2b5481032848525b3dc295ce9b57896f931e63fc16f79805144321f72976383fc249584672a75cc18d6777c6b757603f372f745981 languageName: node linkType: hard -"picomatch@npm:^2.2.3": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.3": version: 2.3.0 resolution: "picomatch@npm:2.3.0" checksum: 16818720ea7c5872b6af110760dee856c8e4cd79aed1c7a006d076b1cc09eff3ae41ca5019966694c33fbd2e1cc6ea617ab10e4adac6df06556168f13be3fca2 @@ -6053,11 +6587,18 @@ fsevents@^2.3.2: linkType: hard "pirates@npm:^4.0.1": - version: 4.0.1 - resolution: "pirates@npm:4.0.1" + version: 4.0.4 + resolution: "pirates@npm:4.0.4" + checksum: 6b7187d526fd025a2b91e8fd289c78d88c4adc3ea947b9facbe9cb300a896b0ec00f3e77b36a043001695312a8debbf714453495283bd8a4eaad3bc0c38df425 + languageName: node + linkType: hard + +"pkg-dir@npm:^2.0.0": + version: 2.0.0 + resolution: "pkg-dir@npm:2.0.0" dependencies: - node-modules-regexp: ^1.0.0 - checksum: 091e232aac19f0049a681838fa9fcb4af824b5b1eb0e9325aa07b9d13245bfe3e4fa57a7766b9fdcd19cb89f2c15c688b46023be3047cb288023a0c079d3b2a3 + find-up: ^2.1.0 + checksum: 8c72b712305b51e1108f0ffda5ec1525a8307e54a5855db8fb1dcf77561a5ae98e2ba3b4814c9806a679f76b2f7e5dd98bde18d07e594ddd9fdd25e9cf242ea1 languageName: node linkType: hard @@ -6084,6 +6625,15 @@ fsevents@^2.3.2: languageName: node linkType: hard +"prettier-linter-helpers@npm:^1.0.0": + version: 1.0.0 + resolution: "prettier-linter-helpers@npm:1.0.0" + dependencies: + fast-diff: ^1.1.2 + checksum: 00ce8011cf6430158d27f9c92cfea0a7699405633f7f1d4a45f07e21bf78e99895911cbcdc3853db3a824201a7c745bd49bfea8abd5fb9883e765a90f74f8392 + languageName: node + linkType: hard + "prettier@npm:2.x": version: 2.5.1 resolution: "prettier@npm:2.5.1" @@ -6119,13 +6669,30 @@ fsevents@^2.3.2: languageName: node linkType: hard +"promise-inflight@npm:^1.0.1": + version: 1.0.1 + resolution: "promise-inflight@npm:1.0.1" + checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: ^2.0.2 + retry: ^0.12.0 + checksum: f96a3f6d90b92b568a26f71e966cbbc0f63ab85ea6ff6c81284dc869b41510e6cdef99b6b65f9030f0db422bf7c96652a3fff9f2e8fb4a0f069d8f4430359429 + languageName: node + linkType: hard + "prompts@npm:^2.0.1": - version: 2.3.2 - resolution: "prompts@npm:2.3.2" + version: 2.4.2 + resolution: "prompts@npm:2.4.2" dependencies: kleur: ^3.0.3 - sisteransi: ^1.0.4 - checksum: b1f9e92b59e3be30bcca757dd8458205bc0ff2923cc98b533c09971d4aa2507437d102f73300931a2c50fb4b21f3160a4d4b4683d07c1c1a85d730b463127e18 + sisteransi: ^1.0.5 + checksum: d8fd1fe63820be2412c13bfc5d0a01909acc1f0367e32396962e737cb2fc52d004f3302475d5ce7d18a1e8a79985f93ff04ee03007d091029c3f9104bffc007d languageName: node linkType: hard @@ -6136,6 +6703,17 @@ fsevents@^2.3.2: languageName: node linkType: hard +"proxyquire@npm:^2.1.3": + version: 2.1.3 + resolution: "proxyquire@npm:2.1.3" + dependencies: + fill-keys: ^1.0.2 + module-not-found-error: ^1.0.1 + resolve: ^1.11.1 + checksum: a320f1a04d65aeb41625bfd6bbf848492523b730b07926b6c1ed48f9342f2a30c4a4c0b399e07391e76691b65f604773327767c33a8578e5e4ab19299ba46a02 + languageName: node + linkType: hard + "psl@npm:^1.1.28, psl@npm:^1.1.33": version: 1.8.0 resolution: "psl@npm:1.8.0" @@ -6164,6 +6742,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: b676f8c040cdc5b12723ad2f91414d267605b26419d5c821ff03befa817ddd10e238d22b25d604920340fd73efd8ba795465a0377c4adf45a4a41e4234e42dc4 + languageName: node + linkType: hard + "quick-lru@npm:^4.0.1": version: 4.0.1 resolution: "quick-lru@npm:4.0.1" @@ -6222,7 +6807,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"readable-stream@npm:2 || 3, readable-stream@npm:3, readable-stream@npm:^3.0.0": +"readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.6.0": version: 3.6.0 resolution: "readable-stream@npm:3.6.0" dependencies: @@ -6233,7 +6818,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"readable-stream@npm:^2.0.6, readable-stream@npm:~2.3.6": +"readable-stream@npm:~2.3.6": version: 2.3.7 resolution: "readable-stream@npm:2.3.7" dependencies: @@ -6265,6 +6850,24 @@ fsevents@^2.3.2: languageName: node linkType: hard +"regexp-match-indices@npm:1.0.2": + version: 1.0.2 + resolution: "regexp-match-indices@npm:1.0.2" + dependencies: + regexp-tree: ^0.1.11 + checksum: 8cc779f6cf8f404ead828d09970a7d4bd66bd78d43ab9eb2b5e65f2ef2ba1ed53536f5b5fa839fb90b350365fb44b6a851c7f16289afc3f37789c113ab2a7916 + languageName: node + linkType: hard + +"regexp-tree@npm:^0.1.11": + version: 0.1.24 + resolution: "regexp-tree@npm:0.1.24" + bin: + regexp-tree: bin/regexp-tree + checksum: 5807013289d9205288d665e0f8d8cff94843dfd55fdedd1833eb9d9bbd07188a37dfa02942ec5cdc671180037f715148fac1ba6f18fd6be4268e5a8feb49d340 + languageName: node + linkType: hard + "regexpp@npm:^3.2.0": version: 3.2.0 resolution: "regexpp@npm:3.2.0" @@ -6371,16 +6974,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"resolve@^1.10.0, resolve@^1.3.2": - version: 1.17.0 - resolution: "resolve@npm:1.17.0" - dependencies: - path-parse: ^1.0.6 - checksum: 9ceaf83b3429f2d7ff5d0281b8d8f18a1f05b6ca86efea7633e76b8f76547f33800799dfdd24434942dec4fbd9e651ed3aef577d9a6b5ec87ad89c1060e24759 - languageName: node - linkType: hard - -"resolve@^1.19.0, resolve@^1.20.0": +"resolve@npm:^1.10.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0": version: 1.20.0 resolution: "resolve@npm:1.20.0" dependencies: @@ -6390,16 +6984,20 @@ fsevents@^2.3.2: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.3.2#~builtin": - version: 1.17.0 - resolution: "resolve@patch:resolve@npm%3A1.17.0#~builtin::version=1.17.0&hash=07638b" +"resolve@npm:^1.11.1": + version: 1.21.0 + resolution: "resolve@npm:1.21.0" dependencies: - path-parse: ^1.0.6 - checksum: 6fd799f282ddf078c4bc20ce863e3af01fa8cb218f0658d9162c57161a2dbafe092b13015b9a4c58d0e1e801cf7aa7a4f13115fea9db98c3f9a0c43e429bad6f + is-core-module: ^2.8.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: d7d9092a5c04a048bea16c7e5a2eb605ac3e8363a0cc5644de1fde17d5028e8d5f4343aab1d99bd327b98e91a66ea83e242718150c64dfedcb96e5e7aad6c4f5 languageName: node linkType: hard -"resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin": +"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin": version: 1.20.0 resolution: "resolve@patch:resolve@npm%3A1.20.0#~builtin::version=1.20.0&hash=07638b" dependencies: @@ -6409,6 +7007,19 @@ fsevents@^2.3.2: languageName: node linkType: hard +"resolve@patch:resolve@^1.11.1#~builtin": + version: 1.21.0 + resolution: "resolve@patch:resolve@npm%3A1.21.0#~builtin::version=1.21.0&hash=07638b" + dependencies: + is-core-module: ^2.8.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: a0a4d1f7409e73190f31f901f8a619960bb3bd4ae38ba3a54c7ea7e1c87758d28a73256bb8d6a35996a903d1bf14f53883f0dcac6c571c063cb8162d813ad26e + languageName: node + linkType: hard + "restore-cursor@npm:^3.1.0": version: 3.1.0 resolution: "restore-cursor@npm:3.1.0" @@ -6419,6 +7030,20 @@ fsevents@^2.3.2: languageName: node linkType: hard +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 623bd7d2e5119467ba66202d733ec3c2e2e26568074923bc0585b6b99db14f357e79bdedb63cab56cec47491c4a0da7e6021a7465ca6dc4f481d3898fdd3158c + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.0.4 + resolution: "reusify@npm:1.0.4" + checksum: c3076ebcc22a6bc252cb0b9c77561795256c22b757f40c0d8110b1300723f15ec0fc8685e8d4ea6d7666f36c79ccc793b1939c748bf36f18f542744a4e379fcc + languageName: node + linkType: hard + "rfdc@npm:^1.3.0": version: 1.3.0 resolution: "rfdc@npm:1.3.0" @@ -6426,17 +7051,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"rimraf@npm:^2.6.3": - version: 2.6.3 - resolution: "rimraf@npm:2.6.3" - dependencies: - glob: ^7.1.3 - bin: - rimraf: ./bin.js - checksum: 3ea587b981a19016297edb96d1ffe48af7e6af69660e3b371dbfc73722a73a0b0e9be5c88089fbeeb866c389c1098e07f64929c7414290504b855f54f901ab10 - languageName: node - linkType: hard - "rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": version: 3.0.2 resolution: "rimraf@npm:3.0.2" @@ -6448,6 +7062,15 @@ fsevents@^2.3.2: languageName: node linkType: hard +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: ^1.2.2 + checksum: cb4f97ad25a75ebc11a8ef4e33bb962f8af8516bb2001082ceabd8902e15b98f4b84b4f8a9b222e5d57fc3bd1379c483886ed4619367a7680dad65316993021d + languageName: node + linkType: hard + "rxjs@npm:^7.4.0": version: 7.4.0 resolution: "rxjs@npm:7.4.0" @@ -6471,7 +7094,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 @@ -6494,7 +7117,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.4.1": +"semver@npm:2 || 3 || 4 || 5": version: 5.7.1 resolution: "semver@npm:5.7.1" bin: @@ -6503,7 +7126,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"semver@npm:7.3.5, semver@npm:7.x, semver@npm:^7.3.4": +"semver@npm:7.3.5, semver@npm:7.x, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5": version: 7.3.5 resolution: "semver@npm:7.3.5" dependencies: @@ -6523,16 +7146,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"semver@npm:^7.2.1, semver@npm:^7.3.2": - version: 7.3.2 - resolution: "semver@npm:7.3.2" - bin: - semver: bin/semver.js - checksum: 692f4900dadb43919614b0df9af23fe05743051cda0d1735b5e4d76f93c9e43a266fae73cfc928f5d1489f022c5c0e65dfd2900fcf5b1839c4e9a239729afa7b - languageName: node - linkType: hard - -"set-blocking@npm:~2.0.0": +"set-blocking@npm:^2.0.0": version: 2.0.0 resolution: "set-blocking@npm:2.0.0" checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 @@ -6555,10 +7169,21 @@ fsevents@^2.3.2: languageName: node linkType: hard +"side-channel@npm:^1.0.4": + version: 1.0.4 + resolution: "side-channel@npm:1.0.4" + dependencies: + call-bind: ^1.0.0 + get-intrinsic: ^1.0.2 + object-inspect: ^1.9.0 + checksum: 351e41b947079c10bd0858364f32bb3a7379514c399edb64ab3dce683933483fc63fb5e4efe0a15a2e8a7e3c436b6a91736ddb8d8c6591b0460a24bb4a1ee245 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3": - version: 3.0.3 - resolution: "signal-exit@npm:3.0.3" - checksum: f0169d3f1263d06df32ca072b0bf33b34c6f8f0341a7a1621558a2444dfbe8f5fec76b35537fcc6f0bc4944bdb5336fe0bdcf41a5422c4e45a1dba3f45475e6c + version: 3.0.6 + resolution: "signal-exit@npm:3.0.6" + checksum: b819ac81ba757af559dad0804233ae31bf6f054591cd8a671e9cbcf09f21c72ec3076fe87d1e04861f5b33b47d63f0694b568de99c99cd733ee2060515beb6d5 languageName: node linkType: hard @@ -6576,7 +7201,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"sisteransi@npm:^1.0.4": +"sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" checksum: aba6438f46d2bfcef94cf112c835ab395172c75f67453fe05c340c770d3c402363018ae1ab4172a1026a90c47eaccf3af7b6ff6fa749a680c2929bd7fa2b37a4 @@ -6622,7 +7247,35 @@ fsevents@^2.3.2: languageName: node linkType: hard -"source-map-support@npm:0.5.19, source-map-support@npm:^0.5.6": +"smart-buffer@npm:^4.1.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: b5167a7142c1da704c0e3af85c402002b597081dd9575031a90b4f229ca5678e9a36e8a374f1814c8156a725d17008ae3bde63b92f9cfd132526379e580bec8b + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^6.0.0": + version: 6.1.1 + resolution: "socks-proxy-agent@npm:6.1.1" + dependencies: + agent-base: ^6.0.2 + debug: ^4.3.1 + socks: ^2.6.1 + checksum: 9a8a4f791bba0060315cf7291ca6f9db37d6fc280fd0860d73d8887d3efe4c22e823aa25a8d5375f6079279f8dc91b50c075345179bf832bfe3c7c26d3582e3c + languageName: node + linkType: hard + +"socks@npm:^2.6.1": + version: 2.6.1 + resolution: "socks@npm:2.6.1" + dependencies: + ip: ^1.1.5 + smart-buffer: ^4.1.0 + checksum: 2ca9d616e424f645838ebaabb04f85d94ea999e0f8393dc07f86c435af22ed88cb83958feeabd1bb7bc537c635ed47454255635502c6808a6df61af1f41af750 + languageName: node + linkType: hard + +"source-map-support@npm:0.5.19": version: 0.5.19 resolution: "source-map-support@npm:0.5.19" dependencies: @@ -6632,7 +7285,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"source-map-support@npm:^0.5.17": +"source-map-support@npm:^0.5.17, source-map-support@npm:^0.5.6": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" dependencies: @@ -6698,18 +7351,9 @@ fsevents@^2.3.2: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.5 - resolution: "spdx-license-ids@npm:3.0.5" - checksum: b1ceea3f87407ec375d1de90f6fc7610d6c845ff5f8db21d4d752b3d4e121df563c78113df7c564daff4e8778ad54b9a9024a7e9ea3779f13a43dd0e9128c08e - languageName: node - linkType: hard - -"split2@npm:^2.0.0": - version: 2.2.0 - resolution: "split2@npm:2.2.0" - dependencies: - through2: ^2.0.2 - checksum: 06a9fe364f1c37098539e8d9b460c8c2e00320600a5e040dd3fa006f6bdabbbe6ee983568a62a585f41862c61b9672f59b93ef0accf4add346c716edcd6d21b7 + version: 3.0.11 + resolution: "spdx-license-ids@npm:3.0.11" + checksum: 1da1acb090257773e60b022094050e810ae9fec874dc1461f65dc0400cd42dd830ab2df6e64fb49c2db3dce386dd0362110780e1b154db7c0bb413488836aaeb languageName: node linkType: hard @@ -6759,6 +7403,15 @@ fsevents@^2.3.2: languageName: node linkType: hard +"ssri@npm:^8.0.0, ssri@npm:^8.0.1": + version: 8.0.1 + resolution: "ssri@npm:8.0.1" + dependencies: + minipass: ^3.1.1 + checksum: bc447f5af814fa9713aa201ec2522208ae0f4d8f3bda7a1f445a797c7b929a02720436ff7c478fb5edc4045adb02b1b88d2341b436a80798734e2494f1067b36 + languageName: node + linkType: hard + "stack-chain@npm:^2.0.0": version: 2.0.0 resolution: "stack-chain@npm:2.0.0" @@ -6776,11 +7429,11 @@ fsevents@^2.3.2: linkType: hard "stack-utils@npm:^2.0.3": - version: 2.0.3 - resolution: "stack-utils@npm:2.0.3" + version: 2.0.5 + resolution: "stack-utils@npm:2.0.5" dependencies: escape-string-regexp: ^2.0.0 - checksum: c86ac08f58d1a9bce3f17946cb2f18268f55f8180f5396ae147deecb4d23cd54f3d27e4a8d3227d525b0f0c89b7f7e839e223851a577136a763ccd7e488440be + checksum: 76b69da0f5b48a34a0f93c98ee2a96544d2c4ca2557f7eef5ddb961d3bdc33870b46f498a84a7c4f4ffb781df639840e7ebf6639164ed4da5e1aeb659615b9c7 languageName: node linkType: hard @@ -6820,48 +7473,16 @@ fsevents@^2.3.2: linkType: hard "string-length@npm:^4.0.1": - version: 4.0.1 - resolution: "string-length@npm:4.0.1" + version: 4.0.2 + resolution: "string-length@npm:4.0.2" dependencies: char-regex: ^1.0.2 strip-ansi: ^6.0.0 - checksum: 7bd3191668ddafa6f574a8b17a1bd1b085737d64ceefa51f72cdd19c45a730422cd70d984eee7584d6e5b5c84b6318633c6d6a720a4bfd7c58769985fa77573e - languageName: node - linkType: hard - -"string-width@npm:^1.0.1": - version: 1.0.2 - resolution: "string-width@npm:1.0.2" - dependencies: - code-point-at: ^1.0.0 - is-fullwidth-code-point: ^1.0.0 - strip-ansi: ^3.0.0 - checksum: 5c79439e95bc3bd7233a332c5f5926ab2ee90b23816ed4faa380ce3b2576d7800b0a5bb15ae88ed28737acc7ea06a518c2eef39142dd727adad0e45c776cd37e - languageName: node - linkType: hard - -"string-width@npm:^1.0.2 || 2": - version: 2.1.1 - resolution: "string-width@npm:2.1.1" - dependencies: - is-fullwidth-code-point: ^2.0.0 - strip-ansi: ^4.0.0 - checksum: d6173abe088c615c8dffaf3861dc5d5906ed3dc2d6fd67ff2bd2e2b5dce7fd683c5240699cf0b1b8aa679a3b3bd6b28b5053c824cb89b813d7f6541d8f89064a - languageName: node - linkType: hard - -"string-width@npm:^4.1.0, string-width@npm:^4.2.0": - version: 4.2.0 - resolution: "string-width@npm:4.2.0" - dependencies: - emoji-regex: ^8.0.0 - is-fullwidth-code-point: ^3.0.0 - strip-ansi: ^6.0.0 - checksum: ee2c68df9a3ce4256565d2bdc8490f5706f195f88e799d3d425889264d3eff3d7984fe8b38dfc983dac948e03d8cdc737294b1c81f1528c37c9935d86b67593d + checksum: ce85533ef5113fcb7e522bcf9e62cb33871aa99b3729cec5595f4447f660b0cefd542ca6df4150c97a677d58b0cb727a3fe09ac1de94071d05526c73579bf505 languageName: node linkType: hard -"string-width@npm:^4.2.3": +"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -6883,6 +7504,26 @@ fsevents@^2.3.2: languageName: node linkType: hard +"string.prototype.trimend@npm:^1.0.4": + version: 1.0.4 + resolution: "string.prototype.trimend@npm:1.0.4" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + checksum: 17e5aa45c3983f582693161f972c1c1fa4bbbdf22e70e582b00c91b6575f01680dc34e83005b98e31abe4d5d29e0b21fcc24690239c106c7b2315aade6a898ac + languageName: node + linkType: hard + +"string.prototype.trimstart@npm:^1.0.4": + version: 1.0.4 + resolution: "string.prototype.trimstart@npm:1.0.4" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.3 + checksum: 3fb06818d3cccac5fa3f5f9873d984794ca0e9f6616fae6fcc745885d9efed4e17fe15f832515d9af5e16c279857fdbffdfc489ca4ed577811b017721b30302f + languageName: node + linkType: hard + "string_decoder@npm:^1.1.1": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" @@ -6901,34 +7542,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1": - version: 3.0.1 - resolution: "strip-ansi@npm:3.0.1" - dependencies: - ansi-regex: ^2.0.0 - checksum: 9b974de611ce5075c70629c00fa98c46144043db92ae17748fb780f706f7a789e9989fd10597b7c2053ae8d1513fd707816a91f1879b2f71e6ac0b6a863db465 - languageName: node - linkType: hard - -"strip-ansi@npm:^4.0.0": - version: 4.0.0 - resolution: "strip-ansi@npm:4.0.0" - dependencies: - ansi-regex: ^3.0.0 - checksum: d9186e6c0cf78f25274f6750ee5e4a5725fb91b70fdd79aa5fe648eab092a0ec5b9621b22d69d4534a56319f75d8944efbd84e3afa8d4ad1b9a9491f12c84eca - languageName: node - linkType: hard - -"strip-ansi@npm:^6.0.0": - version: 6.0.0 - resolution: "strip-ansi@npm:6.0.0" - dependencies: - ansi-regex: ^5.0.0 - checksum: 04c3239ede44c4d195b0e66c0ad58b932f08bec7d05290416d361ff908ad282ecdaf5d9731e322c84f151d427436bde01f05b7422c3ec26dd927586736b0e5d0 - languageName: node - linkType: hard - -"strip-ansi@npm:^6.0.1": +"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" dependencies: @@ -7001,16 +7615,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0": - version: 7.1.0 - resolution: "supports-color@npm:7.1.0" - dependencies: - has-flag: ^4.0.0 - checksum: 899480ac858a650abcca4a02ae655555270e6ace833b15a74e4a2d3456f54cd19b6b12ce14e9bac997c18dd69a0596ee65b95ba013f209dd0f99ebfe87783e41 - languageName: node - linkType: hard - -"supports-color@npm:^7.2.0": +"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0, supports-color@npm:^7.2.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" dependencies: @@ -7028,7 +7633,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"supports-color@npm:^9.0.2": +"supports-color@npm:^9.2.1": version: 9.2.1 resolution: "supports-color@npm:9.2.1" checksum: 8a2bfeb64c1512d21a1a998c1f64acdaa85cf1f6a101627286548f19785524b329d7b28d567a28fc2d708fc7aba32f4c82a9b224f76b30a337a39d3e53418ff7 @@ -7036,12 +7641,19 @@ fsevents@^2.3.2: linkType: hard "supports-hyperlinks@npm:^2.0.0": - version: 2.1.0 - resolution: "supports-hyperlinks@npm:2.1.0" + version: 2.2.0 + resolution: "supports-hyperlinks@npm:2.2.0" dependencies: has-flag: ^4.0.0 supports-color: ^7.0.0 - checksum: e4f430c870a258c9854b8bd7f166a9c1e76e3b851da84d4399d6a8f1d4a485e4ec36c16455dde80acf06c86e7c0a6df76ed22b6a4644a6ae3eced8616b3f21b5 + checksum: aef04fb41f4a67f1bc128f7c3e88a81b6cf2794c800fccf137006efe5bafde281da3e42e72bf9206c2fcf42e6438f37e3a820a389214d0a88613ca1f2d36076a + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 53b1e247e68e05db7b3808b99b892bd36fb096e6fba213a06da7fab22045e97597db425c724f2bbd6c99a3c295e1e73f3e4de78592289f38431049e1277ca0ae languageName: node linkType: hard @@ -7059,9 +7671,9 @@ fsevents@^2.3.2: languageName: node linkType: hard -"tar@npm:^6.0.1": - version: 6.0.5 - resolution: "tar@npm:6.0.5" +"tar@npm:^6.0.2, tar@npm:^6.1.2": + version: 6.1.11 + resolution: "tar@npm:6.1.11" dependencies: chownr: ^2.0.0 fs-minipass: ^2.0.0 @@ -7069,7 +7681,7 @@ fsevents@^2.3.2: minizlib: ^2.1.1 mkdirp: ^1.0.3 yallist: ^4.0.0 - checksum: e1bdfbf5cc00a5912a855ddf5e0372547ddb56f263839a99c49cad74cfbc20421b1dce1a3231c77348f1a14773924d118512f8c39ff234194896a7590eec7354 + checksum: a04c07bb9e2d8f46776517d4618f2406fb977a74d914ad98b264fc3db0fe8224da5bec11e5f8902c5b9bcb8ace22d95fbe3c7b36b8593b7dfc8391a25898f32f languageName: node linkType: hard @@ -7150,7 +7762,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"through2@npm:^2.0.0, through2@npm:^2.0.2": +"through2@npm:^2.0.0": version: 2.0.5 resolution: "through2@npm:2.0.5" dependencies: @@ -7160,16 +7772,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"through2@npm:^3.0.0": - version: 3.0.2 - resolution: "through2@npm:3.0.2" - dependencies: - inherits: ^2.0.4 - readable-stream: 2 || 3 - checksum: 47c9586c735e7d9cbbc1029f3ff422108212f7cc42e06d5cc9fff7901e659c948143c790e0d0d41b1b5f89f1d1200bdd200c7b72ad34f42f9edbeb32ea49e8b7 - languageName: node - linkType: hard - "through2@npm:^4.0.0": version: 4.0.2 resolution: "through2@npm:4.0.2" @@ -7195,10 +7797,10 @@ fsevents@^2.3.2: languageName: node linkType: hard -"tmpl@npm:1.0.x": - version: 1.0.4 - resolution: "tmpl@npm:1.0.4" - checksum: 72c93335044b5b8771207d2e9cf71e8c26b110d0f0f924f6d6c06b509d89552c7c0e4086a574ce4f05110ac40c1faf6277ecba7221afeb57ebbab70d8de39cc4 +"tmpl@npm:1.0.5": + version: 1.0.5 + resolution: "tmpl@npm:1.0.5" + checksum: cd922d9b853c00fe414c5a774817be65b058d54a2d01ebb415840960406c669a0fc632f66df885e24cb022ec812739199ccbdb8d1164c3e513f85bfca5ab2873 languageName: node linkType: hard @@ -7239,15 +7841,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"tr46@npm:^2.0.2": - version: 2.0.2 - resolution: "tr46@npm:2.0.2" - dependencies: - punycode: ^2.1.1 - checksum: 2b2b3dfa6bc65d027b2fac729fba0fb5b9d98af7b69ad6876c0f088ebf127f2d53e5a4d4464e5de40380cf721f392262c9183d2a05cea4967a890e8801c842f6 - languageName: node - linkType: hard - "tr46@npm:^2.1.0": version: 2.1.0 resolution: "tr46@npm:2.1.0" @@ -7258,9 +7851,9 @@ fsevents@^2.3.2: linkType: hard "trim-newlines@npm:^3.0.0": - version: 3.0.0 - resolution: "trim-newlines@npm:3.0.0" - checksum: ad99b771e7e6fc785cfdd60f3eeb794a6f2f230dd291987107974abd0c95a051d7cf3b6d45b542a59bfe67eb680c5b259ec19741e6fdfdbee0ab783ab8861585 + version: 3.0.1 + resolution: "trim-newlines@npm:3.0.1" + checksum: b530f3fadf78e570cf3c761fb74fef655beff6b0f84b29209bac6c9622db75ad1417f4a7b5d54c96605dcd72734ad44526fef9f396807b90839449eb543c6206 languageName: node linkType: hard @@ -7274,8 +7867,8 @@ fsevents@^2.3.2: linkType: hard "ts-jest@npm:27.x": - version: 27.1.1 - resolution: "ts-jest@npm:27.1.1" + version: 27.1.2 + resolution: "ts-jest@npm:27.1.2" dependencies: bs-logger: 0.x fast-json-stable-stringify: 2.x @@ -7303,7 +7896,43 @@ fsevents@^2.3.2: optional: true bin: ts-jest: cli.js - checksum: 3aea68083399c49e9079dbd1583ee5cce7da9648a6cdaef96c2b5f2f85de375c186227724336f359ecd2bc7871bff0911667d1f4867e0e275417b3e4887841d9 + checksum: 2e7275f8a3545ec1340b37c458ace9244b5903e86861eb108beffff97d433f296c1254f76a41b573b1fe6245110b21bb62150bb88d55159f1dc7a929886535cb + languageName: node + linkType: hard + +"ts-node@npm:10.x": + version: 10.4.0 + resolution: "ts-node@npm:10.4.0" + dependencies: + "@cspotcode/source-map-support": 0.7.0 + "@tsconfig/node10": ^1.0.7 + "@tsconfig/node12": ^1.0.7 + "@tsconfig/node14": ^1.0.0 + "@tsconfig/node16": ^1.0.2 + acorn: ^8.4.1 + acorn-walk: ^8.1.1 + arg: ^4.1.0 + create-require: ^1.1.0 + diff: ^4.0.1 + make-error: ^1.1.1 + yn: 3.1.1 + peerDependencies: + "@swc/core": ">=1.2.50" + "@swc/wasm": ">=1.2.50" + "@types/node": "*" + typescript: ">=2.7" + peerDependenciesMeta: + "@swc/core": + optional: true + "@swc/wasm": + optional: true + bin: + ts-node: dist/bin.js + ts-node-cwd: dist/bin-cwd.js + ts-node-script: dist/bin-script.js + ts-node-transpile-only: dist/bin-transpile.js + ts-script: dist/bin-script-deprecated.js + checksum: 3933ac0a937d33c45e04a6750fcdd3e765eb2897d1da1307cd97ac52af093bcfb632ec0453a75000a65c8b5b7bdb32b1077050a186dcc556e62657cb592e6d49 languageName: node linkType: hard @@ -7328,17 +7957,29 @@ fsevents@^2.3.2: languageName: node linkType: hard -"tslib@npm:^2": - version: 2.3.1 - resolution: "tslib@npm:2.3.1" - checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 +"tsconfig-paths@npm:^3.11.0": + version: 3.12.0 + resolution: "tsconfig-paths@npm:3.12.0" + dependencies: + "@types/json5": ^0.0.29 + json5: ^1.0.1 + minimist: ^1.2.0 + strip-bom: ^3.0.0 + checksum: 4999ec6cd1c7cc06750a460dbc0d39fe3595a4308cb5f1d0d0a8283009cf9c0a30d5a156508c28fe3a47760508af5263ab288fc23d71e9762779674257a95d3b languageName: node linkType: hard -"tslib@npm:^2.0.3": - version: 2.3.0 - resolution: "tslib@npm:2.3.0" - checksum: 8869694c26e4a7b56d449662fd54a4f9ba872c889d991202c74462bd99f10e61d5bd63199566c4284c0f742277736292a969642cc7b590f98727a7cae9529122 +"tslib@npm:^1.8.1": + version: 1.14.1 + resolution: "tslib@npm:1.14.1" + checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd + languageName: node + linkType: hard + +"tslib@npm:^2, tslib@npm:^2.0.3": + version: 2.3.1 + resolution: "tslib@npm:2.3.1" + checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 languageName: node linkType: hard @@ -7349,6 +7990,17 @@ fsevents@^2.3.2: languageName: node linkType: hard +"tsutils@npm:^3.21.0": + version: 3.21.0 + resolution: "tsutils@npm:3.21.0" + dependencies: + tslib: ^1.8.1 + peerDependencies: + typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + checksum: 1843f4c1b2e0f975e08c4c21caa4af4f7f65a12ac1b81b3b8489366826259323feb3fc7a243123453d2d1a02314205a7634e048d4a8009921da19f99755cdc48 + languageName: node + linkType: hard + "tunnel-agent@npm:^0.6.0": version: 0.6.0 resolution: "tunnel-agent@npm:0.6.0" @@ -7390,20 +8042,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"type-fest@npm:^0.11.0": - version: 0.11.0 - resolution: "type-fest@npm:0.11.0" - checksum: 8e7589e1eb5ced6c8e1d3051553b59b9f525c41e58baa898229915781c7bf55db8cb2f74e56d8031f6af5af2eecc7cb8da9ca3af7e5b80b49d8ca5a81891f3f9 - languageName: node - linkType: hard - -"type-fest@npm:^0.13.1": - version: 0.13.1 - resolution: "type-fest@npm:0.13.1" - checksum: e6bf2e3c449f27d4ef5d56faf8b86feafbc3aec3025fc9a5fbe2db0a2587c44714521f9c30d8516a833c8c506d6263f5cc11267522b10c6ccdb6cc55b0a9d1c4 - languageName: node - linkType: hard - "type-fest@npm:^0.18.0": version: 0.18.1 resolution: "type-fest@npm:0.18.1" @@ -7418,6 +8056,13 @@ fsevents@^2.3.2: languageName: node linkType: hard +"type-fest@npm:^0.21.3": + version: 0.21.3 + resolution: "type-fest@npm:0.21.3" + checksum: e6b32a3b3877f04339bae01c193b273c62ba7bfc9e325b8703c4ee1b32dc8fe4ef5dfa54bf78265e069f7667d058e360ae0f37be5af9f153b22382cd55a9afe0 + languageName: node + linkType: hard + "type-fest@npm:^0.6.0": version: 0.6.0 resolution: "type-fest@npm:0.6.0" @@ -7439,10 +8084,10 @@ fsevents@^2.3.2: languageName: node linkType: hard -"type@npm:^2.0.0": - version: 2.0.0 - resolution: "type@npm:2.0.0" - checksum: 43f56b90e0da625c2f08f897c580d65162c16287960a0ef62c1a935743c09ddbc0ca85a4067bc79be0c215a1ee517c902af260fc7777d62a38c659d0eb43529f +"type@npm:^2.5.0": + version: 2.5.0 + resolution: "type@npm:2.5.0" + checksum: 0fe1bb4e8ba298b2b245fdc6bca6178887e29e2134d231e468366615b3adffd651d464eb51d8b15f8cfd168577c282a17e19bf80f036a60d4df16308a83a93c4 languageName: node linkType: hard @@ -7456,22 +8101,22 @@ fsevents@^2.3.2: linkType: hard "typescript@npm:4.5.x, typescript@npm:^4.4.3": - version: 4.5.3 - resolution: "typescript@npm:4.5.3" + version: 4.5.4 + resolution: "typescript@npm:4.5.4" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 2ac278c20418882816789ad94a5d9e7f11969db7a2a23fa09e61f2fedf3814265cbd4f89d7f5b6e1f0e7be2b5f169becf5dab913d0da255b073ea3c8c324e6d4 + checksum: 59f3243f9cd6fe3161e6150ff6bf795fc843b4234a655dbd938a310515e0d61afd1ac942799e7415e4334255e41c2c49b7dd5d9fd38a17acd25a6779ca7e0961 languageName: node linkType: hard "typescript@patch:typescript@4.5.x#~builtin, typescript@patch:typescript@^4.4.3#~builtin": - version: 4.5.3 - resolution: "typescript@patch:typescript@npm%3A4.5.3#~builtin::version=4.5.3&hash=ddd1e8" + version: 4.5.4 + resolution: "typescript@patch:typescript@npm%3A4.5.4#~builtin::version=4.5.4&hash=ddd1e8" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 39ce769cf7489fff727350b70f7ccb59ce60fef32572892050ced2b1a5dac9c1f9ac8d74b08a5b2dc8d52c9d0f3031ea26b60b82fb1c059dd65704d21fb19d8b + checksum: 270255355c3236076dbbd0900ff0b7b159fac7e6a95f9ed8c59f57361c7dace9f1fbffd3b5eb21377c7f636027382ad89eb64b2acfbcd9e08574f04cfc75ca3d languageName: node linkType: hard @@ -7483,18 +8128,48 @@ fsevents@^2.3.2: linkType: hard "uglify-js@npm:^3.1.4": - version: 3.10.1 - resolution: "uglify-js@npm:3.10.1" + version: 3.14.5 + resolution: "uglify-js@npm:3.14.5" bin: uglifyjs: bin/uglifyjs - checksum: 66cfeb0954e7cc2f3476ac3f21e7f6bc18c85cdadfef1df4875b9cde1b50e015c62cdd43681522efe18672fe431b412aba9cf814e0e81d8242bac242fe0839bf + checksum: 0330eb11a36f4181b6d9a00336355989bfad9dd2203049fc63a59454b0d12337612272ad011bc571b9a382bf74d829ca20409ebfe089e38edb26cfc06bfa2cc9 + languageName: node + linkType: hard + +"unbox-primitive@npm:^1.0.1": + version: 1.0.1 + resolution: "unbox-primitive@npm:1.0.1" + dependencies: + function-bind: ^1.1.1 + has-bigints: ^1.0.1 + has-symbols: ^1.0.2 + which-boxed-primitive: ^1.0.2 + checksum: 89d950e18fb45672bc6b3c961f1e72c07beb9640c7ceed847b571ba6f7d2af570ae1a2584cfee268b9d9ea1e3293f7e33e0bc29eaeb9f8e8a0bab057ff9e6bba languageName: node linkType: hard "underscore@npm:~1.13.1": - version: 1.13.1 - resolution: "underscore@npm:1.13.1" - checksum: 69bb4e6dd92c387ad322dd5b105f496fa64896d92ff1eea987610920d452667a12bca0938def4c4d60acd12da62410540fd268e7ca4f2480d89324498382fcac + version: 1.13.2 + resolution: "underscore@npm:1.13.2" + checksum: 6ab156c845ccc757fd01d8b9eb28be18ba89ac68993370dd7397a66a95b124f2ba26947fd53e687c084d334429914fc3dd1620d5123b6e0a7cf112cdcf4e859f + languageName: node + linkType: hard + +"unique-filename@npm:^1.1.1": + version: 1.1.1 + resolution: "unique-filename@npm:1.1.1" + dependencies: + unique-slug: ^2.0.0 + checksum: cf4998c9228cc7647ba7814e255dec51be43673903897b1786eff2ac2d670f54d4d733357eb08dea969aa5e6875d0e1bd391d668fbdb5a179744e7c7551a6f80 + languageName: node + linkType: hard + +"unique-slug@npm:^2.0.0": + version: 2.0.2 + resolution: "unique-slug@npm:2.0.2" + dependencies: + imurmurhash: ^0.1.4 + checksum: 5b6876a645da08d505dedb970d1571f6cebdf87044cb6b740c8dbb24f0d6e1dc8bdbf46825fd09f994d7cf50760e6f6e063cfa197d51c5902c00a861702eb75a languageName: node linkType: hard @@ -7505,13 +8180,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"universalify@npm:^1.0.0": - version: 1.0.0 - resolution: "universalify@npm:1.0.0" - checksum: 095a808f2b915e3b89d29b6f3b4ee4163962b02fa5b7cb686970b8d0439f4ca789bc43f319b7cbb1ce552ae724e631d148e5aee9ce04c4f46a7fe0c5bbfd2b9e - languageName: node - linkType: hard - "universalify@npm:^2.0.0": version: 2.0.0 resolution: "universalify@npm:2.0.0" @@ -7529,11 +8197,11 @@ fsevents@^2.3.2: linkType: hard "uri-js@npm:^4.2.2": - version: 4.2.2 - resolution: "uri-js@npm:4.2.2" + version: 4.4.1 + resolution: "uri-js@npm:4.4.1" dependencies: punycode: ^2.1.0 - checksum: 5a91c55d8ae6d9a1ff9dc1b0774888a99aae7cc6e9056c57b709275c0f6753b05cd1a9f2728a1479244b93a9f57ab37c60d277a48d9f2d032d6ae65837bf9bc7 + checksum: 7167432de6817fe8e9e0c9684f1d2de2bb688c94388f7569f7dbdb1587c9f4ca2a77962f134ec90be0cc4d004c939ff0d05acc9f34a0db39a3c797dada262633 languageName: node linkType: hard @@ -7570,9 +8238,9 @@ fsevents@^2.3.2: linkType: hard "v8-compile-cache@npm:^2.0.3": - version: 2.1.1 - resolution: "v8-compile-cache@npm:2.1.1" - checksum: 692f6bc698df9167cb71e5ba1232e90ab06f9da0de6723e7be33c507d1b094472d791affd94c6b1121a3259855b7438e6cb5d3b40a84fead9b74ede985a201ca + version: 2.3.0 + resolution: "v8-compile-cache@npm:2.3.0" + checksum: adb0a271eaa2297f2f4c536acbfee872d0dd26ec2d76f66921aa7fc437319132773483344207bdbeee169225f4739016d8d2dbf0553913a52bb34da6d0334f8e languageName: node linkType: hard @@ -7597,7 +8265,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"verror@npm:1.10.0, verror@npm:^1.10.0": +"verror@npm:1.10.0": version: 1.10.0 resolution: "verror@npm:1.10.0" dependencies: @@ -7608,6 +8276,17 @@ fsevents@^2.3.2: languageName: node linkType: hard +"verror@npm:^1.10.0": + version: 1.10.1 + resolution: "verror@npm:1.10.1" + dependencies: + assert-plus: ^1.0.0 + core-util-is: 1.0.2 + extsprintf: ^1.2.0 + checksum: 690a8d6ad5a4001672290e9719e3107c86269bc45fe19f844758eecf502e59f8aa9631b19b839f6d3dea562334884d22d1eb95ae7c863032075a9212c889e116 + languageName: node + linkType: hard + "w3c-hr-time@npm:^1.0.2": version: 1.0.2 resolution: "w3c-hr-time@npm:1.0.2" @@ -7627,11 +8306,11 @@ fsevents@^2.3.2: linkType: hard "walker@npm:^1.0.7": - version: 1.0.7 - resolution: "walker@npm:1.0.7" + version: 1.0.8 + resolution: "walker@npm:1.0.8" dependencies: - makeerror: 1.0.x - checksum: 4038fcf92f6ab0288267ad05008aec9e089a759f1bd32e1ea45cc2eb498eb12095ec43cf8ca2bf23a465f4580a0d33b25b89f450ba521dd27083cbc695ee6bf5 + makeerror: 1.0.12 + checksum: ad7a257ea1e662e57ef2e018f97b3c02a7240ad5093c392186ce0bcf1f1a60bbadd520d073b9beb921ed99f64f065efb63dfc8eec689a80e569f93c1c5d5e16c languageName: node linkType: hard @@ -7665,18 +8344,7 @@ fsevents@^2.3.2: languageName: node linkType: hard -"whatwg-url@npm:^8.0.0": - version: 8.1.0 - resolution: "whatwg-url@npm:8.1.0" - dependencies: - lodash.sortby: ^4.7.0 - tr46: ^2.0.2 - webidl-conversions: ^5.0.0 - checksum: 10642be39ae676474df005163991f5007ef0b61a070a997b3dd393975978bf4dc1b81fa9499f97f62d5aef03b1ba313da0e05fde4e7a9dc84db7959b95a3838b - languageName: node - linkType: hard - -"whatwg-url@npm:^8.5.0": +"whatwg-url@npm:^8.0.0, whatwg-url@npm:^8.5.0": version: 8.7.0 resolution: "whatwg-url@npm:8.7.0" dependencies: @@ -7687,6 +8355,19 @@ fsevents@^2.3.2: languageName: node linkType: hard +"which-boxed-primitive@npm:^1.0.2": + version: 1.0.2 + resolution: "which-boxed-primitive@npm:1.0.2" + dependencies: + is-bigint: ^1.0.1 + is-boolean-object: ^1.1.0 + is-number-object: ^1.0.4 + is-string: ^1.0.5 + is-symbol: ^1.0.3 + checksum: 53ce774c7379071729533922adcca47220228405e1895f26673bbd71bdf7fb09bee38c1d6399395927c6289476b5ae0629863427fd151491b71c4b6cb04f3a5e + languageName: node + linkType: hard + "which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" @@ -7698,12 +8379,12 @@ fsevents@^2.3.2: languageName: node linkType: hard -"wide-align@npm:^1.1.0": - version: 1.1.3 - resolution: "wide-align@npm:1.1.3" +"wide-align@npm:^1.1.2": + version: 1.1.5 + resolution: "wide-align@npm:1.1.5" dependencies: - string-width: ^1.0.2 || 2 - checksum: d09c8012652a9e6cab3e82338d1874a4d7db2ad1bd19ab43eb744acf0b9b5632ec406bdbbbb970a8f4771a7d5ef49824d038ba70aa884e7723f5b090ab87134d + string-width: ^1.0.2 || 2 || 3 || 4 + checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 languageName: node linkType: hard @@ -7762,9 +8443,9 @@ fsevents@^2.3.2: languageName: node linkType: hard -"ws@npm:^7.4.5": - version: 7.5.2 - resolution: "ws@npm:7.5.2" +"ws@npm:^7.4.6": + version: 7.5.6 + resolution: "ws@npm:7.5.6" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -7773,7 +8454,7 @@ fsevents@^2.3.2: optional: true utf-8-validate: optional: true - checksum: 23aef9248676d1f2695b85ed52de2b8babc7989f126e4ee60ef4ceaa3dcdf5cb7447822f0341d9ab2907ccad2a232247f59220f2292a30269adc7d0a30bf0e34 + checksum: 0c2ffc9a539dd61dd2b00ff6cc5c98a3371e2521011fe23da4b3578bb7ac26cbdf7ca8a68e8e08023c122ae247013216dde2a20c908de415a6bcc87bdef68c87 languageName: node linkType: hard @@ -7791,10 +8472,10 @@ fsevents@^2.3.2: languageName: node linkType: hard -"xmlcreate@npm:^2.0.3": - version: 2.0.3 - resolution: "xmlcreate@npm:2.0.3" - checksum: ce71823ca87be26fa05e67c9a514d4d92edef263c98388daeb4ab5ca7504ca9cf2e24d8284bf4e397f6893157885d5838c0fa78196e7f6450396c8b92460b125 +"xmlcreate@npm:^2.0.4": + version: 2.0.4 + resolution: "xmlcreate@npm:2.0.4" + checksum: b8dd52668b9aea77cd1408fa85538c14bb8dcc98b4e7bb51e76696c9c115d59eba7240298d0c4fd2caf8f1a8e283ab4e5c7b9a6bcfcf23a8b48f5068b677b748 languageName: node linkType: hard @@ -7833,16 +8514,6 @@ fsevents@^2.3.2: languageName: node linkType: hard -"yargs-parser@npm:^18.1.3": - version: 18.1.3 - resolution: "yargs-parser@npm:18.1.3" - dependencies: - camelcase: ^5.0.0 - decamelize: ^1.2.0 - checksum: 60e8c7d1b85814594d3719300ecad4e6ae3796748b0926137bfec1f3042581b8646d67e83c6fc80a692ef08b8390f21ddcacb9464476c39bbdf52e34961dd4d9 - languageName: node - linkType: hard - "yargs-parser@npm:^21.0.0": version: 21.0.0 resolution: "yargs-parser@npm:21.0.0"