diff --git a/.commitlintrc.json b/.commitlintrc.json new file mode 100644 index 00000000..f8c663b7 --- /dev/null +++ b/.commitlintrc.json @@ -0,0 +1,6 @@ +{ + "extends": ["@commitlint/config-conventional"], + "rules": { + "subject-case": [2, "never", ["start-case", "pascal-case", "upper-case"]] + } +} diff --git a/.cspell.json b/.cspell.json index ae6b3690..455c9f64 100644 --- a/.cspell.json +++ b/.cspell.json @@ -22,10 +22,13 @@ "contributorsrc", "simplegit", "lcov", - "Ilts" + "toolbelt" ], "ignorePaths": [ "**/coverage/**", + "reports/**", + "coverage/**", + ".stryker-tmp/**", "**/node_modules/**", "**/dist/**", "**/*.{json,snap}", diff --git a/.eslintignore b/.eslintignore index 0c1e4809..ce40ff51 100644 --- a/.eslintignore +++ b/.eslintignore @@ -8,3 +8,5 @@ coverage .github .nyc_output scripts +.stryker-tmp +/reports/ diff --git a/.eslintrc.json b/.eslintrc.json index 62a4c997..858b5f1f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -117,7 +117,12 @@ "max-len": [ "error", { - "code": 130 + "code": 120, + "ignoreComments": true, + "ignoreUrls": true, + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreRegExpLiterals": true } ], "new-parens": "error", @@ -153,5 +158,13 @@ "use-isnan": "error", "valid-typeof": "off" }, - "settings": {} + "settings": {}, + "overrides": [ + { + "files": ["*.spec.ts"], + "rules": { + "@typescript-eslint/unbound-method": "off" + } + } + ] } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7415c64..7ee86ce0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ on: types: [retest] env: - PRIMARY_NODE_VERSION: 10.x + PRIMARY_NODE_VERSION: 12.x PRIMARY_OS: ubuntu-latest FORCE_COLOR: true JEST_CI: true @@ -111,6 +111,13 @@ jobs: name: codecov fail_ci_if_error: true + - name: Upload coverage reports artifact + if: matrix.node-version == env.PRIMARY_NODE_VERSION && matrix.os == env.PRIMARY_OS + uses: actions/upload-artifact@v1 + with: + name: coverage + path: coverage + - name: Run unit tests if: "!(matrix.node-version == env.PRIMARY_NODE_VERSION && matrix.os == env.PRIMARY_OS)" run: yarn run test:unit @@ -121,6 +128,17 @@ jobs: - name: Run e2e tests run: yarn run test:e2e + - name: Run mutation tests + if: matrix.node-version == env.PRIMARY_NODE_VERSION && matrix.os == env.PRIMARY_OS && github.ref == 'refs/heads/master' + run: yarn run mutation-test + + - name: Upload mutation reports artifact + if: matrix.node-version == env.PRIMARY_NODE_VERSION && matrix.os == env.PRIMARY_OS && github.ref == 'refs/heads/master' + uses: actions/upload-artifact@v1 + with: + name: mutation + path: reports/mutation + publish_version: name: Publish runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index bcd9c147..a3db77e7 100755 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ pids # Directory for instrumented libs generated by jscoverage/JSCover lib-cov - +/.stryker-tmp # Coverage directory used by tools like istanbul coverage .cache @@ -65,3 +65,7 @@ typings/ # dotenv environment variables file .env + +# stryker temp files +.stryker-tmp +/reports diff --git a/.npmignore b/.npmignore index b47bc14b..dcf60d50 100644 --- a/.npmignore +++ b/.npmignore @@ -95,3 +95,7 @@ typings/ /tsconfig.build.json /tsconfig.test.json /jest.config.js +/stryker.conf.json +/stryker.conf.js +/.stryker-tmp/ +/reports/ diff --git a/.prettierignore b/.prettierignore index 3cf4f6fb..16cf5bd7 100644 --- a/.prettierignore +++ b/.prettierignore @@ -7,3 +7,5 @@ .nyc_output CONTRIBUTORS.md CHANGELOG.md +.stryker-tmp +/reports/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26a367df..39500e80 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,6 +66,8 @@ Where `` is one of: - `fix` - for any bug fixes that don't add new functionality - `test` - if you only change tests, and not shipped code - `docs` - if you only change documentation, and not shipped code +- `refactor` - refactoring +- `style` - linting fixes - `chore` - anything else And `` is a succinct title for the PR. diff --git a/README.md b/README.md index e5762e3a..4d8d3486 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ [![Npm Version](https://img.shields.io/npm/v/node-upgrade-checker.svg?style=popout)](https://www.npmjs.com/package/node-upgrade-checker) -![node](https://img.shields.io/node/v/node-upgrade-checker) +![node](https://img.shields.io/node/v-lts/node-upgrade-checker) ![platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-green) ![CI](https://github.com/PruvoNet/node-upgrade-checker/workflows/CI/badge.svg?branch=master) [![codecov](https://codecov.io/gh/PruvoNet/node-upgrade-checker/branch/master/graph/badge.svg)](https://codecov.io/gh/PruvoNet/node-upgrade-checker) +[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FPruvoNet%2Fnode-upgrade-checker%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/PruvoNet/node-upgrade-checker/master) [![Known Vulnerabilities](https://snyk.io/test/github/PruvoNet/node-upgrade-checker/badge.svg?targetFile=package.json)](https://snyk.io/test/github/PruvoNet/node-upgrade-checker?targetFile=package.json) [![dependencies Status](https://david-dm.org/PruvoNet/node-upgrade-checker/status.svg)](https://david-dm.org/PruvoNet/node-upgrade-checker) [![devDependencies Status](https://david-dm.org/PruvoNet/node-upgrade-checker/dev-status.svg)](https://david-dm.org/PruvoNet/node-upgrade-checker?type=dev) diff --git a/jest.config.js b/jest.config.js index b00ba627..923269ca 100644 --- a/jest.config.js +++ b/jest.config.js @@ -25,6 +25,7 @@ const config = { if (process.env.JEST_CI) { config.runner = 'jest-serial-runner'; + config.coverageReporters.push(`text`); } module.exports = config; diff --git a/package.json b/package.json index 9882c962..b47d16de 100644 --- a/package.json +++ b/package.json @@ -14,20 +14,31 @@ "update", "lts" ], + "author": "Regev Brody ", + "license": "ISC", + "engineStrict": true, + "engines": { + "node": "^10 || ^12 || ^13", + "yarn": "^1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/PruvoNet/node-upgrade-checker.git" + }, "main": "dist/index.js", "types": "dist/index.d.ts", "bin": { "nuc": "./dist/cli/index.js" }, - "engineStrict": true, - "engines": { - "node": "^10 || ^12 || ^13" - }, + "files": [ + "dist/**/*.d.ts", + "dist/**/*.js" + ], "scripts": { "envcheck": "node -e \"if(!/yarn\\.js$/.test(process.env.npm_execpath))throw new Error('Use yarn')\"", "preinstall": "yarn run envcheck", "postinstall": "yarn run compile", - "versionupdate": "yarn run ncu && yarn install", + "versionupdate": "yarn run ncu && rm yarn.lock && yarn install", "clean:dist": "rm -rf dist && rm -f .buildcache", "prebuild": "yarn run lint && yarn run clean:dist", "build": "yarn run tsc -p tsconfig.build.json", @@ -58,71 +69,71 @@ "check:spelling": "cspell --config=.cspell.json \"**/*.{md,ts,js}\"", "generate-contributors": "yarn run ts-node --transpile-only ./tools/generate-contributors.ts && yarn run all-contributors generate", "check-clean-workspace-after-install": "git diff --quiet --exit-code", - "docs:changelog": "yarn run changelog --allow-unknown" + "docs:changelog": "yarn run changelog --allow-unknown", + "mutation-test": "yarn run stryker run" }, - "repository": { - "type": "git", - "url": "git+https://github.com/PruvoNet/node-upgrade-checker.git" - }, - "author": "Regev Brody ", - "files": [ - "dist/**/*.d.ts", - "dist/**/*.js" - ], - "license": "ISC", "devDependencies": { "@commitlint/cli": "^8.3.5", "@commitlint/config-conventional": "^8.3.4", + "@stryker-mutator/core": "^3.1.0", + "@stryker-mutator/jest-runner": "^3.1.0", + "@stryker-mutator/typescript": "^3.1.0", "@types/cross-spawn": "^6.0.1", - "@types/jest": "^25.1.4", - "@types/moment": "^2.13.0", - "@types/node": "^13.9.3", - "@types/node-fetch": "^2.5.5", - "@types/pacote": "^9.5.1", + "@types/jest": "^25.2.1", + "@types/jest-when": "^2.7.1", + "@types/node": "^13.13.0", + "@types/node-fetch": "^2.5.6", + "@types/pacote": "^11.1.0", "@types/semver": "^7.1.0", + "@types/shell-quote": "^1.6.1", "@types/tmp": "^0.1.0", "@types/yaml": "^1.2.0", - "@typescript-eslint/eslint-plugin": "^2.25.0", - "@typescript-eslint/parser": "^2.25.0", - "all-contributors-cli": "^6.14.0", - "cspell": "^4.0.55", + "@types/yargs-parser": "^15.0.0", + "@typescript-eslint/eslint-plugin": "^2.28.0", + "@typescript-eslint/parser": "^2.28.0", + "all-contributors-cli": "^6.14.2", + "cspell": "^4.0.57", "eslint": "^6.8.0", "eslint-config-prettier": "^6.10.1", "eslint-plugin-jest": "^23.8.2", - "eslint-plugin-prefer-arrow": "^1.1.7", - "eslint-plugin-prettier": "^3.1.2", + "eslint-plugin-prefer-arrow": "^1.2.0", + "eslint-plugin-prettier": "^3.1.3", + "expect": "^25.3.0", "generate-changelog": "^1.8.0", - "jest": "^25.1.0", + "husky": "^4.2.5", + "jest": "^25.3.0", + "jest-mock-extended": "^1.0.8", "jest-serial-runner": "^1.1.0", - "lint-staged": "^10.0.9", + "jest-when": "^2.7.1", + "lint-staged": "^10.1.6", "mock-spawn": "^0.2.6", "node-fetch": "^2.6.0", - "npm-check-updates": "^4.0.5", - "prettier": "^2.0.2", - "semver": "^7.1.3", - "ts-jest": "^25.2.1", - "ts-node": "^8.8.1", + "npm-check-updates": "^4.1.2", + "prettier": "^2.0.4", + "semver": "^7.3.2", + "ts-jest": "^25.4.0", + "ts-node": "^8.8.2", + "ts-toolbelt": "^6.5.1", "typescript": "^3.8.3" }, "dependencies": { + "array-flat-polyfill": "~1.0.1", "axios": "~0.19.2", - "chalk": "~3.0.0", + "chalk": "~4.0.0", "consola": "~2.11.3", - "cross-spawn": "~7.0.1", + "cross-spawn": "~7.0.2", "figures": "~3.2.0", "inversify": "~5.0.1", "moment": "~2.24.0", "pacote": "~11.1.4", "reflect-metadata": "~0.1.13", + "shell-quote": "^1.7.2", "simple-git": "~1.132.0", "sqlite3": "~4.1.1", "tmp": "~0.1.0", + "ts-type-guards": "~0.6.1", "typeorm": "~0.2.24", - "yaml": "~1.8.3" - }, - "commitlint": { - "extends": [ - "@commitlint/config-conventional" - ] + "yaml": "~1.9.1", + "yargs-parser": "~18.1.3" } } diff --git a/src/container/index.ts b/src/container/index.ts index ceaa8676..8f011623 100644 --- a/src/container/index.ts +++ b/src/container/index.ts @@ -7,14 +7,16 @@ import { ciResolverModulesBinder } from '../resolvers/ciResolver'; import { dbModulesBinder } from '../db'; import { cacheResolveModulesBinder } from '../resolvers/cacheResolver'; import { testResolverModulesBinder } from '../resolvers/testResolver'; -import { flowModulesBinder } from '../flow'; -import { ltsModulesBinder } from '../utils/lts'; +import { dependencyCheckerModulesBinder } from '../dependencyChecker'; +import { nodeVersionsModulesBinder } from '../utils/nodeVersions'; import { packageInfoModulesBinder } from '../utils/packageInfo'; import { nodeModulesBinder } from './nodeModulesContainer'; import { enginesResolveModulesBinder } from '../resolvers/enginesResolver'; import { loggerModuleBinder } from '../utils/logger'; -export const container = new Container(); +export const container = new Container({ + skipBaseClassChecks: true, +}); type Binder = (bind: Bind) => void; @@ -27,8 +29,8 @@ const binders: Binder[] = [ dbModulesBinder, cacheResolveModulesBinder, testResolverModulesBinder, - flowModulesBinder, - ltsModulesBinder, + dependencyCheckerModulesBinder, + nodeVersionsModulesBinder, packageInfoModulesBinder, enginesResolveModulesBinder, loggerModuleBinder, diff --git a/src/container/utils.ts b/src/container/utils.ts new file mode 100644 index 00000000..e04b5d6d --- /dev/null +++ b/src/container/utils.ts @@ -0,0 +1,13 @@ +import { METADATA_KEY, namedConstraint, taggedConstraint, interfaces } from 'inversify'; +import Request = interfaces.Request; +import Abstract = interfaces.Abstract; + +export type Constraint = (request: Request) => boolean; + +export const namedOrMultiConstraint = (symbol: symbol, target: Abstract): Constraint => { + const tagConstraint = namedConstraint(symbol); + const multiInjectConstraint = taggedConstraint(METADATA_KEY.MULTI_INJECT_TAG)(target); + return (request: Request): boolean => { + return multiInjectConstraint(request) || tagConstraint(request); + }; +}; diff --git a/src/db/entities/dependency.ts b/src/db/entities/dependency.ts index c6f1e207..f706bea7 100644 --- a/src/db/entities/dependency.ts +++ b/src/db/entities/dependency.ts @@ -11,19 +11,15 @@ export interface IDependencyOptions { @Entity() export class Dependency extends IEntity { - @PrimaryColumn(`text`, { - nullable: false, - }) + public static TAG = Symbol.for(`Dependency`); + + @PrimaryColumn(`text`) public name!: string; - @PrimaryColumn(`text`, { - nullable: false, - }) + @PrimaryColumn(`text`) public version!: string; - @PrimaryColumn(`text`, { - nullable: false, - }) + @PrimaryColumn(`text`) public targetNode!: string; @Column(`boolean`, { diff --git a/src/db/entities/dependencyVersion.ts b/src/db/entities/dependencyVersion.ts index cf7cfe34..a9b33a78 100644 --- a/src/db/entities/dependencyVersion.ts +++ b/src/db/entities/dependencyVersion.ts @@ -15,23 +15,18 @@ export interface IDependencyVersionOptions { @Entity() export class DependencyVersion extends IEntity { - @PrimaryColumn(`text`, { - nullable: false, - }) + public static TAG = Symbol.for(`DependencyVersion`); + + @PrimaryColumn(`text`) public name!: string; - @PrimaryColumn(`text`, { - nullable: false, - }) + @PrimaryColumn(`text`) public version!: string; - @Column(`text`, { - nullable: false, - }) + @Column(`text`) public repoUrl!: string; @Column(`text`, { - nullable: false, transformer: buildDateTransformer(dateFormat), }) public releaseDate!: Moment; diff --git a/src/db/impl/AbstractRepositoryProvider.ts b/src/db/impl/AbstractRepositoryProvider.ts new file mode 100644 index 00000000..f18ce596 --- /dev/null +++ b/src/db/impl/AbstractRepositoryProvider.ts @@ -0,0 +1,20 @@ +import { Repository } from 'typeorm'; +import { memoize } from '../../utils/memoize/memoize'; +import { IEntity } from '../interfaces/IEntity'; +import { IConnectionProvider } from '..'; +import { IRepositoryProvider } from '../interfaces/IRepositoryProvider'; + +export abstract class AbstractRepositoryProvider extends IRepositoryProvider { + protected constructor( + private readonly connectionProvider: IConnectionProvider, + private readonly Entity: new () => T + ) { + super(); + } + + @memoize() + public async getRepository(): Promise> { + const connection = await this.connectionProvider.getConnection(); + return connection.getRepository(this.Entity); + } +} diff --git a/src/db/impl/connectionProvider.ts b/src/db/impl/connectionProvider.ts index e3e1fe7d..7795a3c6 100644 --- a/src/db/impl/connectionProvider.ts +++ b/src/db/impl/connectionProvider.ts @@ -6,22 +6,29 @@ import { IConnectionProvider } from '../interfaces/IConnectionProvider'; import { IConnectionSettings } from '../interfaces/IConnectionSettings'; import { TypeOrm, TYPES } from '../../container/nodeModulesContainer'; import { IEntity, IEntityConstructor } from '../interfaces/IEntity'; +import { ILoggerFactory } from '../../utils/logger'; +import { ILogger } from '../../utils/logger/interfaces/ILogger'; +import { ConnectionOptions } from 'typeorm/connection/ConnectionOptions'; const DB_FILE = `cache.db`; @injectable() export class ConnectionProvider extends IConnectionProvider { + private readonly logger: ILogger; + constructor( - private settings: IConnectionSettings, - @inject(TYPES.TypeOrm) private typeorm: TypeOrm, - @multiInject(IEntity) private entities: IEntityConstructor[] + private readonly settings: IConnectionSettings, + @inject(TYPES.TypeOrm) private readonly typeorm: TypeOrm, + @multiInject(IEntity) private readonly entities: IEntityConstructor[], + loggerFactory: ILoggerFactory ) { super(); + this.logger = loggerFactory.getLogger(`Connection Provider`); } @memoize() public async getConnection(): Promise { - return await this.typeorm.createConnection({ + const connectionOptions: ConnectionOptions = { name: this.settings.databaseFilePath, type: `sqlite`, database: path.join(this.settings.databaseFilePath, DB_FILE), @@ -29,6 +36,10 @@ export class ConnectionProvider extends IConnectionProvider { logging: false, dropSchema: this.settings.dropSchema, entities: this.entities, - }); + }; + this.logger.debug(`Creating connection of type ${connectionOptions.type} to db ${connectionOptions.database}`); + const connection = await this.typeorm.createConnection(connectionOptions); + this.logger.debug(`Connection created successfully`); + return connection; } } diff --git a/src/db/impl/dependencyRepositoryProvider.ts b/src/db/impl/dependencyRepositoryProvider.ts index 6394942b..d8d1984a 100644 --- a/src/db/impl/dependencyRepositoryProvider.ts +++ b/src/db/impl/dependencyRepositoryProvider.ts @@ -1,19 +1,11 @@ -import { Dependency } from '../entities/dependency'; import { injectable } from 'inversify'; -import { Repository } from 'typeorm'; -import { IDependencyRepositoryProvider } from '../interfaces/IDependencyRepositoryProvider'; -import { memoize } from '../../utils/memoize/memoize'; import { IConnectionProvider } from '../interfaces/IConnectionProvider'; +import { AbstractRepositoryProvider } from './AbstractRepositoryProvider'; +import { Dependency } from '../entities/dependency'; @injectable() -export class DependencyRepositoryProvider extends IDependencyRepositoryProvider { - constructor(private connectionProvider: IConnectionProvider) { - super(); - } - - @memoize() - public async getRepository(): Promise> { - const connection = await this.connectionProvider.getConnection(); - return connection.getRepository(Dependency); +export class DependencyRepositoryProvider extends AbstractRepositoryProvider { + constructor(connectionProvider: IConnectionProvider) { + super(connectionProvider, Dependency); } } diff --git a/src/db/impl/dependencyVersionRepositoryProvider.ts b/src/db/impl/dependencyVersionRepositoryProvider.ts index 087a4e9c..cd866c6d 100644 --- a/src/db/impl/dependencyVersionRepositoryProvider.ts +++ b/src/db/impl/dependencyVersionRepositoryProvider.ts @@ -1,19 +1,11 @@ import { injectable } from 'inversify'; -import { Repository } from 'typeorm'; -import { DependencyVersion } from '../entities/dependencyVersion'; -import { IDependencyVersionRepositoryProvider } from '../interfaces/IDependencyVersionRepositoryProvider'; -import { memoize } from '../../utils/memoize/memoize'; import { IConnectionProvider } from '../interfaces/IConnectionProvider'; +import { AbstractRepositoryProvider } from './AbstractRepositoryProvider'; +import { DependencyVersion } from '../entities/dependencyVersion'; @injectable() -export class DependencyVersionRepositoryProvider extends IDependencyVersionRepositoryProvider { - constructor(private connectionProvider: IConnectionProvider) { - super(); - } - - @memoize() - public async getRepository(): Promise> { - const connection = await this.connectionProvider.getConnection(); - return connection.getRepository(DependencyVersion); +export class DependencyVersionRepositoryProvider extends AbstractRepositoryProvider { + constructor(connectionProvider: IConnectionProvider) { + super(connectionProvider, DependencyVersion); } } diff --git a/src/db/index.ts b/src/db/index.ts index cdbee29f..296c2a59 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -9,6 +9,12 @@ import Bind = interfaces.Bind; import { IEntity } from './interfaces/IEntity'; import { Dependency } from './entities/dependency'; import { DependencyVersion } from './entities/dependencyVersion'; +import { namedOrMultiConstraint } from '../container/utils'; + +export const EntitiesTags = { + dependencyVersion: DependencyVersion.TAG, + dependency: Dependency.TAG, +}; export const dbModulesBinder = (bind: Bind): void => { bind(IDependencyRepositoryProvider) @@ -18,8 +24,10 @@ export const dbModulesBinder = (bind: Bind): void => { .to(DependencyVersionRepositoryProvider) .inSingletonScope(); bind(IConnectionProvider).to(ConnectionProvider).inSingletonScope(); - bind(IEntity).toConstantValue(Dependency); - bind(IEntity).toConstantValue(DependencyVersion); + bind(IEntity).toConstantValue(Dependency).when(namedOrMultiConstraint(EntitiesTags.dependency, IEntity)); + bind(IEntity) + .toConstantValue(DependencyVersion) + .when(namedOrMultiConstraint(EntitiesTags.dependencyVersion, IEntity)); }; export * from './interfaces/IConnectionProvider'; diff --git a/src/db/interfaces/IConnectionProvider.ts b/src/db/interfaces/IConnectionProvider.ts index ff839fea..006fbfdf 100644 --- a/src/db/interfaces/IConnectionProvider.ts +++ b/src/db/interfaces/IConnectionProvider.ts @@ -1,7 +1,5 @@ -import { injectable } from 'inversify'; import { Connection } from 'typeorm/connection/Connection'; -@injectable() export abstract class IConnectionProvider { public abstract async getConnection(): Promise; } diff --git a/src/db/interfaces/IConnectionSettings.ts b/src/db/interfaces/IConnectionSettings.ts index c11da8d9..8e6d087b 100644 --- a/src/db/interfaces/IConnectionSettings.ts +++ b/src/db/interfaces/IConnectionSettings.ts @@ -1,6 +1,3 @@ -import { injectable } from 'inversify'; - -@injectable() export abstract class IConnectionSettings { public abstract readonly databaseFilePath: string; public abstract readonly dropSchema: boolean; diff --git a/src/db/interfaces/IDependencyRepositoryProvider.ts b/src/db/interfaces/IDependencyRepositoryProvider.ts index b2ae4350..78386da5 100644 --- a/src/db/interfaces/IDependencyRepositoryProvider.ts +++ b/src/db/interfaces/IDependencyRepositoryProvider.ts @@ -1,8 +1,4 @@ import { Dependency } from '../entities/dependency'; -import { injectable } from 'inversify'; -import { Repository } from 'typeorm'; +import { IRepositoryProvider } from './IRepositoryProvider'; -@injectable() -export abstract class IDependencyRepositoryProvider { - public abstract async getRepository(): Promise>; -} +export abstract class IDependencyRepositoryProvider extends IRepositoryProvider {} diff --git a/src/db/interfaces/IDependencyVersionRepositoryProvider.ts b/src/db/interfaces/IDependencyVersionRepositoryProvider.ts index d1e272ad..8b7404c7 100644 --- a/src/db/interfaces/IDependencyVersionRepositoryProvider.ts +++ b/src/db/interfaces/IDependencyVersionRepositoryProvider.ts @@ -1,8 +1,4 @@ -import { injectable } from 'inversify'; -import { Repository } from 'typeorm'; import { DependencyVersion } from '../entities/dependencyVersion'; +import { IRepositoryProvider } from './IRepositoryProvider'; -@injectable() -export abstract class IDependencyVersionRepositoryProvider { - public abstract async getRepository(): Promise>; -} +export abstract class IDependencyVersionRepositoryProvider extends IRepositoryProvider {} diff --git a/src/db/interfaces/IRepositoryProvider.ts b/src/db/interfaces/IRepositoryProvider.ts new file mode 100644 index 00000000..5dbf0b1b --- /dev/null +++ b/src/db/interfaces/IRepositoryProvider.ts @@ -0,0 +1,6 @@ +import { Repository } from 'typeorm'; +import { IEntity } from './IEntity'; + +export abstract class IRepositoryProvider { + public abstract async getRepository(): Promise>; +} diff --git a/src/dependencyChecker/impl/dependencyChecker.ts b/src/dependencyChecker/impl/dependencyChecker.ts new file mode 100644 index 00000000..2b16a07b --- /dev/null +++ b/src/dependencyChecker/impl/dependencyChecker.ts @@ -0,0 +1,109 @@ +import { injectable } from 'inversify'; +import { + IDependencyChecker, + IDependencyCheckerRunOptions, + IDependencyCheckerRunResult, +} from '../interfaces/IDependencyChecker'; +import { IGitCheckout } from '../../utils/git'; +import { ICacheResolver } from '../../resolvers/cacheResolver'; +import { ICIResolver } from '../../resolvers/ciResolver'; +import { ITestResolver } from '../../resolvers/testResolver'; +import { IEnginesResolver } from '../../resolvers/enginesResolver'; +import { IPackageInfo } from '../../utils/packageInfo'; +import { ILoggerFactory } from '../../utils/logger'; +import { ILogger } from '../../utils/logger/interfaces/ILogger'; + +@injectable() +export class DependencyChecker extends IDependencyChecker { + private readonly logger: ILogger; + + constructor( + private readonly gitCheckout: IGitCheckout, + private readonly cacheResolver: ICacheResolver, + private readonly ciResolver: ICIResolver, + private readonly testResolver: ITestResolver, + private readonly engineResolver: IEnginesResolver, + private readonly packageInfo: IPackageInfo, + loggerFactory: ILoggerFactory + ) { + super(); + this.logger = loggerFactory.getLogger(`Dependency Checker`); + } + + public async run({ + repo, + targetNode, + pkg, + workDir, + skip, + }: IDependencyCheckerRunOptions): Promise { + this.logger.info(`Checking ${pkg.dependencyType} dependency - ${pkg.name}@${pkg.version}`); + if (!skip.cache.ignoreAll) { + const cacheResult = await this.cacheResolver.resolve({ + targetNode, + repo: { + name: pkg.name, + version: pkg.version, + }, + }); + if (cacheResult.isMatch) { + if (cacheResult.result && !skip.cache.ignoreTruthy) { + return { + isMatch: true, + resolverName: cacheResult.resolverName, + }; + } else if (!cacheResult.result && !skip.cache.ignoreFalsy) { + return { + isMatch: false, + }; + } + } + } + // TODO get packageInfo from cache, probably we should move all this logic to a different unit + const packageInfo = await this.packageInfo.getPackageInfo({ + semver: pkg.version, + name: pkg.name, + }); + const engineResult = await this.engineResolver.resolve({ + targetNode, + engines: packageInfo.engines, + }); + if (engineResult.isMatch) { + return { + isMatch: true, + resolverName: engineResult.resolverName, + }; + } + const repoPath = await this.gitCheckout.checkoutRepo({ + tag: pkg.version, + commitSha: repo.commitSha, + baseDir: workDir, + url: repo.url, + }); + const ciResult = await this.ciResolver.resolve({ + targetNode, + repoPath, + packageReleaseDate: pkg.releaseDate, + }); + if (ciResult.isMatch) { + return { + isMatch: true, + resolverName: ciResult.resolverName, + }; + } + if (!skip.yarnTest) { + const testResult = await this.testResolver.resolve({ + repoPath, + }); + if (testResult.isMatch) { + return { + isMatch: true, + resolverName: testResult.resolverName, + }; + } + } + return { + isMatch: false, + }; + } +} diff --git a/src/dependencyChecker/index.ts b/src/dependencyChecker/index.ts new file mode 100644 index 00000000..9d5a4d2e --- /dev/null +++ b/src/dependencyChecker/index.ts @@ -0,0 +1,10 @@ +import { IDependencyChecker } from './interfaces/IDependencyChecker'; +import { DependencyChecker } from './impl/dependencyChecker'; +import { interfaces } from 'inversify'; +import Bind = interfaces.Bind; + +export const dependencyCheckerModulesBinder = (bind: Bind): void => { + bind(IDependencyChecker).to(DependencyChecker).inSingletonScope(); +}; + +export * from './interfaces/IDependencyChecker'; diff --git a/src/dependencyChecker/interfaces/IDependencyChecker.ts b/src/dependencyChecker/interfaces/IDependencyChecker.ts new file mode 100644 index 00000000..0dbc5721 --- /dev/null +++ b/src/dependencyChecker/interfaces/IDependencyChecker.ts @@ -0,0 +1,39 @@ +import { Moment } from 'moment'; + +export enum DependencyType { + PROD = `production`, + DEV = `development`, + PEER = `peer`, +} + +export interface IDependencyCheckerRunOptions { + pkg: { + version: string; + name: string; + releaseDate: Moment; + dependencyType: DependencyType; + }; + repo: { + url: string; + commitSha?: string; + }; + workDir: string; + targetNode: string; + skip: { + cache: { + ignoreAll: boolean; + ignoreFalsy: boolean; + ignoreTruthy: boolean; + }; + yarnTest: boolean; + }; +} + +export interface IDependencyCheckerRunResult { + isMatch: boolean; + resolverName?: string; +} + +export abstract class IDependencyChecker { + public abstract async run(options: IDependencyCheckerRunOptions): Promise; +} diff --git a/src/flow/impl/flow.ts b/src/flow/impl/flow.ts deleted file mode 100644 index e9098960..00000000 --- a/src/flow/impl/flow.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { injectable } from 'inversify'; -import { IFlow, IRunFlowOptions, IRunFlowResult } from '../interfaces/IFlow'; -import { IGitCheckout } from '../../utils/git'; -import { ICacheResolver } from '../../resolvers/cacheResolver'; -import { ICIResolver } from '../../resolvers/ciResolver'; -import { ITestResolver } from '../../resolvers/testResolver'; - -@injectable() -export class Flow extends IFlow { - constructor( - private gitCheckout: IGitCheckout, - private cacheResolver: ICacheResolver, - private ciResolver: ICIResolver, - private testResolver: ITestResolver - ) { - super(); - } - - public async runFlow({ repo, targetNode, pkg, workDir }: IRunFlowOptions): Promise { - const cacheResult = await this.cacheResolver.resolve({ - targetNode, - repo: { - name: pkg.name, - version: pkg.version, - }, - }); - if (cacheResult.isMatch) { - return { - isMatch: true, - resolverName: cacheResult.resolverName, - }; - } - const repoPath = await this.gitCheckout.checkoutRepo({ - tag: pkg.version, - commitSha: repo.commitSha, - baseDir: workDir, - url: repo.url, - }); - const ciResult = await this.ciResolver.resolve({ - targetNode, - repoPath, - packageReleaseDate: pkg.releaseDate, - }); - if (ciResult.isMatch) { - return { - isMatch: true, - resolverName: cacheResult.resolverName, - }; - } - const testResult = await this.testResolver.resolve({ - repoPath, - }); - if (testResult.isMatch) { - return { - isMatch: true, - resolverName: cacheResult.resolverName, - }; - } - return { - isMatch: false, - }; - } -} diff --git a/src/flow/index.ts b/src/flow/index.ts deleted file mode 100644 index c63d68e7..00000000 --- a/src/flow/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IFlow } from './interfaces/IFlow'; -import { Flow } from './impl/flow'; -import { interfaces } from 'inversify'; -import Bind = interfaces.Bind; - -export const flowModulesBinder = (bind: Bind): void => { - bind(IFlow).to(Flow).inSingletonScope(); -}; - -export * from './interfaces/IFlow'; diff --git a/src/flow/interfaces/IFlow.ts b/src/flow/interfaces/IFlow.ts deleted file mode 100644 index 9992e5af..00000000 --- a/src/flow/interfaces/IFlow.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { injectable } from 'inversify'; -import { Moment } from 'moment'; - -export interface IRunFlowOptions { - pkg: { - version: string; - name: string; - releaseDate: Moment; - }; - repo: { - url: string; - commitSha?: string; - }; - workDir: string; - targetNode: string; -} - -export interface IRunFlowResult { - isMatch: boolean; - resolverName?: string; -} - -@injectable() -export abstract class IFlow { - public abstract async runFlow(options: IRunFlowOptions): Promise; -} diff --git a/src/resolvers/cacheResolver/impl/cacheResolver.ts b/src/resolvers/cacheResolver/impl/cacheResolver.ts index 4437ca1a..e7dab524 100644 --- a/src/resolvers/cacheResolver/impl/cacheResolver.ts +++ b/src/resolvers/cacheResolver/impl/cacheResolver.ts @@ -1,31 +1,48 @@ import { injectable } from 'inversify'; -import { ICacheResolver, ICacheResolverOptions } from '../interfaces/ICacheResolver'; +import { ICacheResolver, ICacheResolverOptions, ICacheResolverResult } from '../interfaces/ICacheResolver'; import { IDependencyRepositoryProvider } from '../../../db'; -import { IResolverResult } from '../../types'; +import { ILoggerFactory } from '../../../utils/logger'; +import { ILogger } from '../../../utils/logger/interfaces/ILogger'; +import { isBoolean } from 'ts-type-guards'; @injectable() export class CacheResolver extends ICacheResolver { - constructor(private dependencyRepositoryProvider: IDependencyRepositoryProvider) { + private readonly logger: ILogger; + constructor( + private readonly dependencyRepositoryProvider: IDependencyRepositoryProvider, + loggerFactory: ILoggerFactory + ) { super(); + this.logger = loggerFactory.getLogger(`Cache Resolver`); } - public async resolve({ repo, targetNode }: ICacheResolverOptions): Promise { + public async resolve({ repo, targetNode }: ICacheResolverOptions): Promise { try { + this.logger.info(`Checking if there are cached results for ${repo.name}@$${repo.version}`); const dependencyRepository = await this.dependencyRepositoryProvider.getRepository(); const dependency = await dependencyRepository.findOne({ name: repo.name, version: repo.version, targetNode, }); - if (dependency && dependency.match) { + if (dependency && isBoolean(dependency.match)) { + this.logger.info(`Located cached results for ${repo.name}@$${repo.version}`); return { isMatch: true, + result: dependency.match, resolverName: `${dependency.reason} (cache)`, }; + } else { + this.logger.info(`No cached results for ${repo.name}@$${repo.version}`); + return { + isMatch: false, + }; } - } catch (e) {} - return { - isMatch: false, - }; + } catch (err) { + this.logger.debug(`Failed to located cache results for ${repo.name}@$${repo.version}`, err); + return { + isMatch: false, + }; + } } } diff --git a/src/resolvers/cacheResolver/interfaces/ICacheResolver.ts b/src/resolvers/cacheResolver/interfaces/ICacheResolver.ts index 4d171464..3bb67979 100644 --- a/src/resolvers/cacheResolver/interfaces/ICacheResolver.ts +++ b/src/resolvers/cacheResolver/interfaces/ICacheResolver.ts @@ -1,6 +1,3 @@ -import { injectable } from 'inversify'; -import { IResolverResult } from '../../types'; - export interface ICacheResolverOptions { repo: { name: string; @@ -9,7 +6,18 @@ export interface ICacheResolverOptions { targetNode: string; } -@injectable() +export interface ICacheResolverPositiveResult { + isMatch: true; + result: boolean; + resolverName: string; +} + +export interface ICacheResolverNegativeResult { + isMatch: false; +} + +export type ICacheResolverResult = ICacheResolverNegativeResult | ICacheResolverPositiveResult; + export abstract class ICacheResolver { - public abstract async resolve(options: ICacheResolverOptions): Promise; + public abstract async resolve(options: ICacheResolverOptions): Promise; } diff --git a/src/resolvers/ciResolver/impl/ciResolver.ts b/src/resolvers/ciResolver/impl/ciResolver.ts index 765d27aa..f402a3af 100644 --- a/src/resolvers/ciResolver/impl/ciResolver.ts +++ b/src/resolvers/ciResolver/impl/ciResolver.ts @@ -1,48 +1,45 @@ import { ICIResolveOptions, ICIResolver } from '../interfaces/ICIResolver'; import { injectable, multiInject } from 'inversify'; import { ISpecificCIResolver } from '../interfaces/ISpecificCIResolver'; -import { ITargetMatcher } from '../interfaces/ITargetMatcher'; import { IResolverResult } from '../../types'; +import { ILoggerFactory } from '../../../utils/logger'; +import { ILogger } from '../../../utils/logger/interfaces/ILogger'; +import { asyncFilter } from '../../../utils/asyncFilter/asyncFilter'; +import { ISpecificCIResolverRunner } from '../interfaces/ISpecificCIResolverRunner'; @injectable() export class CiResolver extends ICIResolver { + private readonly logger: ILogger; + constructor( - @multiInject(ISpecificCIResolver) private resolvers: ISpecificCIResolver[], - private targetMatcher: ITargetMatcher + @multiInject(ISpecificCIResolver) private readonly resolvers: ISpecificCIResolver[], + private readonly specificCIResolverRunner: ISpecificCIResolverRunner, + loggerFactory: ILoggerFactory ) { super(); + this.logger = loggerFactory.getLogger(`Ci Resolver`); } - async resolve({ repoPath, targetNode, packageReleaseDate }: ICIResolveOptions): Promise { - for (const resolver of this.resolvers) { - let nodeVersions: string[] | undefined; - try { - nodeVersions = await resolver.resolve({ - repoPath, - }); - } catch (e) { - console.log(`Failed to find node versions in resolver ${resolver.resolverName} due to an unknown error`, e); - } - if (nodeVersions) { - if (nodeVersions.length === 0) { - console.log(`Failed to find node versions in resolver ${resolver.resolverName}`); - } else { - const isMatch = await this.targetMatcher.match({ - candidates: nodeVersions, - targetNode, - packageReleaseDate, - }); - if (isMatch) { - return { - isMatch: true, - resolverName: resolver.resolverName, - }; - } - } - } - } - return { - isMatch: false, + public async resolve({ repoPath, targetNode, packageReleaseDate }: ICIResolveOptions): Promise { + this.logger.debug( + `Checking if repo in ${repoPath} has a ci that uses node ${targetNode} (package release date is ${packageReleaseDate.toJSON()})` + ); + this.logger.debug(`Trying to find relevant resolvers from ${this.resolvers.length} resolvers`); + const resolverOptions = { + repoPath, }; + const relevantResolvers = await asyncFilter(this.resolvers, (resolver) => resolver.isRelevant(resolverOptions)); + this.logger.debug(`Found ${relevantResolvers.length} relevant resolver(s). Will run each`); + const relevantResolversResults = await Promise.all( + relevantResolvers.map((resolver) => + this.specificCIResolverRunner.resolve({ repoPath, targetNode, packageReleaseDate, resolver }) + ) + ); + const relevantResults = relevantResolversResults + .flatMap((result) => (result.isMatch ? [result] : [])) + .map((result) => result.resolverName); + this.logger.debug(`Found ${relevantResults.length} matching resolvers(s) - ${JSON.stringify(relevantResults)}`); + const resolverName = relevantResults[0]; + return { isMatch: Boolean(resolverName), resolverName }; } } diff --git a/src/resolvers/ciResolver/impl/nvmHandler.ts b/src/resolvers/ciResolver/impl/nvmHandler.ts new file mode 100644 index 00000000..d074706a --- /dev/null +++ b/src/resolvers/ciResolver/impl/nvmHandler.ts @@ -0,0 +1,138 @@ +import * as yargsParser from 'yargs-parser'; +import { injectable } from 'inversify'; +import { Env, INvmHandler, EnvMatrix } from '../interfaces/INvmHandler'; +import { ITargetMatcher } from '../interfaces/ITargetMatcher'; +import { Options } from 'yargs-parser'; +import { parse } from 'shell-quote'; +import { isString } from 'ts-type-guards'; + +const LTS_REGEX = /lts\/(.+)/i; +const NEW_LINE_REGEX = /(?:\r\n|\r|\n)/g; + +const yargsOptions: Options = { + // eslint-disable-next-line id-blacklist + boolean: [`-s`, `latest-npm`, `skip-default-packages`], + // eslint-disable-next-line id-blacklist + string: [`reinstall-packages-from`], + configuration: { + 'parse-numbers': false, + }, +}; + +const expendMatrixAux = (matrix: EnvMatrix, keys: string[], keyIndex: number, environments: Env[], env: Env): void => { + if (keyIndex >= keys.length) { + environments.push(env); + return; + } + const key = keys[keyIndex]; + const values = matrix[key]; + for (const value of values) { + const currentEnv: Env = { + ...env, + [key]: value, + }; + expendMatrixAux(matrix, keys, keyIndex + 1, environments, currentEnv); + } +}; + +const expendMatrix = (matrix: EnvMatrix): Env[] => { + const keys = Object.keys(matrix); + const environments: Env[] = []; + expendMatrixAux(matrix, keys, 0, environments, {}); + return environments; +}; + +const envIdentity = (key: string): string => { + return key; +}; + +@injectable() +export class NvmHandler extends INvmHandler { + constructor(private readonly targetMatcher: ITargetMatcher) { + super(); + } + + public getNvmVersions(cmd: string, environments: Env[]): Set { + const versions = new Set(); + if (environments.length === 0) { + environments.push({}); + } + environments.forEach((env) => { + const version = this.getNvmVersion(cmd, env); + if (version) { + versions.add(version); + } + }); + return versions; + } + + public getNvmVersionsFromMatrix(cmd: string, matrix: EnvMatrix): Set { + const environments: Env[] = expendMatrix(matrix); + return this.getNvmVersions(cmd, environments); + } + + public getNvmVersion(cmd: string, env: Env): string | undefined { + const argsArray = this.getNvmCommandArgs(cmd, env); + const parsedArgs = yargsParser(argsArray, yargsOptions); + if (parsedArgs.lts === true) { + return this.targetMatcher.getLatestLtsVersionPlaceholder(); + } else if (parsedArgs.lts) { + return this.targetMatcher.getLtsVersionPlaceholder({ codename: parsedArgs.lts }); + } + const freeParams = parsedArgs._; + if (freeParams.length > 0) { + const version = freeParams[freeParams.length - 1]; + if (version === `node`) { + return this.targetMatcher.getStableVersionPlaceholder(); + } else if (version === `lts/*`) { + return this.targetMatcher.getLatestLtsVersionPlaceholder(); + } else { + const lts = LTS_REGEX.exec(version)?.[1]; + if (lts) { + return this.targetMatcher.getLtsVersionPlaceholder({ codename: lts }); + } + return version; + } + } + return; + } + + public isNvmCommand(cmd: string): boolean { + return this.getNvmCommandArgs(cmd).length > 0; + } + + private getNvmCommandArgs(cmd: string, env?: Env): string[] { + const formattedCed = cmd.replace(NEW_LINE_REGEX, `;`); + const rawArgsArray = parse(formattedCed, env || envIdentity); + let startIndex: number | undefined; + let reachedEnd = false; + let args: string[] = []; + rawArgsArray.forEach((arg, index) => { + if (startIndex === undefined) { + if (arg === `nvm`) { + startIndex = index; + args = []; + return; + } + } else if (!reachedEnd) { + if (index === startIndex + 1) { + if (arg !== `install`) { + startIndex = undefined; + } + return; + } else if (!isString(arg)) { + reachedEnd = true; + } else if (arg) { + if (index === rawArgsArray.length - 1) { + reachedEnd = true; + } + args.push(arg); + } + } + }); + if (reachedEnd && args) { + return args; + } + return []; + } +} diff --git a/src/resolvers/ciResolver/impl/resolvers/appveyor.ts b/src/resolvers/ciResolver/impl/resolvers/appveyor.ts deleted file mode 100644 index bf3c9a20..00000000 --- a/src/resolvers/ciResolver/impl/resolvers/appveyor.ts +++ /dev/null @@ -1,95 +0,0 @@ -import * as path from 'path'; -import { ISpecificCIResolverOptions, ISpecificCIResolver } from '../../interfaces/ISpecificCIResolver'; -import { inject, injectable } from 'inversify'; -import { FS, TYPES } from '../../../../container/nodeModulesContainer'; -import { parse } from 'yaml'; - -const nodeVersionRegex = /node ([^\s]+)/i; -const nodeVersionInstallRegex = /Install-Product node ([^\s]+)/i; -const nvmInstallRegex = /nvm install ([^\s]+)/i; -const nodeEnvRegex = /\$env:(.+)/i; - -const nodeVersionInstallMapper = (command: string | undefined): string | undefined => { - if (!command) { - return; - } - const match = nodeVersionInstallRegex.exec(command) || nvmInstallRegex.exec(command); - return match?.[1]; -}; - -const nodeVersionMapper = (command: string): string | undefined => { - const match = nodeVersionRegex.exec(command); - return match?.[1]; -}; - -const emptyFilter = (command: any): boolean => { - return Boolean(command); -}; - -const psObjectMapper = (command: any): string | undefined => { - return command.ps || command.cmd || command.sh; -}; - -const envObjectMapper = (variable: string) => (command: any): string | undefined => { - return command[variable]; -}; - -const parseStack = (stack: string | undefined, versions: string[]): void => { - if (!stack) { - return; - } - stack - .split(`,`) - .map(nodeVersionMapper) - .forEach((version) => { - if (version) { - versions.push(version); - } - }); -}; - -const ciFileName = `.appveyor.yml`; -const resolverName = `appVeyor`; - -@injectable() -export class AppVeyorResolver extends ISpecificCIResolver { - public readonly resolverName = resolverName; - - constructor(@inject(TYPES.FS) private fs: FS) { - super(); - } - - public async resolve({ repoPath }: ISpecificCIResolverOptions): Promise { - const fileName = path.join(repoPath, ciFileName); - let fileContents: string; - try { - fileContents = await this.fs.promises.readFile(fileName, `utf-8`); - } catch (e) { - return; - } - const versions: string[] = []; - const yaml = parse(fileContents); - const stack = yaml.stack; - parseStack(stack, versions); - const installCommands: string[] = yaml.install; - if (!installCommands) { - return versions; - } - const nodeVersion = installCommands.map(psObjectMapper).map(nodeVersionInstallMapper).filter(emptyFilter)[0]; - if (!nodeVersion) { - return versions; - } - const match = nodeEnvRegex.exec(nodeVersion); - if (!match) { - versions.push(nodeVersion); - return versions; - } else { - const matrixVarName = match[1]; - const environment: any[] = yaml.environment?.matrix || yaml.environment; - if (!environment) { - return versions; - } - return versions.concat(environment.map(envObjectMapper(matrixVarName)).filter(emptyFilter) as string[]); - } - } -} diff --git a/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser.ts b/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser.ts new file mode 100644 index 00000000..92d47565 --- /dev/null +++ b/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser.ts @@ -0,0 +1,182 @@ +import { ISpecificCIResolverResponse } from '../../../interfaces/ISpecificCIResolver'; +import { injectable } from 'inversify'; +import { ILoggerFactory } from '../../../../../utils/logger'; +import { ILogger } from '../../../../../utils/logger/interfaces/ILogger'; +import { INode, keySorter, NodeSorter, objectIterator } from '../../../../../utils/objectIterator/objectIterator'; +import { isString } from 'ts-type-guards'; +import { INvmHandler } from '../../../interfaces/INvmHandler'; +import { isStringOrNumber } from '../../../../../utils/types/typeGuards'; +import { Key, StringOrNumber } from '../../../../../utils/types/types'; + +const nodeVersionRegex = /node ([^\s]+)/i; +const nodeVersionInstallRegex = /Install-Product node ([^\s]+)/i; +const nodeEnvReplaceRegex = /\$env:(.+)/gi; +const nodeEnvRegex = /\$env:(.+)/i; + +type Matrix = Record[]; + +const nodeVersionMapper = (command: string): string | undefined => { + const match = nodeVersionRegex.exec(command); + return match?.[1]; +}; + +const parseStack = (stack: string, versions: Set): void => { + stack + .split(`,`) + .map(nodeVersionMapper) + .forEach((version) => { + if (version) { + versions.add(version); + } + }); +}; + +const envOrStackChecker = (node: INode): boolean => { + return node.key === `environment` || node.key === `stack`; +}; + +const sorter: NodeSorter = (a: INode, b: INode) => { + if (a.depth === 0) { + if (envOrStackChecker(a)) { + return -1; + } else if (envOrStackChecker(b)) { + return 1; + } + } + if (a.parent.isNonRootNode && a.parent.key === `environment`) { + if (a.key === `matrix`) { + return -1; + } else if (b.key === `matrix`) { + return 1; + } + } + return keySorter(a, b); +}; + +export interface IAppVeyorConfigParserOptions { + config: Record; +} + +const coerceString = (o: Record): Record => { + const newObj: Record = {}; + Object.keys(o).forEach((k) => { + newObj[k] = String(o[k]); + }); + return newObj; +}; + +const isStackNode = (value: unknown, node: INode): value is string => { + const { parent, isLeaf, key } = node; + return !parent.isNonRootNode && isLeaf && key === `stack` && isString(value); +}; + +const isMatrixNode = (node: INode): boolean => { + const { parent, isLeaf } = node; + return ( + !isLeaf && + parent.isNonRootNode && + parent.key === `matrix` && + parent.parent.isNonRootNode && + parent.parent.key === `environment` + ); +}; + +const isGlobalEnvNode = (value: unknown, node: INode): value is StringOrNumber => { + const { parent, isLeaf } = node; + return ( + isLeaf && + parent.isNonRootNode && + parent.key === `global` && + parent.parent.isNonRootNode && + parent.parent.key === `environment` && + isStringOrNumber(value) + ); +}; + +const isEnvNode = (value: unknown, node: INode): value is StringOrNumber => { + const { parent, isLeaf } = node; + return isLeaf && parent.isNonRootNode && parent.key === `environment` && isStringOrNumber(value); +}; + +const isCommandNode = (value: unknown, node: INode): value is string => { + const { key, isLeaf } = node; + return isLeaf && (key === `ps` || key === `sh` || key === `cmd`) && isString(value); +}; + +@injectable() +export class AppVeyorConfigParser { + private readonly logger: ILogger; + + constructor(private readonly nvmHandler: INvmHandler, loggerFactory: ILoggerFactory) { + this.logger = loggerFactory.getLogger(`AppVeyor Config Parser`); + } + + public async parse({ config }: IAppVeyorConfigParserOptions): Promise { + this.logger.debug(`Parsing AppVeyor configuration`); + const versions = new Set(); + const matrix: Matrix = []; + const commands: string[] = []; + const it = objectIterator(config, sorter); + let iteration = it.next(); + while (!iteration.done) { + const { value: node } = iteration; + const { value, key } = node; + let skip = false; + if (isStackNode(value, node)) { + parseStack(value, versions); + } else if (!node.isLeaf && isMatrixNode(node)) { + matrix.push(coerceString(node.value)); + skip = true; + } else if (isGlobalEnvNode(value, node)) { + matrix.forEach((mat) => { + mat[key] = String(value); + }); + } else if (isEnvNode(value, node)) { + if (matrix.length === 0) { + matrix.push({}); + } + matrix.forEach((mat) => { + mat[key] = String(value); + }); + } else if (isCommandNode(value, node)) { + commands.push(value); + skip = true; + } + iteration = it.next(skip); + } + commands.forEach((command) => { + this.nodeVersionCommandParser(command, matrix, versions); + }); + this.logger.debug(`Parsed AppVeyor configuration with the following matrix - ${JSON.stringify(matrix)}`); + this.logger.debug(`Finished parsing AppVeyor configuration, Found ${versions.size} version(s)`); + return { + nodeVersions: versions, + }; + } + + private nodeVersionCommandParser(command: string, matrix: Matrix, versions: Set): void { + if (this.nvmHandler.isNvmCommand(command)) { + this.logger.debug(`Found nvm command - ${command}`); + const normalizedCommand = command.replace(nodeEnvReplaceRegex, `\${$1}`); + const foundVersions = this.nvmHandler.getNvmVersions(normalizedCommand, matrix); + for (const version of foundVersions) { + versions.add(version); + } + } else { + const version = nodeVersionInstallRegex.exec(command)?.[1]; + if (version) { + const envKey = nodeEnvRegex.exec(version)?.[1]; + if (envKey) { + matrix.forEach((mat) => { + const matrixVersion = mat[envKey]; + if (matrixVersion) { + versions.add(String(matrixVersion)); + } + }); + } else { + versions.add(version); + } + } + } + } +} diff --git a/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.ts b/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.ts new file mode 100644 index 00000000..21911bf3 --- /dev/null +++ b/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.ts @@ -0,0 +1,60 @@ +import * as path from 'path'; +import { + ISpecificCIResolverOptions, + ISpecificCIResolver, + ISpecificCIResolverResponse, +} from '../../../interfaces/ISpecificCIResolver'; +import { inject, injectable } from 'inversify'; +import { FS, TYPES } from '../../../../../container/nodeModulesContainer'; +import { parse } from 'yaml'; +import { ILoggerFactory } from '../../../../../utils/logger'; +import { ILogger } from '../../../../../utils/logger/interfaces/ILogger'; +import { AppVeyorConfigParser } from './appVeyorConfigParser'; + +const ciFileName = `.appveyor.yml`; +const resolverName = `AppVeyor`; + +const getCiFileName = (repoPath: string): string => { + return path.join(repoPath, ciFileName); +}; + +@injectable() +export class AppVeyorResolver extends ISpecificCIResolver { + public readonly resolverName = resolverName; + private readonly logger: ILogger; + + public static TAG = Symbol.for(resolverName); + + constructor( + @inject(TYPES.FS) private fs: FS, + private readonly configParser: AppVeyorConfigParser, + loggerFactory: ILoggerFactory + ) { + super(); + this.logger = loggerFactory.getLogger(`AppVeyor Resolver`); + } + + public async isRelevant({ repoPath }: ISpecificCIResolverOptions): Promise { + const fileName = getCiFileName(repoPath); + this.logger.debug(`Checking if ${fileName} exists and readable`); + try { + await this.fs.promises.access(fileName, this.fs.constants.R_OK); + this.logger.debug(`File ${fileName} exists and readable`); + return true; + } catch (err) { + this.logger.debug(`File ${fileName} is not readable`, err); + return false; + } + } + + public async resolve({ repoPath }: ISpecificCIResolverOptions): Promise { + const fileName = getCiFileName(repoPath); + this.logger.debug(`Reading file ${fileName}`); + const fileContents = await this.fs.promises.readFile(fileName, `utf-8`); + this.logger.debug(`Parsing yml config file`); + const config = parse(fileContents); + return this.configParser.parse({ + config, + }); + } +} diff --git a/src/resolvers/ciResolver/impl/resolvers/circle.ts b/src/resolvers/ciResolver/impl/resolvers/circle.ts deleted file mode 100644 index 4cd2b5d1..00000000 --- a/src/resolvers/ciResolver/impl/resolvers/circle.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as path from 'path'; -import { ISpecificCIResolverOptions, ISpecificCIResolver } from '../../interfaces/ISpecificCIResolver'; -import { inject, injectable } from 'inversify'; -import { FS, TYPES } from '../../../../container/nodeModulesContainer'; - -const nodeVersionRegex = new RegExp(`image: node:(\\d+)`, `ig`); -const nodeVersionRegex2 = new RegExp(`image: \.+?\/node:(\\d+)`, `ig`); - -const ciFilePath = path.join(`.circleci`, `config.yml`); -const resolverName = `circleCi`; - -@injectable() -export class CircleCiResolver extends ISpecificCIResolver { - public readonly resolverName = resolverName; - - constructor(@inject(TYPES.FS) private fs: FS) { - super(); - } - - public async resolve({ repoPath }: ISpecificCIResolverOptions): Promise { - const fileName = path.join(repoPath, ciFilePath); - const versions = new Set(); - let fileContents: string; - try { - fileContents = await this.fs.promises.readFile(fileName, `utf-8`); - } catch (e) { - return; - } - let match: RegExpExecArray | null; - do { - match = nodeVersionRegex.exec(fileContents); - if (match) { - versions.add(match[1]); - } - } while (match); - do { - match = nodeVersionRegex2.exec(fileContents); - if (match) { - versions.add(match[1]); - } - } while (match); - return Array.from(versions); - } -} diff --git a/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser.ts b/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser.ts new file mode 100644 index 00000000..c427d998 --- /dev/null +++ b/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser.ts @@ -0,0 +1,111 @@ +import { ISpecificCIResolverResponse } from '../../../interfaces/ISpecificCIResolver'; +import { injectable } from 'inversify'; +import { ILoggerFactory } from '../../../../../utils/logger'; +import { ILogger } from '../../../../../utils/logger/interfaces/ILogger'; +import { INode, objectIterator } from '../../../../../utils/objectIterator/objectIterator'; +import { isString } from 'ts-type-guards'; +import { ITargetMatcher } from '../../../interfaces/ITargetMatcher'; +import { INvmHandler } from '../../../interfaces/INvmHandler'; +import { isStringOrNumber } from '../../../../../utils/types/typeGuards'; +import { StringOrNumber } from '../../../../../utils/types/types'; + +const nodeVersionRegex = /node:([^-.]+)-?/i; + +type Matrix = Record; + +export interface ICircleCiConfigParserOptions { + config: Record; +} + +const isVersionNode = (value: unknown, node: INode): value is string => { + const { parent, isLeaf, key, isNonRootNode } = node; + return ( + isLeaf && + key === `image` && + isString(value) && + isNonRootNode && + parent.isNonRootNode && + parent.parent.isNonRootNode && + parent.parent.key === `docker` + ); +}; + +const isEnvNode = (value: unknown, node: INode): value is StringOrNumber => { + const { parent, isLeaf } = node; + return isLeaf && parent.isNonRootNode && parent.key === `environment` && isStringOrNumber(value); +}; + +@injectable() +export class CircleCiConfigParser { + private readonly logger: ILogger; + + constructor( + private readonly nvmHandler: INvmHandler, + private readonly targetMatcher: ITargetMatcher, + loggerFactory: ILoggerFactory + ) { + this.logger = loggerFactory.getLogger(`CircleCi Config Parser`); + } + + public async parse({ config }: ICircleCiConfigParserOptions): Promise { + this.logger.debug(`Parsing Circle CI configuration`); + const versions = new Set(); + const matrix: Matrix = {}; + const nvmCommands: string[] = []; + for (const node of objectIterator(config)) { + const { value, key } = node; + if (isVersionNode(value, node)) { + this.parseVersion(value, versions); + } else if (isEnvNode(value, node)) { + if (matrix[key]) { + matrix[key].push(String(value)); + } else { + matrix[key] = [String(value)]; + } + } else if (this.isNvmCommandNode(value, node)) { + this.logger.debug(`Found nvm command - ${value}`); + nvmCommands.push(value); + } + } + nvmCommands.forEach((command) => { + this.nodeVersionCommandParser(command, matrix, versions); + }); + this.logger.debug(`Finished parsing CircleCi configuration, Found ${versions.size} version(s)`); + return { + nodeVersions: versions, + }; + } + + private isNvmCommandNode(value: unknown, node: INode): value is string { + const { isLeaf } = node; + return isLeaf && isString(value) && this.nvmHandler.isNvmCommand(value); + } + + private nodeVersionCommandParser(command: string, matrix: Matrix, versions: Set): void { + const foundVersions = this.nvmHandler.getNvmVersionsFromMatrix(command, matrix); + for (const version of foundVersions) { + versions.add(version); + } + } + + private parseVersion(value: string, versions: Set): void { + const version = nodeVersionRegex.exec(value)?.[1]; + if (version) { + if (version === `latest` || version === `current`) { + versions.add(this.targetMatcher.getStableVersionPlaceholder()); + } else if (version === `lts`) { + versions.add(this.targetMatcher.getLatestLtsVersionPlaceholder()); + } else { + if (!isNaN(parseInt(version.charAt(0), 10))) { + versions.add(version); + } else { + versions.add( + this.targetMatcher.getLtsVersionPlaceholder({ + codename: version, + }) + ); + } + } + } + } +} diff --git a/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.ts b/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.ts new file mode 100644 index 00000000..7963f4b3 --- /dev/null +++ b/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.ts @@ -0,0 +1,96 @@ +import * as path from 'path'; +import { + ISpecificCIResolverOptions, + ISpecificCIResolver, + ISpecificCIResolverResponse, +} from '../../../interfaces/ISpecificCIResolver'; +import { inject, injectable } from 'inversify'; +import { FS, TYPES } from '../../../../../container/nodeModulesContainer'; +import { ILoggerFactory } from '../../../../../utils/logger'; +import { ILogger } from '../../../../../utils/logger/interfaces/ILogger'; +import { CircleCiConfigParser } from './circleCiConfigParser'; +import { parse } from 'yaml'; + +const ciFilePathV1 = `circle.yml`; +const ciFilePathV2 = path.join(`.circleci`, `config.yml`); +const resolverName = `CircleCi`; + +const getCiFileNameV1 = (repoPath: string): string => { + return path.join(repoPath, ciFilePathV1); +}; + +const getCiFileNameV2 = (repoPath: string): string => { + return path.join(repoPath, ciFilePathV2); +}; + +@injectable() +export class CircleCiResolver extends ISpecificCIResolver { + public readonly resolverName = resolverName; + private readonly logger: ILogger; + + public static TAG = Symbol.for(resolverName); + + constructor( + @inject(TYPES.FS) private fs: FS, + private readonly configParser: CircleCiConfigParser, + loggerFactory: ILoggerFactory + ) { + super(); + this.logger = loggerFactory.getLogger(`Circle CI Resolver`); + } + + public async isRelevant({ repoPath }: ISpecificCIResolverOptions): Promise { + const fileNames = [getCiFileNameV1(repoPath), getCiFileNameV2(repoPath)]; + const accessPromises = fileNames.map(this.checkAccess.bind(this)); + const accessResults = await Promise.all(accessPromises); + const matching = accessResults.filter((result) => result).length; + this.logger.debug(`Found ${matching} relevant config file(s)`); + return matching > 0; + } + + public async resolve({ repoPath }: ISpecificCIResolverOptions): Promise { + const fileNames = [getCiFileNameV1(repoPath), getCiFileNameV2(repoPath)]; + const configPromises = fileNames.map(this.parseFile.bind(this)); + const parseResults = await Promise.all(configPromises); + const versions = parseResults.reduce((currentVersions, result) => { + if (result) { + for (const version of result.nodeVersions) { + currentVersions.add(version); + } + } + return currentVersions; + }, new Set()); + this.logger.debug(`Found ${versions.size} node versions`); + return { + nodeVersions: versions, + }; + } + + private async parseFile(fileName: string): Promise { + this.logger.debug(`Reading file ${fileName}`); + let fileContents: string; + try { + fileContents = await this.fs.promises.readFile(fileName, `utf-8`); + } catch (err) { + this.logger.debug(`File ${fileName} does not exist`, err); + return; + } + this.logger.debug(`Parsing yml config file ${fileName}`); + const config = parse(fileContents); + return this.configParser.parse({ + config, + }); + } + + private async checkAccess(fileName: string): Promise { + this.logger.debug(`Checking if ${fileName} exists and readable`); + try { + await this.fs.promises.access(fileName, this.fs.constants.R_OK); + } catch (err) { + this.logger.debug(`File ${fileName} is not readable`, err); + return false; + } + this.logger.debug(`File ${fileName} exists and readable`); + return true; + } +} diff --git a/src/resolvers/ciResolver/impl/resolvers/github.ts b/src/resolvers/ciResolver/impl/resolvers/github.ts deleted file mode 100644 index d0760154..00000000 --- a/src/resolvers/ciResolver/impl/resolvers/github.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as path from 'path'; -import { ISpecificCIResolverOptions, ISpecificCIResolver } from '../../interfaces/ISpecificCIResolver'; -import { inject, injectable } from 'inversify'; -import { FS, TYPES } from '../../../../container/nodeModulesContainer'; - -const nodeVersionRegex = new RegExp(`node-version:\\s*(.+)`, `ig`); - -const ciFilePath = path.join(`.github`, `workflows`); -const resolverName = `githubActions`; - -@injectable() -export class GithubActionsResolver extends ISpecificCIResolver { - public readonly resolverName = resolverName; - - constructor(@inject(TYPES.FS) private fs: FS) { - super(); - } - - public async resolve({ repoPath }: ISpecificCIResolverOptions): Promise { - const folderName = path.join(repoPath, ciFilePath); - const foundVersions = new Set(); - let files: string[]; - try { - files = await this.fs.promises.readdir(folderName); - } catch (e) { - return; - } - for (const fileName of files) { - const fileContents = await this.fs.promises.readFile(path.join(folderName, fileName), `utf-8`); - let match: RegExpExecArray | null; - do { - match = nodeVersionRegex.exec(fileContents); - if (match) { - const versionsStr = match[1]; - if (!versionsStr.startsWith(`$`)) { - let versions: string[]; - if (versionsStr.startsWith(`[`) && versionsStr.endsWith(`]`)) { - versions = versionsStr.substr(1, versionsStr.length - 2).split(`,`); - } else { - versions = [versionsStr]; - } - versions.forEach((version) => { - version = version.replace(/['"]/g, ``).trim(); - foundVersions.add(version); - }); - } - } - } while (match); - } - return Array.from(foundVersions); - } -} diff --git a/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser.ts b/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser.ts new file mode 100644 index 00000000..63442fd7 --- /dev/null +++ b/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser.ts @@ -0,0 +1,159 @@ +import { ISpecificCIResolverResponse } from '../../../interfaces/ISpecificCIResolver'; +import { injectable } from 'inversify'; +import { ILoggerFactory } from '../../../../../utils/logger'; +import { ILogger } from '../../../../../utils/logger/interfaces/ILogger'; +import { INode, keySorter, NodeSorter, objectIterator } from '../../../../../utils/objectIterator/objectIterator'; +import { is, isArrayOfObjects, isString } from 'ts-type-guards'; +import { INvmHandler } from '../../../interfaces/INvmHandler'; +import { isStringOrNumber } from '../../../../../utils/types/typeGuards'; +import { StringOrNumber } from '../../../../../utils/types/types'; + +const envRegex = /\${{\s*(?:env|matrix|secrets)\.([^\s]+)\s*}}/i; +const nodeEnvReplaceRegex = /\${{\s*(?:env|matrix|secrets)\.([^\s]+)\s*}}/i; + +type Matrix = Record; + +const toString = (value: StringOrNumber): string => String(value); + +const nodeVersionCommandParser = (matrix: Matrix) => (version: StringOrNumber): string[] => { + version = String(version); + const matrixKey = envRegex.exec(version)?.[1]; + if (matrixKey) { + const matrixVersions = matrix[matrixKey.trim()]; + return matrixVersions || []; + } else { + return [version]; + } +}; + +const sorter: NodeSorter = (a: INode, b: INode) => { + if (a.parent.isNonRootNode && a.parent.key === `matrix`) { + if (a.key === `include`) { + return 1; + } else if (b.key === `include`) { + return -1; + } + } + return keySorter(a, b); +}; + +const isStringOrNumberArray = (x: any): x is StringOrNumber[] => { + return is(Array)(x) && x.every(isStringOrNumber); +}; + +export interface IGithubActionsConfigParserOptions { + config: Record; +} + +const isEnvNode = (value: unknown, node: INode): value is StringOrNumber => { + const { parent, isLeaf } = node; + return isLeaf && parent.isNonRootNode && parent.key === `env` && isStringOrNumber(value); +}; + +const isMatrixNode = (value: unknown, node: INode): value is StringOrNumber[] => { + const { parent } = node; + return ( + parent.isNonRootNode && + parent.key === `matrix` && + parent.parent.isNonRootNode && + parent.parent.key === `strategy` && + isStringOrNumberArray(value) + ); +}; + +const isIncludeMatrixNode = (value: unknown, node: INode): value is object[] => { + const { parent, key } = node; + return ( + parent.isNonRootNode && + parent.key === `matrix` && + parent.parent.isNonRootNode && + parent.parent.key === `strategy` && + key === `include` && + isArrayOfObjects(value) + ); +}; + +const isNodeVersionNode = (value: unknown, node: INode): value is StringOrNumber => { + const { parent, key, isLeaf } = node; + return isLeaf && key === `node-version` && parent.isNonRootNode && parent.key === `with` && isStringOrNumber(value); +}; + +const isRunNode = (value: unknown, node: INode): value is string => { + const { key, isLeaf } = node; + return isLeaf && key === `run` && isString(value); +}; + +@injectable() +export class GithubActionsConfigParser { + private readonly logger: ILogger; + + constructor(private readonly nvmHandler: INvmHandler, loggerFactory: ILoggerFactory) { + this.logger = loggerFactory.getLogger(`Github Actions Config Parser`); + } + + public async parse({ config }: IGithubActionsConfigParserOptions): Promise { + this.logger.debug(`Parsing Github Actions configuration`); + const rawVersions: StringOrNumber[] = []; + const matrix: Matrix = {}; + const nvmCommands: string[] = []; + const it = objectIterator(config, sorter); + let iteration = it.next(); + while (!iteration.done) { + const { value: node } = iteration; + const { value, key } = node; + let skip = false; + if (isEnvNode(value, node)) { + if (matrix[key]) { + matrix[key].push(String(value)); + } else { + matrix[key] = [String(value)]; + } + skip = true; + } else if (isMatrixNode(value, node)) { + if (matrix[key]) { + matrix[key].push(...value.map(toString)); + } else { + matrix[key] = value.map(toString); + } + skip = true; + } else if (isIncludeMatrixNode(value, node)) { + for (const element of value) { + for (const [currentKey, currentValue] of Object.entries(element)) { + if (matrix[currentKey]) { + matrix[currentKey].push(String(currentValue)); + } else { + matrix[currentKey] = [String(currentValue)]; + } + } + } + skip = true; + } else if (isNodeVersionNode(value, node)) { + rawVersions.push(value); + skip = true; + } else if (isRunNode(value, node)) { + const command = value.replace(nodeEnvReplaceRegex, `\${$1}`); + if (this.nvmHandler.isNvmCommand(command)) { + this.logger.debug(`Found nvm command - ${command}`); + nvmCommands.push(command); + } + skip = true; + } + iteration = it.next(skip); + } + const versions = new Set(rawVersions.flatMap(nodeVersionCommandParser(matrix))); + nvmCommands.forEach((command) => { + this.nodeVersionCommandParser(command, matrix, versions); + }); + this.logger.debug(`Finished parsing AppVeyor configuration, Found ${versions.size} version(s)`); + return { + nodeVersions: versions, + }; + } + + private nodeVersionCommandParser(command: string, matrix: Matrix, versions: Set): void { + const foundVersions = this.nvmHandler.getNvmVersionsFromMatrix(command, matrix); + for (const version of foundVersions) { + versions.add(version); + } + } +} diff --git a/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.ts b/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.ts new file mode 100644 index 00000000..9529d647 --- /dev/null +++ b/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.ts @@ -0,0 +1,88 @@ +import * as path from 'path'; +import { + ISpecificCIResolverOptions, + ISpecificCIResolver, + ISpecificCIResolverResponse, +} from '../../../interfaces/ISpecificCIResolver'; +import { inject, injectable } from 'inversify'; +import { FS, TYPES } from '../../../../../container/nodeModulesContainer'; +import { ILoggerFactory } from '../../../../../utils/logger'; +import { ILogger } from '../../../../../utils/logger/interfaces/ILogger'; +import { GithubActionsConfigParser } from './githubActionsConfigParser'; +import { parse } from 'yaml'; + +const ciFilesPath = path.join(`.github`, `workflows`); +const resolverName = `Github Actions`; + +const getCiFilesPath = (repoPath: string): string => { + return path.join(repoPath, ciFilesPath); +}; + +@injectable() +export class GithubActionsResolver extends ISpecificCIResolver { + public readonly resolverName = resolverName; + private readonly logger: ILogger; + + public static TAG = Symbol.for(resolverName); + + constructor( + @inject(TYPES.FS) private readonly fs: FS, + private readonly configParser: GithubActionsConfigParser, + loggerFactory: ILoggerFactory + ) { + super(); + this.logger = loggerFactory.getLogger(`Github Actions Resolver`); + } + + public async isRelevant({ repoPath }: ISpecificCIResolverOptions): Promise { + const folderName = getCiFilesPath(repoPath); + this.logger.debug(`Checking if ${folderName} exists and readable`); + try { + await this.fs.promises.access(folderName, this.fs.constants.R_OK); + this.logger.debug(`Folder ${folderName} exists and readable. Checking if is is a directory`); + const stat = await this.fs.promises.stat(folderName); + const isDirectory = stat.isDirectory(); + this.logger.debug(`Folder ${folderName} is directory? ${isDirectory}`); + return isDirectory; + } catch (err) { + this.logger.debug(`Folder ${folderName} is not readable`, err); + return false; + } + } + + public async resolve({ repoPath }: ISpecificCIResolverOptions): Promise { + const folderName = getCiFilesPath(repoPath); + const files = await this.fs.promises.readdir(folderName); + const ymlFiles = files.filter((name) => name.endsWith(`.yml`)).map((name) => path.join(folderName, name)); + const configPromises = ymlFiles.map(this.parseFile.bind(this)); + const parseResults = await Promise.all(configPromises); + const versions = parseResults.reduce((currentVersions, result) => { + if (result) { + for (const version of result.nodeVersions) { + currentVersions.add(version); + } + } + return currentVersions; + }, new Set()); + this.logger.debug(`Found ${versions.size} node versions`); + return { + nodeVersions: versions, + }; + } + + private async parseFile(fileName: string): Promise { + this.logger.debug(`Reading file ${fileName}`); + let fileContents: string; + try { + fileContents = await this.fs.promises.readFile(fileName, `utf-8`); + } catch (err) { + this.logger.debug(`File ${fileName} does not exist`, err); + return; + } + this.logger.debug(`Parsing yml config file ${fileName}`); + const config = parse(fileContents); + return this.configParser.parse({ + config, + }); + } +} diff --git a/src/resolvers/ciResolver/impl/resolvers/travis.ts b/src/resolvers/ciResolver/impl/resolvers/travis.ts deleted file mode 100644 index d904ad00..00000000 --- a/src/resolvers/ciResolver/impl/resolvers/travis.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as path from 'path'; -import { ISpecificCIResolverOptions, ISpecificCIResolver, LTS_VERSION } from '../../interfaces/ISpecificCIResolver'; -import { inject, injectable } from 'inversify'; -import { FS, TYPES } from '../../../../container/nodeModulesContainer'; -import { parse } from 'yaml'; - -const ciFileName = `.travis.yml`; -const resolverName = `travisCi`; - -const ltsMapper = (nodeVersion: string): string => { - if (nodeVersion === `lts/*`) { - return LTS_VERSION; - } - return nodeVersion; -}; - -@injectable() -export class TravisCiResolver extends ISpecificCIResolver { - public readonly resolverName = resolverName; - - constructor(@inject(TYPES.FS) private fs: FS) { - super(); - } - - public async resolve({ repoPath }: ISpecificCIResolverOptions): Promise { - const fileName = path.join(repoPath, ciFileName); - let fileContents: string; - try { - fileContents = await this.fs.promises.readFile(fileName, `utf-8`); - } catch (e) { - return; - } - const yaml = parse(fileContents); - const versions = yaml.node_js; - return versions.map(ltsMapper); - } -} diff --git a/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser.ts b/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser.ts new file mode 100644 index 00000000..260ff417 --- /dev/null +++ b/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser.ts @@ -0,0 +1,152 @@ +import { ISpecificCIResolverResponse } from '../../../interfaces/ISpecificCIResolver'; +import { injectable } from 'inversify'; +import { ILoggerFactory } from '../../../../../utils/logger'; +import { ILogger } from '../../../../../utils/logger/interfaces/ILogger'; +import { INode, objectIterator } from '../../../../../utils/objectIterator/objectIterator'; +import { isString } from 'ts-type-guards'; +import { ITargetMatcher } from '../../../interfaces/ITargetMatcher'; +import { parse, ParseEntry } from 'shell-quote'; +import * as yargsParser from 'yargs-parser'; +import { Options } from 'yargs-parser'; +import { INvmHandler } from '../../../interfaces/INvmHandler'; +import { isStringOrNumber } from '../../../../../utils/types/typeGuards'; +import { StringOrNumber } from '../../../../../utils/types/types'; + +const yargsOptions: Options = { + configuration: { + 'parse-numbers': false, + }, +}; + +export interface ICircleCiConfigParserOptions { + config: Record; +} + +const ltsRegex = new RegExp(`lts/(.+)`, `i`); + +const argsFilter = (arg: ParseEntry): string[] => (typeof arg === `string` ? [arg] : []); + +type Matrix = Record; + +const parseEnvVariable = (value: string, matrix: Matrix): void => { + const envArray = parse(value).flatMap(argsFilter); + const parsedEnvArray = yargsParser(envArray, yargsOptions)._; + parsedEnvArray.forEach((env) => { + const [currentKey, currentValue] = env.split(`=`, 2); + if (matrix[currentKey]) { + matrix[currentKey].push(currentValue); + } else { + matrix[currentKey] = [currentValue]; + } + }); +}; + +const isEnvNode = (value: unknown, node: INode): value is string => { + const { parent, isLeaf } = node; + return isLeaf && parent.isNonRootNode && parent.key === `env` && isString(value); +}; + +const isStringEnvNode = (value: unknown, node: INode): value is string => { + const { key, isLeaf } = node; + return isLeaf && key === `env` && isString(value); +}; + +const isEntryEnvNode = (value: unknown, node: INode): value is string => { + const { parent, isLeaf } = node; + return ( + isLeaf && parent.isNonRootNode && parent.parent.isNonRootNode && parent.parent.key === `env` && isString(value) + ); +}; + +const isNodeJsNode = (value: unknown, node: INode): value is StringOrNumber => { + const { key, isLeaf } = node; + return isLeaf && key === `node_js` && isStringOrNumber(value); +}; + +const isNodeJsEntryNode = (value: unknown, node: INode): value is StringOrNumber => { + const { parent, isLeaf } = node; + return isLeaf && parent.isNonRootNode && parent.key === `node_js` && isStringOrNumber(value); +}; + +@injectable() +export class TravisCiConfigParser { + private readonly logger: ILogger; + + constructor( + private readonly nvmHandler: INvmHandler, + private readonly targetMatcher: ITargetMatcher, + loggerFactory: ILoggerFactory + ) { + this.logger = loggerFactory.getLogger(`Travis CI Config Parser`); + } + + public async parse({ config }: ICircleCiConfigParserOptions): Promise { + this.logger.debug(`Parsing Travis Ci configuration`); + const versions = new Set(); + const matrix: Matrix = {}; + const nvmCommands: string[] = []; + const it = objectIterator(config); + let iteration = it.next(); + while (!iteration.done) { + const { value: node } = iteration; + const { value } = node; + let skip = false; + if (isEnvNode(value, node)) { + parseEnvVariable(value, matrix); + skip = true; + } else if (isStringEnvNode(value, node)) { + parseEnvVariable(value, matrix); + skip = true; + } else if (isEntryEnvNode(value, node)) { + parseEnvVariable(value, matrix); + skip = true; + } else if (isNodeJsNode(value, node)) { + const version = this.parseVersion(value); + versions.add(version); + } else if (isNodeJsEntryNode(value, node)) { + const version = this.parseVersion(value); + versions.add(version); + } else if (this.isNvmCommandNode(value, node)) { + this.logger.debug(`Found nvm command - ${value}`); + nvmCommands.push(value); + } + iteration = it.next(skip); + } + nvmCommands.forEach((command) => { + this.nodeVersionCommandParser(command, matrix, versions); + }); + this.logger.debug(`Finished parsing CircleCi configuration, Found ${versions.size} version(s)`); + return { + nodeVersions: versions, + }; + } + + private isNvmCommandNode(value: unknown, node: INode): value is string { + const { isLeaf } = node; + return isLeaf && isString(value) && this.nvmHandler.isNvmCommand(value); + } + + private nodeVersionCommandParser(command: string, matrix: Matrix, versions: Set): void { + const foundVersions = this.nvmHandler.getNvmVersionsFromMatrix(command, matrix); + for (const version of foundVersions) { + versions.add(version); + } + } + + private parseVersion(version: StringOrNumber): string { + const versionStr = String(version); + if (versionStr === `node` || versionStr === `stable`) { + return this.targetMatcher.getStableVersionPlaceholder(); + } + if (versionStr === `lts/*`) { + return this.targetMatcher.getLatestLtsVersionPlaceholder(); + } + const ltsVersion = ltsRegex.exec(versionStr)?.[1]; + if (ltsVersion) { + return this.targetMatcher.getLtsVersionPlaceholder({ + codename: ltsVersion, + }); + } + return versionStr; + } +} diff --git a/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.ts b/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.ts new file mode 100644 index 00000000..33c79439 --- /dev/null +++ b/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.ts @@ -0,0 +1,60 @@ +import * as path from 'path'; +import { + ISpecificCIResolverOptions, + ISpecificCIResolver, + ISpecificCIResolverResponse, +} from '../../../interfaces/ISpecificCIResolver'; +import { inject, injectable } from 'inversify'; +import { FS, TYPES } from '../../../../../container/nodeModulesContainer'; +import { parse } from 'yaml'; +import { ILoggerFactory } from '../../../../../utils/logger'; +import { ILogger } from '../../../../../utils/logger/interfaces/ILogger'; +import { TravisCiConfigParser } from './travisCiConfigParser'; + +const ciFileName = `.travis.yml`; +const resolverName = `TravisCi`; + +const getCiFileName = (repoPath: string): string => { + return path.join(repoPath, ciFileName); +}; + +@injectable() +export class TravisCiResolver extends ISpecificCIResolver { + public readonly resolverName = resolverName; + private readonly logger: ILogger; + + public static TAG = Symbol.for(resolverName); + + constructor( + @inject(TYPES.FS) private fs: FS, + private readonly configParser: TravisCiConfigParser, + loggerFactory: ILoggerFactory + ) { + super(); + this.logger = loggerFactory.getLogger(`Travis CI Resolver`); + } + + public async isRelevant({ repoPath }: ISpecificCIResolverOptions): Promise { + const fileName = getCiFileName(repoPath); + this.logger.debug(`Checking if ${fileName} exists and readable`); + try { + await this.fs.promises.access(fileName, this.fs.constants.R_OK); + this.logger.debug(`File ${fileName} exists and readable`); + return true; + } catch (err) { + this.logger.debug(`File ${fileName} is not readable`, err); + return false; + } + } + + public async resolve({ repoPath }: ISpecificCIResolverOptions): Promise { + const fileName = getCiFileName(repoPath); + this.logger.debug(`Reading file ${fileName}`); + const fileContents = await this.fs.promises.readFile(fileName, `utf-8`); + this.logger.debug(`Parsing yml config file`); + const config = parse(fileContents); + return this.configParser.parse({ + config, + }); + } +} diff --git a/src/resolvers/ciResolver/impl/specificCIResolverRunner.ts b/src/resolvers/ciResolver/impl/specificCIResolverRunner.ts new file mode 100644 index 00000000..7132b8dd --- /dev/null +++ b/src/resolvers/ciResolver/impl/specificCIResolverRunner.ts @@ -0,0 +1,59 @@ +import { injectable } from 'inversify'; +import { ITargetMatcher } from '../interfaces/ITargetMatcher'; +import { IResolverResult } from '../../types'; +import { ILoggerFactory } from '../../../utils/logger'; +import { ILogger } from '../../../utils/logger/interfaces/ILogger'; +import { ISpecificCIResolverRunner, ISpecificCIResolverRunnerOptions } from '../interfaces/ISpecificCIResolverRunner'; + +@injectable() +export class SpecificCIResolverRunner extends ISpecificCIResolverRunner { + private readonly logger: ILogger; + + constructor(private readonly targetMatcher: ITargetMatcher, loggerFactory: ILoggerFactory) { + super(); + this.logger = loggerFactory.getLogger(`Specific Ci Resolver Runner`); + } + + public async resolve({ + repoPath, + targetNode, + packageReleaseDate, + resolver, + }: ISpecificCIResolverRunnerOptions): Promise { + const result: IResolverResult = { + isMatch: false, + }; + let nodeVersions: Set; + try { + this.logger.debug(`Trying to resolve using ${resolver.resolverName}`); + const resolverResult = await resolver.resolve({ + repoPath, + }); + nodeVersions = resolverResult.nodeVersions; + } catch (e) { + this.logger.debug(`Failed to find node versions in resolver ${resolver.resolverName} due to an unknown error`, e); + return result; + } + if (nodeVersions.size === 0) { + this.logger.debug(`No node versions were found in resolver ${resolver.resolverName}`); + return result; + } else { + this.logger.debug(`Resolver ${resolver.resolverName} found ${nodeVersions.size} node versions`); + const isMatch = await this.targetMatcher.match({ + candidates: nodeVersions, + targetNode, + packageReleaseDate, + }); + if (isMatch) { + this.logger.debug(`Resolver ${resolver.resolverName} found a matching node version`); + return { + isMatch: true, + resolverName: resolver.resolverName, + }; + } else { + this.logger.debug(`Resolver ${resolver.resolverName} did not find a matching node version`); + return result; + } + } + } +} diff --git a/src/resolvers/ciResolver/impl/targetMatcher.ts b/src/resolvers/ciResolver/impl/targetMatcher.ts index 27c8b72b..c12b01ed 100644 --- a/src/resolvers/ciResolver/impl/targetMatcher.ts +++ b/src/resolvers/ciResolver/impl/targetMatcher.ts @@ -1,49 +1,93 @@ -import { ITargetMatcher, ITargetMatcherOptions } from '../interfaces/ITargetMatcher'; +import { IGetLtsVersionPlaceholderOptions, ITargetMatcher, ITargetMatcherOptions } from '../interfaces/ITargetMatcher'; import { injectable } from 'inversify'; -import { ILts } from '../../../utils/lts'; -import { LTS_VERSION } from '..'; +import { INodeVersions } from '../../../utils/nodeVersions'; import semver = require('semver'); - -const semverOptions = { - loose: true, -}; +import { ILoggerFactory } from '../../../utils/logger'; +import { ILogger } from '../../../utils/logger/interfaces/ILogger'; const coerce = (version: string): string | undefined => { - const coercedAll = semver.coerce(version, semverOptions); + const coercedAll = semver.coerce(version); if (!coercedAll) { return undefined; } const majorStr = coercedAll.major.toFixed(0); - const coerced = semver.coerce(majorStr, semverOptions); + const coerced = semver.coerce(majorStr); return coerced!.format(); }; +const STABLE_PLACEHOLDER = `__STABLE__`; +const LATEST_LTS_PLACEHOLDER = `__LTS*__`; +const LTS_PLACEHOLDER = `__LTS__`; +const ltsPlaceholderRegex = new RegExp(`${LTS_PLACEHOLDER}(.+)`, `i`); + @injectable() export class TargetMatcher extends ITargetMatcher { - constructor(private lts: ILts) { + private readonly logger: ILogger; + constructor(private readonly nodeVersions: INodeVersions, loggerFactory: ILoggerFactory) { super(); + this.logger = loggerFactory.getLogger(`Target Matcher`); + } + + public getStableVersionPlaceholder(): string { + return STABLE_PLACEHOLDER; + } + + public getLatestLtsVersionPlaceholder(): string { + return LATEST_LTS_PLACEHOLDER; + } + + public getLtsVersionPlaceholder({ codename }: IGetLtsVersionPlaceholderOptions): string { + return `${LTS_PLACEHOLDER}${codename}`; } public async match({ targetNode, candidates, packageReleaseDate }: ITargetMatcherOptions): Promise { + this.logger.debug(`Trying to match to ${targetNode}`); const validTarget = coerce(targetNode); if (!validTarget) { + this.logger.debug(`Failed to coerce target ${targetNode}`); throw new TypeError(`Node target version ${targetNode} is not valid`); } + this.logger.debug(`Coerced target is ${validTarget}`); const resolvedCandidates: string[] = []; for (const candidate of candidates) { - if (candidate === LTS_VERSION) { - const resolved = await this.lts.resolveLtsVersion({ + if (candidate === STABLE_PLACEHOLDER) { + this.logger.debug(`Resolving stable version in ${packageReleaseDate.toJSON()}`); + const resolved = await this.nodeVersions.resolveStableVersion({ + date: packageReleaseDate, + }); + if (resolved) { + resolvedCandidates.push(resolved); + } + } + if (candidate === LATEST_LTS_PLACEHOLDER) { + this.logger.debug(`Resolving latest lts version in ${packageReleaseDate.toJSON()}`); + const resolved = await this.nodeVersions.resolveLatestLtsVersion({ date: packageReleaseDate, }); - Array.prototype.push.apply(resolvedCandidates, resolved); + if (resolved) { + resolvedCandidates.push(resolved); + } } else { - resolvedCandidates.push(candidate); + const codename = ltsPlaceholderRegex.exec(candidate)?.[1]; + if (codename) { + this.logger.debug(`Resolving LTS version of ${codename}`); + const resolved = await this.nodeVersions.resolveLtsVersion({ + codename, + }); + if (resolved) { + resolvedCandidates.push(resolved); + } + } else { + resolvedCandidates.push(candidate); + } } } const matchingCandidates = resolvedCandidates.filter((candidate) => { const validCandidate = coerce(candidate); + this.logger.debug(`Coerced version of candidate ${candidate} is ${validCandidate}`); return validCandidate && semver.eq(validTarget, validCandidate); }); + this.logger.debug(`Found ${matchingCandidates.length} match(es)`); return matchingCandidates.length > 0; } } diff --git a/src/resolvers/ciResolver/index.ts b/src/resolvers/ciResolver/index.ts index 43c456f3..056201f2 100644 --- a/src/resolvers/ciResolver/index.ts +++ b/src/resolvers/ciResolver/index.ts @@ -1,22 +1,56 @@ import { ICIResolver } from './interfaces/ICIResolver'; import { CiResolver } from './impl/ciResolver'; import { ISpecificCIResolver } from './interfaces/ISpecificCIResolver'; -import { TravisCiResolver } from './impl/resolvers/travis'; -import { CircleCiResolver } from './impl/resolvers/circle'; -import { GithubActionsResolver } from './impl/resolvers/github'; +import { CircleCiResolver } from './impl/resolvers/circleci/circleCiResolver'; +import { GithubActionsResolver } from './impl/resolvers/githubActions/githubActionsResolver'; import { ITargetMatcher } from './interfaces/ITargetMatcher'; import { TargetMatcher } from './impl/targetMatcher'; -import { AppVeyorResolver } from './impl/resolvers/appveyor'; +import { AppVeyorResolver } from './impl/resolvers/appveyor/appVeyorResolver'; import { interfaces } from 'inversify'; import Bind = interfaces.Bind; +import { SpecificCIResolverRunner } from './impl/specificCIResolverRunner'; +import { ISpecificCIResolverRunner } from './interfaces/ISpecificCIResolverRunner'; +import { AppVeyorConfigParser } from './impl/resolvers/appveyor/appVeyorConfigParser'; +import { CircleCiConfigParser } from './impl/resolvers/circleci/circleCiConfigParser'; +import { TravisCiConfigParser } from './impl/resolvers/travisci/travisCiConfigParser'; +import { TravisCiResolver } from './impl/resolvers/travisci/travisCiResolver'; +import { GithubActionsConfigParser } from './impl/resolvers/githubActions/githubActionsConfigParser'; +import { namedOrMultiConstraint } from '../../container/utils'; +import { INvmHandler } from './interfaces/INvmHandler'; +import { NvmHandler } from './impl/nvmHandler'; + +export const SpecificCIResolverTags = { + travisCi: TravisCiResolver.TAG, + appVeyor: AppVeyorResolver.TAG, + circleCi: CircleCiResolver.TAG, + githubActions: GithubActionsResolver.TAG, +}; export const ciResolverModulesBinder = (bind: Bind): void => { - bind(ICIResolver).to(CiResolver).inSingletonScope(); + bind(INvmHandler).to(NvmHandler).inSingletonScope(); bind(ITargetMatcher).to(TargetMatcher).inSingletonScope(); - bind(ISpecificCIResolver).to(TravisCiResolver).inSingletonScope(); - bind(ISpecificCIResolver).to(AppVeyorResolver).inSingletonScope(); - bind(ISpecificCIResolver).to(CircleCiResolver).inSingletonScope(); - bind(ISpecificCIResolver).to(GithubActionsResolver).inSingletonScope(); + bind(ISpecificCIResolverRunner).to(SpecificCIResolverRunner).inSingletonScope(); + bind(ICIResolver).to(CiResolver).inSingletonScope(); + bind(ISpecificCIResolver) + .to(TravisCiResolver) + .inSingletonScope() + .when(namedOrMultiConstraint(SpecificCIResolverTags.travisCi, ISpecificCIResolver)); + bind(ISpecificCIResolver) + .to(AppVeyorResolver) + .inSingletonScope() + .when(namedOrMultiConstraint(SpecificCIResolverTags.appVeyor, ISpecificCIResolver)); + bind(ISpecificCIResolver) + .to(CircleCiResolver) + .inSingletonScope() + .when(namedOrMultiConstraint(SpecificCIResolverTags.circleCi, ISpecificCIResolver)); + bind(ISpecificCIResolver) + .to(GithubActionsResolver) + .inSingletonScope() + .when(namedOrMultiConstraint(SpecificCIResolverTags.githubActions, ISpecificCIResolver)); + bind(AppVeyorConfigParser).toSelf().inSingletonScope(); + bind(CircleCiConfigParser).toSelf().inSingletonScope(); + bind(TravisCiConfigParser).toSelf().inSingletonScope(); + bind(GithubActionsConfigParser).toSelf().inSingletonScope(); }; export * from './interfaces/ISpecificCIResolver'; diff --git a/src/resolvers/ciResolver/interfaces/ICIResolver.ts b/src/resolvers/ciResolver/interfaces/ICIResolver.ts index a9a8a573..35ac6552 100644 --- a/src/resolvers/ciResolver/interfaces/ICIResolver.ts +++ b/src/resolvers/ciResolver/interfaces/ICIResolver.ts @@ -1,4 +1,3 @@ -import { injectable } from 'inversify'; import { IResolverResult } from '../../types'; import { Moment } from 'moment'; @@ -8,7 +7,6 @@ export interface ICIResolveOptions { packageReleaseDate: Moment; } -@injectable() export abstract class ICIResolver { public abstract async resolve(options: ICIResolveOptions): Promise; } diff --git a/src/resolvers/ciResolver/interfaces/INvmHandler.ts b/src/resolvers/ciResolver/interfaces/INvmHandler.ts new file mode 100644 index 00000000..cf33867d --- /dev/null +++ b/src/resolvers/ciResolver/interfaces/INvmHandler.ts @@ -0,0 +1,9 @@ +export type Env = Record; +export type EnvMatrix = Record; + +export abstract class INvmHandler { + public abstract isNvmCommand(cmd: string): boolean; + public abstract getNvmVersion(cmd: string, env: Env): string | undefined; + public abstract getNvmVersions(cmd: string, environments: Env[]): Set; + public abstract getNvmVersionsFromMatrix(cmd: string, matrix: EnvMatrix): Set; +} diff --git a/src/resolvers/ciResolver/interfaces/ISpecificCIResolver.ts b/src/resolvers/ciResolver/interfaces/ISpecificCIResolver.ts index 23173b07..5648772c 100644 --- a/src/resolvers/ciResolver/interfaces/ISpecificCIResolver.ts +++ b/src/resolvers/ciResolver/interfaces/ISpecificCIResolver.ts @@ -1,13 +1,13 @@ -import { injectable } from 'inversify'; - export interface ISpecificCIResolverOptions { repoPath: string; } -export const LTS_VERSION = `LTS_VERSION`; +export interface ISpecificCIResolverResponse { + nodeVersions: Set; +} -@injectable() export abstract class ISpecificCIResolver { - public abstract async resolve(options: ISpecificCIResolverOptions): Promise; + public abstract async isRelevant(options: ISpecificCIResolverOptions): Promise; + public abstract async resolve(options: ISpecificCIResolverOptions): Promise; public abstract readonly resolverName: string; } diff --git a/src/resolvers/ciResolver/interfaces/ISpecificCIResolverRunner.ts b/src/resolvers/ciResolver/interfaces/ISpecificCIResolverRunner.ts new file mode 100644 index 00000000..4b9e31fa --- /dev/null +++ b/src/resolvers/ciResolver/interfaces/ISpecificCIResolverRunner.ts @@ -0,0 +1,14 @@ +import { Moment } from 'moment'; +import { IResolverResult } from '../../types'; +import { ISpecificCIResolver } from './ISpecificCIResolver'; + +export interface ISpecificCIResolverRunnerOptions { + repoPath: string; + targetNode: string; + packageReleaseDate: Moment; + resolver: ISpecificCIResolver; +} + +export abstract class ISpecificCIResolverRunner { + public abstract async resolve(options: ISpecificCIResolverRunnerOptions): Promise; +} diff --git a/src/resolvers/ciResolver/interfaces/ITargetMatcher.ts b/src/resolvers/ciResolver/interfaces/ITargetMatcher.ts index 8b1c2b92..5b301dbb 100644 --- a/src/resolvers/ciResolver/interfaces/ITargetMatcher.ts +++ b/src/resolvers/ciResolver/interfaces/ITargetMatcher.ts @@ -1,13 +1,18 @@ -import { injectable } from 'inversify'; import { Moment } from 'moment'; export interface ITargetMatcherOptions { targetNode: string; - candidates: string[]; + candidates: Set; packageReleaseDate: Moment; } -@injectable() +export interface IGetLtsVersionPlaceholderOptions { + codename: string; +} + export abstract class ITargetMatcher { + public abstract getStableVersionPlaceholder(): string; + public abstract getLatestLtsVersionPlaceholder(): string; + public abstract getLtsVersionPlaceholder(options: IGetLtsVersionPlaceholderOptions): string; public abstract async match(options: ITargetMatcherOptions): Promise; } diff --git a/src/resolvers/enginesResolver/impl/enginesResolver.ts b/src/resolvers/enginesResolver/impl/enginesResolver.ts index 2bcdca0e..47cb48bc 100644 --- a/src/resolvers/enginesResolver/impl/enginesResolver.ts +++ b/src/resolvers/enginesResolver/impl/enginesResolver.ts @@ -11,13 +11,13 @@ const semverOptions = { const coerceSemVer = (version: SemVer): SemVer => { const majorStr = version.major.toFixed(0); - return semver.coerce(majorStr, semverOptions)!; + return semver.coerce(majorStr)!; }; -const coerce = (version: string): string | undefined => { - const coercedAll = semver.coerce(version, semverOptions); +const coerce = (version: string): string | null => { + const coercedAll = semver.coerce(version); if (!coercedAll) { - return undefined; + return null; } const coerced = coerceSemVer(coercedAll); return coerced.format(); diff --git a/src/resolvers/enginesResolver/interfaces/IEnginesResolver.ts b/src/resolvers/enginesResolver/interfaces/IEnginesResolver.ts index 3429929f..54687928 100644 --- a/src/resolvers/enginesResolver/interfaces/IEnginesResolver.ts +++ b/src/resolvers/enginesResolver/interfaces/IEnginesResolver.ts @@ -1,4 +1,3 @@ -import { injectable } from 'inversify'; import { IResolverResult } from '../../types'; export interface IEnginesResolverOptions { @@ -6,7 +5,6 @@ export interface IEnginesResolverOptions { targetNode: string; } -@injectable() export abstract class IEnginesResolver { public abstract async resolve(options: IEnginesResolverOptions): Promise; } diff --git a/src/resolvers/testResolver/impl/testResolver.ts b/src/resolvers/testResolver/impl/testResolver.ts index 771c6f4f..21bcc204 100644 --- a/src/resolvers/testResolver/impl/testResolver.ts +++ b/src/resolvers/testResolver/impl/testResolver.ts @@ -2,16 +2,21 @@ import { injectable } from 'inversify'; import { IResolverResult } from '../../types'; import { ITestResolver, ITestResolverOptions } from '../interfaces/ITestResolver'; import { IYarn, IYarnOptions } from '../../../utils/yarn'; +import { ILoggerFactory } from '../../../utils/logger'; +import { ILogger } from '../../../utils/logger/interfaces/ILogger'; const resolverName = `yarn run test`; @injectable() export class TestResolver extends ITestResolver { - constructor(private yarn: IYarn) { + private readonly logger: ILogger; + constructor(private readonly yarn: IYarn, loggerFactory: ILoggerFactory) { super(); + this.logger = loggerFactory.getLogger(`Test Resolver`); } public async resolve({ repoPath }: ITestResolverOptions): Promise { + this.logger.info(`Running tests in ${repoPath}`); try { const yarnOptions: IYarnOptions = { cwd: repoPath, @@ -19,13 +24,17 @@ export class TestResolver extends ITestResolver { await this.yarn.install(yarnOptions); await this.yarn.build(yarnOptions); await this.yarn.test(yarnOptions); + this.logger.success(`Tests ran successfully`); return { isMatch: true, resolverName, }; - } catch (e) {} - return { - isMatch: false, - }; + } catch (err) { + this.logger.error(`Failed Running tests in ${repoPath}`); + this.logger.debug(`Failed Running tests in ${repoPath}`, err); + return { + isMatch: false, + }; + } } } diff --git a/src/resolvers/testResolver/interfaces/ITestResolver.ts b/src/resolvers/testResolver/interfaces/ITestResolver.ts index f45a7135..03ce888c 100644 --- a/src/resolvers/testResolver/interfaces/ITestResolver.ts +++ b/src/resolvers/testResolver/interfaces/ITestResolver.ts @@ -1,11 +1,9 @@ -import { injectable } from 'inversify'; import { IResolverResult } from '../../types'; export interface ITestResolverOptions { repoPath: string; } -@injectable() export abstract class ITestResolver { public abstract async resolve(options: ITestResolverOptions): Promise; } diff --git a/src/resolvers/types/index.ts b/src/resolvers/types/index.ts index f7892a6d..aa5f9eb3 100644 --- a/src/resolvers/types/index.ts +++ b/src/resolvers/types/index.ts @@ -1,4 +1,10 @@ -export interface IResolverResult { - isMatch: boolean; - resolverName?: string; +export interface IResolverPositiveResult { + isMatch: true; + resolverName: string; } + +export interface IResolverNegativeResult { + isMatch: false; +} + +export type IResolverResult = IResolverNegativeResult | IResolverPositiveResult; diff --git a/src/utils/commandRunner/interfaces/ICommandRunner.ts b/src/utils/commandRunner/interfaces/ICommandRunner.ts index 714ab7c4..f7ce745f 100644 --- a/src/utils/commandRunner/interfaces/ICommandRunner.ts +++ b/src/utils/commandRunner/interfaces/ICommandRunner.ts @@ -1,12 +1,10 @@ import { SpawnOptionsWithoutStdio } from 'child_process'; -import { injectable } from 'inversify'; export interface IExecuteCommandOptions { command: string[]; execOptions: SpawnOptionsWithoutStdio; } -@injectable() export abstract class ICommandRunner { public abstract async executeCommand(options: IExecuteCommandOptions): Promise; } diff --git a/src/utils/git/interfaces/IGitCheckout.ts b/src/utils/git/interfaces/IGitCheckout.ts index 818aae56..07bf336c 100644 --- a/src/utils/git/interfaces/IGitCheckout.ts +++ b/src/utils/git/interfaces/IGitCheckout.ts @@ -1,5 +1,3 @@ -import { injectable } from 'inversify'; - export interface ICheckoutOptions { url: string; baseDir: string; @@ -7,7 +5,6 @@ export interface ICheckoutOptions { commitSha?: string; } -@injectable() export abstract class IGitCheckout { public abstract async checkoutRepo(options: ICheckoutOptions): Promise; } diff --git a/src/utils/logger/impl/loggerFactory.ts b/src/utils/logger/impl/loggerFactory.ts index 486d5bcc..6ae5d5d8 100644 --- a/src/utils/logger/impl/loggerFactory.ts +++ b/src/utils/logger/impl/loggerFactory.ts @@ -22,7 +22,7 @@ export class LoggerFactory extends ILoggerFactory { }; } - @memoize((category) => category) + @memoize((category: string): string => category) public getLogger(category: string): ILogger { return new Logger({ ...this.options, diff --git a/src/utils/logger/interfaces/ILoggerFactory.ts b/src/utils/logger/interfaces/ILoggerFactory.ts index 75daa8c9..22f83249 100644 --- a/src/utils/logger/interfaces/ILoggerFactory.ts +++ b/src/utils/logger/interfaces/ILoggerFactory.ts @@ -1,7 +1,5 @@ -import { injectable } from 'inversify'; import { ILogger } from './ILogger'; -@injectable() export abstract class ILoggerFactory { public abstract getLogger(category: string): ILogger; } diff --git a/src/utils/logger/interfaces/ILoggerSettings.ts b/src/utils/logger/interfaces/ILoggerSettings.ts index 9d6d857a..b1436c42 100644 --- a/src/utils/logger/interfaces/ILoggerSettings.ts +++ b/src/utils/logger/interfaces/ILoggerSettings.ts @@ -1,6 +1,3 @@ -import { injectable } from 'inversify'; - -@injectable() export abstract class ILoggerSettings { public abstract readonly debugMode: boolean; public abstract readonly traceMode: boolean; diff --git a/src/utils/lts/impl/lts.ts b/src/utils/lts/impl/lts.ts deleted file mode 100644 index c58bad1f..00000000 --- a/src/utils/lts/impl/lts.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { inject, injectable } from 'inversify'; -import { ILts, ILtsOptions } from '../interfaces/ILts'; -import { Moment } from 'moment'; -import moment = require('moment'); -import { memoize } from '../../memoize/memoize'; -import { Axios, TYPES } from '../../../container/nodeModulesContainer'; -import { ILoggerFactory } from '../../logger'; -import { ILogger } from '../../logger/interfaces/ILogger'; - -const NODE_RELEASE_FILE = `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json`; -const dateFormat = `YYYY-MM-DD`; - -interface IRemoteNodeVersion { - start: string; - end: string; - lts?: string; -} - -type RemoteNodeVersions = Record; - -interface INodeVersion { - from: Moment; - to: Moment; - version: string; -} - -@injectable() -export class Lts extends ILts { - private readonly logger: ILogger; - constructor(@inject(TYPES.Axios) private readonly axios: Axios, loggerFactory: ILoggerFactory) { - super(); - this.logger = loggerFactory.getLogger(`LTS`); - } - - async resolveLtsVersion({ date }: ILtsOptions): Promise { - this.logger.debug(`Resolving LTS versions from ${date.toJSON()}`); - const versions = await this.getAllVersions(); - const relevantVersions = versions - .filter((lts: INodeVersion) => lts.from.isSameOrBefore(date) && lts.to.isSameOrAfter(date)) - .map((lts: INodeVersion) => lts.version); - this.logger.debug(`Resolved LTS versions from ${date.toJSON()} to ${JSON.stringify(relevantVersions)}`); - return relevantVersions; - } - - @memoize() - private async getAllVersions(): Promise { - this.logger.debug(`Fetching all node release versions`); - try { - const response = await this.axios.get(NODE_RELEASE_FILE); - this.logger.debug(`Parsing versions result object`); - return Object.entries(response.data) - .filter(([, data]) => data.lts) - .map(([version, data]) => { - return { - from: moment(data.start, dateFormat), - to: moment(data.end, dateFormat), - version: version.substr(1), - }; - }); - } catch (err) { - this.logger.error(`Failed to fetch node release versions`, err); - throw new Error(`Failed to fetch node release versions`); - } - } -} diff --git a/src/utils/lts/index.ts b/src/utils/lts/index.ts deleted file mode 100644 index cf0d3032..00000000 --- a/src/utils/lts/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ILts } from './interfaces/ILts'; -import { Lts } from './impl/lts'; -import { interfaces } from 'inversify'; -import Bind = interfaces.Bind; - -export const ltsModulesBinder = (bind: Bind): void => { - bind(ILts).to(Lts).inSingletonScope(); -}; - -export * from './interfaces/ILts'; diff --git a/src/utils/lts/interfaces/ILts.ts b/src/utils/lts/interfaces/ILts.ts deleted file mode 100644 index 52f35be1..00000000 --- a/src/utils/lts/interfaces/ILts.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { injectable } from 'inversify'; -import { Moment } from 'moment'; - -export interface ILtsOptions { - date: Moment; -} - -@injectable() -export abstract class ILts { - public abstract async resolveLtsVersion(options: ILtsOptions): Promise; -} diff --git a/src/utils/memoize/memoize.ts b/src/utils/memoize/memoize.ts index 5bda9c5c..0f4c582f 100644 --- a/src/utils/memoize/memoize.ts +++ b/src/utils/memoize/memoize.ts @@ -1,12 +1,23 @@ +import { Key, StringOrNumber } from '../types/types'; + const cacheProp = Symbol.for(`[memoize]`); +const NO_ARGS = `__no_args__`; + +export type GenericFunction = (...args: A) => R; -export type GenericFunction = (...args: any[]) => any; +type Check = T[K] extends (...a: any[]) => any + ? T[K] extends TSig + ? Parameters['length'] extends 0 + ? unknown + : ['Method must have exactly 0 parameters, Found Parameters:', Parameters] + : ['Parameters types not matched, expected [', Parameters, '] Found Parameters:', Parameters] + : unknown; -const defaultKeyBuilder = (v: any): any => { - return v; +const defaultKeyBuilder: GenericFunction<[], string> = (): string => { + return NO_ARGS; }; -const getCache = (target: any): Record> => { +const getCache = (target: any): Record> => { if (!target[cacheProp]) { Object.defineProperty(target, cacheProp, { value: Object.create(null), @@ -16,16 +27,21 @@ const getCache = (target: any): Record> => { return target[cacheProp]; }; -const getKeyCache = (target: any, key: string): Map => { +const getKeyCache = (target: any, key: Key): Map => { const dict = getCache(target); - if (!dict[key]) { - dict[key] = new Map(); + const coercedKey: StringOrNumber = key as StringOrNumber; + if (!dict[coercedKey]) { + dict[coercedKey] = new Map(); } - return dict[key]; + return dict[coercedKey]; }; -const memoizeFn = (namespace: string, func: GenericFunction, keyBuilder: GenericFunction): GenericFunction => { - return function (this: any, ...args: any[]): any { +const memoizeFn = ( + namespace: Key, + func: GenericFunction, + keyBuilder: GenericFunction +): GenericFunction => { + return function (this: any, ...args: A): any { const cache = getKeyCache(this, namespace); const key = keyBuilder.apply(this, args); if (cache.has(key)) { @@ -37,8 +53,34 @@ const memoizeFn = (namespace: string, func: GenericFunction, keyBuilder: Generic }; }; -export const memoize = (keyBuilder?: GenericFunction) => { - return (_: object, propertyKey: string, descriptor: TypedPropertyDescriptor): void => { - descriptor.value = memoizeFn(propertyKey, descriptor.value, keyBuilder || defaultKeyBuilder); +type MemoizeReturnValue> = < + TTarget, + TKey extends keyof TTarget +>( + _: TTarget, + propertyKey: TKey, + descriptor: TypedPropertyDescriptor> +) => void; + +type MemoizeReturnValueNoArgs any> = ( + _: TTarget, + propertyKey: TKey & Check, + descriptor: TypedPropertyDescriptor> +) => void; + +export function memoize any>(): MemoizeReturnValueNoArgs; +export function memoize>( + keyBuilder: GenericFunction +): MemoizeReturnValue; +// eslint-disable-next-line prefer-arrow/prefer-arrow-functions +export function memoize>( + keyBuilder?: GenericFunction +): MemoizeReturnValue | MemoizeReturnValueNoArgs { + return ( + _: TTarget, + propertyKey: TKey, + descriptor: TypedPropertyDescriptor> + ): void => { + descriptor.value = memoizeFn(propertyKey, descriptor.value!, keyBuilder || defaultKeyBuilder); }; -}; +} diff --git a/src/utils/nodeVersions/impl/nodeVersions.ts b/src/utils/nodeVersions/impl/nodeVersions.ts new file mode 100644 index 00000000..13129911 --- /dev/null +++ b/src/utils/nodeVersions/impl/nodeVersions.ts @@ -0,0 +1,120 @@ +import { inject, injectable } from 'inversify'; +import { INodeVersions, INodeVersionsLtsOptions, INodeVersionsLatestOptions } from '../interfaces/INodeVersions'; +import { Moment } from 'moment'; +import moment = require('moment'); +import { memoize } from '../../memoize/memoize'; +import { Axios, TYPES } from '../../../container/nodeModulesContainer'; +import { ILoggerFactory } from '../../logger'; +import { ILogger } from '../../logger/interfaces/ILogger'; +import { O } from 'ts-toolbelt'; + +const NODE_RELEASE_FILE = `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json`; +const dateFormat = `YYYY-MM-DD`; + +interface IRemoteNodeVersion { + start: string; + end: string; + codename?: string; + lts?: string; +} + +type RemoteNodeVersions = Record; + +interface INodeVersion { + from: Moment; + ltsFrom: Moment; + to: Moment; + version: string; + codename: string; +} + +const dateSorter = (key: O.SelectKeys) => (a: INodeVersion, b: INodeVersion): number => { + return a[key].valueOf() - b[key].valueOf(); +}; + +@injectable() +export class NodeVersions extends INodeVersions { + private readonly logger: ILogger; + constructor(@inject(TYPES.Axios) private readonly axios: Axios, loggerFactory: ILoggerFactory) { + super(); + this.logger = loggerFactory.getLogger(`Node Versions`); + } + + @memoize(({ codename }: INodeVersionsLtsOptions): string => codename.toLowerCase()) + public async resolveLtsVersion({ codename }: INodeVersionsLtsOptions): Promise { + codename = codename.toLowerCase(); + this.logger.debug(`Resolving LTS version of ${codename}`); + const versions = await this.getAllVersions(); + const relevantVersions = versions + .filter((nodeVersion: INodeVersion) => nodeVersion.codename === codename) + .map((nodeVersion: INodeVersion) => nodeVersion.version); + const lts = relevantVersions[0]; + if (!lts) { + this.logger.error(`Failed to resolve LTS version of ${codename}`); + return; + } + this.logger.debug(`Resolved LTS version of ${codename} to ${lts}`); + return lts; + } + + @memoize(({ date }: INodeVersionsLatestOptions): string => date.toJSON()) + public async resolveLatestLtsVersion({ date }: INodeVersionsLatestOptions): Promise { + this.logger.debug(`Resolving latest lts version in ${date.toJSON()}`); + const versions = await this.getAllVersions(); + const relevantVersions = versions + .filter( + (nodeVersion: INodeVersion) => nodeVersion.ltsFrom.isSameOrBefore(date) && nodeVersion.to.isSameOrAfter(date) + ) + .sort(dateSorter(`ltsFrom`)) + .map((nodeVersion: INodeVersion) => nodeVersion.version); + const latest = relevantVersions[relevantVersions.length - 1]; + if (!latest) { + this.logger.error(`Failed to resolve latest lts version in ${date.toJSON()}`); + return; + } + this.logger.debug(`Resolved latest lts version in ${date.toJSON()} to ${latest}`); + return latest; + } + + @memoize(({ date }: INodeVersionsLatestOptions): string => date.toJSON()) + public async resolveStableVersion({ date }: INodeVersionsLatestOptions): Promise { + this.logger.debug(`Resolving stable version in ${date.toJSON()}`); + const versions = await this.getAllVersions(); + const relevantVersions = versions + .filter( + (nodeVersion: INodeVersion) => nodeVersion.from.isSameOrBefore(date) && nodeVersion.to.isSameOrAfter(date) + ) + .sort(dateSorter(`from`)) + .map((nodeVersion: INodeVersion) => nodeVersion.version); + const latest = relevantVersions[relevantVersions.length - 1]; + if (!latest) { + this.logger.error(`Failed to resolve stable version in ${date.toJSON()}`); + return; + } + this.logger.debug(`Resolved stable version in ${date.toJSON()} to ${latest}`); + return latest; + } + + @memoize() + public async getAllVersions(): Promise { + this.logger.debug(`Fetching all node release versions`); + try { + const response = await this.axios.get(NODE_RELEASE_FILE); + this.logger.debug(`Parsing versions result object`); + return Object.entries(response.data) + .filter(([, data]) => data.lts) + .map(([version, data]) => { + return { + from: moment.utc(data.start, dateFormat), + to: moment.utc(data.end, dateFormat), + ltsFrom: moment.utc(data.lts, dateFormat), + version: version.substr(1), + codename: (data.codename || ``).toLowerCase(), + }; + }); + } catch (err) { + this.logger.error(`Failed to fetch node release versions`, err); + throw new Error(`Failed to fetch node release versions`); + } + } +} diff --git a/src/utils/nodeVersions/index.ts b/src/utils/nodeVersions/index.ts new file mode 100644 index 00000000..f61ef055 --- /dev/null +++ b/src/utils/nodeVersions/index.ts @@ -0,0 +1,10 @@ +import { INodeVersions } from './interfaces/INodeVersions'; +import { NodeVersions } from './impl/nodeVersions'; +import { interfaces } from 'inversify'; +import Bind = interfaces.Bind; + +export const nodeVersionsModulesBinder = (bind: Bind): void => { + bind(INodeVersions).to(NodeVersions).inSingletonScope(); +}; + +export * from './interfaces/INodeVersions'; diff --git a/src/utils/nodeVersions/interfaces/INodeVersions.ts b/src/utils/nodeVersions/interfaces/INodeVersions.ts new file mode 100644 index 00000000..ed382466 --- /dev/null +++ b/src/utils/nodeVersions/interfaces/INodeVersions.ts @@ -0,0 +1,15 @@ +import { Moment } from 'moment'; + +export interface INodeVersionsLtsOptions { + codename: string; +} + +export interface INodeVersionsLatestOptions { + date: Moment; +} + +export abstract class INodeVersions { + public abstract async resolveLtsVersion(options: INodeVersionsLtsOptions): Promise; + public abstract async resolveLatestLtsVersion(options: INodeVersionsLatestOptions): Promise; + public abstract async resolveStableVersion(options: INodeVersionsLatestOptions): Promise; +} diff --git a/src/utils/objectIterator/objectIterator.ts b/src/utils/objectIterator/objectIterator.ts index ac013f58..0851dc58 100644 --- a/src/utils/objectIterator/objectIterator.ts +++ b/src/utils/objectIterator/objectIterator.ts @@ -1,13 +1,31 @@ +import { Key } from '../types/types'; + export interface IRootNode { - value: any; + value: unknown; + isNonRootNode: false; } -export interface INode extends IRootNode { +export interface IBaseNode { parent: IParent; key: string; depth: number; + isNonRootNode: true; +} + +export interface ILeafNode extends IBaseNode { + isLeaf: true; + value: unknown; +} + +export interface INoneLeafNode extends IBaseNode { + isLeaf: false; + value: Obj; } +export type INode = INoneLeafNode | ILeafNode; + +export type Obj = Record; + export type IParent = INode | IRootNode; export type ObjectIterator = Generator; export type NodeSorter = (a: INode, b: INode) => number; @@ -16,28 +34,32 @@ export const keySorter: NodeSorter = (a: INode, b: INode) => { return a.key.localeCompare(b.key); }; -export function* objectIterator(obj: any, sorter?: NodeSorter): ObjectIterator { - yield* iterator({ value: obj }, obj, 0, sorter || keySorter, new Set()); +export function* objectIterator(obj: Obj, sorter?: NodeSorter): ObjectIterator { + yield* iterator({ value: obj, isNonRootNode: false }, obj, 0, sorter || keySorter, new Set()); } -function* iterator(parent: IParent, obj: any, depth: number, sorter: NodeSorter, cache: Set): ObjectIterator { - if (!isObject(obj) || cache.has(obj)) { +function* iterator(parent: IParent, obj: Obj, depth: number, sorter: NodeSorter, cache: Set): ObjectIterator { + if (cache.has(obj)) { return; } cache.add(obj); const nextDepth = depth + 1; - const nodes = Object.keys(obj).map((key) => { + const nodes: INode[] = Object.keys(obj).map((key) => { const value = obj[key]; - return { key, value, depth, parent }; + if (!isObject(value)) { + return { key, value, depth, parent, isLeaf: true, isNonRootNode: true }; + } else { + return { key, value, depth, parent, isLeaf: false, isNonRootNode: true }; + } }); nodes.sort(sorter); for (const node of nodes) { - if (!(yield node)) { + if (!(yield node) && !node.isLeaf) { yield* iterator(node, node.value, nextDepth, sorter, cache); } } } -const isObject = (obj: any): obj is Record => { +const isObject = (obj: unknown): obj is Record => { return obj !== null && typeof obj === `object`; }; diff --git a/src/utils/packageInfo/impl/packageInfo.ts b/src/utils/packageInfo/impl/packageInfo.ts index b27b82b8..a874b185 100644 --- a/src/utils/packageInfo/impl/packageInfo.ts +++ b/src/utils/packageInfo/impl/packageInfo.ts @@ -1,17 +1,24 @@ import { inject, injectable } from 'inversify'; import { IPackageInfo, IPackageInfoOptions, IPackageInfoResult } from '../interfaces/IPackageInfo'; import { Pacote, TYPES } from '../../../container/nodeModulesContainer'; +import { ILoggerFactory } from '../../logger'; +import { ILogger } from '../../logger/interfaces/ILogger'; @injectable() export class PackageInfo extends IPackageInfo { - constructor(@inject(TYPES.Pacote) private pacote: Pacote) { + private readonly logger: ILogger; + + constructor(@inject(TYPES.Pacote) private readonly pacote: Pacote, loggerFactory: ILoggerFactory) { super(); + this.logger = loggerFactory.getLogger(`Package Info`); } async getPackageInfo({ name, semver }: IPackageInfoOptions): Promise { + this.logger.info(`Getting package information of ${name}@${semver}`); const result = await this.pacote.manifest(`${name}@${semver}`, { fullMetadata: true, }); + this.logger.debug(`Got package information of ${name}@${semver}`); const version = result.version; const engines = result.engines?.node; const repoUrl = (result.repository as any)?.url; diff --git a/src/utils/packageInfo/interfaces/IPackageInfo.ts b/src/utils/packageInfo/interfaces/IPackageInfo.ts index 70a398a3..28241ba1 100644 --- a/src/utils/packageInfo/interfaces/IPackageInfo.ts +++ b/src/utils/packageInfo/interfaces/IPackageInfo.ts @@ -1,5 +1,3 @@ -import { injectable } from 'inversify'; - export interface IPackageInfoOptions { name: string; semver: string; @@ -14,7 +12,6 @@ export interface IPackageInfoResult { commitSha?: string; } -@injectable() export abstract class IPackageInfo { public abstract async getPackageInfo(options: IPackageInfoOptions): Promise; } diff --git a/src/utils/types/typeGuards.ts b/src/utils/types/typeGuards.ts new file mode 100644 index 00000000..d60bb062 --- /dev/null +++ b/src/utils/types/typeGuards.ts @@ -0,0 +1,6 @@ +import { isNumber, isString } from 'ts-type-guards'; +import { StringOrNumber } from './types'; + +export const isStringOrNumber = (x: any): x is StringOrNumber => { + return isString(x) || isNumber(x); +}; diff --git a/src/utils/types/types.ts b/src/utils/types/types.ts new file mode 100644 index 00000000..6b351de4 --- /dev/null +++ b/src/utils/types/types.ts @@ -0,0 +1,2 @@ +export type StringOrNumber = string | number; +export type Key = string | number | symbol; diff --git a/src/utils/yarn/interfaces/IYarn.ts b/src/utils/yarn/interfaces/IYarn.ts index 751ef700..314505f8 100644 --- a/src/utils/yarn/interfaces/IYarn.ts +++ b/src/utils/yarn/interfaces/IYarn.ts @@ -1,10 +1,7 @@ -import { injectable } from 'inversify'; - export interface IYarnOptions { cwd: string; } -@injectable() export abstract class IYarn { public abstract async install(options: IYarnOptions): Promise; diff --git a/stryker.conf.js b/stryker.conf.js new file mode 100644 index 00000000..b0ad9538 --- /dev/null +++ b/stryker.conf.js @@ -0,0 +1,34 @@ +const jestConfig = require('./jest.config.js'); +jestConfig.testMatch = ['/test/**/*.spec.ts']; +const config = { + mutator: { name: 'typescript' }, + packageManager: 'yarn', + reporters: ['html', 'progress'], + testFramework: 'jest', + testRunner: 'jest', + coverageAnalysis: 'off', + tsconfigFile: 'tsconfig.json', + mutate: ['src/**/*.ts'], + dashboard: { + reportType: 'full', + }, + timeoutMs: 20000, + maxConcurrentTestRunners: 4, + thresholds: { + high: 90, + low: 70, + break: 70, + }, + jest: { + projectType: 'custom', + config: jestConfig, + enableFindRelatedTests: true, + }, +}; + +if (process.env.JEST_CI) { + config.maxConcurrentTestRunners = 1; + config.reporters.push('dashboard', 'clear-text'); +} + +module.exports = config; diff --git a/test/common/bindingTester.ts b/test/common/bindingTester.ts deleted file mode 100644 index 3f039cb8..00000000 --- a/test/common/bindingTester.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* eslint-disable jest/no-export */ -import { interfaces } from 'inversify'; -import Bind = interfaces.Bind; - -export type BinderFn = (bind: Bind) => void; - -export enum BindingTypes { - SINGELTON = `SINGELTON`, - CONSTANT = `CONSTANT`, -} - -export interface IBindingToTest { - binder: any; - binded: any; - type: BindingTypes; -} - -export interface IBindingTestOptions { - bindings: IBindingToTest[]; - binderFn: BinderFn; - name: string; -} - -const getBindName = (bind: any): string => { - if (typeof bind === `symbol`) { - return bind.toString(); - } - return bind.name || bind.constructor?.name || bind.toString?.() || bind; -}; - -export const testBindings = ({ binderFn, bindings, name }: IBindingTestOptions): void => { - describe(name, () => { - const inSingletonScopeMock = jest.fn(); - const toSpy = { - inSingletonScope: inSingletonScopeMock, - }; - const toMock = jest.fn(); - toMock.mockReturnValue(toSpy); - const toConstantValueMock = jest.fn(); - const bindSpy = { - to: toMock, - toConstantValue: toConstantValueMock, - }; - const bindMock = jest.fn(); - bindMock.mockReturnValue(bindSpy); - binderFn(bindMock); - let bindExpectedCalls = 0; - let toExpectedCalls = 0; - let toConstantValueExpectedCalls = 0; - let inSingletonScopeExpectedCalls = 0; - bindings.forEach((binding) => { - const { binder, binded, type } = binding; - it(`Should bind ${getBindName(binder)} to ${getBindName(binded)} - ${type}`, () => { - bindExpectedCalls++; - expect(bindMock).toHaveBeenNthCalledWith(bindExpectedCalls, binder); - if (type === BindingTypes.SINGELTON) { - toExpectedCalls++; - inSingletonScopeExpectedCalls++; - expect(toMock).toHaveBeenNthCalledWith(toExpectedCalls, binded); - expect(inSingletonScopeMock).toHaveBeenNthCalledWith(inSingletonScopeExpectedCalls); - } else if (type === BindingTypes.CONSTANT) { - toConstantValueExpectedCalls++; - expect(toConstantValueMock).toHaveBeenNthCalledWith(toConstantValueExpectedCalls, binded); - } - }); - }); - it(`Should not call more bindings than expected`, () => { - expect(bindMock).toHaveBeenCalledTimes(bindExpectedCalls); - expect(toMock).toHaveBeenCalledTimes(toExpectedCalls); - expect(toConstantValueMock).toHaveBeenCalledTimes(toConstantValueExpectedCalls); - expect(inSingletonScopeMock).toHaveBeenCalledTimes(inSingletonScopeExpectedCalls); - }); - }); -}; diff --git a/test/common/inMemoryDb.ts b/test/common/inMemoryDb.ts new file mode 100644 index 00000000..48b73689 --- /dev/null +++ b/test/common/inMemoryDb.ts @@ -0,0 +1,13 @@ +import { Connection, createConnection } from 'typeorm'; + +export const getInMemoryDb = async (entity: Function): Promise => { + return createConnection({ + name: Math.random().toString(36).substring(7), + type: `sqlite`, + database: `:memory:`, + dropSchema: true, + entities: [entity], + synchronize: true, + logging: false, + }); +}; diff --git a/test/common/isEqual.ts b/test/common/isEqual.ts new file mode 100644 index 00000000..8a2de0ae --- /dev/null +++ b/test/common/isEqual.ts @@ -0,0 +1,7 @@ +import { Matcher } from 'jest-mock-extended'; +import { equals } from 'expect/build/jasmineUtils'; + +export const isEqual = (expectedValue: T): Matcher => + new Matcher((actualValue: T) => { + return equals(actualValue, expectedValue); + }); diff --git a/test/common/safeMockFn.ts b/test/common/safeMockFn.ts new file mode 100644 index 00000000..0bcd92dd --- /dev/null +++ b/test/common/safeMockFn.ts @@ -0,0 +1,8 @@ +import { calledWithFn, CalledWithMock } from 'jest-mock-extended'; + +export const mockFn = (): T extends (...args: infer A) => infer B + ? CalledWithMock & T + : T => { + // @ts-ignore + return calledWithFn(); +}; diff --git a/test/common/testers/bindingTester.ts b/test/common/testers/bindingTester.ts new file mode 100644 index 00000000..e5dd8c9f --- /dev/null +++ b/test/common/testers/bindingTester.ts @@ -0,0 +1,173 @@ +/* eslint-disable jest/no-export */ +/* eslint-disable @typescript-eslint/unbound-method */ +import { interfaces, METADATA_KEY } from 'inversify'; +import Bind = interfaces.Bind; +import Request = interfaces.Request; +import BindingWhenOnSyntax = interfaces.BindingWhenOnSyntax; +import BindingInWhenOnSyntax = interfaces.BindingInWhenOnSyntax; +import { Constraint } from '../../../src/container/utils'; +import Abstract = interfaces.Abstract; +import { mock, mockClear, mockDeep } from 'jest-mock-extended'; +import BindingToSyntax = interfaces.BindingToSyntax; +import { mockFn } from '../safeMockFn'; + +export type BinderFn = (bind: Bind) => void; + +export enum BindingTypes { + SINGELTON = `SINGELTON`, + CONSTANT = `CONSTANT`, + SELF = `SELF`, +} + +export interface IBindingToTest { + binder: any; + binded: any; + type: BindingTypes; + tag?: symbol; + multi?: boolean; +} + +export interface IBindingTestOptions { + bindings: IBindingToTest[]; + binderFn: BinderFn; + name: string; +} + +interface ConstraintTest { + constraint: Constraint; + binding: { + binder: any; + tag: symbol; + multi: boolean; + }; +} + +const getBindName = (bind: any): string => { + if (typeof bind === `symbol`) { + return bind.toString(); + } else if (typeof bind === `string`) { + return bind; + } + return bind.name || bind.constructor?.name || bind.toString?.() || bind; +}; + +export const testNameOrMultiConstraint = (symbol: symbol, target: Abstract, constraint: Constraint): void => { + const matchesTagNameMock = mockFn>(); + const matchesTagMultiMock = mockFn>(); + const requestMock = mockDeep(); + requestMock.target.matchesTag.calledWith(METADATA_KEY.NAMED_TAG).mockReturnValue(matchesTagNameMock); + requestMock.target.matchesTag.calledWith(METADATA_KEY.MULTI_INJECT_TAG).mockReturnValue(matchesTagMultiMock); + + beforeEach(() => { + matchesTagNameMock.mockReset(); + matchesTagMultiMock.mockReset(); + mockClear(requestMock); + }); + + it(`should match by multi inject`, () => { + matchesTagMultiMock.mockReturnValue(true); + const result = constraint(requestMock); + expect(result).toBe(true); + expect(requestMock.target.matchesTag).toHaveBeenCalledTimes(1); + expect(matchesTagNameMock).toHaveBeenCalledTimes(0); + expect(matchesTagMultiMock).toHaveBeenCalledTimes(1); + expect(requestMock.target.matchesTag).toHaveBeenCalledWith(METADATA_KEY.MULTI_INJECT_TAG); + expect(matchesTagMultiMock).toHaveBeenCalledWith(target); + }); + + it(`should match by name`, () => { + matchesTagNameMock.mockReturnValue(true); + matchesTagMultiMock.mockReturnValue(false); + const result = constraint(requestMock); + expect(result).toBe(true); + expect(requestMock.target.matchesTag).toHaveBeenCalledTimes(2); + expect(matchesTagNameMock).toHaveBeenCalledTimes(1); + expect(matchesTagMultiMock).toHaveBeenCalledTimes(1); + expect(requestMock.target.matchesTag).toHaveBeenCalledWith(METADATA_KEY.NAMED_TAG); + expect(matchesTagNameMock).toHaveBeenCalledWith(symbol); + expect(requestMock.target.matchesTag).toHaveBeenCalledWith(METADATA_KEY.MULTI_INJECT_TAG); + expect(matchesTagMultiMock).toHaveBeenCalledWith(target); + }); + + it(`should fail to match`, () => { + matchesTagNameMock.mockReturnValue(false); + matchesTagMultiMock.mockReturnValue(false); + const result = constraint(requestMock); + expect(result).toBe(false); + expect(requestMock.target.matchesTag).toHaveBeenCalledTimes(2); + expect(matchesTagNameMock).toHaveBeenCalledTimes(1); + expect(matchesTagMultiMock).toHaveBeenCalledTimes(1); + expect(requestMock.target.matchesTag).toHaveBeenCalledWith(METADATA_KEY.NAMED_TAG); + expect(matchesTagNameMock).toHaveBeenCalledWith(symbol); + expect(requestMock.target.matchesTag).toHaveBeenCalledWith(METADATA_KEY.MULTI_INJECT_TAG); + expect(matchesTagMultiMock).toHaveBeenCalledWith(target); + }); +}; + +export const testBindings = ({ binderFn, bindings, name }: IBindingTestOptions): void => { + describe(name, () => { + const bindingWhenOnSyntaxMock = mock>(); + const bindingInWhenOnSyntaxMock = mock>(); + bindingInWhenOnSyntaxMock.inSingletonScope.mockReturnValue(bindingWhenOnSyntaxMock); + const bindingToSyntaxMock = mock>(); + bindingToSyntaxMock.to.mockReturnValue(bindingInWhenOnSyntaxMock); + bindingToSyntaxMock.toSelf.mockReturnValue(bindingInWhenOnSyntaxMock); + bindingToSyntaxMock.toConstantValue.mockReturnValue(bindingWhenOnSyntaxMock); + const bindMock = mockFn(); + bindMock.mockReturnValue(bindingToSyntaxMock); + binderFn(bindMock); + let bindExpectedCalls = 0; + let toExpectedCalls = 0; + let toSelfExpectedCalls = 0; + let toConstantValueExpectedCalls = 0; + let inSingletonScopeExpectedCalls = 0; + let whenTestCalls = 0; + const constraintTests: ConstraintTest[] = []; + bindings.forEach(({ binder, binded, type, tag, multi }) => { + if (tag && multi) { + const constraint = bindingWhenOnSyntaxMock.when.mock.calls[whenTestCalls][0]; + constraintTests.push({ + constraint, + binding: { + binder, + multi, + tag, + }, + }); + whenTestCalls++; + } + it(`Should bind ${getBindName(binder)} to ${getBindName(binded)} - ${type}`, () => { + bindExpectedCalls++; + expect(bindMock).toHaveBeenNthCalledWith(bindExpectedCalls, binder); + if (type === BindingTypes.SINGELTON) { + inSingletonScopeExpectedCalls++; + if (binded === BindingTypes.SELF) { + toSelfExpectedCalls++; + expect(bindingToSyntaxMock.toSelf).toHaveBeenNthCalledWith(toSelfExpectedCalls); + } else { + toExpectedCalls++; + expect(bindingToSyntaxMock.to).toHaveBeenNthCalledWith(toExpectedCalls, binded); + } + expect(bindingInWhenOnSyntaxMock.inSingletonScope).toHaveBeenNthCalledWith(inSingletonScopeExpectedCalls); + } else if (type === BindingTypes.CONSTANT) { + toConstantValueExpectedCalls++; + expect(bindingToSyntaxMock.toConstantValue).toHaveBeenNthCalledWith(toConstantValueExpectedCalls, binded); + } + }); + }); + constraintTests.forEach(({ constraint, binding }) => { + const { binder, tag } = binding; + describe(`Should bind ${getBindName(binder)} to ${tag.toString()} and multi inject`, () => { + testNameOrMultiConstraint(tag, binder, constraint); + }); + }); + it(`Should not call more bindings than expected`, () => { + expect(bindMock).toHaveBeenCalledTimes(bindExpectedCalls); + expect(bindingToSyntaxMock.to).toHaveBeenCalledTimes(toExpectedCalls); + expect(bindingToSyntaxMock.toSelf).toHaveBeenCalledTimes(toSelfExpectedCalls); + expect(bindingToSyntaxMock.toConstantValue).toHaveBeenCalledTimes(toConstantValueExpectedCalls); + expect(bindingInWhenOnSyntaxMock.inSingletonScope).toHaveBeenCalledTimes(inSingletonScopeExpectedCalls); + expect(bindingWhenOnSyntaxMock.when).toHaveBeenCalledTimes(whenTestCalls); + }); + }); +}; diff --git a/test/common/testers/entityMetadataTester.ts b/test/common/testers/entityMetadataTester.ts new file mode 100644 index 00000000..f5ccdd06 --- /dev/null +++ b/test/common/testers/entityMetadataTester.ts @@ -0,0 +1,35 @@ +/* eslint-disable jest/no-export */ +import { Connection } from 'typeorm'; +import { IEntityConstructor } from '../../../src/db/interfaces/IEntity'; +import { ColumnType } from 'typeorm/driver/types/ColumnTypes'; +import { getInMemoryDb } from '../inMemoryDb'; + +export interface ColumnMatcher { + databaseName: string; + isPrimary: boolean; + isNullable: boolean; + type: ColumnType; +} + +export const entityMetadataTester = (entity: T, columns: ColumnMatcher[]): void => { + describe(`metadata of entity ${entity}`, () => { + let connection: Connection; + + beforeAll(async () => { + connection = await getInMemoryDb(entity); + }); + + afterAll(async () => { + await connection.close(); + }); + + it(`Should be configured properly`, () => { + const meta = connection.getMetadata(entity); + const matchers = columns.map((col) => { + return expect.objectContaining(col); + }); + expect(meta.columns).toHaveLength(columns.length); + expect(meta.columns).toEqual(expect.arrayContaining(matchers)); + }); + }); +}; diff --git a/test/common/testers/repositoryProviderTester.ts b/test/common/testers/repositoryProviderTester.ts new file mode 100644 index 00000000..19b70fff --- /dev/null +++ b/test/common/testers/repositoryProviderTester.ts @@ -0,0 +1,47 @@ +/* eslint-disable jest/no-export */ +/* eslint-disable @typescript-eslint/unbound-method */ +import { mockClear, mock } from 'jest-mock-extended'; +import { Connection } from 'typeorm/connection/Connection'; +import { Repository } from 'typeorm/repository/Repository'; +import { IEntity } from '../../../src/db/interfaces/IEntity'; +import { IConnectionProvider } from '../../../src/db'; +import { IRepositoryProvider } from '../../../src/db/interfaces/IRepositoryProvider'; + +export const testRepositoryProvider = >( + Entity: new () => E, + RepositoryProvider: new (connectionProvider: IConnectionProvider) => P +): void => { + describe(`${Entity} repository provider`, () => { + const repositoryMock = mock>(); + const connectionMock = mock(); + connectionMock.getRepository.mockReturnValue(repositoryMock); + const connectionProviderMock = mock(); + connectionProviderMock.getConnection.mockResolvedValue(connectionMock); + let dependencyRepositoryProvider: IRepositoryProvider; + + beforeEach(() => { + mockClear(connectionProviderMock); + mockClear(connectionMock); + mockClear(repositoryMock); + dependencyRepositoryProvider = new RepositoryProvider(connectionProviderMock); + }); + + it(`should cache repo`, async () => { + const repo = await dependencyRepositoryProvider.getRepository(); + const repo2 = await dependencyRepositoryProvider.getRepository(); + expect(repo).toBe(repo2); + expect(repo).toBe(repositoryMock); + expect(connectionMock.getRepository).toBeCalledTimes(1); + expect(connectionProviderMock.getConnection).toBeCalledTimes(1); + }); + + it(`should use connection properly`, async () => { + const repo = await dependencyRepositoryProvider.getRepository(); + expect(repo).toBe(repositoryMock); + expect(connectionProviderMock.getConnection).toBeCalledTimes(1); + expect(connectionMock.getRepository).toBeCalledTimes(1); + expect(connectionProviderMock.getConnection).toHaveBeenCalledWith(); + expect(connectionMock.getRepository).toHaveBeenCalledWith(Entity); + }); + }); +}; diff --git a/test/setup.ts b/test/setup.ts index 77f901ac..102a7cc0 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -1,4 +1,5 @@ import 'reflect-metadata'; +import 'array-flat-polyfill'; import { container } from '../src/container'; import { ILoggerSettings } from '../src/utils/logger'; import { loggerSettings } from './common/logger'; diff --git a/test/src/container/index.spec.ts b/test/src/container/index.spec.ts index 8754ef45..0236a788 100644 --- a/test/src/container/index.spec.ts +++ b/test/src/container/index.spec.ts @@ -2,10 +2,10 @@ import { container } from '../../../src/container'; import { IEntity } from '../../../src/db/interfaces/IEntity'; import { Dependency, DependencyVersion } from '../../../src/db'; import { ISpecificCIResolver } from '../../../src/resolvers/ciResolver'; -import { TravisCiResolver } from '../../../src/resolvers/ciResolver/impl/resolvers/travis'; -import { GithubActionsResolver } from '../../../src/resolvers/ciResolver/impl/resolvers/github'; -import { CircleCiResolver } from '../../../src/resolvers/ciResolver/impl/resolvers/circle'; -import { AppVeyorResolver } from '../../../src/resolvers/ciResolver/impl/resolvers/appveyor'; +import { GithubActionsResolver } from '../../../src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver'; +import { CircleCiResolver } from '../../../src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver'; +import { AppVeyorResolver } from '../../../src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver'; +import { TravisCiResolver } from '../../../src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver'; describe(`container`, () => { beforeEach(() => { @@ -18,15 +18,15 @@ describe(`container`, () => { it(`Should inject all entities`, () => { const entities = container.getAll(IEntity); - expect(entities.length).toBe(2); + expect(entities).toHaveLength(2); expect(entities).toContain(Dependency); expect(entities).toContain(DependencyVersion); }); it(`Should inject all specific ci resolvers`, () => { - const entities = container.getAll(ISpecificCIResolver); - expect(entities).toHaveLength(4); - expect(entities).toEqual( + const resolvers = container.getAll(ISpecificCIResolver); + expect(resolvers).toHaveLength(4); + expect(resolvers).toEqual( expect.arrayContaining([ expect.any(AppVeyorResolver), expect.any(CircleCiResolver), diff --git a/test/src/container/nodeModulesContainer.spec.ts b/test/src/container/nodeModulesContainer.spec.ts index f3a40b53..0f070e15 100644 --- a/test/src/container/nodeModulesContainer.spec.ts +++ b/test/src/container/nodeModulesContainer.spec.ts @@ -4,7 +4,7 @@ import * as simplegit from 'simple-git/promise'; import * as pacote from 'pacote'; import * as spawn from 'cross-spawn'; import axios from 'axios'; -import { BindingTypes, testBindings } from '../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../common/testers/bindingTester'; import { nodeModulesBinder, TYPES } from '../../../src/container/nodeModulesContainer'; testBindings({ diff --git a/test/src/container/utils.spec.ts b/test/src/container/utils.spec.ts new file mode 100644 index 00000000..434c429c --- /dev/null +++ b/test/src/container/utils.spec.ts @@ -0,0 +1,11 @@ +import { testNameOrMultiConstraint } from '../../common/testers/bindingTester'; +import { namedOrMultiConstraint } from '../../../src/container/utils'; + +describe(`utils`, () => { + describe(`namedOrMultiConstraint`, () => { + abstract class AClass {} + const symbol = Symbol.for(`dep-name`); + const constraint = namedOrMultiConstraint(symbol, AClass); + testNameOrMultiConstraint(symbol, AClass, constraint); + }); +}); diff --git a/test/src/db/entities/dependency.spec.ts b/test/src/db/entities/dependency.spec.ts index b6aa4ad0..d0b8a5b4 100644 --- a/test/src/db/entities/dependency.spec.ts +++ b/test/src/db/entities/dependency.spec.ts @@ -1,6 +1,39 @@ import { Dependency } from '../../../../src/db'; - +import { entityMetadataTester } from '../../../common/testers/entityMetadataTester'; describe(`dependency entity`, () => { + entityMetadataTester(Dependency, [ + { + databaseName: `reason`, + isPrimary: false, + isNullable: true, + type: `text` as const, + }, + { + databaseName: `match`, + isPrimary: false, + isNullable: true, + type: `boolean` as const, + }, + { + databaseName: `targetNode`, + isPrimary: true, + isNullable: false, + type: `text` as const, + }, + { + databaseName: `version`, + isPrimary: true, + isNullable: false, + type: `text` as const, + }, + { + databaseName: `name`, + isPrimary: true, + isNullable: false, + type: `text` as const, + }, + ]); + it(`should set properties from constructor`, async () => { const dependency = new Dependency({ targetNode: `12`, @@ -15,6 +48,7 @@ describe(`dependency entity`, () => { expect(dependency.reason).toBeUndefined(); expect(dependency.match).toBeUndefined(); }); + it(`should set properties from constructor full`, async () => { const dependency = new Dependency({ targetNode: `12`, @@ -29,6 +63,7 @@ describe(`dependency entity`, () => { expect(dependency.reason).toBe(`my reason`); expect(dependency.match).toBe(true); }); + it(`should set properties from constructor full 2`, async () => { const dependency = new Dependency({ targetNode: `12`, @@ -43,6 +78,7 @@ describe(`dependency entity`, () => { expect(dependency.reason).toBe(`my reason`); expect(dependency.match).toBe(false); }); + it(`should work with empty constructor`, async () => { const dependency = new Dependency(); expect(dependency.targetNode).toBeUndefined(); diff --git a/test/src/db/entities/dependencyVersion.spec.ts b/test/src/db/entities/dependencyVersion.spec.ts index b0d43435..e335a6fc 100644 --- a/test/src/db/entities/dependencyVersion.spec.ts +++ b/test/src/db/entities/dependencyVersion.spec.ts @@ -1,7 +1,69 @@ import { DependencyVersion } from '../../../../src/db'; import moment = require('moment'); +import { entityMetadataTester } from '../../../common/testers/entityMetadataTester'; +import { Connection } from 'typeorm'; +import { getInMemoryDb } from '../../../common/inMemoryDb'; describe(`dependencyVersion entity`, () => { + entityMetadataTester(DependencyVersion, [ + { + databaseName: `releaseDate`, + isPrimary: false, + isNullable: false, + type: `text` as const, + }, + { + databaseName: `commitSha`, + isPrimary: false, + isNullable: true, + type: `text` as const, + }, + { + databaseName: `repoUrl`, + isPrimary: false, + isNullable: false, + type: `text` as const, + }, + { + databaseName: `version`, + isPrimary: true, + isNullable: false, + type: `text` as const, + }, + { + databaseName: `name`, + isPrimary: true, + isNullable: false, + type: `text` as const, + }, + ]); + + let connection: Connection; + + beforeAll(async () => { + connection = await getInMemoryDb(DependencyVersion); + }); + + afterAll(async () => { + await connection.close(); + }); + + it(`should properly transform releaseDate field`, async () => { + const dateFormat = `YYYY-MM-DD`; + const releaseDate = moment.utc(`2015-10-02`, dateFormat); + const dependencyVersion = new DependencyVersion({ + version: `4.0.1`, + name: `test dependency`, + repoUrl: `https://www.github.com/example/test.git`, + commitSha: `595e42ff-1a21-4c99-a0c9-f5ddbadbdce4`, + releaseDate, + }); + const repo = connection.getRepository(DependencyVersion); + await repo.save(dependencyVersion); + const entity = await repo.findOne(); + expect(entity).toEqual(dependencyVersion); + }); + it(`should set properties from constructor`, async () => { const releaseDate = moment.utc(); const dependency = new DependencyVersion({ diff --git a/test/src/db/impl/connectionProvider.spec.ts b/test/src/db/impl/connectionProvider.spec.ts index 0ff8d38c..5c4df509 100644 --- a/test/src/db/impl/connectionProvider.spec.ts +++ b/test/src/db/impl/connectionProvider.spec.ts @@ -1,25 +1,21 @@ -import Mock = jest.Mock; import * as path from 'path'; import { Dependency, DependencyVersion, IConnectionSettings } from '../../../../src/db'; import * as tmp from 'tmp'; import { ConnectionProvider } from '../../../../src/db/impl/connectionProvider'; import { TypeOrm } from '../../../../src/container/nodeModulesContainer'; +import { loggerFactory } from '../../../common/logger'; +import { mock, mockClear } from 'jest-mock-extended'; +import { Connection } from 'typeorm'; describe(`connection provider`, () => { - const placeholder = `PLACEHOLDER`; - let tmpDir: string; - - let createConnectionMock: Mock; - let typeOrmSpy: TypeOrm; + const connectionMock = mock(); + const typeOrmMock = mock(); + typeOrmMock.createConnection.mockResolvedValue(connectionMock); beforeEach(() => { tmpDir = tmp.dirSync().name; - createConnectionMock = jest.fn(); - createConnectionMock.mockResolvedValue(placeholder); - typeOrmSpy = ({ - createConnection: createConnectionMock, - } as any) as TypeOrm; + mockClear(typeOrmMock); }); it(`should call createConnection properly`, async () => { @@ -27,11 +23,11 @@ describe(`connection provider`, () => { databaseFilePath: tmpDir, dropSchema: false, }; - const connectionProvider = new ConnectionProvider(settings, typeOrmSpy, [Dependency]); + const connectionProvider = new ConnectionProvider(settings, typeOrmMock, [Dependency], loggerFactory); const conn = await connectionProvider.getConnection(); - expect(conn).toBe(placeholder); - expect(createConnectionMock).toBeCalledTimes(1); - expect(createConnectionMock).toHaveBeenCalledWith({ + expect(conn).toBe(connectionMock); + expect(typeOrmMock.createConnection).toBeCalledTimes(1); + expect(typeOrmMock.createConnection).toHaveBeenCalledWith({ name: tmpDir, type: `sqlite`, database: path.join(tmpDir, `cache.db`), @@ -47,11 +43,11 @@ describe(`connection provider`, () => { databaseFilePath: tmpDir, dropSchema: true, }; - const connectionProvider = new ConnectionProvider(settings, typeOrmSpy, [DependencyVersion]); + const connectionProvider = new ConnectionProvider(settings, typeOrmMock, [DependencyVersion], loggerFactory); const conn = await connectionProvider.getConnection(); - expect(conn).toBe(placeholder); - expect(createConnectionMock).toBeCalledTimes(1); - expect(createConnectionMock).toHaveBeenCalledWith({ + expect(conn).toBe(connectionMock); + expect(typeOrmMock.createConnection).toBeCalledTimes(1); + expect(typeOrmMock.createConnection).toHaveBeenCalledWith({ name: tmpDir, type: `sqlite`, database: path.join(tmpDir, `cache.db`), @@ -67,11 +63,11 @@ describe(`connection provider`, () => { databaseFilePath: tmpDir, dropSchema: false, }; - const connectionProvider = new ConnectionProvider(settings, typeOrmSpy, []); + const connectionProvider = new ConnectionProvider(settings, typeOrmMock, [], loggerFactory); const conn = await connectionProvider.getConnection(); const conn2 = await connectionProvider.getConnection(); expect(conn).toBe(conn2); - expect(conn).toBe(placeholder); - expect(createConnectionMock).toBeCalledTimes(1); + expect(conn).toBe(connectionMock); + expect(typeOrmMock.createConnection).toBeCalledTimes(1); }); }); diff --git a/test/src/db/impl/dependencyRepositoryProvider.spec.ts b/test/src/db/impl/dependencyRepositoryProvider.spec.ts index 56d6594b..cc5ba0d8 100644 --- a/test/src/db/impl/dependencyRepositoryProvider.spec.ts +++ b/test/src/db/impl/dependencyRepositoryProvider.spec.ts @@ -1,43 +1,5 @@ -import { Dependency, IConnectionProvider } from '../../../../src/db'; +import { Dependency } from '../../../../src/db'; import { DependencyRepositoryProvider } from '../../../../src/db/impl/dependencyRepositoryProvider'; -import { Connection } from 'typeorm/connection/Connection'; +import { testRepositoryProvider } from '../../../common/testers/repositoryProviderTester'; -describe(`dependency repository provider`, () => { - const placeholder = `PLACEHOLDER`; - - const getRepositoryMock = jest.fn(); - const connectionSpy = ({ - getRepository: getRepositoryMock, - } as any) as Connection; - const getConnectionMock = jest.fn(); - const connectionProviderSpy = ({ - getConnection: getConnectionMock, - } as any) as IConnectionProvider; - let dependencyRepositoryProvider: DependencyRepositoryProvider; - - beforeEach(() => { - dependencyRepositoryProvider = new DependencyRepositoryProvider(connectionProviderSpy); - getConnectionMock.mockReset(); - getConnectionMock.mockResolvedValue(connectionSpy); - getRepositoryMock.mockReset(); - getRepositoryMock.mockReturnValue(placeholder); - }); - - it(`should cache repo`, async () => { - const repo = await dependencyRepositoryProvider.getRepository(); - const repo2 = await dependencyRepositoryProvider.getRepository(); - expect(repo).toEqual(repo2); - expect(repo).toBe(placeholder); - expect(getConnectionMock).toBeCalledTimes(1); - expect(getRepositoryMock).toBeCalledTimes(1); - }); - - it(`should use connection properly`, async () => { - const repo = await dependencyRepositoryProvider.getRepository(); - expect(repo).toBe(placeholder); - expect(getConnectionMock).toBeCalledTimes(1); - expect(getRepositoryMock).toBeCalledTimes(1); - expect(getConnectionMock).toHaveBeenCalledWith(); - expect(getRepositoryMock).toHaveBeenCalledWith(Dependency); - }); -}); +testRepositoryProvider(Dependency, DependencyRepositoryProvider); diff --git a/test/src/db/impl/dependencyVersionRepositoryProvider.spec.ts b/test/src/db/impl/dependencyVersionRepositoryProvider.spec.ts index 82fcb444..c4929586 100644 --- a/test/src/db/impl/dependencyVersionRepositoryProvider.spec.ts +++ b/test/src/db/impl/dependencyVersionRepositoryProvider.spec.ts @@ -1,42 +1,5 @@ -import { DependencyVersion, IConnectionProvider } from '../../../../src/db'; -import { Connection } from 'typeorm/connection/Connection'; +import { DependencyVersion } from '../../../../src/db'; import { DependencyVersionRepositoryProvider } from '../../../../src/db/impl/dependencyVersionRepositoryProvider'; +import { testRepositoryProvider } from '../../../common/testers/repositoryProviderTester'; -describe(`dependency version repository provider`, () => { - const placeholder = `PLACEHOLDER`; - const getRepositoryMock = jest.fn(); - const connectionSpy = ({ - getRepository: getRepositoryMock, - } as any) as Connection; - const getConnectionMock = jest.fn(); - const connectionProviderSpy = ({ - getConnection: getConnectionMock, - } as any) as IConnectionProvider; - let dependencyVersionRepositoryProvider: DependencyVersionRepositoryProvider; - - beforeEach(() => { - dependencyVersionRepositoryProvider = new DependencyVersionRepositoryProvider(connectionProviderSpy); - getConnectionMock.mockReset(); - getConnectionMock.mockResolvedValue(connectionSpy); - getRepositoryMock.mockReset(); - getRepositoryMock.mockReturnValue(placeholder); - }); - - it(`should cache repo`, async () => { - const repo = await dependencyVersionRepositoryProvider.getRepository(); - const repo2 = await dependencyVersionRepositoryProvider.getRepository(); - expect(repo).toEqual(repo2); - expect(repo).toBe(placeholder); - expect(getConnectionMock).toBeCalledTimes(1); - expect(getRepositoryMock).toBeCalledTimes(1); - }); - - it(`should use connection properly`, async () => { - const repo = await dependencyVersionRepositoryProvider.getRepository(); - expect(repo).toBe(placeholder); - expect(getConnectionMock).toBeCalledTimes(1); - expect(getRepositoryMock).toBeCalledTimes(1); - expect(getConnectionMock).toHaveBeenCalledWith(); - expect(getRepositoryMock).toHaveBeenCalledWith(DependencyVersion); - }); -}); +testRepositoryProvider(DependencyVersion, DependencyVersionRepositoryProvider); diff --git a/test/src/db/index.spec.ts b/test/src/db/index.spec.ts index 4eb98150..c22a3327 100644 --- a/test/src/db/index.spec.ts +++ b/test/src/db/index.spec.ts @@ -2,6 +2,7 @@ import { dbModulesBinder, Dependency, DependencyVersion, + EntitiesTags, IConnectionProvider, IDependencyRepositoryProvider, IDependencyVersionRepositoryProvider, @@ -10,7 +11,7 @@ import { DependencyRepositoryProvider } from '../../../src/db/impl/dependencyRep import { DependencyVersionRepositoryProvider } from '../../../src/db/impl/dependencyVersionRepositoryProvider'; import { ConnectionProvider } from '../../../src/db/impl/connectionProvider'; import { IEntity } from '../../../src/db/interfaces/IEntity'; -import { BindingTypes, testBindings } from '../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../common/testers/bindingTester'; testBindings({ name: `db module container`, @@ -35,11 +36,15 @@ testBindings({ binder: IEntity, binded: Dependency, type: BindingTypes.CONSTANT, + multi: true, + tag: EntitiesTags.dependency, }, { binder: IEntity, binded: DependencyVersion, type: BindingTypes.CONSTANT, + multi: true, + tag: EntitiesTags.dependencyVersion, }, ], }); diff --git a/test/src/dependencyChecker/index.spec.ts b/test/src/dependencyChecker/index.spec.ts new file mode 100644 index 00000000..63025c5a --- /dev/null +++ b/test/src/dependencyChecker/index.spec.ts @@ -0,0 +1,15 @@ +import { BindingTypes, testBindings } from '../../common/testers/bindingTester'; +import { dependencyCheckerModulesBinder, IDependencyChecker } from '../../../src/dependencyChecker'; +import { DependencyChecker } from '../../../src/dependencyChecker/impl/dependencyChecker'; + +testBindings({ + name: `dependency checker module container`, + binderFn: dependencyCheckerModulesBinder, + bindings: [ + { + binder: IDependencyChecker, + binded: DependencyChecker, + type: BindingTypes.SINGELTON, + }, + ], +}); diff --git a/test/src/flow/index.spec.ts b/test/src/flow/index.spec.ts deleted file mode 100644 index 18936f8a..00000000 --- a/test/src/flow/index.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { BindingTypes, testBindings } from '../../common/bindingTester'; -import { flowModulesBinder, IFlow } from '../../../src/flow'; -import { Flow } from '../../../src/flow/impl/flow'; - -testBindings({ - name: `flow module container`, - binderFn: flowModulesBinder, - bindings: [ - { - binder: IFlow, - binded: Flow, - type: BindingTypes.SINGELTON, - }, - ], -}); diff --git a/test/src/resolvers/cacheResolver/impl/cacheResolver.spec.ts b/test/src/resolvers/cacheResolver/impl/cacheResolver.spec.ts index 29e0f248..488051bd 100644 --- a/test/src/resolvers/cacheResolver/impl/cacheResolver.spec.ts +++ b/test/src/resolvers/cacheResolver/impl/cacheResolver.spec.ts @@ -1,21 +1,17 @@ import { Dependency, IDependencyRepositoryProvider } from '../../../../../src/db'; import { Repository } from 'typeorm'; import { CacheResolver } from '../../../../../src/resolvers/cacheResolver/impl/cacheResolver'; +import { loggerFactory } from '../../../../common/logger'; +import { mock, mockReset } from 'jest-mock-extended'; describe(`cache resolver`, () => { - const findOneStub = jest.fn(); - const repositorySpy = ({ - findOne: findOneStub, - } as any) as Repository; - const dependencyRepositoryProviderSpy: IDependencyRepositoryProvider = { - getRepository: async (): Promise> => { - return repositorySpy; - }, - }; - const cacheResolver = new CacheResolver(dependencyRepositoryProviderSpy); + const repositoryMock = mock>(); + const dependencyRepositoryProviderMock = mock(); + dependencyRepositoryProviderMock.getRepository.mockResolvedValue(repositoryMock); + const cacheResolver = new CacheResolver(dependencyRepositoryProviderMock, loggerFactory); beforeEach(() => { - findOneStub.mockReset(); + mockReset(repositoryMock); }); it(`should resolve if match in cache`, async () => { @@ -26,7 +22,7 @@ describe(`cache resolver`, () => { name: `test dependency`, reason: `circleCi`, }); - findOneStub.mockResolvedValue(dependency); + repositoryMock.findOne.mockResolvedValue(dependency); const targetNode = `8`; const result = await cacheResolver.resolve({ repo: { @@ -35,13 +31,72 @@ describe(`cache resolver`, () => { }, targetNode, }); - expect(findOneStub).toHaveBeenCalledWith({ + expect(repositoryMock.findOne).toHaveBeenCalledWith({ name: dependency.name, version: dependency.version, targetNode, }); - expect(result.isMatch).toBe(true); - expect(result.resolverName).toBe(`circleCi (cache)`); + expect(result).toEqual({ + isMatch: true, + resolverName: `circleCi (cache)`, + result: true, + }); + }); + + it(`should resolve if no match in cache`, async () => { + const dependency = new Dependency({ + targetNode: `12`, + match: false, + version: `4.0.1`, + name: `test dependency`, + reason: `circleCi`, + }); + repositoryMock.findOne.mockResolvedValue(dependency); + const targetNode = `8`; + const result = await cacheResolver.resolve({ + repo: { + version: dependency.version, + name: dependency.name, + }, + targetNode, + }); + expect(repositoryMock.findOne).toHaveBeenCalledWith({ + name: dependency.name, + version: dependency.version, + targetNode, + }); + expect(result).toEqual({ + isMatch: true, + resolverName: `circleCi (cache)`, + result: false, + }); + }); + + it(`should not resolve if in cache without match result`, async () => { + const dependency = new Dependency({ + targetNode: `12`, + match: undefined, + version: `4.0.1`, + name: `test dependency`, + reason: `circleCi`, + }); + repositoryMock.findOne.mockResolvedValue(undefined); + const targetNode = `8`; + const result = await cacheResolver.resolve({ + repo: { + version: dependency.version, + name: dependency.name, + }, + targetNode, + }); + expect(repositoryMock.findOne).toHaveBeenCalledWith({ + name: dependency.name, + version: dependency.version, + targetNode, + }); + expect(result).toEqual({ + isMatch: false, + }); }); it(`should not resolve if not in cache`, async () => { @@ -52,7 +107,7 @@ describe(`cache resolver`, () => { name: `test dependency`, reason: `circleCi`, }); - findOneStub.mockResolvedValue(undefined); + repositoryMock.findOne.mockResolvedValue(undefined); const targetNode = `8`; const result = await cacheResolver.resolve({ repo: { @@ -61,12 +116,14 @@ describe(`cache resolver`, () => { }, targetNode, }); - expect(findOneStub).toHaveBeenCalledWith({ + expect(repositoryMock.findOne).toHaveBeenCalledWith({ name: dependency.name, version: dependency.version, targetNode, }); - expect(result.isMatch).toBe(false); + expect(result).toEqual({ + isMatch: false, + }); }); it(`should not resolve if cache error`, async () => { @@ -77,7 +134,7 @@ describe(`cache resolver`, () => { name: `test dependency`, reason: `circleCi`, }); - findOneStub.mockRejectedValue(new Error()); + repositoryMock.findOne.mockRejectedValue(new Error()); const targetNode = `8`; const result = await cacheResolver.resolve({ repo: { @@ -86,7 +143,7 @@ describe(`cache resolver`, () => { }, targetNode, }); - expect(findOneStub).toHaveBeenCalledWith({ + expect(repositoryMock.findOne).toHaveBeenCalledWith({ name: dependency.name, version: dependency.version, targetNode, diff --git a/test/src/resolvers/cacheResolver/index.spec.ts b/test/src/resolvers/cacheResolver/index.spec.ts index 395d53c4..c569c326 100644 --- a/test/src/resolvers/cacheResolver/index.spec.ts +++ b/test/src/resolvers/cacheResolver/index.spec.ts @@ -1,6 +1,6 @@ import { cacheResolveModulesBinder, ICacheResolver } from '../../../../src/resolvers/cacheResolver'; import { CacheResolver } from '../../../../src/resolvers/cacheResolver/impl/cacheResolver'; -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; testBindings({ name: `cache resolver module container`, diff --git a/test/src/resolvers/ciResolver/impl/ciResolver.spec.ts b/test/src/resolvers/ciResolver/impl/ciResolver.spec.ts new file mode 100644 index 00000000..1a4990e2 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/ciResolver.spec.ts @@ -0,0 +1,127 @@ +import { loggerFactory } from '../../../../common/logger'; +import { CiResolver } from '../../../../../src/resolvers/ciResolver/impl/ciResolver'; +import moment = require('moment'); +import { + ICIResolveOptions, + ISpecificCIResolver, + ISpecificCIResolverOptions, +} from '../../../../../src/resolvers/ciResolver'; +import { when } from 'jest-when'; +import { ISpecificCIResolverRunner } from '../../../../../src/resolvers/ciResolver/interfaces/ISpecificCIResolverRunner'; +import { mock, mockReset } from 'jest-mock-extended'; + +describe(`ci resolver`, () => { + const targetNode = `6`; + const repoPath = `repoPath`; + const packageReleaseDate = moment(); + const resolveOptions: ICIResolveOptions = { + targetNode, + repoPath, + packageReleaseDate, + }; + const resolveSpecificOptions: ISpecificCIResolverOptions = { + repoPath, + }; + const resolverName1 = `resolverName1`; + const resolverMock1 = mock({ + resolverName: resolverName1, + }); + const resolverName2 = `resolverName2`; + const resolverMock2 = mock({ + resolverName: resolverName2, + }); + const resolvers: ISpecificCIResolver[] = [resolverMock1, resolverMock2]; + const specificCIResolverRunnerMock = mock(); + const ciResolver = new CiResolver(resolvers, specificCIResolverRunnerMock, loggerFactory); + + beforeEach(() => { + mockReset(specificCIResolverRunnerMock); + mockReset(resolverMock1); + mockReset(resolverMock2); + }); + + it(`should resolve with 1 relevant and 1 matching`, async () => { + resolverMock1.isRelevant.mockResolvedValue(true); + resolverMock2.isRelevant.mockResolvedValue(false); + specificCIResolverRunnerMock.resolve.mockResolvedValue({ + isMatch: true, + resolverName: resolverName1, + }); + const result = await ciResolver.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: true, + resolverName: resolverName1, + }); + expect(resolverMock1.isRelevant).toHaveBeenCalledTimes(1); + expect(resolverMock1.isRelevant).toHaveBeenCalledWith(resolveSpecificOptions); + expect(resolverMock2.isRelevant).toHaveBeenCalledTimes(1); + expect(resolverMock2.isRelevant).toHaveBeenCalledWith(resolveSpecificOptions); + expect(specificCIResolverRunnerMock.resolve).toHaveBeenCalledTimes(1); + expect(specificCIResolverRunnerMock.resolve.mock.calls[0][0].resolver).toBe(resolverMock1); + expect(specificCIResolverRunnerMock.resolve.mock.calls[0][0]).toEqual(expect.objectContaining(resolveOptions)); + }); + + it(`should resolve with 2 relevant and 2 matching`, async () => { + resolverMock1.isRelevant.mockResolvedValue(true); + resolverMock2.isRelevant.mockResolvedValue(true); + specificCIResolverRunnerMock.resolve.mockResolvedValueOnce({ + isMatch: true, + resolverName: resolverName1, + }); + specificCIResolverRunnerMock.resolve.mockResolvedValueOnce({ + isMatch: true, + resolverName: resolverName2, + }); + const result = await ciResolver.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: true, + resolverName: resolverName1, + }); + expect(resolverMock1.isRelevant).toHaveBeenCalledTimes(1); + expect(resolverMock1.isRelevant).toHaveBeenCalledWith(resolveSpecificOptions); + expect(resolverMock2.isRelevant).toHaveBeenCalledTimes(1); + expect(resolverMock2.isRelevant).toHaveBeenCalledWith(resolveSpecificOptions); + expect(specificCIResolverRunnerMock.resolve).toHaveBeenCalledTimes(2); + expect(specificCIResolverRunnerMock.resolve.mock.calls[0][0].resolver).toBe(resolverMock1); + expect(specificCIResolverRunnerMock.resolve.mock.calls[0][0]).toEqual(expect.objectContaining(resolveOptions)); + expect(specificCIResolverRunnerMock.resolve.mock.calls[1][0].resolver).toBe(resolverMock2); + expect(specificCIResolverRunnerMock.resolve.mock.calls[1][0]).toEqual(expect.objectContaining(resolveOptions)); + }); + + it(`should resolve with 2 relevant and 1 matching`, async () => { + resolverMock1.isRelevant.mockResolvedValue(true); + resolverMock2.isRelevant.mockResolvedValue(true); + const resolveCallArgs1 = { + ...resolveOptions, + resolver: resolverMock1, + }; + const resolveCallArgs2 = { + ...resolveOptions, + resolver: resolverMock2, + }; + when(specificCIResolverRunnerMock.resolve) + .calledWith(resolveCallArgs1) + .mockResolvedValue({ + isMatch: false, + }) + .calledWith(resolveCallArgs2) + .mockResolvedValue({ + isMatch: true, + resolverName: resolverName2, + }); + const result = await ciResolver.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: true, + resolverName: resolverName2, + }); + expect(resolverMock1.isRelevant).toHaveBeenCalledTimes(1); + expect(resolverMock1.isRelevant).toHaveBeenCalledWith(resolveSpecificOptions); + expect(resolverMock2.isRelevant).toHaveBeenCalledTimes(1); + expect(resolverMock2.isRelevant).toHaveBeenCalledWith(resolveSpecificOptions); + expect(specificCIResolverRunnerMock.resolve).toHaveBeenCalledTimes(2); + expect(specificCIResolverRunnerMock.resolve.mock.calls[0][0].resolver).toBe(resolverMock1); + expect(specificCIResolverRunnerMock.resolve.mock.calls[0][0]).toEqual(expect.objectContaining(resolveOptions)); + expect(specificCIResolverRunnerMock.resolve.mock.calls[1][0].resolver).toBe(resolverMock2); + expect(specificCIResolverRunnerMock.resolve.mock.calls[1][0]).toEqual(expect.objectContaining(resolveOptions)); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/nvmHandler.spec.ts b/test/src/resolvers/ciResolver/impl/nvmHandler.spec.ts new file mode 100644 index 00000000..e1e431c6 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/nvmHandler.spec.ts @@ -0,0 +1,206 @@ +import { NvmHandler } from '../../../../../src/resolvers/ciResolver/impl/nvmHandler'; +import { mock, mockClear } from 'jest-mock-extended'; +import { ITargetMatcher } from '../../../../../src/resolvers/ciResolver'; +import { when } from 'jest-when'; + +describe(`nvm handler`, () => { + const stablePlaceholder = `__STABLE__`; + const latestLtsPlaceholder = `__LTS*__`; + const ltsPlaceholder = `__LTS__`; + const ltsVersion = `dummy`; + const targetMatcherMock = mock(); + targetMatcherMock.getStableVersionPlaceholder.mockReturnValue(stablePlaceholder); + targetMatcherMock.getLatestLtsVersionPlaceholder.mockReturnValue(latestLtsPlaceholder); + when(targetMatcherMock.getLtsVersionPlaceholder).calledWith({ codename: ltsVersion }).mockReturnValue(ltsPlaceholder); + const nvmHandler = new NvmHandler(targetMatcherMock); + beforeEach(() => { + mockClear(targetMatcherMock); + }); + + describe(`is nvm command`, () => { + it(`should realize nvm command`, () => { + expect(nvmHandler.isNvmCommand(`nvm install v12`)).toBe(true); + }); + + it(`should realize nvm command with env variable`, () => { + expect(nvmHandler.isNvmCommand(`nvm install $NODE`)).toBe(true); + }); + + it(`should realize nvm command in complex shell`, () => { + expect( + nvmHandler.isNvmCommand( + `if [ -n "\${NODE-}" ]; then . nvm.sh && set -ex && nvm install --latest-npm "\${NODE}" && npm --version; fi` + ) + ).toBe(true); + }); + + it(`should not realize nvm command when missing node version`, () => { + expect(nvmHandler.isNvmCommand(`nvm install`)).toBe(false); + }); + + it(`should not realize nvm sub command is wrong`, () => { + expect(nvmHandler.isNvmCommand(`nvm ls`)).toBe(false); + }); + + it(`should return false if not nvm command`, () => { + expect(nvmHandler.isNvmCommand(`bla install`)).toBe(false); + }); + }); + + describe(`get nvm version`, () => { + describe(`bad nvm command`, () => { + it(`should return nothing if not nvm command`, () => { + expect(nvmHandler.getNvmVersion(`bla install`, {})).toBe(undefined); + }); + + it(`should return nothing if not complete nvm command`, () => { + expect(nvmHandler.getNvmVersion(`nvm install`, {})).toBe(undefined); + }); + + it(`should fail if no matching env variable`, () => { + expect(nvmHandler.getNvmVersion(`nvm install $nodejs_version`, {})).toBe(undefined); + }); + }); + + describe(`with env`, () => { + it(`should return node version with env variable 1`, () => { + expect(nvmHandler.getNvmVersion(`nvm install "\${NODE}"`, { NODE: `12` })).toBe(`12`); + }); + + it(`should return node version with env variable 2`, () => { + expect(nvmHandler.getNvmVersion(`nvm install \${NODE}`, { NODE: `12` })).toBe(`12`); + }); + + it(`should return node version with env variable 3`, () => { + expect(nvmHandler.getNvmVersion(`nvm install \$NODE`, { NODE: `12` })).toBe(`12`); + }); + + it(`should return param specific lts node version using env`, () => { + expect(nvmHandler.getNvmVersion(`nvm install --lts=$NODE`, { NODE: `dummy` })).toBe(ltsPlaceholder); + }); + + it(`should return specific lts node version using env`, () => { + expect(nvmHandler.getNvmVersion(`nvm install lts/$NODE`, { NODE: `dummy` })).toBe(ltsPlaceholder); + }); + }); + + describe(`no env`, () => { + it(`should return node version with complex shell command`, () => { + expect( + nvmHandler.getNvmVersion( + `if [ -n "\${NODE-}" ]; then . nvm.sh && set -ex && nvm install --latest-npm "12" && npm --version; fi`, + {} + ) + ).toBe(`12`); + }); + + it(`should return node version with complex shell command 2`, () => { + expect( + nvmHandler.getNvmVersion( + `if [ -n "\${NODE-}" ]; then . nvm.sh && set -ex && nvm install --latest-npm "12" || npm --version; fi`, + {} + ) + ).toBe(`12`); + }); + + it(`should return node version with complex shell command 3`, () => { + expect( + nvmHandler.getNvmVersion( + `if [ -n "\${NODE-}" ]; then . nvm.sh && set -ex && nvm install --latest-npm "12" ; npm --version; fi`, + {} + ) + ).toBe(`12`); + }); + + it(`should return node version with multiple nvm commands`, () => { + expect(nvmHandler.getNvmVersion(`nvm ls && nvm install --latest-npm "12"`, {})).toBe(`12`); + }); + + it(`should return node version with multi line nvm command`, () => { + expect( + nvmHandler.getNvmVersion( + `echo "test" + nvm install --latest-npm "12" + echo "test2"`, + {} + ) + ).toBe(`12`); + }); + + it(`should return node version with latest npm`, () => { + expect(nvmHandler.getNvmVersion(`nvm install --latest-npm 12`, {})).toBe(`12`); + }); + + it(`should return plain node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install 12`, {})).toBe(`12`); + }); + + it(`should return full node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install 12.0.1`, {})).toBe(`12.0.1`); + }); + + it(`should return v prefixed node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install v12`, {})).toBe(`v12`); + }); + + it(`should return stable node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install node`, {})).toBe(stablePlaceholder); + }); + + it(`should return raw lts node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install --lts`, {})).toBe(latestLtsPlaceholder); + }); + + it(`should return param specific lts node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install --lts=dummy`, {})).toBe(ltsPlaceholder); + }); + + it(`should return param 2 specific lts node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install --lts dummy`, {})).toBe(ltsPlaceholder); + }); + + it(`should return latest lts node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install 'lts/*'`, {})).toBe(latestLtsPlaceholder); + }); + + it(`should return latest lts node version - single quote`, () => { + expect(nvmHandler.getNvmVersion(`nvm install 'lts/*'`, {})).toBe(latestLtsPlaceholder); + }); + + it(`should return latest lts node version - double quote`, () => { + expect(nvmHandler.getNvmVersion(`nvm install "lts/*"`, {})).toBe(latestLtsPlaceholder); + }); + + it(`should return specific lts node version`, () => { + expect(nvmHandler.getNvmVersion(`nvm install lts/dummy`, {})).toBe(ltsPlaceholder); + }); + + it(`should return specific lts node version - single quot`, () => { + expect(nvmHandler.getNvmVersion(`nvm install 'lts/dummy'`, {})).toBe(ltsPlaceholder); + }); + + it(`should return specific lts node version - double quot`, () => { + expect(nvmHandler.getNvmVersion(`nvm install "lts/dummy"`, {})).toBe(ltsPlaceholder); + }); + }); + }); + + describe(`get nvm versions`, () => { + it(`should return node versions with multiple environments`, () => { + expect(nvmHandler.getNvmVersions(`nvm install "\${NODE}"`, [{ NODE: `12` }, { NODE: `13` }])).toEqual( + new Set([`12`, `13`]) + ); + }); + it(`should return node versions with no environments`, () => { + expect(nvmHandler.getNvmVersions(`nvm install 12`, [])).toEqual(new Set([`12`])); + }); + }); + + describe(`get nvm versions from matrix`, () => { + it(`should return node versions with env matrix`, () => { + expect( + nvmHandler.getNvmVersionsFromMatrix(`nvm install "\${NODE}"`, { NODE: [`12`, `13`], BLA: [`1`, `2`, `3`] }) + ).toEqual(new Set([`12`, `13`])); + }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/appveyor.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/appveyor.spec.ts deleted file mode 100644 index b8c5f4a3..00000000 --- a/test/src/resolvers/ciResolver/impl/resolvers/appveyor.spec.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { AppVeyorResolver } from '../../../../../../src/resolvers/ciResolver/impl/resolvers/appveyor'; -import { FS } from '../../../../../../src/container/nodeModulesContainer'; -import { normalize } from 'path'; - -describe(`appveyor`, () => { - const repoPath = `placeholder`; - const fsMock = jest.fn(); - const fsSpy = ({ - promises: { - readFile: fsMock, - }, - } as any) as FS; - const appVeyorResolver = new AppVeyorResolver(fsSpy); - - beforeEach(() => { - fsMock.mockReset(); - }); - - it(`should expose the proper name`, async () => { - expect(appVeyorResolver.resolverName).toBe(`appVeyor`); - }); - - it(`should get file properly`, async () => { - fsMock.mockResolvedValue(` -environment: - matrix: - - nodejs_version: "4" - - nodejs_version: "6.12" - -install: - - ps: Install-Product node $env:nodejs_version - `); - await appVeyorResolver.resolve({ - repoPath, - }); - expect(fsMock).toBeCalledTimes(1); - expect(fsMock).toHaveBeenCalledWith(normalize(`placeholder/.appveyor.yml`), `utf-8`); - }); - - it(`should resolve node js from configuration matrix`, async () => { - fsMock.mockResolvedValue(` -environment: - matrix: - - nodejs_version: "4" - - nodejs_version: "6.12" - -install: - - ps: Install-Product node $env:nodejs_version - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`4`, `6.12`]); - }); - - it(`should return empty array if fails to find env element`, async () => { - fsMock.mockResolvedValue(` -install: - - ps: Install-Product node $env:nodejs_version - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([]); - }); - - it(`should return empty array if fails to find env variable`, async () => { - fsMock.mockResolvedValue(` -environment: - matrix: - - foo: 4 - -install: - - ps: Install-Product node $env:nodejs_version - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([]); - }); - - it(`should resolve node js from configuration environment`, async () => { - fsMock.mockResolvedValue(` -environment: - - nodejs_version: "4" - - nodejs_version: "6.12" - -install: - - ps: Install-Product node $env:nodejs_version - - sh: dummy command - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`4`, `6.12`]); - }); - - it(`should resolve node js from configuration environment with stack that has no node version in it`, async () => { - fsMock.mockResolvedValue(` -stack: mysql -environment: - - nodejs_version: "4" - - nodejs_version: "6.12" - -install: - - ps: Install-Product node $env:nodejs_version - - fds: dummy command - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`4`, `6.12`]); - }); - - it(`should resolve node js from ps install command`, async () => { - fsMock.mockResolvedValue(` -install: - - ps: Install-Product node 8.12 x64 - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`8.12`]); - }); - - it(`should resolve node js from cmd install command`, async () => { - fsMock.mockResolvedValue(` -install: - - cmd: powershell Install-Product node 8.12 x64 - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`8.12`]); - }); - - it(`should resolve node js from nvm install command`, async () => { - fsMock.mockResolvedValue(` -install: - - sh: nvm install 8.12 - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`8.12`]); - }); - - it(`should resolve node js from nvm install command and stack`, async () => { - fsMock.mockResolvedValue(` -stack: node 9 -install: - - sh: nvm install 8.12 - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`9`, `8.12`]); - }); - - it(`should resolve node js from stack configuration`, async () => { - fsMock.mockResolvedValue(` -stack: mysql, node 8.12 - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`8.12`]); - }); - - it(`should return empty array when failed to find install commands`, async () => { - fsMock.mockResolvedValue(` -install: - - ps: Install-Product bash - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([]); - }); - - it(`should return empty array when no install element`, async () => { - fsMock.mockResolvedValue(` -init: - - ps: Install-Product bash - `); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([]); - }); - - it(`should return undefined from non relevant repo`, async () => { - fsMock.mockRejectedValue(new Error()); - const versions = await appVeyorResolver.resolve({ - repoPath, - }); - expect(versions).toBeFalsy(); - }); -}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser.spec.ts new file mode 100644 index 00000000..440d55e1 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser.spec.ts @@ -0,0 +1,319 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable camelcase */ +import { loggerFactory } from '../../../../../../common/logger'; +import { AppVeyorConfigParser } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser'; +import { mock } from 'jest-mock-extended'; +import { NvmHandler } from '../../../../../../../src/resolvers/ciResolver/impl/nvmHandler'; +import { ITargetMatcher } from '../../../../../../../src/resolvers/ciResolver'; + +describe(`AppVeyor Config Parser`, () => { + const targetMatcherMock = mock(); + const nvmHandler = new NvmHandler(targetMatcherMock); + const appVeyorConfigParser = new AppVeyorConfigParser(nvmHandler, loggerFactory); + + it(`should resolve node js from configuration matrix`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + environment: { + matrix: [ + { + nodejs_version: 4, + }, + { + nodejs_version: `6.12`, + }, + ], + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`, `6.12`]) }); + }); + + it(`should return empty array if fails to find env element`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); + + it(`should return empty array if fails to find env variable`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + environment: { + matrix: [ + { + foo: 4, + }, + ], + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); + + it(`should find version if matrix exists and node js is in env`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + environment: { + nodejs_version: 4, + matrix: [ + { + foo: 4, + }, + ], + z: 1, + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`]) }); + }); + + it(`should find version if matrix exists and node js is in global env`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + environment: { + global: { + nodejs_version: 4, + }, + dummy: 1, + matrix: [ + { + foo: 4, + }, + ], + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`]) }); + }); + + it(`should find version if matrix does not exist and node js is in env`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + environment: { + nodejs_version: 4, + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`]) }); + }); + + it(`should resolve node js from configuration environment object`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + environment: { + nodejs_version: `4`, + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + { + sh: `dummy command`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`]) }); + }); + + it(`should resolve node js from configuration environment with stack that has no node version in it`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + stack: `mysql`, + environment: { + nodejs_version: `6.12`, + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + { + fds: `dummy command`, + }, + ], + z: 1, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`6.12`]) }); + }); + + it(`should resolve node js from configuration environment with empty stack`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + stack: null, + environment: { + nodejs_version: `6.12`, + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + { + fds: `dummy command`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`6.12`]) }); + }); + + it(`should resolve node js from ps install command`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + install: [ + { + ps: `Install-Product node 8.12 x64`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`8.12`]) }); + }); + + it(`should resolve node js from cmd install command`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + install: [ + { + cmd: `powershell Install-Product node 8.12 x64`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`8.12`]) }); + }); + + it(`should resolve node js from configuration matrix with nvm command`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + environment: { + matrix: [ + { + nodejs_version: 4, + }, + { + nodejs_version: `6.12`, + }, + ], + }, + install: [ + { + sh: `nvm install $env:nodejs_version`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`, `6.12`]) }); + }); + + it(`should resolve node js from nvm install command`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + install: [ + { + sh: `nvm install 8.12`, + dummy: `nvm install 9.12`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`8.12`]) }); + }); + + it(`should not resolve node js from faulty nvm install command`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + install: [ + { + sh: `nvm install --dummy`, + dummy: `nvm install 9.12`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set() }); + }); + + it(`should resolve node js from nvm install command and stack`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + stack: `node 9`, + install: [ + { + sh: `nvm install 8.12`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`9`, `8.12`]) }); + }); + + it(`should resolve node js from stack configuration`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + stack: `mysql, node 8.12`, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`8.12`]) }); + }); + + it(`should return empty array when failed to find install commands`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + stack: 5, + dummy: `node 9`, + install: [ + { + ps: `Install-Product bash`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); + + it(`should return empty array when no install element`, async () => { + const versions = await appVeyorConfigParser.parse({ + config: { + init: [ + { + ps: `Install-Product bash`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.spec.ts new file mode 100644 index 00000000..d5868746 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.spec.ts @@ -0,0 +1,91 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable camelcase */ +import { AppVeyorResolver } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver'; +import { FS } from '../../../../../../../src/container/nodeModulesContainer'; +import { normalize } from 'path'; +import { loggerFactory } from '../../../../../../common/logger'; +import { mock, mockDeep, mockReset } from 'jest-mock-extended'; +import { AppVeyorConfigParser } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser'; + +describe(`AppVeyor Resolver`, () => { + const repoPath = `placeholder`; + const fileModePlaceholder = 43345; + const fsMock = mockDeep({ + constants: { + R_OK: fileModePlaceholder, + }, + }); + const parserMock = mock(); + const appVeyorResolver = new AppVeyorResolver(fsMock, parserMock, loggerFactory); + + beforeEach(() => { + mockReset(parserMock); + mockReset(fsMock); + }); + + it(`should expose the proper name`, async () => { + expect(appVeyorResolver.resolverName).toBe(`AppVeyor`); + }); + + it(`should parser config`, async () => { + const parseResult = { nodeVersions: new Set([`4`, `6.12`]) }; + parserMock.parse.mockResolvedValue(parseResult); + fsMock.promises.readFile.mockResolvedValue(` +environment: + matrix: + - nodejs_version: "4" + - nodejs_version: "6.12" + +install: + - ps: Install-Product node $env:nodejs_version + `); + const versions = await appVeyorResolver.resolve({ + repoPath, + }); + expect(versions).toEqual(parseResult); + expect(fsMock.promises.readFile).toBeCalledTimes(1); + expect(fsMock.promises.readFile).toHaveBeenCalledWith(normalize(`placeholder/.appveyor.yml`), `utf-8`); + expect(parserMock.parse).toBeCalledTimes(1); + expect(parserMock.parse).toHaveBeenCalledWith({ + config: { + environment: { + matrix: [ + { + nodejs_version: `4`, + }, + { + nodejs_version: `6.12`, + }, + ], + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + ], + }, + }); + }); + + describe(`isRelevant`, () => { + it(`should return true from relevant repo`, async () => { + fsMock.promises.access.mockResolvedValue(undefined); + const result = await appVeyorResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + expect(fsMock.promises.access).toBeCalledTimes(1); + expect(fsMock.promises.access).toHaveBeenCalledWith(normalize(`placeholder/.appveyor.yml`), fileModePlaceholder); + }); + + it(`should return false from non relevant repo`, async () => { + fsMock.promises.access.mockRejectedValue(new Error()); + const result = await appVeyorResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + expect(fsMock.promises.access).toBeCalledTimes(1); + expect(fsMock.promises.access).toHaveBeenCalledWith(normalize(`placeholder/.appveyor.yml`), fileModePlaceholder); + }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser.spec.ts new file mode 100644 index 00000000..a3b28fe4 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser.spec.ts @@ -0,0 +1,251 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable camelcase */ +import { loggerFactory } from '../../../../../../common/logger'; +import { CircleCiConfigParser } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser'; +import { ITargetMatcher } from '../../../../../../../src/resolvers/ciResolver'; +import { mock, mockReset } from 'jest-mock-extended'; +import { when } from 'jest-when'; +import { NvmHandler } from '../../../../../../../src/resolvers/ciResolver/impl/nvmHandler'; + +describe(`CircleCi Config Parser`, () => { + const stablePlaceholder = `__STABLE__`; + const latestLtsPlaceholder = `__LTS*__`; + const ltsPlaceholder = `__LTS__`; + const targetMatcherMock = mock(); + const nvmHandler = new NvmHandler(targetMatcherMock); + const circleCiConfigParser = new CircleCiConfigParser(nvmHandler, targetMatcherMock, loggerFactory); + + beforeEach(() => { + mockReset(targetMatcherMock); + targetMatcherMock.getStableVersionPlaceholder.mockReturnValue(stablePlaceholder); + targetMatcherMock.getLatestLtsVersionPlaceholder.mockReturnValue(latestLtsPlaceholder); + when(targetMatcherMock.getLtsVersionPlaceholder).calledWith({ codename: `dummy` }).mockReturnValue(ltsPlaceholder); + }); + + it(`should resolve node js`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + version: 2.1, + commands: { + 'test-nodejs': { + steps: [ + { + run: { + name: `Versions`, + }, + }, + ], + }, + }, + jobs: { + 'node-v6': { + docker: [ + { + image: `circleci/node:10.16.3`, + }, + { + image: `mysql:9`, + }, + ], + steps: [`test-nodejs`], + }, + 'node-v8': { + docker: [ + { + image: `node:8`, + }, + ], + steps: [`test-nodejs`], + }, + }, + workflows: { + 'node-multi-build': { + jobs: [`node-v6`, `node-v8`], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`10`, `8`]) }); + }); + + it(`should resolve current node`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + jobs: { + 'node-v6': { + docker: [ + { + image: `circleci/node:current`, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([stablePlaceholder]) }); + }); + + it(`should resolve latest node`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + jobs: { + 'node-v6': { + docker: [ + { + image: `circleci/node:latest`, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([stablePlaceholder]) }); + }); + + it(`should resolve lts node`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + jobs: { + 'node-v6': { + docker: [ + { + image: `circleci/node:lts`, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([latestLtsPlaceholder]) }); + }); + + it(`should resolve lts code node`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + jobs: { + 'node-v6': { + docker: [ + { + image: `circleci/node:dummy`, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([ltsPlaceholder]) }); + }); + + it(`should not resolve node from faulty nvm command`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + jobs: { + node: { + steps: [ + { + run: `nvm install --dummy`, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); + + it(`should resolve node from nvm command`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + jobs: { + node: { + steps: [ + { + run: `nvm install 12`, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12`]) }); + }); + + it(`should resolve node from nvm command with env`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + jobs: { + environment: { + NODE: 12, + }, + node: { + environment: { + NODE: 14, + }, + steps: [ + { + run: `nvm install $NODE`, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12`, `14`]) }); + }); + + it(`should resolve node from nvm complex command`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + jobs: { + node: { + steps: [ + { + run: { + name: `dummy`, + command: `nvm install 12`, + }, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12`]) }); + }); + + it(`should return empty array when failed to find node js`, async () => { + const versions = await circleCiConfigParser.parse({ + config: { + version: 2.1, + commands: { + 'test-nodejs': { + steps: [ + { + run: { + name: `Versions`, + }, + }, + ], + }, + }, + jobs: { + 'node-v6': { + image: `node:9`, + docker: [ + { + image: `mysql:9`, + }, + ], + steps: [`test-nodejs`], + }, + }, + workflows: { + 'node-multi-build': { + jobs: [`node-v6`], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.spec.ts new file mode 100644 index 00000000..6da2de2f --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.spec.ts @@ -0,0 +1,183 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable camelcase */ +import { FS } from '../../../../../../../src/container/nodeModulesContainer'; +import { normalize } from 'path'; +import { loggerFactory } from '../../../../../../common/logger'; +import { CircleCiResolver } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver'; +import { when } from 'jest-when'; +import { mock, mockDeep, mockReset } from 'jest-mock-extended'; +import { CircleCiConfigParser } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser'; + +describe(`CircleCi Resolver`, () => { + const repoPath = `placeholder`; + const v1FilePlaceHolder = `version: v1`; + const v2FilePlaceHolder = `version: v2`; + const fileModePlaceholder = 43345; + const fsMock = mockDeep({ + constants: { + R_OK: fileModePlaceholder, + }, + }); + const parserMock = mock(); + const circleCiResolver = new CircleCiResolver(fsMock, parserMock, loggerFactory); + + beforeEach(() => { + mockReset(parserMock); + mockReset(fsMock); + }); + + it(`should expose the proper name`, async () => { + expect(circleCiResolver.resolverName).toBe(`CircleCi`); + }); + + describe(`parse`, () => { + it(`should parse v1 config`, async () => { + const parseResult = { nodeVersions: new Set([`4`, `6.12`]) }; + parserMock.parse.mockResolvedValue(parseResult); + when(fsMock.promises.readFile) + .calledWith(normalize(`placeholder/circle.yml`), `utf-8`) + .mockResolvedValue(v1FilePlaceHolder) + .calledWith(normalize(`placeholder/.circleci/config.yml`), `utf-8`) + .mockRejectedValue(new Error(`dummy`)); + const versions = await circleCiResolver.resolve({ + repoPath, + }); + expect(versions).toEqual(parseResult); + expect(fsMock.promises.readFile).toBeCalledTimes(2); + expect(fsMock.promises.readFile).toHaveBeenCalledWith(normalize(`placeholder/circle.yml`), `utf-8`); + expect(fsMock.promises.readFile).toHaveBeenCalledWith(normalize(`placeholder/.circleci/config.yml`), `utf-8`); + expect(parserMock.parse).toBeCalledTimes(1); + expect(parserMock.parse).toHaveBeenCalledWith({ + config: { + version: `v1`, + }, + }); + }); + + it(`should parse v2 config`, async () => { + const parseResult = { nodeVersions: new Set([`4`, `6.12`]) }; + parserMock.parse.mockResolvedValue(parseResult); + when(fsMock.promises.readFile) + .calledWith(normalize(`placeholder/circle.yml`), `utf-8`) + .mockRejectedValue(new Error(`dummy`)) + .calledWith(normalize(`placeholder/.circleci/config.yml`), `utf-8`) + .mockResolvedValue(v2FilePlaceHolder); + const versions = await circleCiResolver.resolve({ + repoPath, + }); + expect(versions).toEqual(parseResult); + expect(fsMock.promises.readFile).toBeCalledTimes(2); + expect(fsMock.promises.readFile).toHaveBeenCalledWith(normalize(`placeholder/circle.yml`), `utf-8`); + expect(fsMock.promises.readFile).toHaveBeenCalledWith(normalize(`placeholder/.circleci/config.yml`), `utf-8`); + expect(parserMock.parse).toBeCalledTimes(1); + expect(parserMock.parse).toHaveBeenCalledWith({ + config: { + version: `v2`, + }, + }); + }); + + it(`should parse both config files`, async () => { + const v1Config = { + config: { + version: `v1`, + }, + }; + const v2Config = { + config: { + version: `v2`, + }, + }; + when(parserMock.parse) + .calledWith(v1Config) + .mockResolvedValue({ nodeVersions: new Set([`4`]) }) + .calledWith(v2Config) + .mockResolvedValue({ nodeVersions: new Set([`6.12`]) }); + when(fsMock.promises.readFile) + .calledWith(normalize(`placeholder/circle.yml`), `utf-8`) + .mockResolvedValue(v1FilePlaceHolder) + .calledWith(normalize(`placeholder/.circleci/config.yml`), `utf-8`) + .mockResolvedValue(v2FilePlaceHolder); + const versions = await circleCiResolver.resolve({ + repoPath, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`, `6.12`]) }); + expect(fsMock.promises.readFile).toBeCalledTimes(2); + expect(fsMock.promises.readFile).toHaveBeenCalledWith(normalize(`placeholder/circle.yml`), `utf-8`); + expect(fsMock.promises.readFile).toHaveBeenCalledWith(normalize(`placeholder/.circleci/config.yml`), `utf-8`); + expect(parserMock.parse).toBeCalledTimes(2); + expect(parserMock.parse).toHaveBeenCalledWith(v1Config); + expect(parserMock.parse).toHaveBeenCalledWith(v2Config); + }); + }); + + describe(`isRelevant`, () => { + it(`should return true from relevant repo with both config files`, async () => { + when(fsMock.promises.access) + .calledWith(normalize(`placeholder/circle.yml`), fileModePlaceholder) + .mockResolvedValue(undefined) + .calledWith(normalize(`placeholder/.circleci/config.yml`), fileModePlaceholder) + .mockResolvedValue(undefined); + const result = await circleCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + expect(fsMock.promises.access).toBeCalledTimes(2); + expect(fsMock.promises.access).toHaveBeenCalledWith(normalize(`placeholder/circle.yml`), fileModePlaceholder); + expect(fsMock.promises.access).toHaveBeenCalledWith( + normalize(`placeholder/.circleci/config.yml`), + fileModePlaceholder + ); + }); + + it(`should return true from relevant repo with v1 config file`, async () => { + when(fsMock.promises.access) + .calledWith(normalize(`placeholder/circle.yml`), fileModePlaceholder) + .mockResolvedValue(undefined) + .calledWith(normalize(`placeholder/.circleci/config.yml`), fileModePlaceholder) + .mockRejectedValue(new Error()); + const result = await circleCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + expect(fsMock.promises.access).toBeCalledTimes(2); + expect(fsMock.promises.access).toHaveBeenCalledWith(normalize(`placeholder/circle.yml`), fileModePlaceholder); + expect(fsMock.promises.access).toHaveBeenCalledWith( + normalize(`placeholder/.circleci/config.yml`), + fileModePlaceholder + ); + }); + + it(`should return true from relevant repo with v2 config file`, async () => { + when(fsMock.promises.access) + .calledWith(normalize(`placeholder/circle.yml`), fileModePlaceholder) + .mockRejectedValue(new Error()) + .calledWith(normalize(`placeholder/.circleci/config.yml`), fileModePlaceholder) + .mockResolvedValue(undefined); + const result = await circleCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + expect(fsMock.promises.access).toBeCalledTimes(2); + expect(fsMock.promises.access).toHaveBeenCalledWith(normalize(`placeholder/circle.yml`), fileModePlaceholder); + expect(fsMock.promises.access).toHaveBeenCalledWith( + normalize(`placeholder/.circleci/config.yml`), + fileModePlaceholder + ); + }); + + it(`should return false from non relevant repo`, async () => { + fsMock.promises.access.mockRejectedValue(new Error()); + const result = await circleCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + expect(fsMock.promises.access).toBeCalledTimes(2); + expect(fsMock.promises.access).toHaveBeenCalledWith(normalize(`placeholder/circle.yml`), fileModePlaceholder); + expect(fsMock.promises.access).toHaveBeenCalledWith( + normalize(`placeholder/.circleci/config.yml`), + fileModePlaceholder + ); + }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser.spec.ts new file mode 100644 index 00000000..db6607a8 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser.spec.ts @@ -0,0 +1,175 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable camelcase */ +import { loggerFactory } from '../../../../../../common/logger'; +import { ITargetMatcher } from '../../../../../../../src/resolvers/ciResolver'; +import { mock, mockReset } from 'jest-mock-extended'; +import { when } from 'jest-when'; +import { NvmHandler } from '../../../../../../../src/resolvers/ciResolver/impl/nvmHandler'; +import { GithubActionsConfigParser } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser'; + +describe(`Github Actions Config Parser`, () => { + const stablePlaceholder = `__STABLE__`; + const latestLtsPlaceholder = `__LTS*__`; + const ltsPlaceholder = `__LTS__`; + const targetMatcherMock = mock(); + const nvmHandler = new NvmHandler(targetMatcherMock); + const githubActionsConfigParser = new GithubActionsConfigParser(nvmHandler, loggerFactory); + + beforeEach(() => { + mockReset(targetMatcherMock); + targetMatcherMock.getStableVersionPlaceholder.mockReturnValue(stablePlaceholder); + targetMatcherMock.getLatestLtsVersionPlaceholder.mockReturnValue(latestLtsPlaceholder); + when(targetMatcherMock.getLtsVersionPlaceholder).calledWith({ codename: `dummy` }).mockReturnValue(ltsPlaceholder); + }); + + it(`should resolve node from setup node step`, async () => { + const versions = await githubActionsConfigParser.parse({ + config: { + jobs: { + build: { + steps: [ + { + uses: `actions/setup-node@v1`, + with: { + 'node-version': `12.x`, + }, + }, + ], + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12.x`]) }); + }); + + it(`should resolve node from setup node step with matrix`, async () => { + const versions = await githubActionsConfigParser.parse({ + config: { + strategy: { + matrix: { + 'node-version': [6, `10.x`, `4.x`], + }, + }, + steps: [ + { + uses: `actions/setup-node@v1`, + with: { + 'node-version': `\${{ matrix.node-version }}`, + }, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4.x`, `6`, `10.x`]) }); + }); + + it(`should fail to resolve node from setup node step with no matching env variable`, async () => { + const versions = await githubActionsConfigParser.parse({ + config: { + strategy: { + matrix: { + 'node-version': [6, `10.x`, `4.x`], + }, + }, + steps: [ + { + uses: `actions/setup-node@v1`, + with: { + 'node-version': `\${{ matrix.node }}`, + }, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set() }); + }); + + it(`should resolve node from nvm command`, async () => { + const versions = await githubActionsConfigParser.parse({ + config: { + steps: [ + { + run: `nvm install 12`, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12`]) }); + }); + + it(`should resolve node from nvm command with env`, async () => { + const versions = await githubActionsConfigParser.parse({ + config: { + steps: [ + { + run: `nvm install $NODE`, + env: { + NODE: 12, + }, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12`]) }); + }); + + it(`should resolve node from nvm command with env 2`, async () => { + const versions = await githubActionsConfigParser.parse({ + config: { + steps: [ + { + run: `nvm install \${{ matrix.node }}`, + env: { + node: 12, + }, + }, + ], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12`]) }); + }); + + it(`should resolve node from nvm command with env and matrix`, async () => { + const versions = await githubActionsConfigParser.parse({ + config: { + strategy: { + matrix: { + b: 4, + NODE: [6], + include: [ + { + NODE: `10`, + DUMMY: `2`, + }, + ], + Z: 4, + }, + }, + jobs: { + test: { + steps: [ + { + run: `nvm install $NODE`, + env: { + NODE: 12, + }, + }, + { + run: `echo $NODE`, + env: { + NODE: 14, + }, + }, + ], + strategy: { + matrix: { + NODE: [8], + }, + }, + }, + }, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12`, `6`, `8`, `10`, `14`]) }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.spec.ts new file mode 100644 index 00000000..ea624590 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.spec.ts @@ -0,0 +1,171 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable camelcase */ +import { FS } from '../../../../../../../src/container/nodeModulesContainer'; +import { normalize } from 'path'; +import { loggerFactory } from '../../../../../../common/logger'; +import { mock, mockReset, mockDeep } from 'jest-mock-extended'; +import { GithubActionsResolver } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver'; +import { GithubActionsConfigParser } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser'; +import { when } from 'jest-when'; +import { Stats } from 'fs'; + +describe(`Github Actions Resolver`, () => { + const repoPath = `placeholder`; + const fileModePlaceholder = 43345; + const fsMock = mockDeep({ + constants: { + R_OK: fileModePlaceholder, + }, + }); + const statsMock = mock(); + const parserMock = mock(); + const githubActionsResolver = new GithubActionsResolver(fsMock, parserMock, loggerFactory); + + beforeEach(() => { + mockReset(parserMock); + mockReset(statsMock); + mockReset(fsMock); + }); + + it(`should expose the proper name`, async () => { + expect(githubActionsResolver.resolverName).toBe(`Github Actions`); + }); + + describe(`parse`, () => { + it(`should parse multiple config files`, async () => { + const config1FilePlaceHolder = `version: v1`; + const config2FilePlaceHolder = `version: v2`; + const config1FileName = `ci1.yml`; + const config2FileName = `ci2.yml`; + const config3FileName = `ci3.yml`; + const config1 = { + config: { + version: `v1`, + }, + }; + const config2 = { + config: { + version: `v2`, + }, + }; + when(parserMock.parse) + .calledWith(config1) + .mockResolvedValue({ nodeVersions: new Set([`4`]) }) + .calledWith(config2) + .mockResolvedValue({ nodeVersions: new Set([`6.12`]) }); + when(fsMock.promises.readdir) + // @ts-ignore + .calledWith(normalize(`placeholder/.github/workflows`)) + .mockResolvedValue([config1FileName, config2FileName, config3FileName, `nonYamlFile.sh`]); + when(fsMock.promises.readFile) + .calledWith(normalize(`placeholder/.github/workflows/${config1FileName}`), `utf-8`) + .mockResolvedValue(config1FilePlaceHolder) + .calledWith(normalize(`placeholder/.github/workflows/${config2FileName}`), `utf-8`) + .mockResolvedValue(config2FilePlaceHolder) + .calledWith(normalize(`placeholder/.github/workflows/${config3FileName}`), `utf-8`) + .mockRejectedValue(new Error(`dummy`)); + const versions = await githubActionsResolver.resolve({ + repoPath, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`, `6.12`]) }); + expect(fsMock.promises.readdir).toBeCalledTimes(1); + expect(fsMock.promises.readdir).toHaveBeenCalledWith(normalize(`placeholder/.github/workflows`)); + expect(fsMock.promises.readFile).toBeCalledTimes(3); + expect(fsMock.promises.readFile).toHaveBeenCalledWith( + normalize(`placeholder/.github/workflows/${config1FileName}`), + `utf-8` + ); + expect(fsMock.promises.readFile).toHaveBeenCalledWith( + normalize(`placeholder/.github/workflows/${config2FileName}`), + `utf-8` + ); + expect(fsMock.promises.readFile).toHaveBeenCalledWith( + normalize(`placeholder/.github/workflows/${config3FileName}`), + `utf-8` + ); + expect(parserMock.parse).toBeCalledTimes(2); + expect(parserMock.parse).toHaveBeenCalledWith(config1); + expect(parserMock.parse).toHaveBeenCalledWith(config2); + }); + }); + + describe(`isRelevant`, () => { + it(`should return true from relevant repo`, async () => { + when(fsMock.promises.access) + .calledWith(normalize(`placeholder/.github/workflows`), fileModePlaceholder) + .mockResolvedValue(undefined); + when(fsMock.promises.stat).calledWith(normalize(`placeholder/.github/workflows`)).mockResolvedValue(statsMock); + when(statsMock.isDirectory).calledWith().mockReturnValue(true); + const result = await githubActionsResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + expect(fsMock.promises.access).toBeCalledTimes(1); + expect(fsMock.promises.access).toHaveBeenCalledWith( + normalize(`placeholder/.github/workflows`), + fileModePlaceholder + ); + expect(fsMock.promises.stat).toBeCalledTimes(1); + expect(fsMock.promises.stat).toHaveBeenCalledWith(normalize(`placeholder/.github/workflows`)); + expect(statsMock.isDirectory).toBeCalledTimes(1); + }); + + it(`should return false from non relevant repo - workflows directory does not exist`, async () => { + when(fsMock.promises.access) + .calledWith(normalize(`placeholder/.github/workflows`), fileModePlaceholder) + .mockRejectedValue(new Error(`dummy`)); + const result = await githubActionsResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + expect(fsMock.promises.access).toBeCalledTimes(1); + expect(fsMock.promises.access).toHaveBeenCalledWith( + normalize(`placeholder/.github/workflows`), + fileModePlaceholder + ); + expect(fsMock.promises.stat).toBeCalledTimes(0); + expect(statsMock.isDirectory).toBeCalledTimes(0); + }); + + it(`should return false from non relevant repo - failed to stats directory`, async () => { + when(fsMock.promises.access) + .calledWith(normalize(`placeholder/.github/workflows`), fileModePlaceholder) + .mockResolvedValue(undefined); + when(fsMock.promises.stat) + .calledWith(normalize(`placeholder/.github/workflows`)) + .mockRejectedValue(new Error(`dummy`)); + const result = await githubActionsResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + expect(fsMock.promises.access).toBeCalledTimes(1); + expect(fsMock.promises.access).toHaveBeenCalledWith( + normalize(`placeholder/.github/workflows`), + fileModePlaceholder + ); + expect(fsMock.promises.stat).toBeCalledTimes(1); + expect(fsMock.promises.stat).toHaveBeenCalledWith(normalize(`placeholder/.github/workflows`)); + expect(statsMock.isDirectory).toBeCalledTimes(0); + }); + + it(`should return false from non relevant repo - workflows is not a directory`, async () => { + when(fsMock.promises.access) + .calledWith(normalize(`placeholder/.github/workflows`), fileModePlaceholder) + .mockResolvedValue(undefined); + when(fsMock.promises.stat).calledWith(normalize(`placeholder/.github/workflows`)).mockResolvedValue(statsMock); + when(statsMock.isDirectory).calledWith().mockReturnValue(false); + const result = await githubActionsResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + expect(fsMock.promises.access).toBeCalledTimes(1); + expect(fsMock.promises.access).toHaveBeenCalledWith( + normalize(`placeholder/.github/workflows`), + fileModePlaceholder + ); + expect(fsMock.promises.stat).toBeCalledTimes(1); + expect(fsMock.promises.stat).toHaveBeenCalledWith(normalize(`placeholder/.github/workflows`)); + expect(statsMock.isDirectory).toBeCalledTimes(1); + }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser.spec.ts new file mode 100644 index 00000000..07b58318 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser.spec.ts @@ -0,0 +1,222 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable camelcase */ +import { loggerFactory } from '../../../../../../common/logger'; +import { TravisCiConfigParser } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser'; +import { mock, mockReset } from 'jest-mock-extended'; +import { ITargetMatcher } from '../../../../../../../src/resolvers/ciResolver'; +import { when } from 'jest-when'; +import { NvmHandler } from '../../../../../../../src/resolvers/ciResolver/impl/nvmHandler'; + +describe(`Travis CI Config Parser`, () => { + const stablePlaceholder = `__STABLE__`; + const latestLtsPlaceholder = `__LTS*__`; + const ltsPlaceholder = `__LTS__`; + const ltsVersion = `lts/dummy`; + const targetMatcherMock = mock(); + const nvmHandler = new NvmHandler(targetMatcherMock); + const travisCiConfigParser = new TravisCiConfigParser(nvmHandler, targetMatcherMock, loggerFactory); + + beforeEach(() => { + mockReset(targetMatcherMock); + targetMatcherMock.getStableVersionPlaceholder.mockReturnValue(stablePlaceholder); + targetMatcherMock.getLatestLtsVersionPlaceholder.mockReturnValue(latestLtsPlaceholder); + when(targetMatcherMock.getLtsVersionPlaceholder).calledWith({ codename: `dummy` }).mockReturnValue(ltsPlaceholder); + }); + + it(`should resolve node js number`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + node_js: 8, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`8`]) }); + expect(targetMatcherMock.getStableVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + }); + + it(`should resolve node from nvm command (string)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + before_install: `nvm install 8`, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`8`]) }); + }); + + it(`should resolve node from nvm command (array)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + before_install: [`nvm install 8`], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`8`]) }); + }); + + it(`should resolve node from nvm command (env string)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + env: `NODE=6 BLA="foo bar"`, + before_install: [`nvm install "\${NODE}"`], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`6`]) }); + }); + + it(`should resolve node from nvm command (env array)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + env: [`NODE=6 BLA="foo bar"`], + before_install: [`nvm install "\${NODE}"`], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`6`]) }); + }); + + it(`should resolve node from nvm command (env matrix)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + env: [`NODE=6 BLA="foo bar"`, `NODE=8 BLA="foo bar"`], + before_install: [`nvm install "\${NODE}"`], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`6`, `8`]) }); + }); + + it(`should resolve node from nvm command (env matrix object)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + env: { matrix: [`NODE=6 BLA="foo bar"`, `NODE=8 BLA="foo bar"`] }, + before_install: [`nvm install "\${NODE}"`], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`6`, `8`]) }); + }); + + it(`should resolve node from nvm command (complex env matrix object)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + env: { matrix: [`NODE=6 BLA="foo bar"`, `NODE=8 BLA="foo bar" || X=1`] }, + before_install: [`nvm install "\${NODE}"`], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`6`, `8`]) }); + }); + + it(`should not resolve node from bad nvm command`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + before_install: [`nvm install --dummy`], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set() }); + }); + + it(`should resolve node js string`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + node_js: `8`, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([`8`]) }); + expect(targetMatcherMock.getStableVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + }); + + it(`should resolve node js stable`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + node_js: `stable`, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([stablePlaceholder]) }); + expect(targetMatcherMock.getStableVersionPlaceholder).toHaveBeenCalledTimes(1); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + }); + + it(`should resolve node js stable v2`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + node_js: `node`, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([stablePlaceholder]) }); + expect(targetMatcherMock.getStableVersionPlaceholder).toHaveBeenCalledTimes(1); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + }); + + it(`should resolve node js lts`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + node_js: ltsVersion, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([ltsPlaceholder]) }); + expect(targetMatcherMock.getStableVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledTimes(1); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledWith({ codename: `dummy` }); + }); + + it(`should resolve node js lts star`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + node_js: `lts/*`, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([latestLtsPlaceholder]) }); + expect(targetMatcherMock.getStableVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledTimes(1); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledTimes(0); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledWith(); + }); + + it(`should resolve node js list`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + node_js: [ltsVersion, `node`, `stable`, `8`, `lts/*`, 10], + }, + }); + expect(versions).toEqual({ + nodeVersions: new Set([ltsPlaceholder, stablePlaceholder, `8`, latestLtsPlaceholder, `10`]), + }); + expect(targetMatcherMock.getStableVersionPlaceholder).toHaveBeenCalledTimes(2); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledTimes(1); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledTimes(1); + expect(targetMatcherMock.getLtsVersionPlaceholder).toHaveBeenCalledWith({ codename: `dummy` }); + expect(targetMatcherMock.getStableVersionPlaceholder).toHaveBeenCalledWith(); + expect(targetMatcherMock.getLatestLtsVersionPlaceholder).toHaveBeenCalledWith(); + }); + + it(`should return empty array when failed to find node js`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + version: 2.1, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); + + it(`should return empty array when failed to find node js (bad formatting)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + version: 2.1, + node_js: [true], + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); + + it(`should return empty array when failed to find node js (bad formatting 2)`, async () => { + const versions = await travisCiConfigParser.parse({ + config: { + version: 2.1, + node_js: true, + }, + }); + expect(versions).toEqual({ nodeVersions: new Set([]) }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.spec.ts b/test/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.spec.ts new file mode 100644 index 00000000..58a784a5 --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.spec.ts @@ -0,0 +1,91 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* eslint-disable camelcase */ +import { FS } from '../../../../../../../src/container/nodeModulesContainer'; +import { normalize } from 'path'; +import { loggerFactory } from '../../../../../../common/logger'; +import { TravisCiResolver } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver'; +import { TravisCiConfigParser } from '../../../../../../../src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser'; +import { mock, mockReset, mockDeep } from 'jest-mock-extended'; + +describe(`Travis CI Resolver`, () => { + const repoPath = `placeholder`; + const fileModePlaceholder = 43345; + const fsMock = mockDeep({ + constants: { + R_OK: fileModePlaceholder, + }, + }); + const parserMock = mock(); + const travisCiResolver = new TravisCiResolver(fsMock, parserMock, loggerFactory); + + beforeEach(() => { + mockReset(parserMock); + mockReset(fsMock); + }); + + it(`should expose the proper name`, async () => { + expect(travisCiResolver.resolverName).toBe(`TravisCi`); + }); + + it(`should parser config`, async () => { + const parseResult = { nodeVersions: new Set([`4`, `6.12`]) }; + parserMock.parse.mockResolvedValue(parseResult); + fsMock.promises.readFile.mockResolvedValue(` +environment: + matrix: + - nodejs_version: "4" + - nodejs_version: "6.12" + +install: + - ps: Install-Product node $env:nodejs_version + `); + const versions = await travisCiResolver.resolve({ + repoPath, + }); + expect(versions).toEqual(parseResult); + expect(fsMock.promises.readFile).toBeCalledTimes(1); + expect(fsMock.promises.readFile).toHaveBeenCalledWith(normalize(`placeholder/.travis.yml`), `utf-8`); + expect(parserMock.parse).toBeCalledTimes(1); + expect(parserMock.parse).toHaveBeenCalledWith({ + config: { + environment: { + matrix: [ + { + nodejs_version: `4`, + }, + { + nodejs_version: `6.12`, + }, + ], + }, + install: [ + { + ps: `Install-Product node $env:nodejs_version`, + }, + ], + }, + }); + }); + + describe(`isRelevant`, () => { + it(`should return true from relevant repo`, async () => { + fsMock.promises.access.mockResolvedValue(undefined); + const result = await travisCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + expect(fsMock.promises.access).toBeCalledTimes(1); + expect(fsMock.promises.access).toHaveBeenCalledWith(normalize(`placeholder/.travis.yml`), fileModePlaceholder); + }); + + it(`should return false from non relevant repo`, async () => { + fsMock.promises.access.mockRejectedValue(new Error()); + const result = await travisCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + expect(fsMock.promises.access).toBeCalledTimes(1); + expect(fsMock.promises.access).toHaveBeenCalledWith(normalize(`placeholder/.travis.yml`), fileModePlaceholder); + }); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/specificCIResolverRunner.spec.ts b/test/src/resolvers/ciResolver/impl/specificCIResolverRunner.spec.ts new file mode 100644 index 00000000..9274977c --- /dev/null +++ b/test/src/resolvers/ciResolver/impl/specificCIResolverRunner.spec.ts @@ -0,0 +1,100 @@ +import { loggerFactory } from '../../../../common/logger'; +import moment = require('moment'); +import { SpecificCIResolverRunner } from '../../../../../src/resolvers/ciResolver/impl/specificCIResolverRunner'; +import { ISpecificCIResolverRunnerOptions } from '../../../../../src/resolvers/ciResolver/interfaces/ISpecificCIResolverRunner'; +import { + ISpecificCIResolver, + ISpecificCIResolverOptions, + ITargetMatcher, +} from '../../../../../src/resolvers/ciResolver'; +import { mock, mockReset } from 'jest-mock-extended'; + +describe(`specific ci resolver runner`, () => { + const targetNode = `6`; + const repoPath = `repoPath`; + const packageReleaseDate = moment(); + const matchingVersions = new Set([`1`, `2`]); + const resolverName = `resolverName`; + const resolverMock = mock({ + resolverName, + }); + const targetMatcherMock = mock(); + const specificCIResolverRunner = new SpecificCIResolverRunner(targetMatcherMock, loggerFactory); + const resolveOptions: ISpecificCIResolverRunnerOptions = { + targetNode, + repoPath, + packageReleaseDate, + resolver: resolverMock, + }; + const resolveSpecificOptions: ISpecificCIResolverOptions = { + repoPath, + }; + + beforeEach(() => { + mockReset(targetMatcherMock); + mockReset(resolverMock); + }); + + it(`should match`, async () => { + resolverMock.resolve.mockResolvedValue({ + nodeVersions: matchingVersions, + }); + targetMatcherMock.match.mockResolvedValue(true); + const result = await specificCIResolverRunner.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: true, + resolverName, + }); + expect(resolverMock.resolve).toHaveBeenCalledTimes(1); + expect(resolverMock.resolve).toHaveBeenCalledWith(resolveSpecificOptions); + expect(targetMatcherMock.match).toHaveBeenCalledTimes(1); + expect(targetMatcherMock.match).toHaveBeenCalledWith({ + candidates: matchingVersions, + targetNode, + packageReleaseDate, + }); + }); + + it(`should not match with with non matching node versions`, async () => { + resolverMock.resolve.mockResolvedValue({ + nodeVersions: matchingVersions, + }); + targetMatcherMock.match.mockResolvedValue(false); + const result = await specificCIResolverRunner.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: false, + }); + expect(resolverMock.resolve).toHaveBeenCalledTimes(1); + expect(resolverMock.resolve).toHaveBeenCalledWith(resolveSpecificOptions); + expect(targetMatcherMock.match).toHaveBeenCalledTimes(1); + expect(targetMatcherMock.match).toHaveBeenCalledWith({ + candidates: matchingVersions, + targetNode, + packageReleaseDate, + }); + }); + + it(`should not match with with not found node versions`, async () => { + resolverMock.resolve.mockResolvedValue({ + nodeVersions: new Set([]), + }); + const result = await specificCIResolverRunner.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: false, + }); + expect(resolverMock.resolve).toHaveBeenCalledTimes(1); + expect(resolverMock.resolve).toHaveBeenCalledWith(resolveSpecificOptions); + expect(targetMatcherMock.match).toHaveBeenCalledTimes(0); + }); + + it(`should not match with with resolver throws exception`, async () => { + resolverMock.resolve.mockRejectedValue(new Error(`dummy error`)); + const result = await specificCIResolverRunner.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: false, + }); + expect(resolverMock.resolve).toHaveBeenCalledTimes(1); + expect(resolverMock.resolve).toHaveBeenCalledWith(resolveSpecificOptions); + expect(targetMatcherMock.match).toHaveBeenCalledTimes(0); + }); +}); diff --git a/test/src/resolvers/ciResolver/impl/targetMatcher.spec.ts b/test/src/resolvers/ciResolver/impl/targetMatcher.spec.ts index b8787c8c..ef5dfe26 100644 --- a/test/src/resolvers/ciResolver/impl/targetMatcher.spec.ts +++ b/test/src/resolvers/ciResolver/impl/targetMatcher.spec.ts @@ -1,24 +1,23 @@ -import { ILts } from '../../../../../src/utils/lts'; +import { INodeVersions } from '../../../../../src/utils/nodeVersions'; import { TargetMatcher } from '../../../../../src/resolvers/ciResolver/impl/targetMatcher'; import moment = require('moment'); +import { loggerFactory } from '../../../../common/logger'; +import { mock, mockReset } from 'jest-mock-extended'; const dateFormat = `YYYY-MM-DD`; const packageReleaseDate = moment.utc(`2015-10-02`, dateFormat); describe(`target matcher`, () => { - const resolveLtsVersionMock = jest.fn(); - const ltsMock = ({ - resolveLtsVersion: resolveLtsVersionMock, - } as any) as ILts; - const targetMatcher = new TargetMatcher(ltsMock); + const nodeVersionsMock = mock(); + const targetMatcher = new TargetMatcher(nodeVersionsMock, loggerFactory); beforeEach(() => { - resolveLtsVersionMock.mockReset(); + mockReset(nodeVersionsMock); }); it(`should not match on invalid target version`, async () => { const promise = targetMatcher.match({ - candidates: [`8`], + candidates: new Set([`8`]), targetNode: `foo`, packageReleaseDate, }); @@ -26,60 +25,164 @@ describe(`target matcher`, () => { await expect(promise).rejects.toMatchObject({ message: expect.stringContaining(`Node target version foo is not valid`), }); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); }); it(`should not match on invalid version`, async () => { const result = await targetMatcher.match({ - candidates: [`foo`], + candidates: new Set([`foo`]), targetNode: `8`, packageReleaseDate, }); expect(result).toBe(false); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); }); it(`should match target node from candidates`, async () => { const result = await targetMatcher.match({ - candidates: [`6`, `8`, `10`, `11`], + candidates: new Set([`6`, `8`, `10`, `11`]), targetNode: `8`, packageReleaseDate, }); expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); + }); + + it(`should match target node from candidates while resolving stable`, async () => { + nodeVersionsMock.resolveStableVersion.mockResolvedValue(`6`); + const result = await targetMatcher.match({ + candidates: new Set([targetMatcher.getStableVersionPlaceholder(), `8`, `10`, `11`]), + targetNode: `6`, + packageReleaseDate, + }); + expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(1); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledWith({ + date: packageReleaseDate, + }); + }); + + it(`should match target node from candidates while resolving stable failed`, async () => { + nodeVersionsMock.resolveStableVersion.mockResolvedValue(undefined); + const result = await targetMatcher.match({ + candidates: new Set([targetMatcher.getStableVersionPlaceholder(), `8`, `10`, `11`]), + targetNode: `8`, + packageReleaseDate, + }); + expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(1); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledWith({ + date: packageReleaseDate, + }); + }); + + it(`should match target node from candidates while resolving latest lts`, async () => { + nodeVersionsMock.resolveLatestLtsVersion.mockResolvedValue(`6`); + const result = await targetMatcher.match({ + candidates: new Set([targetMatcher.getLatestLtsVersionPlaceholder(), `8`, `10`, `11`]), + targetNode: `6`, + packageReleaseDate, + }); + expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(1); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledWith({ + date: packageReleaseDate, + }); + }); + + it(`should match target node from candidates while resolving latest lts failed`, async () => { + nodeVersionsMock.resolveLatestLtsVersion.mockResolvedValue(undefined); + const result = await targetMatcher.match({ + candidates: new Set([targetMatcher.getLatestLtsVersionPlaceholder(), `8`, `10`, `11`]), + targetNode: `8`, + packageReleaseDate, + }); + expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(1); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledWith({ + date: packageReleaseDate, + }); }); it(`should match target node from candidates while resolving lts`, async () => { - resolveLtsVersionMock.mockResolvedValue([`4`, `6`]); + nodeVersionsMock.resolveLtsVersion.mockResolvedValue(`6`); const result = await targetMatcher.match({ - candidates: [`LTS_VERSION`, `8`, `10`, `11`], + candidates: new Set([targetMatcher.getLtsVersionPlaceholder({ codename: `born` }), `8`, `10`, `11`]), targetNode: `6`, packageReleaseDate, }); expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(1); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledWith({ + codename: `born`, + }); + }); + + it(`should match target node from candidates while resolving lts failed`, async () => { + nodeVersionsMock.resolveLtsVersion.mockResolvedValue(undefined); + const result = await targetMatcher.match({ + candidates: new Set([targetMatcher.getLtsVersionPlaceholder({ codename: `born` }), `8`, `10`, `11`]), + targetNode: `8`, + packageReleaseDate, + }); + expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(1); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledWith({ + codename: `born`, + }); }); it(`should match target node from complex candidates`, async () => { const result = await targetMatcher.match({ - candidates: [`6`, `8.14`, `10`, `11`], + candidates: new Set([`6`, `8.14`, `10`, `11`]), targetNode: `8`, packageReleaseDate, }); expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); }); it(`should match target node from complex candidates 2`, async () => { const result = await targetMatcher.match({ - candidates: [`6`, `8.x`, `10`, `11`], + candidates: new Set([`6`, `8.x`, `10`, `11`]), targetNode: `8`, packageReleaseDate, }); expect(result).toBe(true); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); }); it(`should not match target node from candidates`, async () => { const result = await targetMatcher.match({ - candidates: [`6`, `8.14`, `10`, `11`], + candidates: new Set([`6`, `8.14`, `10`, `11`]), targetNode: `4`, packageReleaseDate, }); expect(result).toBe(false); + expect(nodeVersionsMock.resolveStableVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLtsVersion).toHaveBeenCalledTimes(0); + expect(nodeVersionsMock.resolveLatestLtsVersion).toHaveBeenCalledTimes(0); }); }); diff --git a/test/src/resolvers/ciResolver/index.spec.ts b/test/src/resolvers/ciResolver/index.spec.ts index 2c8858ac..1d2dd719 100644 --- a/test/src/resolvers/ciResolver/index.spec.ts +++ b/test/src/resolvers/ciResolver/index.spec.ts @@ -1,24 +1,33 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; import { ciResolverModulesBinder, ICIResolver, ISpecificCIResolver, ITargetMatcher, + SpecificCIResolverTags, } from '../../../../src/resolvers/ciResolver'; import { CiResolver } from '../../../../src/resolvers/ciResolver/impl/ciResolver'; import { TargetMatcher } from '../../../../src/resolvers/ciResolver/impl/targetMatcher'; -import { TravisCiResolver } from '../../../../src/resolvers/ciResolver/impl/resolvers/travis'; -import { AppVeyorResolver } from '../../../../src/resolvers/ciResolver/impl/resolvers/appveyor'; -import { CircleCiResolver } from '../../../../src/resolvers/ciResolver/impl/resolvers/circle'; -import { GithubActionsResolver } from '../../../../src/resolvers/ciResolver/impl/resolvers/github'; +import { AppVeyorResolver } from '../../../../src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver'; +import { CircleCiResolver } from '../../../../src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver'; +import { GithubActionsResolver } from '../../../../src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver'; +import { ISpecificCIResolverRunner } from '../../../../src/resolvers/ciResolver/interfaces/ISpecificCIResolverRunner'; +import { SpecificCIResolverRunner } from '../../../../src/resolvers/ciResolver/impl/specificCIResolverRunner'; +import { AppVeyorConfigParser } from '../../../../src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorConfigParser'; +import { CircleCiConfigParser } from '../../../../src/resolvers/ciResolver/impl/resolvers/circleci/circleCiConfigParser'; +import { TravisCiConfigParser } from '../../../../src/resolvers/ciResolver/impl/resolvers/travisci/travisCiConfigParser'; +import { TravisCiResolver } from '../../../../src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver'; +import { GithubActionsConfigParser } from '../../../../src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsConfigParser'; +import { INvmHandler } from '../../../../src/resolvers/ciResolver/interfaces/INvmHandler'; +import { NvmHandler } from '../../../../src/resolvers/ciResolver/impl/nvmHandler'; testBindings({ name: `ci resolver module container`, binderFn: ciResolverModulesBinder, bindings: [ { - binder: ICIResolver, - binded: CiResolver, + binder: INvmHandler, + binded: NvmHandler, type: BindingTypes.SINGELTON, }, { @@ -26,25 +35,63 @@ testBindings({ binded: TargetMatcher, type: BindingTypes.SINGELTON, }, + { + binder: ISpecificCIResolverRunner, + binded: SpecificCIResolverRunner, + type: BindingTypes.SINGELTON, + }, + { + binder: ICIResolver, + binded: CiResolver, + type: BindingTypes.SINGELTON, + }, { binder: ISpecificCIResolver, binded: TravisCiResolver, type: BindingTypes.SINGELTON, + multi: true, + tag: SpecificCIResolverTags.travisCi, }, { binder: ISpecificCIResolver, binded: AppVeyorResolver, type: BindingTypes.SINGELTON, + multi: true, + tag: SpecificCIResolverTags.appVeyor, }, { binder: ISpecificCIResolver, binded: CircleCiResolver, type: BindingTypes.SINGELTON, + multi: true, + tag: SpecificCIResolverTags.circleCi, }, { binder: ISpecificCIResolver, binded: GithubActionsResolver, type: BindingTypes.SINGELTON, + multi: true, + tag: SpecificCIResolverTags.githubActions, + }, + { + binder: AppVeyorConfigParser, + binded: BindingTypes.SELF, + type: BindingTypes.SINGELTON, + }, + { + binder: CircleCiConfigParser, + binded: BindingTypes.SELF, + type: BindingTypes.SINGELTON, + }, + { + binder: TravisCiConfigParser, + binded: BindingTypes.SELF, + type: BindingTypes.SINGELTON, + }, + { + binder: GithubActionsConfigParser, + binded: BindingTypes.SELF, + type: BindingTypes.SINGELTON, }, ], }); diff --git a/test/src/resolvers/enginesResolver/impl/enginesResolver.spec.ts b/test/src/resolvers/enginesResolver/impl/enginesResolver.spec.ts index 919df53a..743688e3 100644 --- a/test/src/resolvers/enginesResolver/impl/enginesResolver.spec.ts +++ b/test/src/resolvers/enginesResolver/impl/enginesResolver.spec.ts @@ -96,4 +96,12 @@ describe(`engines resolver`, () => { }); expect(result.isMatch).toBe(true); }); + + it(`should match if in range hyphen (using loose mode)`, async () => { + const result = await enginesResolver.resolve({ + engines: `6.5.0a - 12`, + targetNode: `8`, + }); + expect(result.isMatch).toBe(true); + }); }); diff --git a/test/src/resolvers/enginesResolver/index.spec.ts b/test/src/resolvers/enginesResolver/index.spec.ts index fa8ff3fe..37cc6e19 100644 --- a/test/src/resolvers/enginesResolver/index.spec.ts +++ b/test/src/resolvers/enginesResolver/index.spec.ts @@ -1,4 +1,4 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; import { enginesResolveModulesBinder, IEnginesResolver } from '../../../../src/resolvers/enginesResolver'; import { EnginesResolver } from '../../../../src/resolvers/enginesResolver/impl/enginesResolver'; diff --git a/test/src/resolvers/testResolver/impl/testResolver.spec.ts b/test/src/resolvers/testResolver/impl/testResolver.spec.ts new file mode 100644 index 00000000..687932d0 --- /dev/null +++ b/test/src/resolvers/testResolver/impl/testResolver.spec.ts @@ -0,0 +1,84 @@ +import { TestResolver } from '../../../../../src/resolvers/testResolver/impl/testResolver'; +import { loggerFactory } from '../../../../common/logger'; +import { IYarn, IYarnOptions } from '../../../../../src/utils/yarn'; +import { ITestResolverOptions } from '../../../../../src/resolvers/testResolver'; +import { mock, mockReset } from 'jest-mock-extended'; + +describe(`test resolver`, () => { + const repoPath = `my-test-path`; + const resolverName = `yarn run test`; + const resolveOptions: ITestResolverOptions = { + repoPath, + }; + const yarnOptions: IYarnOptions = { + cwd: repoPath, + }; + const yarnMock = mock(); + const testResolver = new TestResolver(yarnMock, loggerFactory); + + beforeEach(() => { + mockReset(yarnMock); + }); + + it(`should run yarn flow correctly`, async () => { + yarnMock.install.mockResolvedValue(undefined); + yarnMock.build.mockResolvedValue(undefined); + yarnMock.test.mockResolvedValue(undefined); + const result = await testResolver.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: true, + resolverName, + }); + expect(yarnMock.install).toHaveBeenCalledTimes(1); + expect(yarnMock.install).toHaveBeenCalledWith(yarnOptions); + expect(yarnMock.build).toHaveBeenCalledTimes(1); + expect(yarnMock.build).toHaveBeenCalledWith(yarnOptions); + expect(yarnMock.test).toHaveBeenCalledTimes(1); + expect(yarnMock.test).toHaveBeenCalledWith(yarnOptions); + }); + + it(`should fail if yarn install failed`, async () => { + yarnMock.install.mockRejectedValue(new Error(`dummy error`)); + yarnMock.build.mockResolvedValue(undefined); + yarnMock.test.mockResolvedValue(undefined); + const result = await testResolver.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: false, + }); + expect(yarnMock.install).toHaveBeenCalledTimes(1); + expect(yarnMock.install).toHaveBeenCalledWith(yarnOptions); + expect(yarnMock.build).toHaveBeenCalledTimes(0); + expect(yarnMock.test).toHaveBeenCalledTimes(0); + }); + + it(`should fail if yarn build failed`, async () => { + yarnMock.build.mockRejectedValue(new Error(`dummy error`)); + yarnMock.install.mockResolvedValue(undefined); + yarnMock.test.mockResolvedValue(undefined); + const result = await testResolver.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: false, + }); + expect(yarnMock.install).toHaveBeenCalledTimes(1); + expect(yarnMock.install).toHaveBeenCalledWith(yarnOptions); + expect(yarnMock.build).toHaveBeenCalledTimes(1); + expect(yarnMock.build).toHaveBeenCalledWith(yarnOptions); + expect(yarnMock.test).toHaveBeenCalledTimes(0); + }); + + it(`should fail if yarn test failed`, async () => { + yarnMock.test.mockRejectedValue(new Error(`dummy error`)); + yarnMock.install.mockResolvedValue(undefined); + yarnMock.build.mockResolvedValue(undefined); + const result = await testResolver.resolve(resolveOptions); + expect(result).toEqual({ + isMatch: false, + }); + expect(yarnMock.install).toHaveBeenCalledTimes(1); + expect(yarnMock.install).toHaveBeenCalledWith(yarnOptions); + expect(yarnMock.build).toHaveBeenCalledTimes(1); + expect(yarnMock.build).toHaveBeenCalledWith(yarnOptions); + expect(yarnMock.test).toHaveBeenCalledTimes(1); + expect(yarnMock.test).toHaveBeenCalledWith(yarnOptions); + }); +}); diff --git a/test/src/resolvers/testResolver/index.spec.ts b/test/src/resolvers/testResolver/index.spec.ts index 6b38f9c5..672caa4d 100644 --- a/test/src/resolvers/testResolver/index.spec.ts +++ b/test/src/resolvers/testResolver/index.spec.ts @@ -1,4 +1,4 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; import { ITestResolver, testResolverModulesBinder } from '../../../../src/resolvers/testResolver'; import { TestResolver } from '../../../../src/resolvers/testResolver/impl/testResolver'; diff --git a/test/src/utils/commandRunner/impl/commandRunner.spec.ts b/test/src/utils/commandRunner/impl/commandRunner.spec.ts index e33eb396..79197a25 100644 --- a/test/src/utils/commandRunner/impl/commandRunner.spec.ts +++ b/test/src/utils/commandRunner/impl/commandRunner.spec.ts @@ -3,63 +3,45 @@ import { CommandRunner } from '../../../../../src/utils/commandRunner/impl/comma import mockSpawn = require('mock-spawn'); import { ILoggerFactory } from '../../../../../src/utils/logger'; import { ILogger } from '../../../../../src/utils/logger/interfaces/ILogger'; +import { mock, mockReset } from 'jest-mock-extended'; +import { Spawn } from '../../../../../src/container/nodeModulesContainer'; describe(`command runner`, () => { - let mock = mockSpawn(); - const childProcessSpy: any = { - spawn: mock, - }; - const logErrorMock = jest.fn(); - const logDebugMock = jest.fn(); - const logWarnMock = jest.fn(); - const isDebugEnabledMock = jest.fn(); - const isTraceEnabledMock = jest.fn(); - const loggerSpy = ({ - error: logErrorMock, - debug: logDebugMock, - warn: logWarnMock, - isDebugEnabled: isDebugEnabledMock, - isTraceEnabled: isTraceEnabledMock, - } as any) as ILogger; - const getLoggerMock = jest.fn(); - getLoggerMock.mockReturnValue(loggerSpy); - const loggerFactorySpy: ILoggerFactory = { - getLogger: getLoggerMock as any, - }; - const runner = new CommandRunner(childProcessSpy, loggerFactorySpy); + let spawnMock = mockSpawn(); + const childProcessMock: any = ({ + spawn: spawnMock, + } as any) as Spawn; + const loggerMock = mock(); + const loggerFactoryMock = mock(); + loggerFactoryMock.getLogger.mockReturnValue(loggerMock); + const runner = new CommandRunner(childProcessMock, loggerFactoryMock); beforeEach(() => { - getLoggerMock.mockReset(); - getLoggerMock.mockReturnValue(loggerSpy); - isDebugEnabledMock.mockReset(); - isTraceEnabledMock.mockReset(); - logErrorMock.mockReset(); - logWarnMock.mockReset(); - logDebugMock.mockReset(); - mock = mockSpawn(); - childProcessSpy.spawn = mock; + mockReset(loggerMock); + spawnMock = mockSpawn(); + childProcessMock.spawn = spawnMock; }); it(`should resolve on successful run`, async () => { - isDebugEnabledMock.mockReturnValue(true); - isTraceEnabledMock.mockReturnValue(true); - mock.sequence.add(mock.simple(0)); + loggerMock.isDebugEnabled.mockReturnValue(true); + loggerMock.isTraceEnabled.mockReturnValue(true); + spawnMock.sequence.add(spawnMock.simple(0)); const execOptionsPlaceHolder = {}; await runner.executeCommand({ execOptions: execOptionsPlaceHolder, command: [`npm`, `run`, `build`], }); - expect(mock.calls.length).toBe(1); - const call = mock.calls[0]; + expect(spawnMock.calls.length).toBe(1); + const call = spawnMock.calls[0]; expect(call.command).toBe(`npm`); expect(call.args).toEqual([`run`, `build`]); expect(call.opts).toBe(execOptionsPlaceHolder); }); it(`should log command output`, async () => { - isDebugEnabledMock.mockReturnValue(true); - isTraceEnabledMock.mockReturnValue(true); - mock.sequence.add(function (this: any, cb: any) { + loggerMock.isDebugEnabled.mockReturnValue(true); + loggerMock.isTraceEnabled.mockReturnValue(true); + spawnMock.sequence.add(function (this: any, cb: any) { this.stdout.write(`output data my library expects`); this.stderr.write(`error output data my library expects`); setTimeout(() => { @@ -71,19 +53,19 @@ describe(`command runner`, () => { execOptions: execOptionsPlaceHolder, command: [`npm`, `run`, `build`], }); - expect(mock.calls.length).toBe(1); - const call = mock.calls[0]; + expect(spawnMock.calls.length).toBe(1); + const call = spawnMock.calls[0]; expect(call.command).toBe(`npm`); expect(call.args).toEqual([`run`, `build`]); expect(call.opts).toBe(execOptionsPlaceHolder); - expect(logDebugMock).toHaveBeenCalledWith(`Command error output:\n`, `error output data my library expects`); - expect(logDebugMock).toHaveBeenCalledWith(`Command output:\n`, `output data my library expects`); + expect(loggerMock.debug).toHaveBeenCalledWith(`Command error output:\n`, `error output data my library expects`); + expect(loggerMock.debug).toHaveBeenCalledWith(`Command output:\n`, `output data my library expects`); }); it(`should log error command output`, async () => { - isDebugEnabledMock.mockReturnValue(false); - isTraceEnabledMock.mockReturnValue(false); - mock.sequence.add(function (this: any, cb: any) { + loggerMock.isDebugEnabled.mockReturnValue(false); + loggerMock.isTraceEnabled.mockReturnValue(false); + spawnMock.sequence.add(function (this: any, cb: any) { this.stdout.write(`output data my library expects`); this.stderr.write(`error output data my library expects`); setTimeout(() => { @@ -99,18 +81,18 @@ describe(`command runner`, () => { await expect(promise).rejects.toMatchObject({ message: `"npm run build" exited with code: 1`, }); - expect(mock.calls.length).toBe(1); - const call = mock.calls[0]; + expect(spawnMock.calls.length).toBe(1); + const call = spawnMock.calls[0]; expect(call.command).toBe(`npm`); expect(call.args).toEqual([`run`, `build`]); expect(call.opts).toBe(execOptionsPlaceHolder); - expect(logErrorMock).toHaveBeenCalledWith(`Command error output:\n`, `error output data my library expects`); + expect(loggerMock.error).toHaveBeenCalledWith(`Command error output:\n`, `error output data my library expects`); }); it(`should not log error command output if empty output`, async () => { - isDebugEnabledMock.mockReturnValue(false); - isTraceEnabledMock.mockReturnValue(false); - mock.sequence.add(function (this: any, cb: any) { + loggerMock.isDebugEnabled.mockReturnValue(false); + loggerMock.isTraceEnabled.mockReturnValue(false); + spawnMock.sequence.add(function (this: any, cb: any) { this.stdout.write(`output data my library expects`); setTimeout(() => { cb(1); @@ -125,18 +107,18 @@ describe(`command runner`, () => { await expect(promise).rejects.toMatchObject({ message: `"npm run build" exited with code: 1`, }); - expect(mock.calls.length).toBe(1); - const call = mock.calls[0]; + expect(spawnMock.calls.length).toBe(1); + const call = spawnMock.calls[0]; expect(call.command).toBe(`npm`); expect(call.args).toEqual([`run`, `build`]); expect(call.opts).toBe(execOptionsPlaceHolder); - expect(logErrorMock).toBeCalledTimes(0); + expect(loggerMock.error).toBeCalledTimes(0); }); it(`should not log command output`, async () => { - isDebugEnabledMock.mockReturnValue(false); - isTraceEnabledMock.mockReturnValue(false); - mock.sequence.add(function (this: any, cb: any) { + loggerMock.isDebugEnabled.mockReturnValue(false); + loggerMock.isTraceEnabled.mockReturnValue(false); + spawnMock.sequence.add(function (this: any, cb: any) { this.stdout.write(`output data my library expects`); setTimeout(() => { cb(0); @@ -147,18 +129,18 @@ describe(`command runner`, () => { execOptions: execOptionsPlaceHolder, command: [`npm`, `run`, `build`], }); - expect(mock.calls.length).toBe(1); - const call = mock.calls[0]; + expect(spawnMock.calls.length).toBe(1); + const call = spawnMock.calls[0]; expect(call.command).toBe(`npm`); expect(call.args).toEqual([`run`, `build`]); expect(call.opts).toBe(execOptionsPlaceHolder); - expect(logDebugMock).toBeCalledTimes(0); + expect(loggerMock.debug).toBeCalledTimes(0); }); it(`should reject on bad exit code`, async () => { - isDebugEnabledMock.mockReturnValue(true); - isTraceEnabledMock.mockReturnValue(true); - mock.sequence.add(mock.simple(-1)); + loggerMock.isDebugEnabled.mockReturnValue(true); + loggerMock.isTraceEnabled.mockReturnValue(true); + spawnMock.sequence.add(spawnMock.simple(-1)); const execOptionsPlaceHolder = {}; const promise = runner.executeCommand({ execOptions: execOptionsPlaceHolder, @@ -168,17 +150,17 @@ describe(`command runner`, () => { await expect(promise).rejects.toMatchObject({ message: `"npm run build" exited with code: -1`, }); - expect(mock.calls.length).toBe(1); - const call = mock.calls[0]; + expect(spawnMock.calls.length).toBe(1); + const call = spawnMock.calls[0]; expect(call.command).toBe(`npm`); expect(call.args).toEqual([`run`, `build`]); expect(call.opts).toBe(execOptionsPlaceHolder); }); it(`should reject on failed spawn`, async () => { - isDebugEnabledMock.mockReturnValue(true); - isTraceEnabledMock.mockReturnValue(true); - mock.sequence.add(function (this: any, cb: any) { + loggerMock.isDebugEnabled.mockReturnValue(true); + loggerMock.isTraceEnabled.mockReturnValue(true); + spawnMock.sequence.add(function (this: any, cb: any) { this.emit(`error`, new Error(`spawn ENOENT`)); setTimeout(() => { cb(8); @@ -193,11 +175,12 @@ describe(`command runner`, () => { await expect(promise).rejects.toMatchObject({ message: `"npm run build" exited with code: 8`, }); - expect(mock.calls.length).toBe(1); - const call = mock.calls[0]; + expect(spawnMock.calls.length).toBe(1); + const call = spawnMock.calls[0]; expect(call.command).toBe(`npm`); expect(call.args).toEqual([`run`, `build`]); expect(call.opts).toBe(execOptionsPlaceHolder); - expect(logErrorMock).toHaveBeenCalledWith(`spawn error`, new Error(`spawn ENOENT`)); + expect(loggerMock.error).toHaveBeenCalledWith(`spawn error`, new Error(`spawn ENOENT`)); + expect(loggerMock.debug).not.toHaveBeenCalledWith(`Command error output:\n`, ``); }); }); diff --git a/test/src/utils/commandRunner/index.spec.ts b/test/src/utils/commandRunner/index.spec.ts index 08f003ed..60893c08 100644 --- a/test/src/utils/commandRunner/index.spec.ts +++ b/test/src/utils/commandRunner/index.spec.ts @@ -1,4 +1,4 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; import { ICommandRunner, commandRunnerModuleBinder } from '../../../../src/utils/commandRunner'; import { CommandRunner } from '../../../../src/utils/commandRunner/impl/commandRunner'; diff --git a/test/src/utils/git/impl/git.spec.ts b/test/src/utils/git/impl/git.spec.ts index f3365d51..ad40ec18 100644 --- a/test/src/utils/git/impl/git.spec.ts +++ b/test/src/utils/git/impl/git.spec.ts @@ -1,101 +1,98 @@ import { Git } from '../../../../../src/utils/git/impl/git'; import { loggerFactory } from '../../../../common/logger'; -import { SimpleGit } from 'simple-git/promise'; +import { mock, mockReset } from 'jest-mock-extended'; +import simplegit = require('simple-git/promise'); +import { mockFn } from '../../../../common/safeMockFn'; describe(`git`, () => { - const tagsMock = jest.fn(); - const cloneMock = jest.fn(); - const checkoutMock = jest.fn(); - const simpleGitSpy = ({ - checkout: checkoutMock, - clone: cloneMock, - tags: tagsMock, - _baseDir: `/tmp/testGitDir/`, - } as any) as SimpleGit; - const gitMock = jest.fn(); - gitMock.mockReturnValue(simpleGitSpy); + const simpleGitMock = mock(); + // @ts-ignore + // eslint-disable-next-line no-underscore-dangle + simpleGitMock._baseDir.mockReturnValue(`/tmp/testGitDir/`); + const gitMock = mockFn(); + gitMock.mockReturnValue(simpleGitMock); const git = new Git(gitMock, loggerFactory); beforeEach(() => { - tagsMock.mockReset(); - cloneMock.mockReset(); - checkoutMock.mockReset(); - gitMock.mockReset(); - gitMock.mockReturnValue(simpleGitSpy); + mockReset(simpleGitMock); + gitMock.mockClear(); }); describe(`locate tag`, () => { it(`should locate tag`, async () => { - tagsMock.mockResolvedValue({ + simpleGitMock.tags.mockResolvedValue({ all: [`v1.0.0`, `v1.0.1`, `v1.0.2`, `v2.0.0`], + latest: `latest`, }); const tag = `1.0.2`; const tagResult = await git.locateTag({ - repo: simpleGitSpy, + repo: simpleGitMock, tag, }); - expect(tagsMock).toHaveBeenCalledTimes(1); - expect(tagsMock).toHaveBeenCalledWith(); + expect(simpleGitMock.tags).toHaveBeenCalledTimes(1); + expect(simpleGitMock.tags).toHaveBeenCalledWith(); expect(tagResult).toBe(`v1.0.2`); }); it(`should fail if too many matching tags`, async () => { - tagsMock.mockResolvedValue({ + simpleGitMock.tags.mockResolvedValue({ all: [`v1.0.0`, `v1.0.1`, `v1.0.2`, `v2.0.0`], + latest: `latest`, }); const tag = `1.0`; const promise = git.locateTag({ - repo: simpleGitSpy, + repo: simpleGitMock, tag, }); await expect(promise).rejects.toBeInstanceOf(Error); await expect(promise).rejects.toMatchObject({ message: `Too many matching tags for tag 1.0 - [v1.0.0, v1.0.1, v1.0.2]`, }); - expect(tagsMock).toHaveBeenCalledTimes(1); - expect(tagsMock).toHaveBeenCalledWith(); + expect(simpleGitMock.tags).toHaveBeenCalledTimes(1); + expect(simpleGitMock.tags).toHaveBeenCalledWith(); }); it(`should fail if not many matching tags`, async () => { - tagsMock.mockResolvedValue({ + simpleGitMock.tags.mockResolvedValue({ all: [`v1.0.0`, `v1.0.1`, `v1.0.2`, `v2.0.0`], + latest: `latest`, }); const tag = `3.0.0`; const promise = git.locateTag({ - repo: simpleGitSpy, + repo: simpleGitMock, tag, }); await expect(promise).rejects.toBeInstanceOf(Error); await expect(promise).rejects.toMatchObject({ message: `Failed to locate tag 3.0.0`, }); - expect(tagsMock).toHaveBeenCalledTimes(1); - expect(tagsMock).toHaveBeenCalledWith(); + expect(simpleGitMock.tags).toHaveBeenCalledTimes(1); + expect(simpleGitMock.tags).toHaveBeenCalledWith(); }); }); describe(`checkout commit`, () => { it(`should checkout commit`, async () => { const commitSha = `commitPlaceHolder`; - checkoutMock.mockResolvedValue(undefined); + simpleGitMock.checkout.mockResolvedValue(undefined); await git.checkoutCommit({ - repo: simpleGitSpy, + repo: simpleGitMock, commitSha, }); - expect(checkoutMock).toHaveBeenCalledTimes(1); - expect(checkoutMock).toHaveBeenCalledWith(commitSha); + expect(simpleGitMock.checkout).toHaveBeenCalledTimes(1); + expect(simpleGitMock.checkout).toHaveBeenCalledWith(commitSha); }); it(`should fail to checkout commit`, async () => { const commitSha = `commitPlaceHolder`; - checkoutMock.mockRejectedValue(new Error(`dummy message`)); + simpleGitMock.checkout.mockRejectedValue(new Error(`dummy message`)); const promise = git.checkoutCommit({ - repo: simpleGitSpy, + repo: simpleGitMock, commitSha, }); await expect(promise).rejects.toBeInstanceOf(Error); await expect(promise).rejects.toMatchObject({ message: `Failed to checkout commit commitPlaceHolder`, }); - expect(checkoutMock).toHaveBeenCalledTimes(1); - expect(checkoutMock).toHaveBeenCalledWith(commitSha); + expect(simpleGitMock.checkout).toHaveBeenCalledTimes(1); + expect(simpleGitMock.checkout).toHaveBeenCalledWith(commitSha); }); }); @@ -103,16 +100,16 @@ describe(`git`, () => { it(`should clone repo`, async () => { const dir = `/path/to/repo`; const url = `https://git.com/temp/test.git`; - cloneMock.mockResolvedValue(undefined); + simpleGitMock.clone.mockResolvedValue(`dummy`); const repo = await git.cloneRepo({ dir, url, }); - expect(repo).toBe(simpleGitSpy); + expect(repo).toBe(simpleGitMock); expect(gitMock).toHaveBeenCalledTimes(1); expect(gitMock).toHaveBeenCalledWith(dir); - expect(cloneMock).toHaveBeenCalledTimes(1); - expect(cloneMock).toHaveBeenCalledWith(url, dir); + expect(simpleGitMock.clone).toHaveBeenCalledTimes(1); + expect(simpleGitMock.clone).toHaveBeenCalledWith(url, dir); }); }); @@ -122,7 +119,7 @@ describe(`git`, () => { const repo = await git.openRepo({ path, }); - expect(repo).toBe(simpleGitSpy); + expect(repo).toBe(simpleGitMock); expect(gitMock).toHaveBeenCalledTimes(1); expect(gitMock).toHaveBeenCalledWith(path); }); diff --git a/test/src/utils/git/impl/gitCheckout.spec.ts b/test/src/utils/git/impl/gitCheckout.spec.ts index 6b5f95ed..10910714 100644 --- a/test/src/utils/git/impl/gitCheckout.spec.ts +++ b/test/src/utils/git/impl/gitCheckout.spec.ts @@ -1,50 +1,34 @@ import { loggerFactory } from '../../../../common/logger'; import { GitCheckout } from '../../../../../src/utils/git/impl/gitCheckout'; import { Git } from '../../../../../src/utils/git/impl/git'; -import { FS } from '../../../../../src/container/nodeModulesContainer'; import { normalize } from 'path'; +import { mock, mockDeep, mockReset } from 'jest-mock-extended'; +import * as fs from 'fs'; +import { SimpleGit } from 'simple-git/promise'; +import { Stats } from 'fs'; describe(`git checkout`, () => { - const openRepoMock = jest.fn(); - const cloneRepoMock = jest.fn(); - const checkoutCommitMock = jest.fn(); - const locateTagMock = jest.fn(); - const gitSpy = ({ - openRepo: openRepoMock, - cloneRepo: cloneRepoMock, - checkoutCommit: checkoutCommitMock, - locateTag: locateTagMock, - } as any) as Git; - const statMock = jest.fn(); - const mkdirMock = jest.fn(); - const fsSpy = ({ - promises: { - stat: statMock, - mkdir: mkdirMock, - }, - } as any) as FS; - const gitCheckout = new GitCheckout(gitSpy, fsSpy, loggerFactory); + const statsMock = mock(); + const gitMock = mock(); + const fsMock = mockDeep(); + const gitCheckout = new GitCheckout(gitMock, fsMock, loggerFactory); const url = `https://git.com/temp/test.git`; const baseDir = normalize(`/path/to/repo`); const fullDir = normalize(`/path/to/repo/test`); const tag = `test-tag`; const commitSha = `test-commit`; - const repoPlaceHolder = Symbol.for(`repo`); + const repoPlaceHolder = (Symbol.for(`repo`) as any) as SimpleGit; beforeEach(() => { - openRepoMock.mockReset(); - cloneRepoMock.mockReset(); - checkoutCommitMock.mockReset(); - locateTagMock.mockReset(); - statMock.mockReset(); - mkdirMock.mockReset(); + mockReset(fsMock); + mockReset(gitMock); }); it(`should checkout from existing repo`, async () => { - statMock.mockResolvedValue(undefined); - openRepoMock.mockResolvedValue(repoPlaceHolder); - checkoutCommitMock.mockResolvedValue(undefined); + fsMock.promises.stat.mockResolvedValue(statsMock); + gitMock.openRepo.mockResolvedValue(repoPlaceHolder); + gitMock.checkoutCommit.mockResolvedValue(undefined); const result = await gitCheckout.checkoutRepo({ url, baseDir, @@ -52,23 +36,23 @@ describe(`git checkout`, () => { commitSha, }); expect(result).toBe(fullDir); - expect(statMock).toHaveBeenCalledTimes(1); - expect(statMock).toHaveBeenCalledWith(fullDir); - expect(openRepoMock).toHaveBeenCalledTimes(1); - expect(openRepoMock).toHaveBeenCalledWith({ + expect(fsMock.promises.stat).toHaveBeenCalledTimes(1); + expect(fsMock.promises.stat).toHaveBeenCalledWith(fullDir); + expect(gitMock.openRepo).toHaveBeenCalledTimes(1); + expect(gitMock.openRepo).toHaveBeenCalledWith({ path: fullDir, }); - expect(checkoutCommitMock).toHaveBeenCalledTimes(1); - expect(checkoutCommitMock).toHaveBeenCalledWith({ + expect(gitMock.checkoutCommit).toHaveBeenCalledTimes(1); + expect(gitMock.checkoutCommit).toHaveBeenCalledWith({ repo: repoPlaceHolder, commitSha, }); }); it(`should checkout after clone repo`, async () => { - statMock.mockRejectedValue(new Error(`dummy message`)); - cloneRepoMock.mockResolvedValue(repoPlaceHolder); - checkoutCommitMock.mockResolvedValue(undefined); + fsMock.promises.stat.mockRejectedValue(new Error(`dummy message`)); + gitMock.cloneRepo.mockResolvedValue(repoPlaceHolder); + gitMock.checkoutCommit.mockResolvedValue(undefined); const result = await gitCheckout.checkoutRepo({ url, baseDir, @@ -76,46 +60,46 @@ describe(`git checkout`, () => { commitSha, }); expect(result).toBe(fullDir); - expect(statMock).toHaveBeenCalledTimes(1); - expect(statMock).toHaveBeenCalledWith(fullDir); - expect(mkdirMock).toHaveBeenCalledTimes(1); - expect(mkdirMock).toHaveBeenCalledWith(fullDir); - expect(cloneRepoMock).toHaveBeenCalledTimes(1); - expect(cloneRepoMock).toHaveBeenCalledWith({ + expect(fsMock.promises.stat).toHaveBeenCalledTimes(1); + expect(fsMock.promises.stat).toHaveBeenCalledWith(fullDir); + expect(fsMock.promises.mkdir).toHaveBeenCalledTimes(1); + expect(fsMock.promises.mkdir).toHaveBeenCalledWith(fullDir); + expect(gitMock.cloneRepo).toHaveBeenCalledTimes(1); + expect(gitMock.cloneRepo).toHaveBeenCalledWith({ dir: fullDir, url, }); - expect(checkoutCommitMock).toHaveBeenCalledTimes(1); - expect(checkoutCommitMock).toHaveBeenCalledWith({ + expect(gitMock.checkoutCommit).toHaveBeenCalledTimes(1); + expect(gitMock.checkoutCommit).toHaveBeenCalledWith({ repo: repoPlaceHolder, commitSha, }); }); it(`should checkout by locating tag`, async () => { - statMock.mockResolvedValue(undefined); - openRepoMock.mockResolvedValue(repoPlaceHolder); - checkoutCommitMock.mockResolvedValue(undefined); - locateTagMock.mockResolvedValue(commitSha); + fsMock.promises.stat.mockResolvedValue(statsMock); + gitMock.openRepo.mockResolvedValue(repoPlaceHolder); + gitMock.checkoutCommit.mockResolvedValue(undefined); + gitMock.locateTag.mockResolvedValue(commitSha); const result = await gitCheckout.checkoutRepo({ url, baseDir, tag, }); expect(result).toBe(fullDir); - expect(statMock).toHaveBeenCalledTimes(1); - expect(statMock).toHaveBeenCalledWith(fullDir); - expect(openRepoMock).toHaveBeenCalledTimes(1); - expect(openRepoMock).toHaveBeenCalledWith({ + expect(fsMock.promises.stat).toHaveBeenCalledTimes(1); + expect(fsMock.promises.stat).toHaveBeenCalledWith(fullDir); + expect(gitMock.openRepo).toHaveBeenCalledTimes(1); + expect(gitMock.openRepo).toHaveBeenCalledWith({ path: fullDir, }); - expect(locateTagMock).toHaveBeenCalledTimes(1); - expect(locateTagMock).toHaveBeenCalledWith({ + expect(gitMock.locateTag).toHaveBeenCalledTimes(1); + expect(gitMock.locateTag).toHaveBeenCalledWith({ repo: repoPlaceHolder, tag, }); - expect(checkoutCommitMock).toHaveBeenCalledTimes(1); - expect(checkoutCommitMock).toHaveBeenCalledWith({ + expect(gitMock.checkoutCommit).toHaveBeenCalledTimes(1); + expect(gitMock.checkoutCommit).toHaveBeenCalledWith({ repo: repoPlaceHolder, commitSha, }); diff --git a/test/src/utils/git/index.spec.ts b/test/src/utils/git/index.spec.ts index da2924e4..65f90b90 100644 --- a/test/src/utils/git/index.spec.ts +++ b/test/src/utils/git/index.spec.ts @@ -1,4 +1,4 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; import { gitModuleBinder, IGitCheckout } from '../../../../src/utils/git'; import { Git } from '../../../../src/utils/git/impl/git'; import { GitCheckout } from '../../../../src/utils/git/impl/gitCheckout'; diff --git a/test/src/utils/logger/index.spec.ts b/test/src/utils/logger/index.spec.ts index fd6ef1c0..fee6560e 100644 --- a/test/src/utils/logger/index.spec.ts +++ b/test/src/utils/logger/index.spec.ts @@ -1,4 +1,4 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; import { ILoggerFactory, loggerModuleBinder } from '../../../../src/utils/logger'; import { LoggerFactory } from '../../../../src/utils/logger/impl/loggerFactory'; diff --git a/test/src/utils/lts/impl/lts.spec.ts b/test/src/utils/lts/impl/lts.spec.ts deleted file mode 100644 index 0245f96a..00000000 --- a/test/src/utils/lts/impl/lts.spec.ts +++ /dev/null @@ -1,167 +0,0 @@ -import moment = require('moment'); -import { Lts } from '../../../../../src/utils/lts/impl/lts'; -import { loggerFactory } from '../../../../common/logger'; -import { Axios } from '../../../../../src/container/nodeModulesContainer'; - -const dateFormat = `YYYY-MM-DD`; - -describe(`lts`, () => { - const nodeReleaseVersions = { - 'v0.10': { - start: `2013-03-11`, - end: `2016-10-31`, - }, - 'v0.12': { - start: `2015-02-06`, - end: `2016-12-31`, - }, - v4: { - start: `2015-09-08`, - lts: `2015-10-12`, - maintenance: `2017-04-01`, - end: `2018-04-30`, - codename: `Argon`, - }, - v5: { - start: `2015-10-29`, - maintenance: `2016-04-30`, - end: `2016-06-30`, - }, - v6: { - start: `2016-04-26`, - lts: `2016-10-18`, - maintenance: `2018-04-30`, - end: `2019-04-30`, - codename: `Boron`, - }, - v7: { - start: `2016-10-25`, - maintenance: `2017-04-30`, - end: `2017-06-30`, - }, - v8: { - start: `2017-05-30`, - lts: `2017-10-31`, - maintenance: `2019-01-01`, - end: `2019-12-31`, - codename: `Carbon`, - }, - v9: { - start: `2017-10-01`, - maintenance: `2018-04-01`, - end: `2018-06-30`, - }, - v10: { - start: `2018-04-24`, - lts: `2018-10-30`, - maintenance: `2020-04-30`, - end: `2021-04-30`, - codename: `Dubnium`, - }, - v11: { - start: `2018-10-23`, - maintenance: `2019-04-22`, - end: `2019-06-01`, - }, - v12: { - start: `2019-04-23`, - lts: `2019-10-21`, - maintenance: `2020-10-20`, - end: `2022-04-30`, - codename: `Erbium`, - }, - v13: { - start: `2019-10-22`, - maintenance: `2020-04-01`, - end: `2020-06-01`, - }, - v14: { - start: `2020-04-21`, - lts: `2020-10-20`, - maintenance: `2021-10-19`, - end: `2023-04-30`, - codename: ``, - }, - }; - - const getMock = jest.fn(); - const axiosSpy = { - get: getMock, - }; - let lts: Lts; - - beforeEach(() => { - getMock.mockReset(); - getMock.mockResolvedValue({ - data: nodeReleaseVersions, - }); - lts = new Lts((axiosSpy as any) as Axios, loggerFactory); - }); - - it(`should throw if fails to locate versions from remote url`, async () => { - getMock.mockReset(); - getMock.mockRejectedValue(new Error(`failed to fetch`)); - const promise = lts.resolveLtsVersion({ - date: moment.utc(`2015-10-02`, dateFormat), - }); - await expect(promise).rejects.toBeInstanceOf(Error); - await expect(promise).rejects.toMatchObject({ - message: `Failed to fetch node release versions`, - }); - expect(getMock).toBeCalledTimes(1); - expect(getMock).toHaveBeenCalledWith(`https://raw.githubusercontent.com/nodejs/Release/master/schedule.json`); - }); - - it(`should cache versions result`, async () => { - await lts.resolveLtsVersion({ - date: moment.utc(`2015-10-02`, dateFormat), - }); - await lts.resolveLtsVersion({ - date: moment.utc(`2015-10-02`, dateFormat), - }); - expect(getMock).toBeCalledTimes(1); - expect(getMock).toHaveBeenCalledWith(`https://raw.githubusercontent.com/nodejs/Release/master/schedule.json`); - }); - - it(`should resolve to node 4`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2015-10-02`, dateFormat), - }); - expect(versions).toEqual([`4`]); - }); - - it(`should resolve to node 4 & 6`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2016-10-19`, dateFormat), - }); - expect(versions).toEqual([`4`, `6`]); - }); - - it(`should resolve to node 4 & 6 & 8`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2017-11-01`, dateFormat), - }); - expect(versions).toEqual([`4`, `6`, `8`]); - }); - - it(`should resolve to node 6 & 8 & 10`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2018-11-01`, dateFormat), - }); - expect(versions).toEqual([`6`, `8`, `10`]); - }); - - it(`should resolve to node 8 & 10 && 12`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2019-10-23`, dateFormat), - }); - expect(versions).toEqual([`8`, `10`, `12`]); - }); - - it(`should resolve to node 10 && 12 && 14`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2020-10-21`, dateFormat), - }); - expect(versions).toEqual([`10`, `12`, `14`]); - }); -}); diff --git a/test/src/utils/lts/index.spec.ts b/test/src/utils/lts/index.spec.ts deleted file mode 100644 index 4c2bf749..00000000 --- a/test/src/utils/lts/index.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; -import { ILts, ltsModulesBinder } from '../../../../src/utils/lts'; -import { Lts } from '../../../../src/utils/lts/impl/lts'; - -testBindings({ - name: `lts module container`, - binderFn: ltsModulesBinder, - bindings: [ - { - binder: ILts, - binded: Lts, - type: BindingTypes.SINGELTON, - }, - ], -}); diff --git a/test/src/utils/memoize/memoize.spec.ts b/test/src/utils/memoize/memoize.spec.ts index 75f32f73..e0808bbb 100644 --- a/test/src/utils/memoize/memoize.spec.ts +++ b/test/src/utils/memoize/memoize.spec.ts @@ -7,8 +7,6 @@ interface IData { } describe(`memoize`, () => { - const argHash = jest.fn((...args: any[]) => args.join(`:`)); - class Dummy { public method0Mock = jest.fn(() => { return this.data.method0; @@ -27,14 +25,14 @@ describe(`memoize`, () => { return this.method0Mock(); } - @memoize() - public method1(...args: any[]): string { - return this.method1Mock(...args); + @memoize((arg: string) => arg) + public method1(arg: string): string { + return this.method1Mock(arg); } - @memoize(argHash) - public method2(...args: any[]): string { - return this.method2Mock(...args); + @memoize((arg: string) => arg) + public method2(arg?: string): string { + return this.method2Mock(arg); } } @@ -67,7 +65,7 @@ describe(`memoize`, () => { expect(subjectB.method0Mock).toHaveBeenCalledTimes(1); }); - it(`should remember method values with one args`, () => { + it(`should remember method values with one arg`, () => { expect(subjectA.method1(`foo`)).toBe(`method1 A:foo`); expect(subjectA.method1Mock).toHaveBeenCalledTimes(1); expect(subjectA.method1(`bar`)).toBe(`method1 A:bar`); @@ -76,4 +74,18 @@ describe(`memoize`, () => { expect(subjectA.method1(`bar`)).toBe(`method1 A:bar`); expect(subjectA.method1Mock).toHaveBeenCalledTimes(2); }); + + it(`should remember method values with optional one arg`, () => { + expect(subjectA.method2(`foo`)).toBe(`method2 A:foo`); + expect(subjectA.method2Mock).toHaveBeenCalledTimes(1); + expect(subjectA.method2(`bar`)).toBe(`method2 A:bar`); + expect(subjectA.method2Mock).toHaveBeenCalledTimes(2); + expect(subjectA.method2(`foo`)).toBe(`method2 A:foo`); + expect(subjectA.method2(`bar`)).toBe(`method2 A:bar`); + expect(subjectA.method2Mock).toHaveBeenCalledTimes(2); + expect(subjectA.method2()).toBe(`method2 A:`); + expect(subjectA.method2Mock).toHaveBeenCalledTimes(3); + expect(subjectA.method2()).toBe(`method2 A:`); + expect(subjectA.method2Mock).toHaveBeenCalledTimes(3); + }); }); diff --git a/test/src/utils/nodeVersions/impl/__snapshots__/nodeVersions.spec.ts.snap b/test/src/utils/nodeVersions/impl/__snapshots__/nodeVersions.spec.ts.snap new file mode 100644 index 00000000..bded5ad8 --- /dev/null +++ b/test/src/utils/nodeVersions/impl/__snapshots__/nodeVersions.spec.ts.snap @@ -0,0 +1,48 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`node versions getAllVersions should parse remote versions 1`] = ` +Array [ + Object { + "codename": "argon", + "from": "2015-09-08T00:00:00.000Z", + "ltsFrom": "2015-10-12T00:00:00.000Z", + "to": "2018-04-30T00:00:00.000Z", + "version": "4", + }, + Object { + "codename": "boron", + "from": "2016-04-26T00:00:00.000Z", + "ltsFrom": "2016-10-18T00:00:00.000Z", + "to": "2019-04-30T00:00:00.000Z", + "version": "6", + }, + Object { + "codename": "carbon", + "from": "2017-05-30T00:00:00.000Z", + "ltsFrom": "2017-10-31T00:00:00.000Z", + "to": "2019-12-31T00:00:00.000Z", + "version": "8", + }, + Object { + "codename": "dubnium", + "from": "2018-04-24T00:00:00.000Z", + "ltsFrom": "2018-10-30T00:00:00.000Z", + "to": "2021-04-30T00:00:00.000Z", + "version": "10", + }, + Object { + "codename": "erbium", + "from": "2019-04-23T00:00:00.000Z", + "ltsFrom": "2019-10-21T00:00:00.000Z", + "to": "2022-04-30T00:00:00.000Z", + "version": "12", + }, + Object { + "codename": "", + "from": "2020-04-21T00:00:00.000Z", + "ltsFrom": "2020-10-20T00:00:00.000Z", + "to": "2023-04-30T00:00:00.000Z", + "version": "14", + }, +] +`; diff --git a/test/src/utils/nodeVersions/impl/nodeVersions.spec.ts b/test/src/utils/nodeVersions/impl/nodeVersions.spec.ts new file mode 100644 index 00000000..57fdd0dc --- /dev/null +++ b/test/src/utils/nodeVersions/impl/nodeVersions.spec.ts @@ -0,0 +1,430 @@ +import moment = require('moment'); +import { NodeVersions } from '../../../../../src/utils/nodeVersions/impl/nodeVersions'; +import { Axios } from '../../../../../src/container/nodeModulesContainer'; +import { mock, mockReset } from 'jest-mock-extended'; +import { ILogger } from '../../../../../src/utils/logger/interfaces/ILogger'; +import { ILoggerFactory } from '../../../../../src/utils/logger'; + +const dateFormat = `YYYY-MM-DD`; + +describe(`node versions`, () => { + const nodeReleaseVersions = { + 'v0.10': { + start: `2013-03-11`, + end: `2016-10-31`, + }, + 'v0.12': { + start: `2015-02-06`, + end: `2016-12-31`, + }, + v4: { + start: `2015-09-08`, + lts: `2015-10-12`, + maintenance: `2017-04-01`, + end: `2018-04-30`, + codename: `Argon`, + }, + v5: { + start: `2015-10-29`, + maintenance: `2016-04-30`, + end: `2016-06-30`, + }, + v6: { + start: `2016-04-26`, + lts: `2016-10-18`, + maintenance: `2018-04-30`, + end: `2019-04-30`, + codename: `Boron`, + }, + v7: { + start: `2016-10-25`, + maintenance: `2017-04-30`, + end: `2017-06-30`, + }, + v8: { + start: `2017-05-30`, + lts: `2017-10-31`, + maintenance: `2019-01-01`, + end: `2019-12-31`, + codename: `Carbon`, + }, + v9: { + start: `2017-10-01`, + maintenance: `2018-04-01`, + end: `2018-06-30`, + }, + v10: { + start: `2018-04-24`, + lts: `2018-10-30`, + maintenance: `2020-04-30`, + end: `2021-04-30`, + codename: `Dubnium`, + }, + v11: { + start: `2018-10-23`, + maintenance: `2019-04-22`, + end: `2019-06-01`, + }, + v12: { + start: `2019-04-23`, + lts: `2019-10-21`, + maintenance: `2020-10-20`, + end: `2022-04-30`, + codename: `Erbium`, + }, + v13: { + start: `2019-10-22`, + maintenance: `2020-04-01`, + end: `2020-06-01`, + }, + v14: { + start: `2020-04-21`, + lts: `2020-10-20`, + maintenance: `2021-10-19`, + end: `2023-04-30`, + codename: ``, + }, + }; + + const axiosMock = mock(); + let nodeVersions: NodeVersions; + const loggerMock = mock(); + const loggerFactoryMock = mock(); + loggerFactoryMock.getLogger.mockReturnValue(loggerMock); + + beforeEach(() => { + mockReset(loggerMock); + mockReset(axiosMock); + axiosMock.get.mockResolvedValue({ + data: nodeReleaseVersions, + }); + nodeVersions = new NodeVersions(axiosMock, loggerFactoryMock); + }); + + describe(`stable`, () => { + it(`should throw if fails to locate versions from remote url`, async () => { + mockReset(axiosMock); + axiosMock.get.mockRejectedValue(new Error(`failed to fetch`)); + const promise = nodeVersions.resolveStableVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + await expect(promise).rejects.toBeInstanceOf(Error); + await expect(promise).rejects.toMatchObject({ + message: `Failed to fetch node release versions`, + }); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should cache remote versions result`, async () => { + await nodeVersions.resolveStableVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + await nodeVersions.resolveStableVersion({ + date: moment.utc(`2015-10-03`, dateFormat), + }); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should cache versions result`, async () => { + const res1 = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + const res2 = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + const res3 = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2017-10-03`, dateFormat), + }); + expect(res1).toBe(res2); + expect(res2).not.toBe(res3); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should resolve to node 4`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + expect(versions).toEqual(`4`); + }); + + it(`should resolve to node 6`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2016-10-19`, dateFormat), + }); + expect(versions).toEqual(`6`); + }); + + it(`should resolve to node 8`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2017-11-01`, dateFormat), + }); + expect(versions).toEqual(`8`); + }); + + it(`should resolve to node 10`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2018-11-01`, dateFormat), + }); + expect(versions).toEqual(`10`); + }); + + it(`should resolve to node 12`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2019-10-23`, dateFormat), + }); + expect(versions).toEqual(`12`); + }); + + it(`should resolve to node 14`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2020-10-21`, dateFormat), + }); + expect(versions).toEqual(`14`); + }); + + it(`should not resolve`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2050-10-21`, dateFormat), + }); + expect(versions).toEqual(undefined); + expect(loggerMock.error).toHaveBeenCalledWith(`Failed to resolve stable version in 2050-10-21T00:00:00.000Z`); + }); + }); + + describe(`latest lts`, () => { + it(`should throw if fails to locate versions from remote url`, async () => { + mockReset(axiosMock); + axiosMock.get.mockRejectedValue(new Error(`failed to fetch`)); + const promise = nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + await expect(promise).rejects.toBeInstanceOf(Error); + await expect(promise).rejects.toMatchObject({ + message: `Failed to fetch node release versions`, + }); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should cache remote versions result`, async () => { + const res1 = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + const res2 = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + const res3 = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2017-10-03`, dateFormat), + }); + expect(res1).toBe(res2); + expect(res2).not.toBe(res3); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should cache versions result`, async () => { + await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should resolve to node 4`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2016-04-27`, dateFormat), + }); + expect(versions).toEqual(`4`); + }); + + it(`should resolve to node 6`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2017-06-01`, dateFormat), + }); + expect(versions).toEqual(`6`); + }); + + it(`should resolve to node 8`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2018-04-25`, dateFormat), + }); + expect(versions).toEqual(`8`); + }); + + it(`should resolve to node 10`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2019-04-24`, dateFormat), + }); + expect(versions).toEqual(`10`); + }); + + it(`should resolve to node 12`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2020-04-25`, dateFormat), + }); + expect(versions).toEqual(`12`); + }); + + it(`should resolve to node 14`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2020-10-21`, dateFormat), + }); + expect(versions).toEqual(`14`); + }); + + it(`should not resolve`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2050-10-21`, dateFormat), + }); + expect(versions).toEqual(undefined); + expect(loggerMock.error).toHaveBeenCalledWith(`Failed to resolve latest lts version in 2050-10-21T00:00:00.000Z`); + }); + }); + + describe(`lts`, () => { + it(`should throw if fails to locate versions from remote url`, async () => { + mockReset(axiosMock.get); + axiosMock.get.mockRejectedValue(new Error(`failed to fetch`)); + const promise = nodeVersions.resolveLtsVersion({ + codename: `Argon`, + }); + await expect(promise).rejects.toBeInstanceOf(Error); + await expect(promise).rejects.toMatchObject({ + message: `Failed to fetch node release versions`, + }); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should cache remote versions result`, async () => { + await nodeVersions.resolveLtsVersion({ + codename: `Argon`, + }); + await nodeVersions.resolveLtsVersion({ + codename: `argon`, + }); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should cache versions result`, async () => { + const res1 = await nodeVersions.resolveLtsVersion({ + codename: `Argon`, + }); + const res2 = await nodeVersions.resolveLtsVersion({ + codename: `Argon`, + }); + const res3 = await nodeVersions.resolveLtsVersion({ + codename: `Boron`, + }); + expect(res1).toBe(res2); + expect(res2).not.toBe(res3); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should resolve to node 4 ignore case`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Argon`, + }); + expect(versions).toEqual(`4`); + }); + + it(`should resolve to node 4`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `argon`, + }); + expect(versions).toEqual(`4`); + }); + + it(`should resolve to node 6`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Boron`, + }); + expect(versions).toEqual(`6`); + }); + + it(`should resolve to node 8`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Carbon`, + }); + expect(versions).toEqual(`8`); + }); + + it(`should resolve to node 10`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Dubnium`, + }); + expect(versions).toEqual(`10`); + }); + + it(`should resolve to node 12`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Erbium`, + }); + expect(versions).toEqual(`12`); + }); + + it(`should not resolve`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `dummy`, + }); + expect(versions).toEqual(undefined); + expect(loggerMock.error).toHaveBeenCalledWith(`Failed to resolve LTS version of dummy`); + }); + }); + + describe(`getAllVersions`, () => { + it(`should throw if fails to locate versions`, async () => { + mockReset(axiosMock.get); + axiosMock.get.mockRejectedValue(new Error(`failed to fetch`)); + const promise = nodeVersions.getAllVersions(); + await expect(promise).rejects.toBeInstanceOf(Error); + await expect(promise).rejects.toMatchObject({ + message: `Failed to fetch node release versions`, + }); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should cache remote versions result`, async () => { + await nodeVersions.getAllVersions(); + await nodeVersions.getAllVersions(); + expect(axiosMock.get).toBeCalledTimes(1); + expect(axiosMock.get).toHaveBeenCalledWith( + `https://raw.githubusercontent.com/nodejs/Release/master/schedule.json` + ); + }); + + it(`should parse remote versions`, async () => { + const versions = await nodeVersions.getAllVersions(); + expect(versions).toMatchSnapshot(); + }); + }); +}); diff --git a/test/src/utils/nodeVersions/index.spec.ts b/test/src/utils/nodeVersions/index.spec.ts new file mode 100644 index 00000000..5f875fde --- /dev/null +++ b/test/src/utils/nodeVersions/index.spec.ts @@ -0,0 +1,15 @@ +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; +import { INodeVersions, nodeVersionsModulesBinder } from '../../../../src/utils/nodeVersions'; +import { NodeVersions } from '../../../../src/utils/nodeVersions/impl/nodeVersions'; + +testBindings({ + name: `lts module container`, + binderFn: nodeVersionsModulesBinder, + bindings: [ + { + binder: INodeVersions, + binded: NodeVersions, + type: BindingTypes.SINGELTON, + }, + ], +}); diff --git a/test/src/utils/objectIterator/__snapshots__/objectIterator.spec.ts.snap b/test/src/utils/objectIterator/__snapshots__/objectIterator.spec.ts.snap index 42f57567..5732bb88 100644 --- a/test/src/utils/objectIterator/__snapshots__/objectIterator.spec.ts.snap +++ b/test/src/utils/objectIterator/__snapshots__/objectIterator.spec.ts.snap @@ -4,8 +4,11 @@ exports[`object iterator for of Should iterate complex object 1`] = ` Array [ Object { "depth": 0, + "isLeaf": true, + "isNonRootNode": true, "key": "a", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -25,8 +28,11 @@ Array [ }, Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -55,11 +61,16 @@ Array [ }, Object { "depth": 1, + "isLeaf": true, + "isNonRootNode": true, "key": "c", "parent": Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -90,11 +101,16 @@ Array [ }, Object { "depth": 1, + "isLeaf": false, + "isNonRootNode": true, "key": "d", "parent": Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -131,14 +147,21 @@ Array [ }, Object { "depth": 2, + "isLeaf": true, + "isNonRootNode": true, "key": "e", "parent": Object { "depth": 1, + "isLeaf": false, + "isNonRootNode": true, "key": "d", "parent": Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -177,14 +200,21 @@ Array [ }, Object { "depth": 2, + "isLeaf": false, + "isNonRootNode": true, "key": "f", "parent": Object { "depth": 1, + "isLeaf": false, + "isNonRootNode": true, "key": "d", "parent": Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -226,17 +256,26 @@ Array [ }, Object { "depth": 3, + "isLeaf": true, + "isNonRootNode": true, "key": "0", "parent": Object { "depth": 2, + "isLeaf": false, + "isNonRootNode": true, "key": "f", "parent": Object { "depth": 1, + "isLeaf": false, + "isNonRootNode": true, "key": "d", "parent": Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -280,17 +319,26 @@ Array [ }, Object { "depth": 3, + "isLeaf": true, + "isNonRootNode": true, "key": "1", "parent": Object { "depth": 2, + "isLeaf": false, + "isNonRootNode": true, "key": "f", "parent": Object { "depth": 1, + "isLeaf": false, + "isNonRootNode": true, "key": "d", "parent": Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -334,8 +382,11 @@ Array [ }, Object { "depth": 0, + "isLeaf": true, + "isNonRootNode": true, "key": "g", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -360,8 +411,11 @@ exports[`object iterator skip logic Should iterate complex object 1`] = ` Array [ Object { "depth": 0, + "isLeaf": true, + "isNonRootNode": true, "key": "a", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -381,8 +435,11 @@ Array [ }, Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -411,11 +468,16 @@ Array [ }, Object { "depth": 1, + "isLeaf": true, + "isNonRootNode": true, "key": "c", "parent": Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -446,11 +508,16 @@ Array [ }, Object { "depth": 1, + "isLeaf": false, + "isNonRootNode": true, "key": "d", "parent": Object { "depth": 0, + "isLeaf": false, + "isNonRootNode": true, "key": "b", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { @@ -487,8 +554,11 @@ Array [ }, Object { "depth": 0, + "isLeaf": true, + "isNonRootNode": true, "key": "g", "parent": Object { + "isNonRootNode": false, "value": Object { "a": 1, "b": Object { diff --git a/test/src/utils/objectIterator/objectIterator.spec.ts b/test/src/utils/objectIterator/objectIterator.spec.ts index ea4585f2..425e39e4 100644 --- a/test/src/utils/objectIterator/objectIterator.spec.ts +++ b/test/src/utils/objectIterator/objectIterator.spec.ts @@ -11,11 +11,11 @@ describe(`object iterator`, () => { results.push(node); } expect(results).toEqual([ - { key: `a`, value: 1, depth: 0 }, - { key: `b`, value: obj.b, depth: 0 }, - { key: `c`, value: 2, depth: 1 }, - { key: `d`, value: obj.b.d, depth: 1 }, - { key: `cir`, value: obj.b, depth: 2 }, + { key: `a`, value: 1, depth: 0, isLeaf: true, isNonRootNode: true }, + { key: `b`, value: obj.b, depth: 0, isLeaf: false, isNonRootNode: true }, + { key: `c`, value: 2, depth: 1, isLeaf: true, isNonRootNode: true }, + { key: `d`, value: obj.b.d, depth: 1, isLeaf: false, isNonRootNode: true }, + { key: `cir`, value: obj.b, depth: 2, isLeaf: false, isNonRootNode: true }, ]); }); }); @@ -27,15 +27,45 @@ describe(`object iterator`, () => { results.push(node); } expect(results).toEqual([ - { key: `a`, value: 1, depth: 0, parent: { value: { a: 1, b: { c: 2 }, d: `7` } } }, - { key: `b`, value: { c: 2 }, depth: 0, parent: { value: { a: 1, b: { c: 2 }, d: `7` } } }, + { + key: `a`, + value: 1, + depth: 0, + isLeaf: true, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: `7` }, isNonRootNode: false }, + }, + { + key: `b`, + value: { c: 2 }, + depth: 0, + isLeaf: false, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: `7` }, isNonRootNode: false }, + }, { key: `c`, value: 2, depth: 1, - parent: { key: `b`, value: { c: 2 }, depth: 0, parent: { value: { a: 1, b: { c: 2 }, d: `7` } } }, + isLeaf: true, + isNonRootNode: true, + parent: { + key: `b`, + value: { c: 2 }, + depth: 0, + isLeaf: false, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: `7` }, isNonRootNode: false }, + }, + }, + { + key: `d`, + value: `7`, + depth: 0, + isLeaf: true, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: `7` }, isNonRootNode: false }, }, - { key: `d`, value: `7`, depth: 0, parent: { value: { a: 1, b: { c: 2 }, d: `7` } } }, ]); }); @@ -60,9 +90,30 @@ describe(`object iterator`, () => { result = it.next(node.key === `b`); } expect(results).toEqual([ - { key: `a`, value: 1, depth: 0, parent: { value: { a: 1, b: { c: 2 }, d: `7` } } }, - { key: `b`, value: { c: 2 }, depth: 0, parent: { value: { a: 1, b: { c: 2 }, d: `7` } } }, - { key: `d`, value: `7`, depth: 0, parent: { value: { a: 1, b: { c: 2 }, d: `7` } } }, + { + key: `a`, + value: 1, + depth: 0, + isLeaf: true, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: `7` }, isNonRootNode: false }, + }, + { + key: `b`, + value: { c: 2 }, + depth: 0, + isLeaf: false, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: `7` }, isNonRootNode: false }, + }, + { + key: `d`, + value: `7`, + depth: 0, + isLeaf: true, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: `7` }, isNonRootNode: false }, + }, ]); }); @@ -99,39 +150,58 @@ describe(`object iterator`, () => { result = it.next(node.key === `b`); } expect(results).toEqual([ - { key: `b`, value: { c: 2 }, depth: 0, parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } } } }, + { + key: `b`, + value: { c: 2 }, + depth: 0, + isLeaf: false, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } }, isNonRootNode: false }, + }, { key: `a`, value: 1, depth: 0, - parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } } }, + isLeaf: true, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } }, isNonRootNode: false }, }, { key: `d`, value: { a: 1, b: 2 }, depth: 0, - parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } } }, + isLeaf: false, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } }, isNonRootNode: false }, }, { key: `a`, value: 1, depth: 1, + isLeaf: true, + isNonRootNode: true, parent: { key: `d`, value: { a: 1, b: 2 }, depth: 0, - parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } } }, + isLeaf: false, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } }, isNonRootNode: false }, }, }, { key: `b`, value: 2, depth: 1, + isLeaf: true, + isNonRootNode: true, parent: { key: `d`, value: { a: 1, b: 2 }, depth: 0, - parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } } }, + isLeaf: false, + isNonRootNode: true, + parent: { value: { a: 1, b: { c: 2 }, d: { a: 1, b: 2 } }, isNonRootNode: false }, }, }, ]); diff --git a/test/src/utils/packageInfo/impl/packageInfo.spec.ts b/test/src/utils/packageInfo/impl/packageInfo.spec.ts index 735687c5..58573f05 100644 --- a/test/src/utils/packageInfo/impl/packageInfo.spec.ts +++ b/test/src/utils/packageInfo/impl/packageInfo.spec.ts @@ -1,19 +1,19 @@ import { PackageInfo } from '../../../../../src/utils/packageInfo/impl/packageInfo'; import { Pacote } from '../../../../../src/container/nodeModulesContainer'; +import { loggerFactory } from '../../../../common/logger'; +import { mock, mockReset } from 'jest-mock-extended'; +import { PackageDist } from 'pacote'; describe(`package info`, () => { - const manifestMock = jest.fn(); - const pacoteSpy = ({ - manifest: manifestMock, - } as any) as Pacote; - const packageInfo = new PackageInfo(pacoteSpy); + const pacoteMock = mock(); + const packageInfo = new PackageInfo(pacoteMock, loggerFactory); beforeEach(() => { - manifestMock.mockReset(); + mockReset(pacoteMock); }); it(`should resolve package info properly`, async () => { - manifestMock.mockResolvedValue({ + pacoteMock.manifest.mockResolvedValue({ version: `3.0.1`, engines: { node: `>=6`, @@ -22,6 +22,11 @@ describe(`package info`, () => { url: `git+https://github.com/PruvoNet/squiss-ts.git`, }, gitHead: `5cd3e98b5236ce93bb522a893a7e09fd4de1e39b`, + _from: `_from`, + _resolved: `_resolved`, + _integrity: `_integrity`, + name: `name`, + dist: mock(), }); const result = await packageInfo.getPackageInfo({ name: `squiss-ts`, @@ -35,18 +40,24 @@ describe(`package info`, () => { repoUrl: `git+https://github.com/PruvoNet/squiss-ts.git`, commitSha: `5cd3e98b5236ce93bb522a893a7e09fd4de1e39b`, }); - expect(manifestMock.mock.calls.length).toBe(1); - expect(manifestMock.mock.calls[0].length).toBe(2); - expect(manifestMock.mock.calls[0][0]).toBe(`squiss-ts@^3.0.0`); + expect(pacoteMock.manifest.mock.calls.length).toBe(1); + expect(pacoteMock.manifest).toHaveBeenCalledWith(`squiss-ts@^3.0.0`, { + fullMetadata: true, + }); }); it(`should resolve package info properly when no repo data`, async () => { - manifestMock.mockResolvedValue({ + pacoteMock.manifest.mockResolvedValue({ version: `3.0.1`, engines: { node: `>=6`, }, gitHead: `5cd3e98b5236ce93bb522a893a7e09fd4de1e39b`, + _from: `_from`, + _resolved: `_resolved`, + _integrity: `_integrity`, + name: `name`, + dist: mock(), }); const result = await packageInfo.getPackageInfo({ name: `squiss-ts`, @@ -59,18 +70,23 @@ describe(`package info`, () => { engines: `>=6`, commitSha: `5cd3e98b5236ce93bb522a893a7e09fd4de1e39b`, }); - expect(manifestMock.mock.calls.length).toBe(1); - expect(manifestMock.mock.calls[0].length).toBe(2); - expect(manifestMock.mock.calls[0][0]).toBe(`squiss-ts@^3.0.0`); + expect(pacoteMock.manifest.mock.calls.length).toBe(1); + expect(pacoteMock.manifest.mock.calls[0].length).toBe(2); + expect(pacoteMock.manifest.mock.calls[0][0]).toBe(`squiss-ts@^3.0.0`); }); it(`should resolve package info properly when no engines data`, async () => { - manifestMock.mockResolvedValue({ + pacoteMock.manifest.mockResolvedValue({ version: `3.0.1`, repository: { url: `git+https://github.com/PruvoNet/squiss-ts.git`, }, gitHead: `5cd3e98b5236ce93bb522a893a7e09fd4de1e39b`, + _from: `_from`, + _resolved: `_resolved`, + _integrity: `_integrity`, + name: `name`, + dist: mock(), }); const result = await packageInfo.getPackageInfo({ name: `squiss-ts`, @@ -83,8 +99,8 @@ describe(`package info`, () => { repoUrl: `git+https://github.com/PruvoNet/squiss-ts.git`, commitSha: `5cd3e98b5236ce93bb522a893a7e09fd4de1e39b`, }); - expect(manifestMock.mock.calls.length).toBe(1); - expect(manifestMock.mock.calls[0].length).toBe(2); - expect(manifestMock.mock.calls[0][0]).toBe(`squiss-ts@^3.0.0`); + expect(pacoteMock.manifest.mock.calls.length).toBe(1); + expect(pacoteMock.manifest.mock.calls[0].length).toBe(2); + expect(pacoteMock.manifest.mock.calls[0][0]).toBe(`squiss-ts@^3.0.0`); }); }); diff --git a/test/src/utils/packageInfo/index.spec.ts b/test/src/utils/packageInfo/index.spec.ts index 0ccd6daf..f88f7618 100644 --- a/test/src/utils/packageInfo/index.spec.ts +++ b/test/src/utils/packageInfo/index.spec.ts @@ -1,4 +1,4 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; import { IPackageInfo, packageInfoModulesBinder } from '../../../../src/utils/packageInfo'; import { PackageInfo } from '../../../../src/utils/packageInfo/impl/packageInfo'; diff --git a/test/src/utils/types/typeGuards.spec.ts b/test/src/utils/types/typeGuards.spec.ts new file mode 100644 index 00000000..c9019002 --- /dev/null +++ b/test/src/utils/types/typeGuards.spec.ts @@ -0,0 +1,33 @@ +import { isStringOrNumber } from '../../../../src/utils/types/typeGuards'; + +describe(`type guards`, () => { + describe(`is string or number`, () => { + it(`should resolve string`, () => { + expect(isStringOrNumber(`str`)).toBe(true); + }); + + it(`should resolve number`, () => { + expect(isStringOrNumber(5)).toBe(true); + }); + + it(`should not resolve undefined`, () => { + expect(isStringOrNumber(undefined)).toBe(false); + }); + + it(`should not resolve null`, () => { + expect(isStringOrNumber(null)).toBe(false); + }); + + it(`should not resolve array`, () => { + expect(isStringOrNumber([])).toBe(false); + }); + + it(`should not resolve object`, () => { + expect(isStringOrNumber({})).toBe(false); + }); + + it(`should not resolve symbol`, () => { + expect(isStringOrNumber(Symbol.for(`test`))).toBe(false); + }); + }); +}); diff --git a/test/src/utils/yarn/impl/yarn.spec.ts b/test/src/utils/yarn/impl/yarn.spec.ts index d830f079..23a3930a 100644 --- a/test/src/utils/yarn/impl/yarn.spec.ts +++ b/test/src/utils/yarn/impl/yarn.spec.ts @@ -1,26 +1,24 @@ import { Yarn } from '../../../../../src/utils/yarn/impl/yarn'; import { loggerFactory } from '../../../../common/logger'; import { ICommandRunner } from '../../../../../src/utils/commandRunner'; +import { mock, mockReset } from 'jest-mock-extended'; describe(`yarn`, () => { const cwd = `my cwd`; - const executeCommandMock = jest.fn(); - const runnerSpy: ICommandRunner = { - executeCommand: executeCommandMock, - }; - const yarn = new Yarn(runnerSpy, loggerFactory); + const runnerMock = mock(); + const yarn = new Yarn(runnerMock, loggerFactory); beforeEach(() => { - executeCommandMock.mockReset(); + mockReset(runnerMock); }); it(`should run yarn install`, async () => { - executeCommandMock.mockResolvedValue(undefined); + runnerMock.executeCommand.mockResolvedValue(undefined); await yarn.install({ cwd, }); - expect(executeCommandMock).toBeCalledTimes(1); - expect(executeCommandMock).toHaveBeenCalledWith({ + expect(runnerMock.executeCommand).toBeCalledTimes(1); + expect(runnerMock.executeCommand).toHaveBeenCalledWith({ command: [`yarn`, `install`], execOptions: { cwd, @@ -29,12 +27,12 @@ describe(`yarn`, () => { }); it(`should run yarn build`, async () => { - executeCommandMock.mockResolvedValue(undefined); + runnerMock.executeCommand.mockResolvedValue(undefined); await yarn.build({ cwd, }); - expect(executeCommandMock).toBeCalledTimes(1); - expect(executeCommandMock).toHaveBeenCalledWith({ + expect(runnerMock.executeCommand).toBeCalledTimes(1); + expect(runnerMock.executeCommand).toHaveBeenCalledWith({ command: [`yarn`, `run`, `build`], execOptions: { cwd, @@ -43,12 +41,12 @@ describe(`yarn`, () => { }); it(`should run yarn test`, async () => { - executeCommandMock.mockResolvedValue(undefined); + runnerMock.executeCommand.mockResolvedValue(undefined); await yarn.test({ cwd, }); - expect(executeCommandMock).toBeCalledTimes(1); - expect(executeCommandMock).toHaveBeenCalledWith({ + expect(runnerMock.executeCommand).toBeCalledTimes(1); + expect(runnerMock.executeCommand).toHaveBeenCalledWith({ command: [`yarn`, `run`, `test`], execOptions: { cwd, diff --git a/test/src/utils/yarn/index.spec.ts b/test/src/utils/yarn/index.spec.ts index ac8d6c1b..415aed01 100644 --- a/test/src/utils/yarn/index.spec.ts +++ b/test/src/utils/yarn/index.spec.ts @@ -1,4 +1,4 @@ -import { BindingTypes, testBindings } from '../../../common/bindingTester'; +import { BindingTypes, testBindings } from '../../../common/testers/bindingTester'; import { IYarn, yarnModulesBinder } from '../../../../src/utils/yarn'; import { Yarn } from '../../../../src/utils/yarn/impl/yarn'; diff --git a/testE2E/src/resolvers/testResolver/impl/testResolver.spec.ts b/testE2E/src/resolvers/testResolver/impl/testResolver.spec.ts index 592acb87..74b0241b 100644 --- a/testE2E/src/resolvers/testResolver/impl/testResolver.spec.ts +++ b/testE2E/src/resolvers/testResolver/impl/testResolver.spec.ts @@ -21,8 +21,10 @@ describe(`test resolver`, () => { const result = await testResolver.resolve({ repoPath: tmpDir, }); - expect(result.isMatch).toBe(true); - expect(result.resolverName).toBe(`yarn run test`); + expect(result).toEqual({ + isMatch: true, + resolverName: `yarn run test`, + }); }, 30000); it(`should fail yarn test flow`, async () => { diff --git a/testE2E/src/utils/lts/impl/lts.spec.ts b/testE2E/src/utils/lts/impl/lts.spec.ts deleted file mode 100644 index ba9dd4ee..00000000 --- a/testE2E/src/utils/lts/impl/lts.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/quotes -import moment = require('moment'); -import { container } from '../../../../../src/container'; -import { ILts } from '../../../../../src/utils/lts'; - -const dateFormat = `YYYY-MM-DD`; - -describe(`lts`, () => { - const lts = container.get(ILts); - - it(`should resolve to node 4`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2015-10-02`, dateFormat), - }); - expect(versions).toEqual([`4`]); - }); - - it(`should resolve to node 4 & 6`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2016-10-19`, dateFormat), - }); - expect(versions).toEqual([`4`, `6`]); - }); - - it(`should resolve to node 4 & 6 & 8`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2017-11-01`, dateFormat), - }); - expect(versions).toEqual([`4`, `6`, `8`]); - }); - - it(`should resolve to node 6 & 8 & 10`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2018-11-01`, dateFormat), - }); - expect(versions).toEqual([`6`, `8`, `10`]); - }); - - it(`should resolve to node 8 & 10 && 12`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2019-10-23`, dateFormat), - }); - expect(versions).toEqual([`8`, `10`, `12`]); - }); - - it(`should resolve to node 10 && 12 && 14`, async () => { - const versions = await lts.resolveLtsVersion({ - date: moment.utc(`2020-10-21`, dateFormat), - }); - expect(versions).toEqual([`10`, `12`, `14`]); - }); -}); diff --git a/testE2E/src/utils/nodeVersions/impl/nodeVersions.spec.ts b/testE2E/src/utils/nodeVersions/impl/nodeVersions.spec.ts new file mode 100644 index 00000000..7347f492 --- /dev/null +++ b/testE2E/src/utils/nodeVersions/impl/nodeVersions.spec.ts @@ -0,0 +1,156 @@ +// eslint-disable-next-line @typescript-eslint/quotes +import moment = require('moment'); +import { container } from '../../../../../src/container'; +import { INodeVersions } from '../../../../../src/utils/nodeVersions'; + +const dateFormat = `YYYY-MM-DD`; + +describe(`node versions`, () => { + const nodeVersions = container.get(INodeVersions); + + describe(`stable`, () => { + it(`should resolve to node 4`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2015-10-02`, dateFormat), + }); + expect(versions).toEqual(`4`); + }); + + it(`should resolve to node 6`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2016-10-19`, dateFormat), + }); + expect(versions).toEqual(`6`); + }); + + it(`should resolve to node 8`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2017-11-01`, dateFormat), + }); + expect(versions).toEqual(`8`); + }); + + it(`should resolve to node 10`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2018-11-01`, dateFormat), + }); + expect(versions).toEqual(`10`); + }); + + it(`should resolve to node 12`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2019-10-23`, dateFormat), + }); + expect(versions).toEqual(`12`); + }); + + it(`should resolve to node 14`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2020-10-21`, dateFormat), + }); + expect(versions).toEqual(`14`); + }); + + it(`should not resolve`, async () => { + const versions = await nodeVersions.resolveStableVersion({ + date: moment.utc(`2050-10-21`, dateFormat), + }); + expect(versions).toEqual(undefined); + }); + }); + + describe(`latest lts`, () => { + it(`should resolve to node 4`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2016-04-27`, dateFormat), + }); + expect(versions).toEqual(`4`); + }); + + it(`should resolve to node 6`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2017-06-01`, dateFormat), + }); + expect(versions).toEqual(`6`); + }); + + it(`should resolve to node 8`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2018-04-25`, dateFormat), + }); + expect(versions).toEqual(`8`); + }); + + it(`should resolve to node 10`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2019-04-24`, dateFormat), + }); + expect(versions).toEqual(`10`); + }); + + it(`should resolve to node 12`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2020-04-25`, dateFormat), + }); + expect(versions).toEqual(`12`); + }); + + it(`should resolve to node 14`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2020-10-21`, dateFormat), + }); + expect(versions).toEqual(`14`); + }); + + it(`should not resolve`, async () => { + const versions = await nodeVersions.resolveLatestLtsVersion({ + date: moment.utc(`2050-10-21`, dateFormat), + }); + expect(versions).toEqual(undefined); + }); + }); + + describe(`lts`, () => { + it(`should resolve to node 4`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Argon`, + }); + expect(versions).toEqual(`4`); + }); + + it(`should resolve to node 6`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Boron`, + }); + expect(versions).toEqual(`6`); + }); + + it(`should resolve to node 8`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Carbon`, + }); + expect(versions).toEqual(`8`); + }); + + it(`should resolve to node 10`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Dubnium`, + }); + expect(versions).toEqual(`10`); + }); + + it(`should resolve to node 12`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `Erbium`, + }); + expect(versions).toEqual(`12`); + }); + + it(`should not resolve`, async () => { + const versions = await nodeVersions.resolveLtsVersion({ + codename: `dummy`, + }); + expect(versions).toEqual(undefined); + }); + }); +}); diff --git a/testInteg/resources/appveyor2/.appveyor.yml b/testInteg/resources/appveyor2/.appveyor.yml deleted file mode 100644 index cf86afa2..00000000 --- a/testInteg/resources/appveyor2/.appveyor.yml +++ /dev/null @@ -1,48 +0,0 @@ -# http://www.appveyor.com/docs/appveyor-yml -version: '{build}' -pull_requests: - do_not_increment_build_number: true -skip_tags: true -shallow_clone: true -build: off -deploy: off -platform: x64 -branches: - except: - - gh-pages - -# clear the cache if commit contains given text -init: - - ps: IF ($env:APPVEYOR_REPO_COMMIT_MESSAGE -Match "\[clean ci-cache\]" ) {$env:APPVEYOR_CACHE_SKIP_RESTORE = "true"} - - ps: IF ($env:APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED -Match "\[clean ci-cache\]") {$env:APPVEYOR_CACHE_SKIP_RESTORE = "true"} - -# Install scripts. (runs after repo cloning) -install: - - ps: Install-Product node 8 x64 - - npm install -g npm@^5 - # Typical npm stuff. - # - set CI=true - # Our E2E work dir - - set TS_JEST_E2E_WORKDIR=%APPDATA%\ts-jest-e2e - - set TS_JEST_E2E_OPTIMIZATIONS=1 - - npm ci --ignore-scripts - - npm run clean -- --when-ci-commit-message - -cache: - - .cache -> package.json - - '%APPDATA%\npm-cache' - - '%APPDATA%\ts-jest-e2e\__templates__' - -# Post-install test scripts. -test_script: - - cmd: npm run test:e2e - -# skip_commits: -# files: -# - 'docs/**/*' -# - '**/*.md' -# - .gitignore -# - .gitattributes -# - .travis.yml -# - icon.png -# - commitlint.config.js diff --git a/testInteg/resources/appveyorFaulty/.appveyor.yml b/testInteg/resources/appveyorFaulty/.appveyor.yml deleted file mode 100644 index cde3467e..00000000 --- a/testInteg/resources/appveyorFaulty/.appveyor.yml +++ /dev/null @@ -1,10 +0,0 @@ -environment: - matrix: - # node.js - - nodejs_version: "4" - - nodejs_version: "6" - # io.js - - nodejs_version: "1.0" - -install: - - ps: echo 1 diff --git a/testInteg/resources/circleciFaulty/.circleci/config.yml b/testInteg/resources/circleciFaulty/.circleci/config.yml deleted file mode 100644 index 5db2b3ee..00000000 --- a/testInteg/resources/circleciFaulty/.circleci/config.yml +++ /dev/null @@ -1,82 +0,0 @@ -version: 2.1 - -commands: - test-nodejs: - steps: - - run: - name: Versions - command: npm version - - checkout - - restore_cache: - keys: - - v {{.Environment.CIRCLE_CACHE_VERSION}} - {{arch}} - npm-cache - {{.Branch}} - {{.Environment.CIRCLE_JOB}} - - v {{.Environment.CIRCLE_CACHE_VERSION}} - {{arch}} - npm-cache-master - {{.Environment.CIRCLE_JOB}} - - run: - name: Install dependencies - command: npm ci - - run: - name: Test - command: npm test - - save-npm-cache - test-nodejs-v6: - steps: - - run: - name: Versions - command: npm version - - checkout - - restore_cache: - keys: - - v {{.Environment.CIRCLE_CACHE_VERSION}} - {{arch}} - npm-lock - {{.Branch}} - {{.Environment.CIRCLE_JOB}} - {{checksum "package-lock.json"}} - - v{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ arch }}-npm-lock-master-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package-lock.json" }} - - v{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ arch }}-npm-cache-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }} - - v{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ arch }}-npm-cache-master-{{ .Environment.CIRCLE_JOB }} - - run: - name: Install dependencies - command: npm install - - run: - name: Test - command: npm test - - save-npm-lock - - save-npm-cache - save-npm-lock: - steps: - - save_cache: - key: v{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ arch }}-npm-lock-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package-lock.json" }} - paths: - - node_modules - save-npm-cache: - steps: - - save_cache: - key: v{{ .Environment.CIRCLE_CACHE_VERSION }}-{{ arch }}-npm-cache-{{ .Branch }}-{{ .Environment.CIRCLE_JOB }}-{{ checksum "package-lock.json" }} - paths: - - ~/.npm/_cacache - -jobs: - node-v6: - docker: - - image: node1:6 - steps: - - test-nodejs-v6 - node-v8: - docker: - - image: node1:8 - steps: - - test-nodejs - node-v10: - docker: - - image: node1:10 - steps: - - test-nodejs - node-v11: - docker: - - image: node1:11 - steps: - - test-nodejs - -workflows: - node-multi-build: - jobs: - - node-v6 - - node-v8 - - node-v10 - - node-v11 diff --git a/testInteg/resources/circleci2/.circleci/config.yml b/testInteg/resources/circleciV1/circle.yml similarity index 100% rename from testInteg/resources/circleci2/.circleci/config.yml rename to testInteg/resources/circleciV1/circle.yml diff --git a/testInteg/resources/circleci/.circleci/config.yml b/testInteg/resources/circleciV2/.circleci/config.yml similarity index 100% rename from testInteg/resources/circleci/.circleci/config.yml rename to testInteg/resources/circleciV2/.circleci/config.yml diff --git a/testInteg/resources/github/.github/workflows/config.json b/testInteg/resources/github/.github/workflows/config.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/testInteg/resources/github/.github/workflows/config.json @@ -0,0 +1 @@ +{} diff --git a/testInteg/resources/github/.github/workflows/dummy/config.json b/testInteg/resources/github/.github/workflows/dummy/config.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/testInteg/resources/github/.github/workflows/dummy/config.json @@ -0,0 +1 @@ +{} diff --git a/testInteg/resources/github/.github/workflows/flow1.yml b/testInteg/resources/github/.github/workflows/flow1.yml index ff298f0c..14991ecf 100644 --- a/testInteg/resources/github/.github/workflows/flow1.yml +++ b/testInteg/resources/github/.github/workflows/flow1.yml @@ -11,7 +11,6 @@ jobs: - uses: actions/checkout@v2 - name: Use Node.js uses: actions/setup-node@v1 - node-version: 8.x with: node-version: '12.x' - run: npm install diff --git a/testInteg/resources/githubFaulty/.github/workflows/flow.yml b/testInteg/resources/githubFaulty/.github/workflows/flow.yml deleted file mode 100644 index 3bc420fb..00000000 --- a/testInteg/resources/githubFaulty/.github/workflows/flow.yml +++ /dev/null @@ -1,6 +0,0 @@ -steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} diff --git a/testInteg/resources/travis/.travis.yml b/testInteg/resources/travis/.travis.yml index 392f947e..cbb322d8 100644 --- a/testInteg/resources/travis/.travis.yml +++ b/testInteg/resources/travis/.travis.yml @@ -2,10 +2,12 @@ language: node_js node_js: # - "0.8" - "6" - - "7" + - "lts/dummy" + - "lts/*" - "8" - "9" - - "10" + - 10 + - "stable" env: - TEST_SCRIPT=coverage diff --git a/testInteg/resources/travisFaulty/.travis.yml b/testInteg/resources/travisFaulty/.travis.yml deleted file mode 100644 index b7bec2a5..00000000 --- a/testInteg/resources/travisFaulty/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -language: node_js -env: - - TEST_SCRIPT=coverage - -sudo: false - -matrix: - include: - - node_js: "10" - env: TEST_SCRIPT=test - -addons: - rake: - chrome: stable - -before script: - - export CHROME_BIN=/usr/bin/chromium-browser - -script: - - npm run $TEST_SCRIPT - - "if [[ $TEST_SCRIPT == 'coverage' ]]; then node ./node_modules/.bin/codecov || exit 0; fi" diff --git a/testInteg/resources/travisLts/.travis.yml b/testInteg/resources/travisLts/.travis.yml index 4c7411cb..11da6f12 100644 --- a/testInteg/resources/travisLts/.travis.yml +++ b/testInteg/resources/travisLts/.travis.yml @@ -1,6 +1,5 @@ language: node_js -node_js: - - 'lts/*' +node_js: 'lts/*' env: - TEST_SCRIPT=coverage diff --git a/testE2E/src/db/impl/connectionProvider.spec.ts b/testInteg/src/db/impl/connectionProvider.spec.ts similarity index 81% rename from testE2E/src/db/impl/connectionProvider.spec.ts rename to testInteg/src/db/impl/connectionProvider.spec.ts index 809fde47..32e78c8c 100644 --- a/testE2E/src/db/impl/connectionProvider.spec.ts +++ b/testInteg/src/db/impl/connectionProvider.spec.ts @@ -5,8 +5,9 @@ import { IConnectionProvider, IConnectionSettings } from '../../../../src/db'; describe(`connection provider e2e`, () => { let connectionProvider: IConnectionProvider; + let conn: Connection; - beforeEach(() => { + beforeEach(async () => { container.snapshot(); const tmpDir = tmp.dirSync().name; container.bind(IConnectionSettings).toConstantValue({ @@ -14,14 +15,15 @@ describe(`connection provider e2e`, () => { dropSchema: false, }); connectionProvider = container.get(IConnectionProvider); + conn = await connectionProvider.getConnection(); }); - afterEach(() => { + afterEach(async () => { container.restore(); + await conn.close(); }); it(`should get connection`, async () => { - const conn = await connectionProvider.getConnection(); expect(conn).toBeInstanceOf(Connection); }); }); diff --git a/testE2E/src/db/impl/dependencyRepositoryProvider.spec.ts b/testInteg/src/db/impl/dependencyRepositoryProvider.spec.ts similarity index 88% rename from testE2E/src/db/impl/dependencyRepositoryProvider.spec.ts rename to testInteg/src/db/impl/dependencyRepositoryProvider.spec.ts index 05ce6ec0..184f9339 100644 --- a/testE2E/src/db/impl/dependencyRepositoryProvider.spec.ts +++ b/testInteg/src/db/impl/dependencyRepositoryProvider.spec.ts @@ -1,11 +1,18 @@ -import { Dependency, IConnectionSettings, IDependencyRepositoryProvider } from '../../../../src/db'; +import { + Dependency, + IConnectionProvider, + IConnectionSettings, + IDependencyRepositoryProvider, +} from '../../../../src/db'; import { container } from '../../../../src/container'; import * as tmp from 'tmp'; +import { Connection } from 'typeorm'; describe(`dependency repository provider e2e`, () => { let dependencyRepositoryProvider: IDependencyRepositoryProvider; + let conn: Connection; - beforeEach(() => { + beforeEach(async () => { container.snapshot(); const tmpDir = tmp.dirSync().name; container.bind(IConnectionSettings).toConstantValue({ @@ -13,10 +20,13 @@ describe(`dependency repository provider e2e`, () => { dropSchema: false, }); dependencyRepositoryProvider = container.get(IDependencyRepositoryProvider); + const connectionProvider = container.get(IConnectionProvider); + conn = await connectionProvider.getConnection(); }); - afterEach(() => { + afterEach(async () => { container.restore(); + await conn.close(); }); it(`should save entity`, async () => { diff --git a/testE2E/src/db/impl/dependencyVersionRepositoryProvider.spec.ts b/testInteg/src/db/impl/dependencyVersionRepositoryProvider.spec.ts similarity index 90% rename from testE2E/src/db/impl/dependencyVersionRepositoryProvider.spec.ts rename to testInteg/src/db/impl/dependencyVersionRepositoryProvider.spec.ts index 5d86c495..bcf64333 100644 --- a/testE2E/src/db/impl/dependencyVersionRepositoryProvider.spec.ts +++ b/testInteg/src/db/impl/dependencyVersionRepositoryProvider.spec.ts @@ -1,15 +1,22 @@ -import { IDependencyVersionRepositoryProvider, DependencyVersion, IConnectionSettings } from '../../../../src/db'; +import { + IDependencyVersionRepositoryProvider, + DependencyVersion, + IConnectionSettings, + IConnectionProvider, +} from '../../../../src/db'; import { container } from '../../../../src/container'; import * as tmp from 'tmp'; import moment = require('moment'); +import { Connection } from 'typeorm'; const dateFormat = `YYYY-MM-DD`; const releaseDate = moment.utc(`2015-10-02`, dateFormat); describe(`dependency version repository provider e2e`, () => { let dependencyVersionRepositoryProvider: IDependencyVersionRepositoryProvider; + let conn: Connection; - beforeEach(() => { + beforeEach(async () => { container.snapshot(); const tmpDir = tmp.dirSync().name; container.bind(IConnectionSettings).toConstantValue({ @@ -17,10 +24,13 @@ describe(`dependency version repository provider e2e`, () => { dropSchema: false, }); dependencyVersionRepositoryProvider = container.get(IDependencyVersionRepositoryProvider); + const connectionProvider = container.get(IConnectionProvider); + conn = await connectionProvider.getConnection(); }); - afterEach(() => { + afterEach(async () => { container.restore(); + await conn.close(); }); it(`should save entity`, async () => { diff --git a/testInteg/src/resolvers/ciResolver/impl/ciResolver.spec.ts b/testInteg/src/resolvers/ciResolver/impl/ciResolver.spec.ts index c4885487..fa352e6e 100644 --- a/testInteg/src/resolvers/ciResolver/impl/ciResolver.spec.ts +++ b/testInteg/src/resolvers/ciResolver/impl/ciResolver.spec.ts @@ -8,16 +8,7 @@ const dateFormat = `YYYY-MM-DD`; const packageReleaseDate = moment.utc(`2015-10-02`, dateFormat); describe(`ci resolver`, () => { - let ciResolver: ICIResolver; - - beforeEach(() => { - container.snapshot(); - ciResolver = container.get(ICIResolver); - }); - - afterEach(() => { - container.restore(); - }); + const ciResolver = container.get(ICIResolver); it(`should not resolve node js from repo with no ci`, async () => { const repoPath = path.join(resourcesDir, `empty`); @@ -37,8 +28,10 @@ describe(`ci resolver`, () => { targetNode: `8`, packageReleaseDate, }); - expect(result.isMatch).toBe(true); - expect(result.resolverName).toBe(`travisCi`); + expect(result).toEqual({ + isMatch: true, + resolverName: `TravisCi`, + }); }); it(`should not resolve node js from travis configuration`, async () => { @@ -50,16 +43,6 @@ describe(`ci resolver`, () => { }); expect(result.isMatch).toBe(false); }); - - it(`should not resolve node js from faulty travis configuration`, async () => { - const repoPath = path.join(resourcesDir, `travisFaulty`); - const result = await ciResolver.resolve({ - repoPath, - targetNode: `2`, - packageReleaseDate, - }); - expect(result.isMatch).toBe(false); - }); }); describe(`appveyor`, () => { @@ -70,8 +53,10 @@ describe(`ci resolver`, () => { targetNode: `6`, packageReleaseDate, }); - expect(result.isMatch).toBe(true); - expect(result.resolverName).toBe(`appVeyor`); + expect(result).toEqual({ + isMatch: true, + resolverName: `AppVeyor`, + }); }); it(`should not resolve node js from appveyor configuration`, async () => { @@ -83,42 +68,24 @@ describe(`ci resolver`, () => { }); expect(result.isMatch).toBe(false); }); - - it(`should not resolve node js from faulty appveyor configuration`, async () => { - const repoPath = path.join(resourcesDir, `appveyorFaulty`); - const result = await ciResolver.resolve({ - repoPath, - targetNode: `2`, - packageReleaseDate, - }); - expect(result.isMatch).toBe(false); - }); }); describe(`circleci`, () => { it(`should resolve node js from circleci configuration`, async () => { - const repoPath = path.join(resourcesDir, `circleci`); + const repoPath = path.join(resourcesDir, `circleciV2`); const result = await ciResolver.resolve({ repoPath, targetNode: `10`, packageReleaseDate, }); - expect(result.isMatch).toBe(true); - expect(result.resolverName).toBe(`circleCi`); - }); - - it(`should not resolve node js from circleci configuration`, async () => { - const repoPath = path.join(resourcesDir, `circleci`); - const result = await ciResolver.resolve({ - repoPath, - targetNode: `2`, - packageReleaseDate, + expect(result).toEqual({ + isMatch: true, + resolverName: `CircleCi`, }); - expect(result.isMatch).toBe(false); }); - it(`should not resolve node js from faulty circleci configuration`, async () => { - const repoPath = path.join(resourcesDir, `circleciFaulty`); + it(`should not resolve node js from circleci configuration`, async () => { + const repoPath = path.join(resourcesDir, `circleciV2`); const result = await ciResolver.resolve({ repoPath, targetNode: `2`, @@ -136,8 +103,10 @@ describe(`ci resolver`, () => { targetNode: `10`, packageReleaseDate, }); - expect(result.isMatch).toBe(true); - expect(result.resolverName).toBe(`githubActions`); + expect(result).toEqual({ + isMatch: true, + resolverName: `Github Actions`, + }); }); it(`should not resolve node js from github configuration`, async () => { @@ -149,15 +118,5 @@ describe(`ci resolver`, () => { }); expect(result.isMatch).toBe(false); }); - - it(`should not resolve node js from faulty github configuration`, async () => { - const repoPath = path.join(resourcesDir, `githubFaulty`); - const result = await ciResolver.resolve({ - repoPath, - targetNode: `2`, - packageReleaseDate, - }); - expect(result.isMatch).toBe(false); - }); }); }); diff --git a/testInteg/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.spec.ts b/testInteg/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.spec.ts new file mode 100644 index 00000000..bcebd730 --- /dev/null +++ b/testInteg/src/resolvers/ciResolver/impl/resolvers/appveyor/appVeyorResolver.spec.ts @@ -0,0 +1,36 @@ +import * as path from 'path'; +import { resourcesDir } from '../../../../../../common'; +import { container } from '../../../../../../../src/container'; +import { ISpecificCIResolver, SpecificCIResolverTags } from '../../../../../../../src/resolvers/ciResolver'; + +describe(`AppVeyor Resolver`, () => { + const appVeyorResolver = container.getNamed(ISpecificCIResolver, SpecificCIResolverTags.appVeyor); + + describe(`resolve`, () => { + it(`should resolve node js from appveyor configuration`, async () => { + const repoPath = path.join(resourcesDir, `appveyor`); + const versions = await appVeyorResolver.resolve({ + repoPath, + }); + expect(versions).toEqual({ nodeVersions: new Set([`4`, `6`, `1.0`]) }); + }); + }); + + describe(`isRelevant`, () => { + it(`should return true from relevant repo`, async () => { + const repoPath = path.join(resourcesDir, `appveyor`); + const result = await appVeyorResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + }); + + it(`should return false from non relevant repo`, async () => { + const repoPath = path.join(resourcesDir, `empty`); + const result = await appVeyorResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + }); + }); +}); diff --git a/testInteg/src/resolvers/ciResolver/impl/resolvers/circle.spec.ts b/testInteg/src/resolvers/ciResolver/impl/resolvers/circle.spec.ts deleted file mode 100644 index 66565f64..00000000 --- a/testInteg/src/resolvers/ciResolver/impl/resolvers/circle.spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as path from 'path'; -import { resourcesDir } from '../../../../../common'; -import { CircleCiResolver } from '../../../../../../src/resolvers/ciResolver/impl/resolvers/circle'; -import * as fs from 'fs'; - -describe(`circle ci`, () => { - const circleCiResolver = new CircleCiResolver(fs); - - it(`should expose the proper name`, async () => { - expect(circleCiResolver.resolverName).toBe(`circleCi`); - }); - - it(`should resolve node js from circleci configuration`, async () => { - const repoPath = path.join(resourcesDir, `circleci`); - const versions = await circleCiResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`10`]); - }); - - it(`should resolve node js from circleci configuration multiple nodes`, async () => { - const repoPath = path.join(resourcesDir, `circleci2`); - const versions = await circleCiResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`6`, `8`, `10`, `11`]); - }); - - it(`should return empty array from faulty configuration`, async () => { - const repoPath = path.join(resourcesDir, `circleciFaulty`); - const versions = await circleCiResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([]); - }); - - it(`should return undefined from non relevant repo`, async () => { - const repoPath = path.join(__dirname, `..`, `..`, `..`, `resources`, `empty`); - const versions = await circleCiResolver.resolve({ - repoPath, - }); - expect(versions).toBeFalsy(); - }); -}); diff --git a/testInteg/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.spec.ts b/testInteg/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.spec.ts new file mode 100644 index 00000000..cffd257f --- /dev/null +++ b/testInteg/src/resolvers/ciResolver/impl/resolvers/circleci/circleCiResolver.spec.ts @@ -0,0 +1,52 @@ +import * as path from 'path'; +import { resourcesDir } from '../../../../../../common'; +import { container } from '../../../../../../../src/container'; +import { ISpecificCIResolver, SpecificCIResolverTags } from '../../../../../../../src/resolvers/ciResolver'; + +describe(`CircleCi Resolver`, () => { + const circleCiResolver = container.getNamed(ISpecificCIResolver, SpecificCIResolverTags.circleCi); + + describe(`resolve`, () => { + it(`should resolve node js from circleci configuration v1`, async () => { + const repoPath = path.join(resourcesDir, `circleciV1`); + const versions = await circleCiResolver.resolve({ + repoPath, + }); + expect(versions).toEqual({ nodeVersions: new Set([`10`, `11`, `6`, `8`]) }); + }); + + it(`should resolve node js from circleci configuration v2`, async () => { + const repoPath = path.join(resourcesDir, `circleciV2`); + const versions = await circleCiResolver.resolve({ + repoPath, + }); + expect(versions).toEqual({ nodeVersions: new Set([`10`]) }); + }); + }); + + describe(`isRelevant`, () => { + it(`should return true from relevant repo v1`, async () => { + const repoPath = path.join(resourcesDir, `circleciV1`); + const result = await circleCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + }); + + it(`should return true from relevant repo v2`, async () => { + const repoPath = path.join(resourcesDir, `circleciV2`); + const result = await circleCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + }); + + it(`should return false from non relevant repo`, async () => { + const repoPath = path.join(resourcesDir, `empty`); + const result = await circleCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + }); + }); +}); diff --git a/testInteg/src/resolvers/ciResolver/impl/resolvers/github.spec.ts b/testInteg/src/resolvers/ciResolver/impl/resolvers/github.spec.ts deleted file mode 100644 index 3d32446b..00000000 --- a/testInteg/src/resolvers/ciResolver/impl/resolvers/github.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as path from 'path'; -import { GithubActionsResolver } from '../../../../../../src/resolvers/ciResolver/impl/resolvers/github'; -import { resourcesDir } from '../../../../../common'; -import * as fs from 'fs'; - -describe(`github actions`, () => { - const githubActionsResolver = new GithubActionsResolver(fs); - - it(`should expose the proper name`, async () => { - expect(githubActionsResolver.resolverName).toBe(`githubActions`); - }); - - it(`should resolve node js from github actions configuration`, async () => { - const repoPath = path.join(resourcesDir, `github`); - const versions = await githubActionsResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`8.x`, `12.x`, `6`, `10.x`, `4.x`]); - }); - - it(`should return empty array from faulty configuration`, async () => { - const repoPath = path.join(resourcesDir, `githubFaulty`); - const versions = await githubActionsResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([]); - }); - - it(`should return undefined from non relevant repo`, async () => { - const repoPath = path.join(resourcesDir, `empty`); - const versions = await githubActionsResolver.resolve({ - repoPath, - }); - expect(versions).toBeFalsy(); - }); -}); diff --git a/testInteg/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.spec.ts b/testInteg/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.spec.ts new file mode 100644 index 00000000..f3dcd3d1 --- /dev/null +++ b/testInteg/src/resolvers/ciResolver/impl/resolvers/githubActions/githubActionsResolver.spec.ts @@ -0,0 +1,40 @@ +import * as path from 'path'; +import { resourcesDir } from '../../../../../../common'; +import { container } from '../../../../../../../src/container'; +import { ISpecificCIResolver, SpecificCIResolverTags } from '../../../../../../../src/resolvers/ciResolver'; + +describe(`github actions resolver`, () => { + const githubActionsResolver = container.getNamed(ISpecificCIResolver, SpecificCIResolverTags.githubActions); + + describe(`resolve`, () => { + it(`should expose the proper name`, async () => { + expect(githubActionsResolver.resolverName).toBe(`Github Actions`); + }); + + it(`should resolve node js from github actions configuration`, async () => { + const repoPath = path.join(resourcesDir, `github`); + const versions = await githubActionsResolver.resolve({ + repoPath, + }); + expect(versions).toEqual({ nodeVersions: new Set([`12.x`, `6`, `10.x`, `4.x`]) }); + }); + }); + + describe(`isRelevant`, () => { + it(`should return true from relevant repo`, async () => { + const repoPath = path.join(resourcesDir, `github`); + const result = await githubActionsResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + }); + + it(`should return false from non relevant repo`, async () => { + const repoPath = path.join(resourcesDir, `empty`); + const result = await githubActionsResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + }); + }); +}); diff --git a/testInteg/src/resolvers/ciResolver/impl/resolvers/travis.spec.ts b/testInteg/src/resolvers/ciResolver/impl/resolvers/travis.spec.ts deleted file mode 100644 index aca9aec1..00000000 --- a/testInteg/src/resolvers/ciResolver/impl/resolvers/travis.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { TravisCiResolver } from '../../../../../../src/resolvers/ciResolver/impl/resolvers/travis'; -import * as path from 'path'; -import { resourcesDir } from '../../../../../common'; -import { LTS_VERSION } from '../../../../../../src/resolvers/ciResolver'; -import * as fs from 'fs'; - -describe(`travis ci`, () => { - const travisCiResolver = new TravisCiResolver(fs); - - it(`should expose the proper name`, async () => { - expect(travisCiResolver.resolverName).toBe(`travisCi`); - }); - - it(`should resolve node js from travis configuration`, async () => { - const repoPath = path.join(resourcesDir, `travis`); - const versions = await travisCiResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([`6`, `7`, `8`, `9`, `10`]); - }); - - it(`should resolve lts version`, async () => { - const repoPath = path.join(resourcesDir, `travisLts`); - const versions = await travisCiResolver.resolve({ - repoPath, - }); - expect(versions).toEqual([LTS_VERSION]); - }); - - it(`should throw due to faulty configuration`, async () => { - const repoPath = path.join(resourcesDir, `travisFaulty`); - const promise = travisCiResolver.resolve({ - repoPath, - }); - await expect(promise).rejects.toBeInstanceOf(Error); - }); - - it(`should return undefined from non relevant repo`, async () => { - const repoPath = path.join(resourcesDir, `empty`); - const versions = await travisCiResolver.resolve({ - repoPath, - }); - expect(versions).toBeFalsy(); - }); -}); diff --git a/testInteg/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.spec.ts b/testInteg/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.spec.ts new file mode 100644 index 00000000..79bf26f3 --- /dev/null +++ b/testInteg/src/resolvers/ciResolver/impl/resolvers/travisci/travisCiResolver.spec.ts @@ -0,0 +1,50 @@ +import * as path from 'path'; +import { resourcesDir } from '../../../../../../common'; +import { container } from '../../../../../../../src/container'; +import { ISpecificCIResolver, SpecificCIResolverTags } from '../../../../../../../src/resolvers/ciResolver'; + +describe(`travis ci resolver`, () => { + const travisCiResolver = container.getNamed(ISpecificCIResolver, SpecificCIResolverTags.travisCi); + + describe(`resolve`, () => { + it(`should expose the proper name`, async () => { + expect(travisCiResolver.resolverName).toBe(`TravisCi`); + }); + + it(`should resolve node js from travis configuration`, async () => { + const repoPath = path.join(resourcesDir, `travis`); + const versions = await travisCiResolver.resolve({ + repoPath, + }); + expect(versions).toEqual({ + nodeVersions: new Set([`6`, `__LTS__dummy`, `__LTS*__`, `8`, `9`, `10`, `__STABLE__`]), + }); + }); + + it(`should resolve lts version`, async () => { + const repoPath = path.join(resourcesDir, `travisLts`); + const versions = await travisCiResolver.resolve({ + repoPath, + }); + expect(versions).toEqual({ nodeVersions: new Set([`10`, `__LTS*__`]) }); + }); + }); + + describe(`isRelevant`, () => { + it(`should return true from relevant repo`, async () => { + const repoPath = path.join(resourcesDir, `travis`); + const result = await travisCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(true); + }); + + it(`should return false from non relevant repo`, async () => { + const repoPath = path.join(resourcesDir, `empty`); + const result = await travisCiResolver.isRelevant({ + repoPath, + }); + expect(result).toBe(false); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 560d77d0..402cd388 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "checkJs": false, "allowJs": false, "strict": true, + "skipLibCheck": true, "noImplicitAny": true, "noImplicitThis": true, "noUnusedLocals": true, @@ -17,7 +18,7 @@ "alwaysStrict": true, "noImplicitUseStrict": false, "preserveSymlinks": true, - "lib": ["es2018"], + "lib": ["es2018", "esnext.array"], "sourceMap": false, "inlineSourceMap": true, "declaration": false, diff --git a/yarn.lock b/yarn.lock index 77f0f73e..51c46f30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,54 +10,45 @@ "@babel/highlight" "^7.8.3" "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.7.tgz#b69017d221ccdeb203145ae9da269d72cf102f3b" - integrity sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA== + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" + integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.7" - "@babel/helpers" "^7.8.4" - "@babel/parser" "^7.8.7" + "@babel/generator" "^7.9.0" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helpers" "^7.9.0" + "@babel/parser" "^7.9.0" "@babel/template" "^7.8.6" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.7" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" - json5 "^2.1.0" + json5 "^2.1.2" lodash "^4.17.13" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.0.tgz#0f67adea4ec39dad6e63345f70eec33014d78c89" - integrity sha512-onl4Oy46oGCzymOXtKMQpI7VXtCbTSHK1kqBydZ6AmzuNcacEVqGk9tZtAS+48IA9IstZcDCgIg8hQKnb7suRw== - dependencies: - "@babel/types" "^7.9.0" - jsesc "^2.5.1" - lodash "^4.17.13" - source-map "^0.5.0" - -"@babel/generator@^7.8.6", "@babel/generator@^7.8.7": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.8.tgz#cdcd58caab730834cee9eeadb729e833b625da3e" - integrity sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg== +"@babel/generator@^7.4.0", "@babel/generator@^7.9.0", "@babel/generator@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9" + integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== dependencies: - "@babel/types" "^7.8.7" + "@babel/types" "^7.9.5" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" - integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== +"@babel/helper-function-name@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" + integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== dependencies: "@babel/helper-get-function-arity" "^7.8.3" "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/types" "^7.9.5" "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" @@ -66,11 +57,63 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-member-expression-to-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" + integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-imports@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" + integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-transforms@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" + integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.6" + "@babel/types" "^7.9.0" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" + integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== +"@babel/helper-replace-supers@^7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" + integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.8.6" + "@babel/types" "^7.8.6" + +"@babel/helper-simple-access@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" + integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" @@ -78,64 +121,108 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helper-validator-identifier@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" - integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw== +"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" + integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== -"@babel/helpers@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" - integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w== +"@babel/helpers@^7.9.0": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f" + integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== dependencies: "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.4" - "@babel/types" "^7.8.3" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" "@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" + integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== dependencies: + "@babel/helper-validator-identifier" "^7.9.0" chalk "^2.0.0" - esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.8.7": - version "7.8.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.8.tgz#4c3b7ce36db37e0629be1f0d50a571d2f86f6cd4" - integrity sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA== +"@babel/parser@^7.1.0", "@babel/parser@^7.4.3", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.9.0": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" + integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== -"@babel/parser@^7.4.3", "@babel/parser@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.0.tgz#f821b32313f07ee570976d3f6238e8d2d66e0a8e" - integrity sha512-Iwyp00CZsypoNJcpXCbq3G4tcDgphtlMwMVrMhhZ//XBkqjXF7LW6V511yk0+pBX3ZwwGnPea+pTKNJiqA7pUg== +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-bigint@^7.0.0": +"@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-object-rest-spread@^7.0.0": +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz#6cb933a8872c8d359bfde69bbeaae5162fd1e8f7" + integrity sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz#3995d7d7ffff432f6ddc742b47e730c054599897" + integrity sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" + integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/runtime@^7.7.6": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.0.tgz#337eda67401f5b066a6f205a3113d4ac18ba495b" - integrity sha512-cTIudHnzuWLS56ik4DnRnqqNf8MkdUzV4iFFI1h7Jo9xvrpQROYaAnaSd2mHLQAzzZAPfATynX5ord6YlNYNMA== +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: - regenerator-runtime "^0.13.4" + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" -"@babel/runtime@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" - integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== +"@babel/runtime@^7.7.6", "@babel/runtime@^7.9.2": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" + integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== dependencies: regenerator-runtime "^0.13.4" @@ -148,51 +235,27 @@ "@babel/parser" "^7.8.6" "@babel/types" "^7.8.6" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" - integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2" + integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.6" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/traverse@^7.4.3": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892" - integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w== - dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.9.0" - "@babel/helper-function-name" "^7.8.3" + "@babel/generator" "^7.9.5" + "@babel/helper-function-name" "^7.9.5" "@babel/helper-split-export-declaration" "^7.8.3" "@babel/parser" "^7.9.0" - "@babel/types" "^7.9.0" + "@babel/types" "^7.9.5" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.7.tgz#1fc9729e1acbb2337d5b6977a63979b4819f5d1d" - integrity sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw== +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444" + integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== dependencies: - esutils "^2.0.2" - lodash "^4.17.13" - to-fast-properties "^2.0.0" - -"@babel/types@^7.4.0", "@babel/types@^7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" - integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== - dependencies: - "@babel/helper-validator-identifier" "^7.9.0" + "@babel/helper-validator-identifier" "^7.9.5" lodash "^4.17.13" to-fast-properties "^2.0.0" @@ -363,46 +426,46 @@ chalk "^2.0.1" slash "^2.0.0" -"@jest/console@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.1.0.tgz#1fc765d44a1e11aec5029c08e798246bd37075ab" - integrity sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA== +"@jest/console@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.3.0.tgz#33b56b81238427bf3ebe3f7b3378d2f79cdbd409" + integrity sha512-LvSDNqpmZIZyweFaEQ6wKY7CbexPitlsLHGJtcooNECo0An/w49rFhjCJzu6efeb6+a3ee946xss1Jcd9r03UQ== dependencies: - "@jest/source-map" "^25.1.0" + "@jest/source-map" "^25.2.6" chalk "^3.0.0" - jest-util "^25.1.0" + jest-util "^25.3.0" slash "^3.0.0" -"@jest/core@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.1.0.tgz#3d4634fc3348bb2d7532915d67781cdac0869e47" - integrity sha512-iz05+NmwCmZRzMXvMo6KFipW7nzhbpEawrKrkkdJzgytavPse0biEnCNr2wRlyCsp3SmKaEY+SGv7YWYQnIdig== +"@jest/core@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.3.0.tgz#80f97a7a8b59dde741a24f30871cc26d0197d426" + integrity sha512-+D5a/tFf6pA/Gqft2DLBp/yeSRgXhlJ+Wpst0X/ZkfTRP54qDR3C61VfHwaex+GzZBiTcE9vQeoZ2v5T10+Mqw== dependencies: - "@jest/console" "^25.1.0" - "@jest/reporters" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/console" "^25.3.0" + "@jest/reporters" "^25.3.0" + "@jest/test-result" "^25.3.0" + "@jest/transform" "^25.3.0" + "@jest/types" "^25.3.0" ansi-escapes "^4.2.1" chalk "^3.0.0" exit "^0.1.2" graceful-fs "^4.2.3" - jest-changed-files "^25.1.0" - jest-config "^25.1.0" - jest-haste-map "^25.1.0" - jest-message-util "^25.1.0" - jest-regex-util "^25.1.0" - jest-resolve "^25.1.0" - jest-resolve-dependencies "^25.1.0" - jest-runner "^25.1.0" - jest-runtime "^25.1.0" - jest-snapshot "^25.1.0" - jest-util "^25.1.0" - jest-validate "^25.1.0" - jest-watcher "^25.1.0" + jest-changed-files "^25.3.0" + jest-config "^25.3.0" + jest-haste-map "^25.3.0" + jest-message-util "^25.3.0" + jest-regex-util "^25.2.6" + jest-resolve "^25.3.0" + jest-resolve-dependencies "^25.3.0" + jest-runner "^25.3.0" + jest-runtime "^25.3.0" + jest-snapshot "^25.3.0" + jest-util "^25.3.0" + jest-validate "^25.3.0" + jest-watcher "^25.3.0" micromatch "^4.0.2" p-each-series "^2.1.0" - realpath-native "^1.1.0" + realpath-native "^2.0.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" @@ -417,14 +480,14 @@ "@jest/types" "^24.9.0" jest-mock "^24.9.0" -"@jest/environment@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.1.0.tgz#4a97f64770c9d075f5d2b662b5169207f0a3f787" - integrity sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg== +"@jest/environment@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.3.0.tgz#587f28ddb4b0dfe97404d3d4a4c9dbfa0245fb2e" + integrity sha512-vgooqwJTHLLak4fE+TaCGeYP7Tz1Y3CKOsNxR1sE0V3nx3KRUHn3NUnt+wbcfd5yQWKZQKAfW6wqbuwQLrXo3g== dependencies: - "@jest/fake-timers" "^25.1.0" - "@jest/types" "^25.1.0" - jest-mock "^25.1.0" + "@jest/fake-timers" "^25.3.0" + "@jest/types" "^25.3.0" + jest-mock "^25.3.0" "@jest/fake-timers@^24.9.0": version "24.9.0" @@ -435,28 +498,27 @@ jest-message-util "^24.9.0" jest-mock "^24.9.0" -"@jest/fake-timers@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.1.0.tgz#a1e0eff51ffdbb13ee81f35b52e0c1c11a350ce8" - integrity sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ== +"@jest/fake-timers@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.3.0.tgz#995aad36d5c8984165ca5db12e740ab8dbf7042a" + integrity sha512-NHAj7WbsyR3qBJPpBwSwqaq2WluIvUQsyzpJTN7XDVk7VnlC/y1BAnaYZL3vbPIP8Nhm0Ae5DJe0KExr/SdMJQ== dependencies: - "@jest/types" "^25.1.0" - jest-message-util "^25.1.0" - jest-mock "^25.1.0" - jest-util "^25.1.0" + "@jest/types" "^25.3.0" + jest-message-util "^25.3.0" + jest-mock "^25.3.0" + jest-util "^25.3.0" lolex "^5.0.0" -"@jest/reporters@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.1.0.tgz#9178ecf136c48f125674ac328f82ddea46e482b0" - integrity sha512-ORLT7hq2acJQa8N+NKfs68ZtHFnJPxsGqmofxW7v7urVhzJvpKZG9M7FAcgh9Ee1ZbCteMrirHA3m5JfBtAaDg== +"@jest/reporters@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.3.0.tgz#7f39f0e6911561cc5112a1b54656de18faee269b" + integrity sha512-1u0ZBygs0C9DhdYgLCrRfZfNKQa+9+J7Uo+Z9z0RWLHzgsxhoG32lrmMOtUw48yR6bLNELdvzormwUqSk4H4Vg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^25.1.0" - "@jest/environment" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/console" "^25.3.0" + "@jest/test-result" "^25.3.0" + "@jest/transform" "^25.3.0" + "@jest/types" "^25.3.0" chalk "^3.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" @@ -465,12 +527,11 @@ istanbul-lib-instrument "^4.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.0" - jest-haste-map "^25.1.0" - jest-resolve "^25.1.0" - jest-runtime "^25.1.0" - jest-util "^25.1.0" - jest-worker "^25.1.0" + istanbul-reports "^3.0.2" + jest-haste-map "^25.3.0" + jest-resolve "^25.3.0" + jest-util "^25.3.0" + jest-worker "^25.2.6" slash "^3.0.0" source-map "^0.6.0" string-length "^3.1.0" @@ -488,10 +549,10 @@ graceful-fs "^4.1.15" source-map "^0.6.0" -"@jest/source-map@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.1.0.tgz#b012e6c469ccdbc379413f5c1b1ffb7ba7034fb0" - integrity sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA== +"@jest/source-map@^25.2.6": + version "25.2.6" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.2.6.tgz#0ef2209514c6d445ebccea1438c55647f22abb4c" + integrity sha512-VuIRZF8M2zxYFGTEhkNSvQkUKafQro4y+mwUxy5ewRqs5N/ynSFUODYp3fy1zCnbCMy1pz3k+u57uCqx8QRSQQ== dependencies: callsites "^3.0.0" graceful-fs "^4.2.3" @@ -506,14 +567,13 @@ "@jest/types" "^24.9.0" "@types/istanbul-lib-coverage" "^2.0.0" -"@jest/test-result@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.1.0.tgz#847af2972c1df9822a8200457e64be4ff62821f7" - integrity sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg== +"@jest/test-result@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.3.0.tgz#137fab5e5c6fed36e5d40735d1eb029325e3bf06" + integrity sha512-mqrGuiiPXl1ap09Mydg4O782F3ouDQfsKqtQzIjitpwv3t1cHDwCto21jThw6WRRE+dKcWQvLG70GpyLJICfGw== dependencies: - "@jest/console" "^25.1.0" - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/console" "^25.3.0" + "@jest/types" "^25.3.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" @@ -527,15 +587,15 @@ jest-runner "^24.9.0" jest-runtime "^24.9.0" -"@jest/test-sequencer@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.1.0.tgz#4df47208542f0065f356fcdb80026e3c042851ab" - integrity sha512-WgZLRgVr2b4l/7ED1J1RJQBOharxS11EFhmwDqknpknE0Pm87HLZVS2Asuuw+HQdfQvm2aXL2FvvBLxOD1D0iw== +"@jest/test-sequencer@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.3.0.tgz#271ad5f2b8f8137d092ccedc87e16a50f8676209" + integrity sha512-Xvns3xbji7JCvVcDGvqJ/pf4IpmohPODumoPEZJ0/VgC5gI4XaNVIBET2Dq5Czu6Gk3xFcmhtthh/MBOTljdNg== dependencies: - "@jest/test-result" "^25.1.0" - jest-haste-map "^25.1.0" - jest-runner "^25.1.0" - jest-runtime "^25.1.0" + "@jest/test-result" "^25.3.0" + jest-haste-map "^25.3.0" + jest-runner "^25.3.0" + jest-runtime "^25.3.0" "@jest/transform@^24.9.0": version "24.9.0" @@ -559,24 +619,24 @@ source-map "^0.6.1" write-file-atomic "2.4.1" -"@jest/transform@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.1.0.tgz#221f354f512b4628d88ce776d5b9e601028ea9da" - integrity sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ== +"@jest/transform@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.3.0.tgz#083c5447d5307d9b9494d6968115b647460e71f1" + integrity sha512-W01p8kTDvvEX6kd0tJc7Y5VdYyFaKwNWy1HQz6Jqlhu48z/8Gxp+yFCDVj+H8Rc7ezl3Mg0hDaGuFVkmHOqirg== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" babel-plugin-istanbul "^6.0.0" chalk "^3.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.3" - jest-haste-map "^25.1.0" - jest-regex-util "^25.1.0" - jest-util "^25.1.0" + jest-haste-map "^25.3.0" + jest-regex-util "^25.2.6" + jest-util "^25.3.0" micromatch "^4.0.2" pirates "^4.0.1" - realpath-native "^1.1.0" + realpath-native "^2.0.0" slash "^3.0.0" source-map "^0.6.1" write-file-atomic "^3.0.0" @@ -590,10 +650,10 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@jest/types@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.1.0.tgz#b26831916f0d7c381e11dbb5e103a72aed1b4395" - integrity sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA== +"@jest/types@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.3.0.tgz#88f94b277a1d028fd7117bc1f74451e0fc2131e7" + integrity sha512-UkaDNewdqXAmCDbN2GlUM6amDKS78eCqiw/UmF5nE0mmLTd6moJkiZJML/X52Ke3LH7Swhw883IRXq8o9nWjVw== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^1.1.1" @@ -638,9 +698,9 @@ readdir-scoped-modules "^1.1.0" "@npmcli/promise-spawn@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.1.0.tgz#660009a5c54209142ec7c469c190d212834b6087" - integrity sha512-FwbuYN9KXBkloLeIR3xRgI8dyOdfK/KzaJlChszNuwmUXD1lHXfLlSeo4n4KrKt2udIK9K9/TzlnyCA3ubM2fA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.2.0.tgz#167d70b926f771c8bd8b9183bfc8b5aec29d7e45" + integrity sha512-nFtqjVETliApiRdjbYwKwhlSHx2ZMagyj5b9YbNt0BWeeOVxJd47ZVE2u16vxDHyTOZvk+YLV7INwfAE9a2uow== dependencies: infer-owner "^1.0.4" @@ -657,12 +717,75 @@ integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== "@sinonjs/commons@^1.7.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" - integrity sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ== + version "1.7.2" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" + integrity sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw== dependencies: type-detect "4.0.8" +"@stryker-mutator/api@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/api/-/api-3.1.0.tgz#7eb6f1e1f2af17ff0425c6aa0d4d244ca822e972" + integrity sha512-HhfcATYxcIWpzOZ2J8MiqUkNyipF4QvJi8aDxI2r0Zc2aLkJZWj/VMrOuCVX/5AJqCpAJXSeVk/A+z2A15fQEg== + dependencies: + mutation-testing-report-schema "~1.3.0" + surrial "~2.0.2" + tslib "~1.11.1" + +"@stryker-mutator/core@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/core/-/core-3.1.0.tgz#8e0842534c75359832a5b67852bbdb0716d9124c" + integrity sha512-riO2ccmOklvR5qXVrcEtUBqkryNY27lL85ZkFfcnPtOon0y1XtDClLz/DogLr4FH414RqHBL5ZMjrk0Bf/sKug== + dependencies: + "@stryker-mutator/api" "^3.1.0" + "@stryker-mutator/util" "^3.1.0" + chalk "~3.0.0" + commander "~4.1.0" + file-url "~3.0.0" + get-port "~5.0.0" + glob "~7.1.2" + inquirer "~7.1.0" + istanbul-lib-instrument "~3.3.0" + lodash.flatmap "^4.5.0" + lodash.groupby "^4.6.0" + log4js "6.1.2" + mkdirp "~1.0.3" + mutation-testing-elements "~1.3.0" + mutation-testing-metrics "~1.3.0" + progress "~2.0.0" + rimraf "~3.0.0" + rxjs "~6.5.1" + source-map "~0.7.3" + surrial "~2.0.2" + tree-kill "~1.2.0" + tslib "~1.11.1" + typed-inject "~2.1.1" + typed-rest-client "~1.7.1" + +"@stryker-mutator/jest-runner@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/jest-runner/-/jest-runner-3.1.0.tgz#97051a9a81b620e6f09a9821f88ef4b1ce821b0f" + integrity sha512-IbnMjMEAiKmSuXJRYeScGEI1sssM4hNj82X0UGHfUlDbz8k1HWxfYjueHQ/DXurf6qXPd0LXwKh5rW1WLEYF6A== + dependencies: + "@stryker-mutator/api" "^3.1.0" + semver "~6.3.0" + +"@stryker-mutator/typescript@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/typescript/-/typescript-3.1.0.tgz#beb4f220acded7c8c6498975c673dba5ca57e2b4" + integrity sha512-HExZsNguVD93V4x1nzq+qZqpeGJ7qujGeW+vHLSVX7l3O0hmUNDAW48JA69Q1R9X9drVExNsfVfWNbQH1IEXUQ== + dependencies: + "@stryker-mutator/api" "^3.1.0" + "@stryker-mutator/util" "^3.1.0" + lodash.flatmap "~4.5.0" + semver "~6.3.0" + tslib "~1.11.1" + +"@stryker-mutator/util@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@stryker-mutator/util/-/util-3.1.0.tgz#6b1989a3c50eee76579b1ff20cbe5e62717a141a" + integrity sha512-9u5cekP4VfYOdrBoRIJeYiVXiW/5EQfwPe029GFNn07OiSDsikiQQDI23xzew3DzDFTzQfN1dsPgqjB/r5kolw== + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -675,10 +798,10 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.0.0.tgz#9c13c2574c92d4503b005feca8f2e16cc1611506" integrity sha512-KYyTT/T6ALPkIRd2Ge080X/BsXvy9O0hcWTtMWkPvwAwF99+vn6Dv4GzrFT/Nn1LePr+FFDbRXXlqmsy9lw2zA== -"@types/babel__core@^7.1.0": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.6.tgz#16ff42a5ae203c9af1c6e190ed1f30f83207b610" - integrity sha512-tTnhWszAqvXnhW7m5jQU9PomXSiKXk2sFxpahXvI20SZKu9ylPi8WtIxueZ6ehDWikPT0jeFujMj3X4ZHuf3Tg== +"@types/babel__core@^7.1.0", "@types/babel__core@^7.1.7": + version "7.1.7" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" + integrity sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -702,9 +825,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.9.tgz#be82fab304b141c3eee81a4ce3b034d0eba1590a" - integrity sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw== + version "7.0.10" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.10.tgz#d9a99f017317d9b3d1abc2ced45d3bca68df0daf" + integrity sha512-74fNdUGrWsgIB/V9kTO5FGHPWYY6Eqn+3Z7L6Hc4e/BxjYV7puvBqp5HwsVYYfLm6iURYBNCx4Ut37OF9yitCw== dependencies: "@babel/types" "^7.3.0" @@ -745,53 +868,48 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/jest@^25.1.4": - version "25.1.4" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.1.4.tgz#9e9f1e59dda86d3fd56afce71d1ea1b331f6f760" - integrity sha512-QDDY2uNAhCV7TMCITrxz+MRk1EizcsevzfeS6LykIlq2V1E5oO4wXG8V2ZEd9w7Snxeeagk46YbMgZ8ESHx3sw== +"@types/jest-when@^2.7.1": + version "2.7.1" + resolved "https://registry.yarnpkg.com/@types/jest-when/-/jest-when-2.7.1.tgz#0b04a33a48a17370c390e9830a975822b3ac5e32" + integrity sha512-PRrGzDkU859cdkFL2KwWN4fRLRDGIUkRNT0StbthhKmj+naU4wImpoJeMnhjprvSou4pKAzU0dKfdQvjceJVhg== + dependencies: + "@types/jest" "*" + +"@types/jest@*", "@types/jest@^25.2.1": + version "25.2.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.1.tgz#9544cd438607955381c1bdbdb97767a249297db5" + integrity sha512-msra1bCaAeEdkSyA0CZ6gW1ukMIvZ5YoJkdXw/qhQdsuuDlFTcEUrUw8CLCPt2rVRUfXlClVvK2gvPs9IokZaA== dependencies: - jest-diff "^25.1.0" - pretty-format "^25.1.0" + jest-diff "^25.2.1" + pretty-format "^25.2.1" "@types/json-schema@^7.0.3": version "7.0.4" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== -"@types/moment@^2.13.0": - version "2.13.0" - resolved "https://registry.yarnpkg.com/@types/moment/-/moment-2.13.0.tgz#604ebd189bc3bc34a1548689404e61a2a4aac896" - integrity sha1-YE69GJvDvDShVIaJQE5hoqSqyJY= - dependencies: - moment "*" - -"@types/node-fetch@*", "@types/node-fetch@^2.5.5": - version "2.5.5" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.5.tgz#cd264e20a81f4600a6c52864d38e7fef72485e92" - integrity sha512-IWwjsyYjGw+em3xTvWVQi5MgYKbRs0du57klfTaZkv/B24AEQ/p/IopNeqIYNy3EsfHOpg8ieQSDomPcsYMHpA== +"@types/node-fetch@*", "@types/node-fetch@^2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.6.tgz#df8377a66e64ddf75b65b072e37b3c5c5425a96f" + integrity sha512-2w0NTwMWF1d3NJMK0Uiq2UNN8htVCyOWOD0jIPjPgC5Ph/YP4dVhs9YxxcMcuLuwAslz0dVEcZQUaqkLs3IzOQ== dependencies: "@types/node" "*" form-data "^3.0.0" -"@types/node@*": - version "13.9.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.2.tgz#ace1880c03594cc3e80206d96847157d8e7fa349" - integrity sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg== - -"@types/node@^13.9.3": - version "13.9.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.3.tgz#6356df2647de9eac569f9a52eda3480fa9e70b4d" - integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA== +"@types/node@*", "@types/node@^13.13.0": + version "13.13.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.0.tgz#30d2d09f623fe32cde9cb582c7a6eda2788ce4a8" + integrity sha512-WE4IOAC6r/yBZss1oQGM5zs2D7RuKR6Q+w+X2SouPofnWn+LbCqClRyhO3ZE7Ix8nmFgo/oVuuE01cJT2XB13A== "@types/npm-package-arg@*": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/npm-package-arg/-/npm-package-arg-6.1.0.tgz#88bdfce72f6a3d5fa1053c8d44d655e7850642e4" integrity sha512-vbt5fb0y1svMhu++1lwtKmZL76d0uPChFlw7kEzyUmTwfmpHRcFb8i0R8ElT69q/L+QLgK2hgECivIAvaEDwag== -"@types/npm-registry-fetch@^4": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/npm-registry-fetch/-/npm-registry-fetch-4.0.2.tgz#3272964a88902e6233efd640294b17d32ba43df3" - integrity sha512-0cZ4uIsciQl9BE5cR2v1viyunyhnwJeFm+HuIyjHsPXjCIemgwchhh61nh5LehvaEAs3RGDCbCJpkgaEseLk9w== +"@types/npm-registry-fetch@*": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@types/npm-registry-fetch/-/npm-registry-fetch-8.0.0.tgz#b66607c291f0e9f15b50ea3fef0d392e9c4e69de" + integrity sha512-3dtNw1VMy1gnaklK0746/cOkJYMvdY/3FNuRDR5ih+WUWIbgTR6JvKq6hmhW4G1/o6lPdjHECPaWcowXgWZDyg== dependencies: "@types/node" "*" "@types/node-fetch" "*" @@ -804,19 +922,26 @@ resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4" integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA== -"@types/pacote@^9.5.1": - version "9.5.1" - resolved "https://registry.yarnpkg.com/@types/pacote/-/pacote-9.5.1.tgz#592d508814bcb522cb70144eade9b6989f44cbb0" - integrity sha512-c5aRs29+G7wDiiHb1N083tkHEs5p6TeJ+f02fMsDpWqOilZfuD0IQUL3Rh9YjrqkVdu7PXZMcfGJNuT6bZunbg== +"@types/pacote@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@types/pacote/-/pacote-11.1.0.tgz#5324b9d36ecb31ad45bc09c9c29c996b7c0fc27d" + integrity sha512-ULeeKzbZ3e5GRlqbVDUDgi0L0RYg4OJXLSrtOoyTmGJTAN7JdR0IyZ0kXCCjGzkZwh4ABM7TKISwyDkJqnbAgw== dependencies: "@types/node" "*" - "@types/npm-registry-fetch" "^4" + "@types/npm-registry-fetch" "*" + "@types/npmlog" "*" + "@types/ssri" "*" "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== + "@types/semver@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.1.0.tgz#c8c630d4c18cd326beff77404887596f96408408" @@ -824,6 +949,11 @@ dependencies: "@types/node" "*" +"@types/shell-quote@^1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@types/shell-quote/-/shell-quote-1.6.1.tgz#eed0cd93ac3628427d7b44c4550f854f9400e39f" + integrity sha512-t0bxRLQ75MMF7EeICa1eYC//o1/6gPaUV7ELke4l4OkwpZ9apOzvv2oR5F2PmQJ3tM83Lo+MNKfAXn5gQRMcnA== + "@types/ssri@*": version "6.0.1" resolved "https://registry.yarnpkg.com/@types/ssri/-/ssri-6.0.1.tgz#7d15320522d8005e3a435ebf56c76f7d051fe437" @@ -844,7 +974,7 @@ resolved "https://registry.yarnpkg.com/@types/yaml/-/yaml-1.2.0.tgz#4ed577fc4ebbd6b829b28734e56d10c9e6984e09" integrity sha512-GW8b9qM+ebgW3/zjzPm0I1NxMvLaz/YKT9Ph6tTb+Fkeyzd9yLTvQ6ciQ2MorTRmb/qXmfjMerRpG4LviixaqQ== -"@types/yargs-parser@*": +"@types/yargs-parser@*", "@types/yargs-parser@^15.0.0": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== @@ -863,62 +993,40 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.25.0.tgz#0b60917332f20dcff54d0eb9be2a9e9f4c9fbd02" - integrity sha512-W2YyMtjmlrOjtXc+FtTelVs9OhuR6OlYc4XKIslJ8PUJOqgYYAPRJhAqkYRQo3G4sjvG8jSodsNycEn4W2gHUw== +"@typescript-eslint/eslint-plugin@^2.28.0": + version "2.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.28.0.tgz#4431bc6d3af41903e5255770703d4e55a0ccbdec" + integrity sha512-w0Ugcq2iatloEabQP56BRWJowliXUP5Wv6f9fKzjJmDW81hOTBxRoJ4LoEOxRpz9gcY51Libytd2ba3yLmSOfg== dependencies: - "@typescript-eslint/experimental-utils" "2.25.0" + "@typescript-eslint/experimental-utils" "2.28.0" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.25.0.tgz#13691c4fe368bd377b1e5b1e4ad660b220bf7714" - integrity sha512-0IZ4ZR5QkFYbaJk+8eJ2kYeA+1tzOE1sBjbwwtSV85oNWYUBep+EyhlZ7DLUCyhMUGuJpcCCFL0fDtYAP1zMZw== +"@typescript-eslint/experimental-utils@2.28.0", "@typescript-eslint/experimental-utils@^2.5.0": + version "2.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.28.0.tgz#1fd0961cd8ef6522687b4c562647da6e71f8833d" + integrity sha512-4SL9OWjvFbHumM/Zh/ZeEjUFxrYKtdCi7At4GyKTbQlrj1HcphIDXlje4Uu4cY+qzszR5NdVin4CCm6AXCjd6w== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.25.0" + "@typescript-eslint/typescript-estree" "2.28.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/experimental-utils@^2.5.0": - version "2.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.24.0.tgz#a5cb2ed89fedf8b59638dc83484eb0c8c35e1143" - integrity sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.24.0" - eslint-scope "^5.0.0" - -"@typescript-eslint/parser@^2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.25.0.tgz#abfb3d999084824d9a756d9b9c0f36fba03adb76" - integrity sha512-mccBLaBSpNVgp191CP5W+8U1crTyXsRziWliCqzj02kpxdjKMvFHGJbK33NroquH3zB/gZ8H511HEsJBa2fNEg== +"@typescript-eslint/parser@^2.28.0": + version "2.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.28.0.tgz#bb761286efd2b0714761cab9d0ee5847cf080385" + integrity sha512-RqPybRDquui9d+K86lL7iPqH6Dfp9461oyqvlXMNtap+PyqYbkY5dB7LawQjDzot99fqzvS0ZLZdfe+1Bt3Jgw== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.25.0" - "@typescript-eslint/typescript-estree" "2.25.0" + "@typescript-eslint/experimental-utils" "2.28.0" + "@typescript-eslint/typescript-estree" "2.28.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.24.0": - version "2.24.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.24.0.tgz#38bbc8bb479790d2f324797ffbcdb346d897c62a" - integrity sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^6.3.0" - tsutils "^3.17.1" - -"@typescript-eslint/typescript-estree@2.25.0": - version "2.25.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.25.0.tgz#b790497556734b7476fa7dd3fa539955a5c79e2c" - integrity sha512-VUksmx5lDxSi6GfmwSK7SSoIKSw9anukWWNitQPqt58LuYrKalzsgeuignbqnB+rK/xxGlSsCy8lYnwFfB6YJg== +"@typescript-eslint/typescript-estree@2.28.0": + version "2.28.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.28.0.tgz#d34949099ff81092c36dc275b6a1ea580729ba00" + integrity sha512-HDr8MP9wfwkiuqzRVkuM3BeDrOC4cKbO5a6BymZBHUt5y/2pL0BXD6I/C/ceq2IZoHWhcASk+5/zo+dwgu9V8Q== dependencies: debug "^4.1.1" eslint-visitor-keys "^1.1.0" @@ -1004,23 +1112,24 @@ aggregate-error@^3.0.0: indent-string "^4.0.0" ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: - version "6.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7" - integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw== + version "6.12.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.1.tgz#cce4d7dfcd62d3c57b1cd772e688eff5f5cd3839" + integrity sha512-AUh2mDlJDAnzSRaKkMHopTD1GKwC1ApUq8oCzdjAOM5tavncgqWU+JoRu5Y3iYY0Q/euiU+1LWp0/O/QY8CcHw== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.4.1" + opencollective-postinstall "^2.0.2" uri-js "^4.2.2" -all-contributors-cli@^6.14.0: - version "6.14.0" - resolved "https://registry.yarnpkg.com/all-contributors-cli/-/all-contributors-cli-6.14.0.tgz#9ecddabd9c1befef660f7589289b264db94bbbc8" - integrity sha512-rGpKRWXFaxAVWHLRBuJMvkI/86FIW7z2gvuEVTA60FPCw6Z/y0vDZgTATVEJQ4XV2+eSM5BHLZGW7YQTwTHCJQ== +all-contributors-cli@^6.14.2: + version "6.14.2" + resolved "https://registry.yarnpkg.com/all-contributors-cli/-/all-contributors-cli-6.14.2.tgz#4d7652f700a827b9a2f749720f13605ee1c2f8bd" + integrity sha512-DUeZrL0EsXwdYZ1msEeI0cY7DNQqggRKLMfDzgZu+l/72KLf/ZuT97wnUIGAFvSPUlfSAXIVDhivOlZRFjYH+g== dependencies: "@babel/runtime" "^7.7.6" async "^3.0.1" - chalk "^3.0.0" + chalk "^4.0.0" didyoumean "^1.2.1" inquirer "^7.0.4" json-fixer "^1.4.0" @@ -1169,6 +1278,11 @@ array-find-index@^1.0.1: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= +array-flat-polyfill@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-flat-polyfill/-/array-flat-polyfill-1.0.1.tgz#1e3a4255be619dfbffbfd1d635c1cf357cd034e7" + integrity sha512-hfJmKupmQN0lwi0xG6FQ5U8Rd97RnIERplymOv/qpq8AoNKPPAnxJadjFA23FNWm88wykh9HmpLJUUwUtNU/iw== + array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" @@ -1261,16 +1375,16 @@ babel-jest@^24.9.0: chalk "^2.4.2" slash "^2.0.0" -babel-jest@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.1.0.tgz#206093ac380a4b78c4404a05b3277391278f80fb" - integrity sha512-tz0VxUhhOE2y+g8R2oFrO/2VtVjA1lkJeavlhExuRBg3LdNJY9gwQ+Vcvqt9+cqy71MCTJhewvTB7Qtnnr9SWg== +babel-jest@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.3.0.tgz#999d0c19e8427f66b796bf9ea233eedf087b957c" + integrity sha512-qiXeX1Cmw4JZ5yQ4H57WpkO0MZ61Qj+YnsVUwAMnDV5ls+yHon11XjarDdgP7H8lTmiEi6biiZA8y3Tmvx6pCg== dependencies: - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" - "@types/babel__core" "^7.1.0" + "@jest/transform" "^25.3.0" + "@jest/types" "^25.3.0" + "@types/babel__core" "^7.1.7" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^25.1.0" + babel-preset-jest "^25.3.0" chalk "^3.0.0" slash "^3.0.0" @@ -1302,10 +1416,10 @@ babel-plugin-jest-hoist@^24.9.0: dependencies: "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.1.0.tgz#fb62d7b3b53eb36c97d1bc7fec2072f9bd115981" - integrity sha512-oIsopO41vW4YFZ9yNYoLQATnnN46lp+MZ6H4VvPKFkcc2/fkl3CfE/NZZSmnEIEsJRmJAgkVEK0R7Zbl50CpTw== +babel-plugin-jest-hoist@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.6.tgz#2af07632b8ac7aad7d414c1e58425d5fc8e84909" + integrity sha512-qE2xjMathybYxjiGFJg0mLFrz0qNp83aNZycWDY/SuHiZNq+vQfRQtuINqyXyue1ELd8Rd+1OhFSLjms8msMbw== dependencies: "@types/babel__traverse" "^7.0.6" @@ -1318,6 +1432,22 @@ babel-polyfill@6.26.0: core-js "^2.5.0" regenerator-runtime "^0.10.5" +babel-preset-current-node-syntax@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6" + integrity sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + babel-preset-jest@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" @@ -1326,14 +1456,13 @@ babel-preset-jest@^24.9.0: "@babel/plugin-syntax-object-rest-spread" "^7.0.0" babel-plugin-jest-hoist "^24.9.0" -babel-preset-jest@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.1.0.tgz#d0aebfebb2177a21cde710996fce8486d34f1d33" - integrity sha512-eCGn64olaqwUMaugXsTtGAM2I0QTahjEtnRu0ql8Ie+gDWAc1N6wqN0k2NilnyTunM69Pad7gJY7LOtwLimoFQ== +babel-preset-jest@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.3.0.tgz#9ab40aee52a19bdc52b8b1ec2403d5914ac3d86b" + integrity sha512-tjdvLKNMwDI9r+QWz9sZUQGTq1dpoxjUqFUpEasAc7MOtHg9XuLT2fx0udFG+k1nvMV0WvHHVAN7VmCZ+1Zxbw== dependencies: - "@babel/plugin-syntax-bigint" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^25.1.0" + babel-plugin-jest-hoist "^25.2.6" + babel-preset-current-node-syntax "^0.1.2" babel-runtime@^6.23.0, babel-runtime@^6.26.0: version "6.26.0" @@ -1462,9 +1591,9 @@ buffer-from@1.x, buffer-from@^1.0.0: integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== buffer@^5.1.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.5.0.tgz#9c3caa3d623c33dd1c7ef584b89b88bf9c9bc1ce" - integrity sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww== + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -1474,6 +1603,16 @@ builtins@^1.0.3: resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= +bunyan@^1.8.12: + version "1.8.12" + resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.12.tgz#f150f0f6748abdd72aeae84f04403be2ef113797" + integrity sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c= + optionalDependencies: + dtrace-provider "~0.8" + moment "^2.10.6" + mv "~2" + safe-json-stringify "~1" + cacache@^15.0.0: version "15.0.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.0.tgz#133b59edbd2a37ea8ef2d54964c6f247e47e5059" @@ -1608,6 +1747,14 @@ chalk@^3.0.0, chalk@~3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.0.0, chalk@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" + integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -1690,9 +1837,9 @@ cli-truncate@^0.2.1: string-width "^1.0.1" cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== cliui@^5.0.0: version "5.0.0" @@ -1730,9 +1877,9 @@ code-point-at@^1.0.0: integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= collect-v8-coverage@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz#150ee634ac3650b71d9c985eb7f608942334feb1" - integrity sha512-VKIhJgvk8E1W28m5avZ2Gv2Ruv5YiF56ug2oclvaG9md69BuZImMG2sk9g7QNKLUbtYAKQjXjYxbYZVUlMMKmQ== + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== collection-visit@^1.0.0: version "1.0.0" @@ -1783,16 +1930,16 @@ commander@^2.20.3, commander@^2.9.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - commander@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.0.0.tgz#dbf1909b49e5044f8fdaf0adc809f0c0722bdfd0" integrity sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ== +commander@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + comment-json@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/comment-json/-/comment-json-1.1.3.tgz#6986c3330fee0c4c9e00c2398cd61afa5d8f239e" @@ -1808,6 +1955,11 @@ compare-func@^1.3.1: array-ify "^1.0.0" dot-prop "^3.0.0" +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -1936,10 +2088,10 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@~7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" - integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== +cross-spawn@^7.0.0, cross-spawn@~7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" + integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -1957,10 +2109,10 @@ cspell-dict-bash@^1.0.3: dependencies: configstore "^5.0.0" -cspell-dict-companies@^1.0.20: - version "1.0.20" - resolved "https://registry.yarnpkg.com/cspell-dict-companies/-/cspell-dict-companies-1.0.20.tgz#75c76f6128cebdcfd8c89a0d62e37635f4a1cefe" - integrity sha512-LpDV5YMNV0vG8/LA4S8bbHNwaxI3gHTsCe0XZSGMRFlxO3bWWhi3Il3KB3pdDArDaopTGZKCMXDQsYFy5WHhQA== +cspell-dict-companies@^1.0.21: + version "1.0.22" + resolved "https://registry.yarnpkg.com/cspell-dict-companies/-/cspell-dict-companies-1.0.22.tgz#a30983605888ce530e5c7c2ad1b2b9e33c20fcae" + integrity sha512-P7ziSCteONYjlPHFFqZTnisSEJr9h9FXTJh0t9QQIoKcaNR4wij5GiZDv4p4YubCf0z3GeJ7Uao+99RGeHakRQ== dependencies: configstore "^5.0.0" @@ -2014,9 +2166,9 @@ cspell-dict-fonts@^1.0.5: configstore "^5.0.0" cspell-dict-fullstack@^1.0.22: - version "1.0.22" - resolved "https://registry.yarnpkg.com/cspell-dict-fullstack/-/cspell-dict-fullstack-1.0.22.tgz#54122342ff408082f904c6c20e3facb36df0762c" - integrity sha512-k8Op1ltkgKnMTTo/kgkywE0htwi+3EtYrPPWk+mD9o3IFgC6yLKA89Tkrd0kEEPR3qJvC4gQJmGJns6Y25v0Zg== + version "1.0.23" + resolved "https://registry.yarnpkg.com/cspell-dict-fullstack/-/cspell-dict-fullstack-1.0.23.tgz#c933e3987edf6e81e85bf58ca31e574ff79f9d0c" + integrity sha512-vc/aihpKVD/ML+SLVry6kDWFswW/sQfP9QHrr2ZhQLUwhj9pVMnZvx+u1cV8bhMYltWQZxrDhdAe4jrlBrxLHA== dependencies: configstore "^5.0.0" @@ -2104,44 +2256,44 @@ cspell-dict-scala@^1.0.11: dependencies: configstore "^5.0.0" -cspell-dict-software-terms@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/cspell-dict-software-terms/-/cspell-dict-software-terms-1.0.7.tgz#74f33a36b470c7344ab8bd0fb1bc4f82dcbf27c8" - integrity sha512-Fh8NmDqY+GZRrJJuFUNoIDbR9WoP9mte+nVVGK5u8vurNInOG/MgRL0O/dhDfTmrMlSyAMhlUWm+852sXietEA== +cspell-dict-software-terms@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/cspell-dict-software-terms/-/cspell-dict-software-terms-1.0.8.tgz#14ac832dc3ca1c5ac15ff51ba0f85e5c5111019d" + integrity sha512-a8JkYf1pG5xUTKRPFp+7t4Gn7fhH9M5E94LXVuPPSQrns18pE0DGV0OEK/VbZUagsyRaDaHiZFqedIQ83M2Mpw== dependencies: configstore "^5.0.0" -cspell-dict-typescript@^1.0.3: +cspell-dict-typescript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/cspell-dict-typescript/-/cspell-dict-typescript-1.0.4.tgz#95ca26adf15c5e31cda2506e03ce7b7c18e9fbb0" integrity sha512-cniGSmTohYriEgGJ0PgcQP2GCGP+PH/0WZ2N7BTTemQr/mHTU6bKWy8DVK63YEtYPEmhZv+G2xPBgBD41QQypQ== dependencies: configstore "^5.0.0" -cspell-glob@^0.1.17: - version "0.1.17" - resolved "https://registry.yarnpkg.com/cspell-glob/-/cspell-glob-0.1.17.tgz#e8dad6eedc23bc3e98175f2077df25e7e3212a8a" - integrity sha512-gAiKakWJbHay6cobcJnX1+XhNCFYqR7CJM5GPiEpRZ5RFXYR46fYbkVwTdg3sqbFLErJtghQj/0s5Xa0q9NJpQ== +cspell-glob@^0.1.18: + version "0.1.18" + resolved "https://registry.yarnpkg.com/cspell-glob/-/cspell-glob-0.1.18.tgz#6762774f58d2fe176b6d9ed347a9e5862dc551a8" + integrity sha512-j7XDtSRUgHZNLcnFNI2ngTvkAlC7AI43LAuOYTCgU3+zKMdwzq6C7m/a1c9tWjnPYJiIPf+OEkE9bAhIufzk3Q== dependencies: micromatch "^4.0.2" -cspell-io@^4.0.20: - version "4.0.20" - resolved "https://registry.yarnpkg.com/cspell-io/-/cspell-io-4.0.20.tgz#4aecc054852c712e96e075eb270dbbbc4482fca1" - integrity sha512-fomz1P308XgyyxaOEKdNbh82Ac4AKaz26p4JszV7YkJrGDsXMoByTQjVqdDloNN8FchogSEpLPeQoIg648exBA== +cspell-io@^4.0.21: + version "4.0.21" + resolved "https://registry.yarnpkg.com/cspell-io/-/cspell-io-4.0.21.tgz#f3c051294b5229f67caa17e3b4946985ec8f39c4" + integrity sha512-dht81s3CMPQTqtYqcJ/imEbE7WoYgGR4F52Fotgvd7Kky+H8GgSBnJYLJNk/PuT2xJ/8ebhx7v464v9cD73Okw== dependencies: iconv-lite "^0.4.24" iterable-to-stream "^1.0.1" -cspell-lib@^4.1.21: - version "4.1.21" - resolved "https://registry.yarnpkg.com/cspell-lib/-/cspell-lib-4.1.21.tgz#7321652e5bb8d5a1b0a1372d42d125dc275f4161" - integrity sha512-mcbYQRO9GeLjUU3fTrJEwD17pF/t4YlgYoEqVQkLgR0kCQ5exMFlj8II4UQHgNevx8GMJGpqQ9+fM6ZhCYKIzQ== +cspell-lib@^4.1.23: + version "4.1.23" + resolved "https://registry.yarnpkg.com/cspell-lib/-/cspell-lib-4.1.23.tgz#8d74bcb14f604c9d8eb44ddc3d713d59c331ab42" + integrity sha512-UNLsOEq12xbL8o4SWGsDl1xAwyR8BUlGkwI3uv5Acc7noolObMXOJLp/TLE36PrMKWVmbg8s/9pnUKwrcwTC3w== dependencies: comment-json "^1.1.3" configstore "^5.0.1" cspell-dict-bash "^1.0.3" - cspell-dict-companies "^1.0.20" + cspell-dict-companies "^1.0.21" cspell-dict-cpp "^1.1.26" cspell-dict-django "^1.0.15" cspell-dict-dotnet "^1.0.14" @@ -2162,39 +2314,40 @@ cspell-lib@^4.1.21: cspell-dict-ruby "^1.0.3" cspell-dict-rust "^1.0.12" cspell-dict-scala "^1.0.11" - cspell-dict-software-terms "^1.0.6" - cspell-dict-typescript "^1.0.3" - cspell-io "^4.0.20" - cspell-trie-lib "^4.1.8" - cspell-util-bundle "^4.0.9" + cspell-dict-software-terms "^1.0.7" + cspell-dict-typescript "^1.0.4" + cspell-io "^4.0.21" + cspell-trie-lib "^4.1.9" + cspell-util-bundle "^4.0.11" fs-extra "^8.1.0" - gensequence "^3.0.3" + gensequence "^3.1.1" + minimatch "^3.0.4" vscode-uri "^2.1.1" -cspell-trie-lib@^4.1.8: - version "4.1.8" - resolved "https://registry.yarnpkg.com/cspell-trie-lib/-/cspell-trie-lib-4.1.8.tgz#cc40c863c8c03920c61e5330c3acfcaf9871e40f" - integrity sha512-G0Jpybwxyl7rG3c4tzrROEVmiKAsyIjaDdnGxkzOFkl4tjcZeCh7GIVrqLyyk3VWslrWMVvmQi1/eLDccagepw== +cspell-trie-lib@^4.1.9: + version "4.1.9" + resolved "https://registry.yarnpkg.com/cspell-trie-lib/-/cspell-trie-lib-4.1.9.tgz#ebf38b5affd9a35289e945c7482b112152e5102f" + integrity sha512-Qf/bnXwEwm6oRaZPvELuIva6iJfCr+4WDbcNaNZUd+J3snanMpzp+TsqHyH3p1dPxnvO8eAEnU9RWVUdbXXnfA== dependencies: - gensequence "^3.0.3" + gensequence "^3.1.1" -cspell-util-bundle@^4.0.9: - version "4.0.9" - resolved "https://registry.yarnpkg.com/cspell-util-bundle/-/cspell-util-bundle-4.0.9.tgz#9e6a7f3dcd4aef1b9c6743d33d09379cf94ecd08" - integrity sha512-+xhIGJAkPxD7aKl97S0E34B5dF+HSTSoEL6M2f6Y46tusFGc9VdhA/iIZQooZZx2RQy4WaHw/ABfsRfxtnFVLw== +cspell-util-bundle@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/cspell-util-bundle/-/cspell-util-bundle-4.0.11.tgz#838e493a33a063e2f28df0bd81bcf86c3cf15385" + integrity sha512-6AJRN0KbeTJB+IPpwKb11zFUVz2Q8Rgm4qmy/wsbhw6ICFfmgWG5Fr2OzJpZBCm8GJJg1Tjs/VZimSvCdnRj7g== -cspell@^4.0.55: - version "4.0.55" - resolved "https://registry.yarnpkg.com/cspell/-/cspell-4.0.55.tgz#4295b88c9cb7b1bf7290027212913203be5f3f9d" - integrity sha512-LmBk2VNrBSXFPOpmhJalqYGdDF1x68H4wI3c7RDMRLfG/zOaFoZUEuJUYOC07tFXDosczXMu38Qt9cnEVZmrAA== +cspell@^4.0.57: + version "4.0.57" + resolved "https://registry.yarnpkg.com/cspell/-/cspell-4.0.57.tgz#8323c9d198cbc6dc90bb3ce42f2e5a9585bc0f6d" + integrity sha512-iKQ6iWP4nhMiuu1PnbcVGfZ0tE/NXRqRjYA1Kq/UW35a90WLSecIq8YgJn2J48FtnfWujPzXl/U7Tj4WqleGXg== dependencies: chalk "^2.4.2" commander "^2.20.3" comment-json "^1.1.3" - cspell-glob "^0.1.17" - cspell-lib "^4.1.21" + cspell-glob "^0.1.18" + cspell-lib "^4.1.23" fs-extra "^8.1.0" - gensequence "^3.0.3" + gensequence "^3.1.1" get-stdin "^7.0.0" glob "^7.1.6" minimatch "^3.0.4" @@ -2258,6 +2411,16 @@ date-fns@^1.27.2: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== +date-format@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" + integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== + +date-format@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" + integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== + debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -2331,6 +2494,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -2413,10 +2581,10 @@ diff-sequences@^24.9.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== -diff-sequences@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.1.0.tgz#fd29a46f1c913fd66c22645dc75bffbe43051f32" - integrity sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw== +diff-sequences@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" + integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== diff@^4.0.1: version "4.0.2" @@ -2456,6 +2624,13 @@ dotenv@^6.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== +dtrace-provider@~0.8: + version "0.8.8" + resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.8.tgz#2996d5490c37e1347be263b423ed7b297fb0d97e" + integrity sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg== + dependencies: + nan "^2.14.0" + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -2510,10 +2685,10 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: - version "1.17.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" - integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== +es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: + version "1.17.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" + integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== dependencies: es-to-primitive "^1.2.1" function-bind "^1.1.1" @@ -2572,15 +2747,15 @@ eslint-plugin-jest@^23.8.2: dependencies: "@typescript-eslint/experimental-utils" "^2.5.0" -eslint-plugin-prefer-arrow@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.1.7.tgz#4534dd8d2e519cd579a951f95802137365d524a2" - integrity sha512-epsA4g804mRovlOHSbeO1xxW7REGeUjULRME9MJTJDOVscNIA01AkR66TP4cmHDfD+w72EQ9cPhf37MbZiFI2w== +eslint-plugin-prefer-arrow@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.0.tgz#55f07b07e308f441c09e53a77ad12448ce1b7c29" + integrity sha512-/iaWpfc6CsGNG/OSElmN1/hZP9WG/EnxoCIFcJHT1utRqk8FRQYoyX7xWHo2O03p/9I2dw2lSNsVOYbpfNSsZQ== -eslint-plugin-prettier@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" - integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== +eslint-plugin-prettier@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca" + integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ== dependencies: prettier-linter-helpers "^1.0.0" @@ -2674,11 +2849,11 @@ esprima@^4.0.0, esprima@^4.0.1: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.1.0.tgz#c5c0b66f383e7656404f86b31334d72524eddb48" - integrity sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q== + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== dependencies: - estraverse "^4.0.0" + estraverse "^5.1.0" esrecurse@^4.1.0: version "4.2.1" @@ -2687,11 +2862,16 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -2715,7 +2895,7 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^3.2.0, execa@^3.4.0: +execa@^3.2.0: version "3.4.0" resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== @@ -2731,6 +2911,21 @@ execa@^3.2.0, execa@^3.4.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.0.tgz#7f37d6ec17f09e6b8fc53288611695b6d12b9daf" + integrity sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -2749,7 +2944,7 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^24.9.0: +expect@^24.8.0, expect@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== @@ -2761,17 +2956,17 @@ expect@^24.9.0: jest-message-util "^24.9.0" jest-regex-util "^24.9.0" -expect@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-25.1.0.tgz#7e8d7b06a53f7d66ec927278db3304254ee683ee" - integrity sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g== +expect@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-25.3.0.tgz#5fd36e51befd05afb7184bc954f8a4792d184c71" + integrity sha512-buboTXML2h/L0Kh44Ys2Cx49mX20ISc5KDirkxIs3Q9AJv0kazweUAbukegr+nHDOvFRKmxdojjIHCjqAceYfg== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" ansi-styles "^4.0.0" - jest-get-type "^25.1.0" - jest-matcher-utils "^25.1.0" - jest-message-util "^25.1.0" - jest-regex-util "^25.1.0" + jest-get-type "^25.2.6" + jest-matcher-utils "^25.3.0" + jest-message-util "^25.3.0" + jest-regex-util "^25.2.6" extend-shallow@^2.0.1: version "2.0.1" @@ -2854,9 +3049,9 @@ fb-watchman@^2.0.0: bser "2.1.1" figgy-pudding@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" - integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== figlet@^1.1.1: version "1.3.0" @@ -2897,6 +3092,11 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +file-url@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/file-url/-/file-url-3.0.0.tgz#247a586a746ce9f7a8ed05560290968afc262a77" + integrity sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA== + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -2936,6 +3136,13 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-versions@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" + integrity sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww== + dependencies: + semver-regex "^2.0.0" + flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -2945,10 +3152,10 @@ flat-cache@^2.0.1: rimraf "2.6.3" write "1.0.3" -flatted@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== +flatted@^2.0.0, flatted@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== follow-redirects@1.5.10: version "1.5.10" @@ -3076,10 +3283,10 @@ generate-changelog@^1.8.0: commander "^2.9.0" github-url-from-git "^1.4.0" -gensequence@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/gensequence/-/gensequence-3.0.3.tgz#5e76326bb893147e80d6f2ae495c7e9a2795f7cc" - integrity sha512-KM4L8AfWAfjIvdnBhl7erj35iBNf75pP0+8Ww3BKssVEBv95Dqu40cG62kAyVXtuLplb96wh/GUr+GhM6YG9gQ== +gensequence@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/gensequence/-/gensequence-3.1.1.tgz#95c1afc7c0680f92942c17f2d6f83f3d26ea97af" + integrity sha512-ys3h0hiteRwmY6BsvSttPmkhC0vEQHPJduANBRtH/dlDPZ0UBIb/dXy80IcckXyuQ6LKg+PloRqvGER9IS7F7g== gensync@^1.0.0-beta.1: version "1.0.0-beta.1" @@ -3096,6 +3303,13 @@ get-own-enumerable-property-symbols@^3.0.0: resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-port@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.0.0.tgz#aa22b6b86fd926dd7884de3e23332c9f70c031a6" + integrity sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ== + dependencies: + type-fest "^0.3.0" + get-stdin@7.0.0, get-stdin@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" @@ -3149,13 +3363,24 @@ github-url-from-git@^1.4.0: integrity sha1-+YX+3MCpqledyI16/waNVcxiUaA= glob-parent@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" - integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.2: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -3328,9 +3553,9 @@ html-encoding-sniffer@^1.0.2: whatwg-encoding "^1.0.1" html-escaper@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" - integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-cache-semantics@^4.0.0, http-cache-semantics@^4.0.4: version "4.1.0" @@ -3375,6 +3600,22 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/husky/-/husky-4.2.5.tgz#2b4f7622673a71579f901d9885ed448394b5fa36" + integrity sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ== + dependencies: + chalk "^4.0.0" + ci-info "^2.0.0" + compare-versions "^3.6.0" + cosmiconfig "^6.0.0" + find-versions "^3.2.0" + opencollective-postinstall "^2.0.2" + pkg-dir "^4.2.0" + please-upgrade-node "^3.2.0" + slash "^3.0.0" + which-pm-runs "^1.0.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -3471,7 +3712,7 @@ ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -inquirer@^7.0.0, inquirer@^7.0.4: +inquirer@^7.0.0, inquirer@^7.0.4, inquirer@~7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== @@ -3637,9 +3878,9 @@ is-glob@^4.0.0, is-glob@^4.0.1: is-extglob "^2.1.1" is-installed-globally@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.1.tgz#679afef819347a72584617fd19497f010b8ed35f" - integrity sha512-oiEcGoQbGc+3/iijAijrK2qFpkNoNjsHOm/5V5iaeydyrS/hnwaRCEgH5cpW0P3T1lSjV5piB7S5b5lEugNLhg== + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== dependencies: global-dirs "^2.0.1" is-path-inside "^3.0.1" @@ -3798,7 +4039,7 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^3.3.0: +istanbul-lib-instrument@^3.3.0, istanbul-lib-instrument@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== @@ -3842,10 +4083,10 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.0.tgz#d4d16d035db99581b6194e119bbf36c963c5eb70" - integrity sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A== +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -3855,33 +4096,33 @@ iterable-to-stream@^1.0.1: resolved "https://registry.yarnpkg.com/iterable-to-stream/-/iterable-to-stream-1.0.1.tgz#37e86baacf6b1a0e9233dad4eb526d0423d08bf3" integrity sha512-O62gD5ADMUGtJoOoM9U6LQ7i4byPXUNoHJ6mqsmkQJcom331ZJGDApWgDESWyBMEHEJRjtHozgIiTzYo9RU4UA== -jest-changed-files@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.1.0.tgz#73dae9a7d9949fdfa5c278438ce8f2ff3ec78131" - integrity sha512-bdL1aHjIVy3HaBO3eEQeemGttsq1BDlHgWcOjEOIAcga7OOEGWHD2WSu8HhL7I1F0mFFyci8VKU4tRNk+qtwDA== +jest-changed-files@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.3.0.tgz#85d8de6f4bd13dafda9d7f1e3f2565fc0e183c78" + integrity sha512-eqd5hyLbUjIVvLlJ3vQ/MoPxsxfESVXG9gvU19XXjKzxr+dXmZIqCXiY0OiYaibwlHZBJl2Vebkc0ADEMzCXew== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" execa "^3.2.0" throat "^5.0.0" -jest-cli@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.1.0.tgz#75f0b09cf6c4f39360906bf78d580be1048e4372" - integrity sha512-p+aOfczzzKdo3AsLJlhs8J5EW6ffVidfSZZxXedJ0mHPBOln1DccqFmGCoO8JWd4xRycfmwy1eoQkMsF8oekPg== +jest-cli@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.3.0.tgz#d9e11f5700cc5946583cf0d01a9bdebceed448d2" + integrity sha512-XpNQPlW1tzpP7RGG8dxpkRegYDuLjzSiENu92+CYM87nEbmEPb3b4+yo8xcsHOnj0AG7DUt9b3uG8LuHI3MDzw== dependencies: - "@jest/core" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/core" "^25.3.0" + "@jest/test-result" "^25.3.0" + "@jest/types" "^25.3.0" chalk "^3.0.0" exit "^0.1.2" import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^25.1.0" - jest-util "^25.1.0" - jest-validate "^25.1.0" + jest-config "^25.3.0" + jest-util "^25.3.0" + jest-validate "^25.3.0" prompts "^2.0.1" - realpath-native "^1.1.0" - yargs "^15.0.0" + realpath-native "^2.0.0" + yargs "^15.3.1" jest-config@^24.9.0: version "24.9.0" @@ -3906,28 +4147,29 @@ jest-config@^24.9.0: pretty-format "^24.9.0" realpath-native "^1.1.0" -jest-config@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.1.0.tgz#d114e4778c045d3ef239452213b7ad3ec1cbea90" - integrity sha512-tLmsg4SZ5H7tuhBC5bOja0HEblM0coS3Wy5LTCb2C8ZV6eWLewHyK+3qSq9Bi29zmWQ7ojdCd3pxpx4l4d2uGw== +jest-config@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.3.0.tgz#112b5e2f2e57dec4501dd2fe979044c06fb1317e" + integrity sha512-CmF1JnNWFmoCSPC4tnU52wnVBpuxHjilA40qH/03IHxIevkjUInSMwaDeE6ACfxMPTLidBGBCO3EbxvzPbo8wA== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^25.1.0" - "@jest/types" "^25.1.0" - babel-jest "^25.1.0" + "@jest/test-sequencer" "^25.3.0" + "@jest/types" "^25.3.0" + babel-jest "^25.3.0" chalk "^3.0.0" + deepmerge "^4.2.2" glob "^7.1.1" - jest-environment-jsdom "^25.1.0" - jest-environment-node "^25.1.0" - jest-get-type "^25.1.0" - jest-jasmine2 "^25.1.0" - jest-regex-util "^25.1.0" - jest-resolve "^25.1.0" - jest-util "^25.1.0" - jest-validate "^25.1.0" + jest-environment-jsdom "^25.3.0" + jest-environment-node "^25.3.0" + jest-get-type "^25.2.6" + jest-jasmine2 "^25.3.0" + jest-regex-util "^25.2.6" + jest-resolve "^25.3.0" + jest-util "^25.3.0" + jest-validate "^25.3.0" micromatch "^4.0.2" - pretty-format "^25.1.0" - realpath-native "^1.1.0" + pretty-format "^25.3.0" + realpath-native "^2.0.0" jest-diff@^24.9.0: version "24.9.0" @@ -3939,15 +4181,15 @@ jest-diff@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-diff@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.1.0.tgz#58b827e63edea1bc80c1de952b80cec9ac50e1ad" - integrity sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw== +jest-diff@^25.2.1, jest-diff@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.3.0.tgz#0d7d6f5d6171e5dacde9e05be47b3615e147c26f" + integrity sha512-vyvs6RPoVdiwARwY4kqFWd4PirPLm2dmmkNzKqo38uZOzJvLee87yzDjIZLmY1SjM3XR5DwsUH+cdQ12vgqi1w== dependencies: chalk "^3.0.0" - diff-sequences "^25.1.0" - jest-get-type "^25.1.0" - pretty-format "^25.1.0" + diff-sequences "^25.2.6" + jest-get-type "^25.2.6" + pretty-format "^25.3.0" jest-docblock@^24.3.0: version "24.9.0" @@ -3956,10 +4198,10 @@ jest-docblock@^24.3.0: dependencies: detect-newline "^2.1.0" -jest-docblock@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.1.0.tgz#0f44bea3d6ca6dfc38373d465b347c8818eccb64" - integrity sha512-370P/mh1wzoef6hUKiaMcsPtIapY25suP6JqM70V9RJvdKLrV4GaGbfUseUVk4FZJw4oTZ1qSCJNdrClKt5JQA== +jest-docblock@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.3.0.tgz#8b777a27e3477cd77a168c05290c471a575623ef" + integrity sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg== dependencies: detect-newline "^3.0.0" @@ -3974,16 +4216,16 @@ jest-each@^24.9.0: jest-util "^24.9.0" pretty-format "^24.9.0" -jest-each@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.1.0.tgz#a6b260992bdf451c2d64a0ccbb3ac25e9b44c26a" - integrity sha512-R9EL8xWzoPySJ5wa0DXFTj7NrzKpRD40Jy+zQDp3Qr/2QmevJgkN9GqioCGtAJ2bW9P/MQRznQHQQhoeAyra7A== +jest-each@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.3.0.tgz#a319eecf1f6076164ab86f99ca166a55b96c0bd4" + integrity sha512-aBfS4VOf/Qs95yUlX6d6WBv0szvOcTkTTyCIaLuQGj4bSHsT+Wd9dDngVHrCe5uytxpN8VM+NAloI6nbPjXfXw== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" chalk "^3.0.0" - jest-get-type "^25.1.0" - jest-util "^25.1.0" - pretty-format "^25.1.0" + jest-get-type "^25.2.6" + jest-util "^25.3.0" + pretty-format "^25.3.0" jest-environment-jsdom@^24.9.0: version "24.9.0" @@ -3997,17 +4239,17 @@ jest-environment-jsdom@^24.9.0: jest-util "^24.9.0" jsdom "^11.5.1" -jest-environment-jsdom@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.1.0.tgz#6777ab8b3e90fd076801efd3bff8e98694ab43c3" - integrity sha512-ILb4wdrwPAOHX6W82GGDUiaXSSOE274ciuov0lztOIymTChKFtC02ddyicRRCdZlB5YSrv3vzr1Z5xjpEe1OHQ== +jest-environment-jsdom@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.3.0.tgz#c493ab8c41f28001520c70ef67dd88b88be6af05" + integrity sha512-jdE4bQN+k2QEZ9sWOxsqDJvMzbdFSCN/4tw8X0TQaCqyzKz58PyEf41oIr4WO7ERdp7WaJGBSUKF7imR3UW1lg== dependencies: - "@jest/environment" "^25.1.0" - "@jest/fake-timers" "^25.1.0" - "@jest/types" "^25.1.0" - jest-mock "^25.1.0" - jest-util "^25.1.0" - jsdom "^15.1.1" + "@jest/environment" "^25.3.0" + "@jest/fake-timers" "^25.3.0" + "@jest/types" "^25.3.0" + jest-mock "^25.3.0" + jest-util "^25.3.0" + jsdom "^15.2.1" jest-environment-node@^24.9.0: version "24.9.0" @@ -4020,26 +4262,27 @@ jest-environment-node@^24.9.0: jest-mock "^24.9.0" jest-util "^24.9.0" -jest-environment-node@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.1.0.tgz#797bd89b378cf0bd794dc8e3dca6ef21126776db" - integrity sha512-U9kFWTtAPvhgYY5upnH9rq8qZkj6mYLup5l1caAjjx9uNnkLHN2xgZy5mo4SyLdmrh/EtB9UPpKFShvfQHD0Iw== +jest-environment-node@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.3.0.tgz#9845f0e63991e8498448cb0ae804935689533db9" + integrity sha512-XO09S29Nx1NU7TiMPHMoDIkxoGBuKSTbE+sHp0gXbeLDXhIdhysUI25kOqFFSD9AuDgvPvxWCXrvNqiFsOH33g== dependencies: - "@jest/environment" "^25.1.0" - "@jest/fake-timers" "^25.1.0" - "@jest/types" "^25.1.0" - jest-mock "^25.1.0" - jest-util "^25.1.0" + "@jest/environment" "^25.3.0" + "@jest/fake-timers" "^25.3.0" + "@jest/types" "^25.3.0" + jest-mock "^25.3.0" + jest-util "^25.3.0" + semver "^6.3.0" jest-get-type@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== -jest-get-type@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.1.0.tgz#1cfe5fc34f148dc3a8a3b7275f6b9ce9e2e8a876" - integrity sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw== +jest-get-type@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" + integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== jest-haste-map@^24.9.0: version "24.9.0" @@ -4060,21 +4303,22 @@ jest-haste-map@^24.9.0: optionalDependencies: fsevents "^1.2.7" -jest-haste-map@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.1.0.tgz#ae12163d284f19906260aa51fd405b5b2e5a4ad3" - integrity sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw== +jest-haste-map@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.3.0.tgz#b7683031c9c9ddc0521d311564108b244b11e4c6" + integrity sha512-LjXaRa+F8wwtSxo9G+hHD/Cp63PPQzvaBL9XCVoJD2rrcJO0Zr2+YYzAFWWYJ5GlPUkoaJFJtOuk0sL6MJY80A== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.3" - jest-serializer "^25.1.0" - jest-util "^25.1.0" - jest-worker "^25.1.0" + jest-serializer "^25.2.6" + jest-util "^25.3.0" + jest-worker "^25.2.6" micromatch "^4.0.2" sane "^4.0.3" walker "^1.0.7" + which "^2.0.2" optionalDependencies: fsevents "^2.1.2" @@ -4100,27 +4344,27 @@ jest-jasmine2@^24.9.0: pretty-format "^24.9.0" throat "^4.0.0" -jest-jasmine2@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.1.0.tgz#681b59158a430f08d5d0c1cce4f01353e4b48137" - integrity sha512-GdncRq7jJ7sNIQ+dnXvpKO2MyP6j3naNK41DTTjEAhLEdpImaDA9zSAZwDhijjSF/D7cf4O5fdyUApGBZleaEg== +jest-jasmine2@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.3.0.tgz#16ae4f68adef65fb45001b26c864bcbcbf972830" + integrity sha512-NCYOGE6+HNzYFSui52SefgpsnIzvxjn6KAgqw66BdRp37xpMD/4kujDHLNW5bS5i53os5TcMn6jYrzQRO8VPrQ== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^25.1.0" - "@jest/source-map" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/environment" "^25.3.0" + "@jest/source-map" "^25.2.6" + "@jest/test-result" "^25.3.0" + "@jest/types" "^25.3.0" chalk "^3.0.0" co "^4.6.0" - expect "^25.1.0" + expect "^25.3.0" is-generator-fn "^2.0.0" - jest-each "^25.1.0" - jest-matcher-utils "^25.1.0" - jest-message-util "^25.1.0" - jest-runtime "^25.1.0" - jest-snapshot "^25.1.0" - jest-util "^25.1.0" - pretty-format "^25.1.0" + jest-each "^25.3.0" + jest-matcher-utils "^25.3.0" + jest-message-util "^25.3.0" + jest-runtime "^25.3.0" + jest-snapshot "^25.3.0" + jest-util "^25.3.0" + pretty-format "^25.3.0" throat "^5.0.0" jest-leak-detector@^24.9.0: @@ -4131,13 +4375,13 @@ jest-leak-detector@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-leak-detector@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.1.0.tgz#ed6872d15aa1c72c0732d01bd073dacc7c38b5c6" - integrity sha512-3xRI264dnhGaMHRvkFyEKpDeaRzcEBhyNrOG5oT8xPxOyUAblIAQnpiR3QXu4wDor47MDTiHbiFcbypdLcLW5w== +jest-leak-detector@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.3.0.tgz#5b6bf04903b35be56038915a55f47291771f769f" + integrity sha512-jk7k24dMIfk8LUSQQGN8PyOy9+J0NAfHZWiDmUDYVMctY8FLJQ1eQ8+PjMoN8PgwhLIggUqgYJnyRFvUz3jLRw== dependencies: - jest-get-type "^25.1.0" - pretty-format "^25.1.0" + jest-get-type "^25.2.6" + pretty-format "^25.3.0" jest-matcher-utils@^24.9.0: version "24.9.0" @@ -4149,15 +4393,15 @@ jest-matcher-utils@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-matcher-utils@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz#fa5996c45c7193a3c24e73066fc14acdee020220" - integrity sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ== +jest-matcher-utils@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.3.0.tgz#76765788a26edaa8bc5f0100aea52ae383559648" + integrity sha512-ZBUJ2fchNIZt+fyzkuCFBb8SKaU//Rln45augfUtbHaGyVxCO++ANARdBK9oPGXU3hEDgyy7UHnOP/qNOJXFUg== dependencies: chalk "^3.0.0" - jest-diff "^25.1.0" - jest-get-type "^25.1.0" - pretty-format "^25.1.0" + jest-diff "^25.3.0" + jest-get-type "^25.2.6" + pretty-format "^25.3.0" jest-message-util@^24.9.0: version "24.9.0" @@ -4173,20 +4417,26 @@ jest-message-util@^24.9.0: slash "^2.0.0" stack-utils "^1.0.1" -jest-message-util@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.1.0.tgz#702a9a5cb05c144b9aa73f06e17faa219389845e" - integrity sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ== +jest-message-util@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.3.0.tgz#e3836826fe5ca538a337b87d9bd2648190867f85" + integrity sha512-5QNy9Id4WxJbRITEbA1T1kem9bk7y2fD0updZMSTNHtbEDnYOGLDPAuFBhFgVmOZpv0n6OMdVkK+WhyXEPCcOw== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" "@types/stack-utils" "^1.0.1" chalk "^3.0.0" micromatch "^4.0.2" slash "^3.0.0" stack-utils "^1.0.1" +jest-mock-extended@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-1.0.8.tgz#af6cccf78c7ef0c9a0b5fbae4e2625a792f0d6b8" + integrity sha512-lwRgbnngx6Woh0OOapes3uJQzngCj69OIhKnXYybEWwE0O1w7UyBJwLciHH6D/MCedogA90qxhEkEANrA20Sfw== + dependencies: + ts-essentials "^4.0.0" + jest-mock@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" @@ -4194,12 +4444,12 @@ jest-mock@^24.9.0: dependencies: "@jest/types" "^24.9.0" -jest-mock@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.1.0.tgz#411d549e1b326b7350b2e97303a64715c28615fd" - integrity sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag== +jest-mock@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.3.0.tgz#d72644509e40987a732a9a2534a1054f4649402c" + integrity sha512-yRn6GbuqB4j3aYu+Z1ezwRiZfp0o9om5uOcBovVtkcRLeBCNP5mT0ysdenUsxAHnQUgGwPOE1wwhtQYe6NKirQ== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" jest-pnp-resolver@^1.2.1: version "1.2.1" @@ -4211,19 +4461,19 @@ jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== -jest-regex-util@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.1.0.tgz#efaf75914267741838e01de24da07b2192d16d87" - integrity sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w== +jest-regex-util@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964" + integrity sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw== -jest-resolve-dependencies@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.1.0.tgz#8a1789ec64eb6aaa77fd579a1066a783437e70d2" - integrity sha512-Cu/Je38GSsccNy4I2vL12ZnBlD170x2Oh1devzuM9TLH5rrnLW1x51lN8kpZLYTvzx9j+77Y5pqBaTqfdzVzrw== +jest-resolve-dependencies@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.3.0.tgz#b0e4ae053dd44ddacc18c6ee12b5b7c28e445a90" + integrity sha512-bDUlLYmHW+f7J7KgcY2lkq8EMRqKonRl0XoD4Wp5SJkgAxKJnsaIOlrrVNTfXYf+YOu3VCjm/Ac2hPF2nfsCIA== dependencies: - "@jest/types" "^25.1.0" - jest-regex-util "^25.1.0" - jest-snapshot "^25.1.0" + "@jest/types" "^25.3.0" + jest-regex-util "^25.2.6" + jest-snapshot "^25.3.0" jest-resolve@^24.9.0: version "24.9.0" @@ -4236,16 +4486,17 @@ jest-resolve@^24.9.0: jest-pnp-resolver "^1.2.1" realpath-native "^1.1.0" -jest-resolve@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.1.0.tgz#23d8b6a4892362baf2662877c66aa241fa2eaea3" - integrity sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ== +jest-resolve@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.3.0.tgz#cb90a5bbea54a02eccdbbf4126a819595dcf91d6" + integrity sha512-IHoQAAybulsJ+ZgWis+ekYKDAoFkVH5Nx/znpb41zRtpxj4fr2WNV9iDqavdSm8GIpMlsfZxbC/fV9DhW0q9VQ== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" browser-resolve "^1.11.3" chalk "^3.0.0" jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" + realpath-native "^2.0.0" + resolve "^1.15.1" jest-runner@^24.8.0, jest-runner@^24.9.0: version "24.9.0" @@ -4272,28 +4523,28 @@ jest-runner@^24.8.0, jest-runner@^24.9.0: source-map-support "^0.5.6" throat "^4.0.0" -jest-runner@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.1.0.tgz#fef433a4d42c89ab0a6b6b268e4a4fbe6b26e812" - integrity sha512-su3O5fy0ehwgt+e8Wy7A8CaxxAOCMzL4gUBftSs0Ip32S0epxyZPDov9Znvkl1nhVOJNf4UwAsnqfc3plfQH9w== +jest-runner@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.3.0.tgz#673ef2ac79d2810eb6b2c1a3f82398375a3d1174" + integrity sha512-csDqSC9qGHYWDrzrElzEgFbteztFeZJmKhSgY5jlCIcN0+PhActzRNku0DA1Xa1HxGOb0/AfbP1EGJlP4fGPtA== dependencies: - "@jest/console" "^25.1.0" - "@jest/environment" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/console" "^25.3.0" + "@jest/environment" "^25.3.0" + "@jest/test-result" "^25.3.0" + "@jest/types" "^25.3.0" chalk "^3.0.0" exit "^0.1.2" graceful-fs "^4.2.3" - jest-config "^25.1.0" - jest-docblock "^25.1.0" - jest-haste-map "^25.1.0" - jest-jasmine2 "^25.1.0" - jest-leak-detector "^25.1.0" - jest-message-util "^25.1.0" - jest-resolve "^25.1.0" - jest-runtime "^25.1.0" - jest-util "^25.1.0" - jest-worker "^25.1.0" + jest-config "^25.3.0" + jest-docblock "^25.3.0" + jest-haste-map "^25.3.0" + jest-jasmine2 "^25.3.0" + jest-leak-detector "^25.3.0" + jest-message-util "^25.3.0" + jest-resolve "^25.3.0" + jest-runtime "^25.3.0" + jest-util "^25.3.0" + jest-worker "^25.2.6" source-map-support "^0.5.6" throat "^5.0.0" @@ -4326,36 +4577,36 @@ jest-runtime@^24.9.0: strip-bom "^3.0.0" yargs "^13.3.0" -jest-runtime@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.1.0.tgz#02683218f2f95aad0f2ec1c9cdb28c1dc0ec0314" - integrity sha512-mpPYYEdbExKBIBB16ryF6FLZTc1Rbk9Nx0ryIpIMiDDkOeGa0jQOKVI/QeGvVGlunKKm62ywcioeFVzIbK03bA== - dependencies: - "@jest/console" "^25.1.0" - "@jest/environment" "^25.1.0" - "@jest/source-map" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" +jest-runtime@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.3.0.tgz#af4d40dbcc590fa5de9910cb6a120a13d131050b" + integrity sha512-gn5KYB1wxXRM3nfw8fVpthFu60vxQUCr+ShGq41+ZBFF3DRHZRKj3HDWVAVB4iTNBj2y04QeAo5cZ/boYaPg0w== + dependencies: + "@jest/console" "^25.3.0" + "@jest/environment" "^25.3.0" + "@jest/source-map" "^25.2.6" + "@jest/test-result" "^25.3.0" + "@jest/transform" "^25.3.0" + "@jest/types" "^25.3.0" "@types/yargs" "^15.0.0" chalk "^3.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.3" - jest-config "^25.1.0" - jest-haste-map "^25.1.0" - jest-message-util "^25.1.0" - jest-mock "^25.1.0" - jest-regex-util "^25.1.0" - jest-resolve "^25.1.0" - jest-snapshot "^25.1.0" - jest-util "^25.1.0" - jest-validate "^25.1.0" - realpath-native "^1.1.0" + jest-config "^25.3.0" + jest-haste-map "^25.3.0" + jest-message-util "^25.3.0" + jest-mock "^25.3.0" + jest-regex-util "^25.2.6" + jest-resolve "^25.3.0" + jest-snapshot "^25.3.0" + jest-util "^25.3.0" + jest-validate "^25.3.0" + realpath-native "^2.0.0" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.0.0" + yargs "^15.3.1" jest-serial-runner@^1.1.0: version "1.1.0" @@ -4369,10 +4620,10 @@ jest-serializer@^24.9.0: resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== -jest-serializer@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.1.0.tgz#73096ba90e07d19dec4a0c1dd89c355e2f129e5d" - integrity sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA== +jest-serializer@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.2.6.tgz#3bb4cc14fe0d8358489dbbefbb8a4e708ce039b7" + integrity sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ== jest-snapshot@^24.9.0: version "24.9.0" @@ -4393,24 +4644,25 @@ jest-snapshot@^24.9.0: pretty-format "^24.9.0" semver "^6.2.0" -jest-snapshot@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.1.0.tgz#d5880bd4b31faea100454608e15f8d77b9d221d9" - integrity sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A== +jest-snapshot@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.3.0.tgz#d4feb457494f4aaedcc83fbbf1ca21808fc3df71" + integrity sha512-GGpR6Oro2htJPKh5RX4PR1xwo5jCEjtvSPLW1IS7N85y+2bWKbiknHpJJRKSdGXghElb5hWaeQASJI4IiRayGg== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" + "@types/prettier" "^1.19.0" chalk "^3.0.0" - expect "^25.1.0" - jest-diff "^25.1.0" - jest-get-type "^25.1.0" - jest-matcher-utils "^25.1.0" - jest-message-util "^25.1.0" - jest-resolve "^25.1.0" - mkdirp "^0.5.1" + expect "^25.3.0" + jest-diff "^25.3.0" + jest-get-type "^25.2.6" + jest-matcher-utils "^25.3.0" + jest-message-util "^25.3.0" + jest-resolve "^25.3.0" + make-dir "^3.0.0" natural-compare "^1.4.0" - pretty-format "^25.1.0" - semver "^7.1.1" + pretty-format "^25.3.0" + semver "^6.3.0" jest-util@^24.9.0: version "24.9.0" @@ -4430,15 +4682,15 @@ jest-util@^24.9.0: slash "^2.0.0" source-map "^0.6.0" -jest-util@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.1.0.tgz#7bc56f7b2abd534910e9fa252692f50624c897d9" - integrity sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw== +jest-util@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.3.0.tgz#e3b0064165818f10d78514696fd25efba82cf049" + integrity sha512-dc625P/KS/CpWTJJJxKc4bA3A6c+PJGBAqS8JTJqx4HqPoKNqXg/Ec8biL2Z1TabwK7E7Ilf0/ukSEXM1VwzNA== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" chalk "^3.0.0" is-ci "^2.0.0" - mkdirp "^0.5.1" + make-dir "^3.0.0" jest-validate@^24.9.0: version "24.9.0" @@ -4452,30 +4704,38 @@ jest-validate@^24.9.0: leven "^3.1.0" pretty-format "^24.9.0" -jest-validate@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.1.0.tgz#1469fa19f627bb0a9a98e289f3e9ab6a668c732a" - integrity sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA== +jest-validate@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.3.0.tgz#eb95fdee0039647bcd5d4be641b21e4a142a880c" + integrity sha512-3WuXgIZ4HXUvW6gk9twFFkT9j6zUorKnF2oEY8VEsHb7x5LGvVlN3WUsbqazVKuyXwvikO2zFJ/YTySMsMje2w== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" camelcase "^5.3.1" chalk "^3.0.0" - jest-get-type "^25.1.0" + jest-get-type "^25.2.6" leven "^3.1.0" - pretty-format "^25.1.0" + pretty-format "^25.3.0" -jest-watcher@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.1.0.tgz#97cb4a937f676f64c9fad2d07b824c56808e9806" - integrity sha512-Q9eZ7pyaIr6xfU24OeTg4z1fUqBF/4MP6J801lyQfg7CsnZ/TCzAPvCfckKdL5dlBBEKBeHV0AdyjFZ5eWj4ig== +jest-watcher@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.3.0.tgz#fd03fd5ca52f02bd3161ab177466bf1bfdd34e5c" + integrity sha512-dtFkfidFCS9Ucv8azOg2hkiY3sgJEHeTLtGFHS+jfBEE7eRtrO6+2r1BokyDkaG2FOD7485r/SgpC1MFAENfeA== dependencies: - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/test-result" "^25.3.0" + "@jest/types" "^25.3.0" ansi-escapes "^4.2.1" chalk "^3.0.0" - jest-util "^25.1.0" + jest-util "^25.3.0" string-length "^3.1.0" +jest-when@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/jest-when/-/jest-when-2.7.1.tgz#09ab7114d014bdb4e35183deac63cdf2bcec8698" + integrity sha512-4J2kNNEiu8rWmiZTn9HJB8U1HyDfvywpZ+FzgwnUv7yftvDFLo95U7sEiOeVK3j20af8lj6/7nrFf9lyCmOjKA== + dependencies: + bunyan "^1.8.12" + expect "^24.8.0" + jest-worker@^24.6.0, jest-worker@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" @@ -4484,22 +4744,22 @@ jest-worker@^24.6.0, jest-worker@^24.9.0: merge-stream "^2.0.0" supports-color "^6.1.0" -jest-worker@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.1.0.tgz#75d038bad6fdf58eba0d2ec1835856c497e3907a" - integrity sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg== +jest-worker@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.2.6.tgz#d1292625326794ce187c38f51109faced3846c58" + integrity sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA== dependencies: merge-stream "^2.0.0" supports-color "^7.0.0" -jest@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-25.1.0.tgz#b85ef1ddba2fdb00d295deebbd13567106d35be9" - integrity sha512-FV6jEruneBhokkt9MQk0WUFoNTwnF76CLXtwNMfsc0um0TlB/LG2yxUd0KqaFjEJ9laQmVWQWS0sG/t2GsuI0w== +jest@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-25.3.0.tgz#7a5e59741d94b8662664c77a9f346246d6bf228b" + integrity sha512-iKd5ShQSHzFT5IL/6h5RZJhApgqXSoPxhp5HEi94v6OAw9QkF8T7X+liEU2eEHJ1eMFYTHmeWLrpBWulsDpaUg== dependencies: - "@jest/core" "^25.1.0" + "@jest/core" "^25.3.0" import-local "^3.0.2" - jest-cli "^25.1.0" + jest-cli "^25.3.0" jju@^1.1.0: version "1.4.0" @@ -4556,7 +4816,7 @@ jsdom@^11.5.1: ws "^5.2.0" xml-name-validator "^3.0.0" -jsdom@^15.1.1: +jsdom@^15.2.1: version "15.2.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== @@ -4599,12 +4859,12 @@ json-buffer@3.0.0: integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= json-fixer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/json-fixer/-/json-fixer-1.4.0.tgz#591a65c3284b6720940657f8c80a38fa265eed34" - integrity sha512-9Nbl396T9eRPvrUxKnBQeiVgQjuBhvumptJfwSAdjQ+cjUqJ981O8Kts/A7ZEID3SbRr76uTOayxVTi+AbAf/A== + version "1.4.1" + resolved "https://registry.yarnpkg.com/json-fixer/-/json-fixer-1.4.1.tgz#f55e5634d0d85fa231e3781b82c27a6e2a2fc850" + integrity sha512-zbvD9byMClUKRcGsazP2WBO5kG6hS8q6oRAuIWiOvhDDN4zFFIC+tPYG0I20Vhxv8S4W1VeHzesWZB0p5pzvlA== dependencies: "@babel/runtime" "^7.7.6" - chalk "^2.4.2" + chalk "^3.0.0" pegjs "^0.10.0" json-parse-better-errors@^1.0.1: @@ -4651,10 +4911,10 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@2.x, json5@^2.1.0, json5@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e" - integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== +json5@2.x, json5@^2.1.1, json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== dependencies: minimist "^1.2.5" @@ -4755,17 +5015,17 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -lint-staged@^10.0.9: - version "10.0.9" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.0.9.tgz#185aabb2432e9467c84add306c990f1c20da3cdb" - integrity sha512-NKJHYgRa8oI9c4Ic42ZtF2XA6Ps7lFbXwg3q0ZEP0r55Tw3YWykCW1RzW6vu+QIGqbsy7DxndvKu93Wtr5vPQw== +lint-staged@^10.1.6: + version "10.1.6" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.1.6.tgz#086db5e4f5906642ccf648e9b54375d794a9be81" + integrity sha512-45zaGxf4XZuwdUk87yRFE/1b4vTZmH2UnYmUPmindsgdAljOFpWWb0yEjxngmqERUS/MGauJexFF6BjLVg9VMA== dependencies: - chalk "^3.0.0" - commander "^4.0.1" + chalk "^4.0.0" + commander "^5.0.0" cosmiconfig "^6.0.0" debug "^4.1.1" dedent "^0.7.0" - execa "^3.4.0" + execa "^4.0.0" listr "^0.14.3" log-symbols "^3.0.0" micromatch "^4.0.2" @@ -4856,6 +5116,16 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= +lodash.flatmap@^4.5.0, lodash.flatmap@~4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" + integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4= + +lodash.groupby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" + integrity sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E= + lodash.memoize@4.x: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4909,6 +5179,17 @@ log-update@^2.3.0: cli-cursor "^2.0.0" wrap-ansi "^3.0.1" +log4js@6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.1.2.tgz#04688e1f4b8080c127b7dccb0db1c759cbb25dc4" + integrity sha512-knS4Y30pC1e0n7rfx3VxcLOdBCsEo0o6/C7PVTGxdVK+5b1TYOSGQPn9FDcrhkoQBV29qwmA2mtkznPAQKnxQg== + dependencies: + date-format "^3.0.0" + debug "^4.1.1" + flatted "^2.0.1" + rfdc "^1.1.4" + streamroller "^2.2.3" + lolex@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" @@ -5030,6 +5311,14 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +micromatch@4.x, micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" + micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -5049,14 +5338,6 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== - dependencies: - braces "^3.0.1" - picomatch "^2.0.5" - mime-db@1.43.0: version "1.43.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" @@ -5084,7 +5365,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -5190,18 +5471,18 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c" - integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg== +mkdirp@1.x, mkdirp@^1.0.3, mkdirp@~1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" -mkdirp@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea" - integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g== - mock-spawn@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/mock-spawn/-/mock-spawn-0.2.6.tgz#b39c15a1c067504310144151f2c1de344d03937f" @@ -5209,7 +5490,7 @@ mock-spawn@^0.2.6: dependencies: through "2.3.x" -moment@*, moment@~2.24.0: +moment@^2.10.6, moment@~2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== @@ -5236,11 +5517,37 @@ ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mutation-testing-elements@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mutation-testing-elements/-/mutation-testing-elements-1.3.1.tgz#a7796c582bc3a29207b6d17804fa6af02decd0b7" + integrity sha512-XXP/enxyOd8X6lK/lu4nlPGLmwH3wfMwj9eatxLp4er0zrmv0p5gGZVkj4KnuuGfp7rnlVNBI/5EZShPJgK3HA== + +mutation-testing-metrics@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/mutation-testing-metrics/-/mutation-testing-metrics-1.3.0.tgz#e53619128a19194b7640b7bde17d7ecb581d2bb9" + integrity sha512-T7UkUGljyCLMEWGK6YtRTjt4fxqi5+052gjDBkKBR6T5Po6DbwwIx6DAvFyBYzjBzUx6wUhXt7UaxB/wy+JyEg== + dependencies: + mutation-testing-report-schema "^1.3.0" + +mutation-testing-report-schema@^1.3.0, mutation-testing-report-schema@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mutation-testing-report-schema/-/mutation-testing-report-schema-1.3.1.tgz#ad8f4ad2f9626e811f3c0710498f4e7fa2ca6968" + integrity sha512-2T2A5qBg+2SZ7CtAvW5m4W95VJxZ/UsSWVwzv3VZpm7c2VoGgIWZGPiTC76a+gorxJobyCzkWv0902UNs4Wl5Q== + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mv@~2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" + integrity sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI= + dependencies: + mkdirp "~0.5.1" + ncp "~2.0.0" + rimraf "~2.4.0" + mz@^2.4.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" @@ -5250,7 +5557,7 @@ mz@^2.4.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.12.1: +nan@^2.12.1, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -5277,10 +5584,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +ncp@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" + integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= + needle@^2.2.1: - version "2.3.3" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.3.tgz#a041ad1d04a871b0ebb666f40baaf1fb47867117" - integrity sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw== + version "2.4.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" + integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== dependencies: debug "^3.2.6" iconv-lite "^0.4.4" @@ -5330,6 +5642,22 @@ node-notifier@^6.0.0: shellwords "^0.1.1" which "^1.3.1" +node-pre-gyp@*: + version "0.14.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" + integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4.4.2" + node-pre-gyp@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" @@ -5388,10 +5716,10 @@ npm-bundled@^1.0.1, npm-bundled@^1.1.1: dependencies: npm-normalize-package-bin "^1.0.1" -npm-check-updates@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/npm-check-updates/-/npm-check-updates-4.0.5.tgz#d04ede077a5c0902269530949c6c9658f2c65e7d" - integrity sha512-dO2jeEmD1DAa/8NKxQAuhVsICDYWKTL1kZkvUFyRNphK4NiLk1HA7Jk5mCfl2L2xU5FxiGhNq5vgYlivu+2kjw== +npm-check-updates@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npm-check-updates/-/npm-check-updates-4.1.2.tgz#700f52b17999aa914a5fbdd9c1a0a51d093d89c0" + integrity sha512-CRO20Z12fygKL/ow4j4pnpyxevda/PuFbWpsF5E9sFW0B+M3d32A1dD+fTHLDjgderhKXr64W8qQ6M/Gq8OLiw== dependencies: chalk "^3.0.0" cint "^8.2.1" @@ -5404,12 +5732,13 @@ npm-check-updates@^4.0.5: libnpmconfig "^1.2.1" lodash "^4.17.15" node-alias "^1.0.4" + p-map "^4.0.0" pacote "^11.1.4" progress "^2.0.3" prompts "^2.3.2" rc-config-loader "^3.0.0" requireg "^0.2.2" - semver "^7.1.3" + semver "^7.2.1" semver-utils "^1.1.4" spawn-please "^0.3.0" update-notifier "^4.1.0" @@ -5455,9 +5784,9 @@ npm-packlist@^2.1.0: npm-normalize-package-bin "^1.0.1" npm-pick-manifest@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.0.0.tgz#bfde7abe95f2670aed1629a3c18245ccb3cc2eb8" - integrity sha512-PdJpXMvjqt4nftNEDpCgjBUF8yI3Q3MyuAmVB9nemnnCg32F4BPL/JFBfdj8DubgHCYUFQhtLWmBPvdsFtjWMg== + version "6.1.0" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" + integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== dependencies: npm-install-checks "^4.0.0" npm-package-arg "^8.0.0" @@ -5593,6 +5922,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +opencollective-postinstall@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" + integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== + optionator@^0.8.1, optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -5651,9 +5985,9 @@ p-limit@^1.1.0: p-try "^1.0.0" p-limit@^2.0.0, p-limit@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" - integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" @@ -5690,6 +6024,13 @@ p-map@^3.0.0: dependencies: aggregate-error "^3.0.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -5849,9 +6190,9 @@ performance-now@^2.1.0: integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picomatch@^2.0.4, picomatch@^2.0.5: - version "2.2.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" - integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== pify@^3.0.0: version "3.0.0" @@ -5911,10 +6252,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.2.tgz#1ba8f3eb92231e769b7fcd7cb73ae1b6b74ade08" - integrity sha512-5xJQIPT8BraI7ZnaDwSbu5zLrB6vvi8hVV58yHQ+QK64qrY40dULy0HSRlQ2/2IdzeBpjhDkqdcFBnFeDEMVdg== +prettier@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.4.tgz#2d1bae173e355996ee355ec9830a7a1ee05457ef" + integrity sha512-SVJIQ51spzFDvh4fIbCLvciiDMCrRhlN3mbZvv/+ycjvmF5E73bKdGfU8QDLNmjYJf+lsGnDBC4UUnvTe5OO0w== pretty-format@^24.9.0: version "24.9.0" @@ -5926,12 +6267,12 @@ pretty-format@^24.9.0: ansi-styles "^3.2.0" react-is "^16.8.4" -pretty-format@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.1.0.tgz#ed869bdaec1356fc5ae45de045e2c8ec7b07b0c8" - integrity sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ== +pretty-format@^25.2.1, pretty-format@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.3.0.tgz#d0a4f988ff4a6cd350342fdabbb809aeb4d49ad5" + integrity sha512-wToHwF8bkQknIcFkBqNfKu4+UZqnrLn/Vr+wwKQwwvPzkBfDDKp/qIabFqdgtoi5PEnM8LFByVsOrHoa3SpTVA== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.3.0" ansi-regex "^5.0.0" ansi-styles "^4.0.0" react-is "^16.12.0" @@ -5941,7 +6282,7 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.0, progress@^2.0.3: +progress@^2.0.0, progress@^2.0.3, progress@~2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -5968,9 +6309,9 @@ prompts@^2.0.1, prompts@^2.3.2: sisteransi "^1.0.4" psl@^1.1.28: - version "1.7.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" - integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" @@ -5997,6 +6338,11 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +qs@^6.9.1: + version "6.9.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" + integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -6027,12 +6373,7 @@ rc@^1.2.7, rc@^1.2.8, rc@~1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-is@^16.12.0: - version "16.13.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" - integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== - -react-is@^16.8.4: +react-is@^16.12.0, react-is@^16.8.4: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -6109,6 +6450,11 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +realpath-native@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" + integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== + redent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" @@ -6151,9 +6497,9 @@ regexpp@^2.0.1: integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== regexpp@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e" - integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g== + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== registry-auth-token@^4.0.0: version "4.1.1" @@ -6289,10 +6635,10 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.x, resolve@^1.10.0, resolve@^1.3.2: - version "1.15.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== +resolve@1.x, resolve@^1.10.0, resolve@^1.15.1, resolve@^1.3.2: + version "1.16.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.16.1.tgz#49fac5d8bacf1fd53f200fa51247ae736175832c" + integrity sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig== dependencies: path-parse "^1.0.6" @@ -6336,6 +6682,11 @@ retry@^0.10.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= +rfdc@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" + integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== + rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -6350,13 +6701,20 @@ rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: dependencies: glob "^7.1.3" -rimraf@^3.0.0: +rimraf@^3.0.0, rimraf@~3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" +rimraf@~2.4.0: + version "2.4.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" + integrity sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto= + dependencies: + glob "^6.0.1" + rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -6376,10 +6734,10 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.3, rxjs@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" - integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== +rxjs@^6.3.3, rxjs@^6.5.3, rxjs@~6.5.1: + version "6.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" + integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== dependencies: tslib "^1.9.0" @@ -6393,6 +6751,11 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-json-stringify@~1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" + integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -6444,25 +6807,30 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" +semver-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" + integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw== + semver-utils@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/semver-utils/-/semver-utils-1.1.4.tgz#cf0405e669a57488913909fc1c3f29bf2a4871e2" integrity sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA== -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@6.3.0, semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@6.3.0, semver@6.x, semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@~6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.0.0, semver@^7.1.1, semver@^7.1.3: - version "7.1.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" - integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== +semver@^7.0.0, semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -6511,15 +6879,20 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-quote@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" + integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== simple-git@~1.132.0: version "1.132.0" @@ -6621,9 +6994,9 @@ source-map-resolve@^0.5.0: urix "^0.1.0" source-map-support@^0.5.6: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + version "0.5.17" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.17.tgz#29fe1b3c98b9dbd5064ada89052ee8ff070cb46c" + integrity sha512-bwdKOBZ5L0gFRh4KOxNap/J/MpvX9Yxsq9lFDx65s3o7F/NiHy7JRaGIS8MwW6tZPAq9UXE207Il0cfcb5yu/Q== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -6643,7 +7016,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: +source-map@^0.7.3, source-map@~0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -6747,6 +7120,15 @@ stealthy-require@^1.1.1: resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= +streamroller@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.3.tgz#b95c9fad44e2e89005d242141486b3b4962c2d28" + integrity sha512-AegmvQsscTRhHVO46PhCDerjIpxi7E+d2GxgUDu+nzw/HuLnUdxHWr6WQ+mVn/4iJgMKKFFdiUwFcFRDvcjCtw== + dependencies: + date-format "^2.1.0" + debug "^4.1.1" + fs-extra "^8.1.0" + string-argv@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" @@ -6795,21 +7177,39 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string.prototype.trimend@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimleft@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" - integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" + integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" + string.prototype.trimstart "^1.0.0" string.prototype.trimright@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" - integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" + integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" + string.prototype.trimend "^1.0.0" + +string.prototype.trimstart@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" string_decoder@^1.1.1: version "1.3.0" @@ -6888,9 +7288,9 @@ strip-indent@^2.0.0: integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" + integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== strip-json-comments@~2.0.1: version "2.0.1" @@ -6931,6 +7331,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +surrial@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/surrial/-/surrial-2.0.2.tgz#739afbc9821ebf6aa4e3e9f32abf0021b73ef2b3" + integrity sha512-YQ0XyrdBI8Kx10lIK81zOGXdGtc0P+3FTqEtCdaKzfEJKJWDju2QPp+XhzihmN2KOTRDtkKSyQQXZHYP+SqapA== + symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -6951,7 +7356,7 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tar@^4: +tar@^4, tar@^4.4.2: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -7147,6 +7552,11 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +tree-kill@~1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + trim-newlines@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" @@ -7157,10 +7567,15 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -ts-jest@^25.2.1: - version "25.2.1" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.2.1.tgz#49bf05da26a8b7fbfbc36b4ae2fcdc2fef35c85d" - integrity sha512-TnntkEEjuXq/Gxpw7xToarmHbAafgCaAzOpnajnFC6jI7oo1trMzAHA04eWpc3MhV6+yvhE8uUBAmN+teRJh0A== +ts-essentials@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-4.0.0.tgz#506c42b270bbd0465574b90416533175b09205ab" + integrity sha512-uQJX+SRY9mtbKU+g9kl5Fi7AEMofPCvHfJkQlaygpPmHPZrtgaBqbWFOYyiA47RhnSwwnXdepUJrgqUYxoUyhQ== + +ts-jest@^25.4.0: + version "25.4.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.4.0.tgz#5ad504299f8541d463a52e93e5e9d76876be0ba4" + integrity sha512-+0ZrksdaquxGUBwSdTIcdX7VXdwLIlSRsyjivVA9gcO+Cvr6ByqDhu/mi5+HCcb6cMkiQp5xZ8qRO7/eCqLeyw== dependencies: bs-logger "0.x" buffer-from "1.x" @@ -7168,15 +7583,16 @@ ts-jest@^25.2.1: json5 "2.x" lodash.memoize "4.x" make-error "1.x" - mkdirp "0.x" + micromatch "4.x" + mkdirp "1.x" resolve "1.x" - semver "^5.5" - yargs-parser "^16.1.0" + semver "6.x" + yargs-parser "18.x" -ts-node@^8.8.1: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.8.1.tgz#7c4d3e9ed33aa703b64b28d7f9d194768be5064d" - integrity sha512-10DE9ONho06QORKAaCBpPiFCdW+tZJuY/84tyypGtl6r+/C7Asq0dhqbRZURuUlLQtZxxDvT8eoj8cGW0ha6Bg== +ts-node@^8.8.2: + version "8.8.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.8.2.tgz#0b39e690bee39ea5111513a9d2bcdc0bc121755f" + integrity sha512-duVj6BpSpUpD/oM4MfhO98ozgkp3Gt9qIp3jGxwU2DFvl/3IRaEAvbLa8G60uS7C77457e/m5TMowjedeRxI1Q== dependencies: arg "^4.1.0" diff "^4.0.1" @@ -7184,7 +7600,17 @@ ts-node@^8.8.1: source-map-support "^0.5.6" yn "3.1.1" -tslib@^1.8.1, tslib@^1.9.0: +ts-toolbelt@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.5.1.tgz#1316b8c6522797c0bee289ca5deaa159af846231" + integrity sha512-zjnZ/vy1eUA0li3H0JXecl0R3jiP42snpLimsrppt9V3LLbM4NM4jMgjXQ4S67hvehq+r9CxpX4Wj6RnFRzReA== + +ts-type-guards@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/ts-type-guards/-/ts-type-guards-0.6.1.tgz#44fec2f35bfa8d78eeb50476ce22e86954804e62" + integrity sha512-YplsxDSkqPoLH/QiLqMpIc8+VGTx6q58qAvVHMC0SzL10JsF529elVqjIgShFiBL9U2yBw+dQYxDDCNPDDJyQw== + +tslib@^1.8.1, tslib@^1.9.0, tslib@~1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== @@ -7203,6 +7629,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tunnel@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -7225,11 +7656,32 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== +type-fest@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" + integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +typed-inject@~2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/typed-inject/-/typed-inject-2.1.1.tgz#5e16c5d46961fd77f475295f0170627ac81ffd19" + integrity sha512-TaQrNsYjGTMmgfEwKtjP9+qyZu//H1RJ0RYNvvQ/rcAnpQGZLxHajb+O6TnyFZGfLaK/9319VYaG4PFXGjImug== + dependencies: + typescript "^3.6.3" + +typed-rest-client@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/typed-rest-client/-/typed-rest-client-1.7.3.tgz#1beb263b86b14d34596f6127c6172dd5fd652e7b" + integrity sha512-CwTpx/TkRHGZoHkJhBcp4X8K3/WtlzSHVQR0OIFnt10j4tgy4ypgq/SrrgVpA1s6tAL49Q6J3R5C0Cgfh2ddqA== + dependencies: + qs "^6.9.1" + tunnel "0.0.6" + underscore "1.8.3" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -7258,11 +7710,16 @@ typeorm@~0.2.24: yargonaut "^1.1.2" yargs "^13.2.1" -typescript@^3.8.3: +typescript@^3.6.3, typescript@^3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== +underscore@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -7376,9 +7833,9 @@ v8-compile-cache@^2.0.3: integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== v8-to-istanbul@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.2.tgz#387d173be5383dbec209d21af033dcb892e3ac82" - integrity sha512-G9R+Hpw0ITAmPSr47lSlc5A1uekSYzXxTMlFxso2xoffwo4jQnzbv1p9yXIinO8UMZKfAFewaCHwWvnH4Jb4Ug== + version "4.1.3" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz#22fe35709a64955f49a08a7c7c959f6520ad6f20" + integrity sha512-sAjOC+Kki6aJVbUOXJbcR0MnbfjvBzwKZazEJymA2IX49uoOdEdk+4fBq5cXgYgiyKtAyrrJNtBZdOeDIF+Fng== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -7476,6 +7933,11 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -7626,19 +8088,12 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.7.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.2.tgz#a29c03f578faafd57dcb27055f9a5d569cb0c3d9" - integrity sha512-omakb0d7FjMo3R1D2EbTKVIk6dAVLRxFXdLZMEUToeAvuqgG/YuHMuQOZ5fgk+vQ8cx+cnGKwyg+8g8PNT0xQg== - dependencies: - "@babel/runtime" "^7.8.7" - -yaml@~1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.3.tgz#2f420fca58b68ce3a332d0ca64be1d191dd3f87a" - integrity sha512-X/v7VDnK+sxbQ2Imq4Jt2PRUsRsP7UcpSl3Llg6+NRRqWLIvxkMFYtH1FmvwNGYRKKPa+EPA4qDBlI9WVG1UKw== +yaml@^1.7.2, yaml@~1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.9.1.tgz#2df608ca571a0cf94e25e417e2795c08f48acdc5" + integrity sha512-xbWX1ayUVoW8DPM8qxOBowac4XxSTi0mFLbiokRq880ViYglN+F3nJ4Dc2GdypXpykrknKS39d8I3lzFoHv1kA== dependencies: - "@babel/runtime" "^7.8.7" + "@babel/runtime" "^7.9.2" yargonaut@^1.1.2: version "1.1.4" @@ -7649,6 +8104,14 @@ yargonaut@^1.1.2: figlet "^1.1.1" parent-require "^1.0.0" +yargs-parser@18.x, yargs-parser@^18.1.1, yargs-parser@~18.1.3: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -7664,22 +8127,6 @@ yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^16.1.0: - version "16.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1" - integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^18.1.1: - version "18.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37" - integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs@^13.2.1, yargs@^13.3.0: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -7696,7 +8143,7 @@ yargs@^13.2.1, yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^15.0.0, yargs@^15.0.1: +yargs@^15.0.0, yargs@^15.0.1, yargs@^15.3.1: version "15.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==