From 432337b8017d35af3f9ad59939e1597f49e881e6 Mon Sep 17 00:00:00 2001 From: Deepanshu Dhruw Date: Wed, 11 May 2022 15:44:19 +0530 Subject: [PATCH] chore: tests for hoppscotch-cli (#2300) Co-authored-by: Andrew Bastin --- packages/hoppscotch-cli/jest.config.ts | 193 +++++++ packages/hoppscotch-cli/jest.setup.ts | 1 + packages/hoppscotch-cli/package.json | 18 +- .../src/__tests__/commands/test.spec.ts | 64 +++ .../functions/checks/checkFilePath.spec.ts | 26 + .../functions/checks/isHoppCLIError.spec.ts | 19 + .../checks/isHoppErrnoException.spec.ts | 19 + .../functions/checks/isRESTCollection.spec.ts | 84 +++ .../collection/collectionsRunner.spec.ts | 116 ++++ .../collectionsRunnerResult.spec.ts | 35 ++ .../getters/getDurationInSeconds.spec.ts | 24 + .../getters/getEffectiveFinalMetaData.spec.ts | 42 ++ .../mutators/parseCollectionData.spec.ts | 36 ++ .../getEffectiveRESTRequest.spec.ts | 148 ++++++ .../pre-request/getPreRequestMetrics.spec.ts | 24 + .../preRequestScriptRunner.spec.ts | 71 +++ .../request/getRequestMetrics.spec.ts | 24 + .../functions/request/processRequest.spec.ts | 104 ++++ .../functions/request/requestRunner.spec.ts | 111 ++++ .../functions/test/getTestMetrics.spec.ts | 55 ++ .../test/testDescriptorParser.spec.ts | 63 +++ .../functions/test/testRunner.spec.ts | 73 +++ .../src/__tests__/samples/fails.json | 51 ++ .../samples/malformed-collection.json | 50 ++ .../samples/malformed-collection2.json | 7 + .../src/__tests__/samples/notjson.txt | 29 + .../src/__tests__/samples/passes.json | 51 ++ .../hoppscotch-cli/src/__tests__/types.ts | 7 + .../hoppscotch-cli/src/__tests__/utils.ts | 26 + pnpm-lock.yaml | 503 ++++++++++++------ 30 files changed, 1919 insertions(+), 155 deletions(-) create mode 100644 packages/hoppscotch-cli/jest.config.ts create mode 100644 packages/hoppscotch-cli/jest.setup.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/checks/checkFilePath.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/checks/isHoppCLIError.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/checks/isHoppErrnoException.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/checks/isRESTCollection.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/collection/collectionsRunner.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/collection/collectionsRunnerResult.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/getters/getDurationInSeconds.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/getters/getEffectiveFinalMetaData.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/mutators/parseCollectionData.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/pre-request/getEffectiveRESTRequest.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/pre-request/getPreRequestMetrics.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/pre-request/preRequestScriptRunner.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/request/getRequestMetrics.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/request/processRequest.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/request/requestRunner.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/test/getTestMetrics.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/test/testDescriptorParser.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/functions/test/testRunner.spec.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/fails.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/malformed-collection.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/malformed-collection2.json create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/notjson.txt create mode 100644 packages/hoppscotch-cli/src/__tests__/samples/passes.json create mode 100644 packages/hoppscotch-cli/src/__tests__/types.ts create mode 100644 packages/hoppscotch-cli/src/__tests__/utils.ts diff --git a/packages/hoppscotch-cli/jest.config.ts b/packages/hoppscotch-cli/jest.config.ts new file mode 100644 index 000000000..548dc2bc2 --- /dev/null +++ b/packages/hoppscotch-cli/jest.config.ts @@ -0,0 +1,193 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls, instances and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: true, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, + + // The directory where Jest should output its coverage files + // coverageDirectory: undefined, + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + // coverageProvider: "babel", + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + // globals: { + // 'ts-jest': { + // useESM: true, + // }, + // }, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + moduleFileExtensions: ["js", "ts", "json"], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + // moduleNameMapper: { + // '^(\\.{1,2}/.*)\\.js$': '$1', + // }, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + preset: "ts-jest/presets/js-with-babel", + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state before every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state and implementation before every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + setupFilesAfterEnv: ["./jest.setup.ts"], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + "**/src/__tests__/**/*.*.ts", + ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + testPathIgnorePatterns: ["/node_modules/", "/dist/"], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + transform: { + "^.+\\.ts$": "ts-jest", + }, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + verbose: true, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/packages/hoppscotch-cli/jest.setup.ts b/packages/hoppscotch-cli/jest.setup.ts new file mode 100644 index 000000000..7a33702a4 --- /dev/null +++ b/packages/hoppscotch-cli/jest.setup.ts @@ -0,0 +1 @@ +import "@relmify/jest-fp-ts"; diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index 6eb4efaf7..79adb3e83 100644 --- a/packages/hoppscotch-cli/package.json +++ b/packages/hoppscotch-cli/package.json @@ -16,7 +16,8 @@ "debugger": "node debugger.js 9999", "prepublish": "pnpm exec tsup", "prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write", - "do-typecheck": "pnpm exec tsc --noEmit" + "do-typecheck": "pnpm exec tsc --noEmit", + "test": "pnpm run build && jest && rm -rf dist" }, "keywords": [ "cli", @@ -37,19 +38,26 @@ "devDependencies": { "@hoppscotch/data": "workspace:^0.4.2", "@hoppscotch/js-sandbox": "workspace:^2.0.0", - "@swc/core": "^1.2.163", + "@relmify/jest-fp-ts": "^2.0.2", + "@swc/core": "^1.2.181", "@types/axios": "^0.14.0", "@types/chalk": "^2.2.0", "@types/commander": "^2.12.2", + "@types/jest": "^27.4.1", + "@types/lodash": "^4.14.181", + "@types/qs": "^6.9.7", "axios": "^0.21.4", "chalk": "^4.1.1", "commander": "^8.0.0", "esm": "^3.2.25", - "fp-ts": "^2.11.3", + "fp-ts": "^2.12.1", + "io-ts": "^2.2.16", + "jest": "^27.5.1", "lodash": "^4.17.21", "prettier": "^2.6.2", "qs": "^6.10.3", - "tsup": "^5.12.4", - "typescript": "^4.6.3" + "ts-jest": "^27.1.4", + "tsup": "^5.12.7", + "typescript": "^4.6.4" } } diff --git a/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts b/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts new file mode 100644 index 000000000..6850f5ff3 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/commands/test.spec.ts @@ -0,0 +1,64 @@ +import { ExecException } from "child_process"; +import { HoppErrorCode } from "../../types/errors"; +import { execAsync, getErrorCode, getTestJsonFilePath } from "../utils"; + +describe("Test 'hopp test ' command:", () => { + test("No collection file path provided.", async () => { + const cmd = `node ./bin/hopp test`; + const { stdout } = await execAsync(cmd); + const out = getErrorCode(stdout); + + expect(out).toBe("NO_FILE_PATH"); + }); + + test("Collection file not found.", async () => { + const cmd = `node ./bin/hopp test notfound.json`; + const { stdout } = await execAsync(cmd); + const out = getErrorCode(stdout); + + expect(out).toBe("FILE_NOT_FOUND"); + }); + + test("Malformed collection file.", async () => { + const cmd = `node ./bin/hopp test ${getTestJsonFilePath( + "malformed-collection.json" + )}`; + const { stdout } = await execAsync(cmd); + const out = getErrorCode(stdout); + + expect(out).toBe("MALFORMED_COLLECTION"); + }); + + test("Invalid arguement.", async () => { + const cmd = `node ./bin/hopp invalid-arg`; + const { stdout } = await execAsync(cmd); + const out = getErrorCode(stdout); + + expect(out).toBe("INVALID_ARGUMENT"); + }); + + test("Collection file not JSON type.", async () => { + const cmd = `node ./bin/hopp test ${getTestJsonFilePath("notjson.txt")}`; + const { stdout } = await execAsync(cmd); + const out = getErrorCode(stdout); + + expect(out).toBe("FILE_NOT_JSON"); + }); + + test("Some errors occured (exit code 1).", async () => { + const cmd = `node ./bin/hopp test ${getTestJsonFilePath("fails.json")}`; + const { error } = await execAsync(cmd); + + expect(error).not.toBeNull(); + expect(error).toMatchObject({ + code: 1, + }); + }); + + test("No errors occured (exit code 0).", async () => { + const cmd = `node ./bin/hopp test ${getTestJsonFilePath("passes.json")}`; + const { error } = await execAsync(cmd); + + expect(error).toBeNull(); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/checks/checkFilePath.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/checks/checkFilePath.spec.ts new file mode 100644 index 000000000..7b7366d66 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/checks/checkFilePath.spec.ts @@ -0,0 +1,26 @@ +import { HoppCLIError } from "../../../types/errors"; +import { checkFilePath } from "../../../utils/checks"; + +describe("checkFilePath", () => { + test("File doesn't exists.", () => { + return expect( + checkFilePath("./src/samples/this-file-not-exists.json")() + ).resolves.toSubsetEqualLeft({ + code: "FILE_NOT_FOUND", + }); + }); + + test("File not of JSON type.", () => { + return expect( + checkFilePath("./src/__tests__/samples/notjson.txt")() + ).resolves.toSubsetEqualLeft({ + code: "FILE_NOT_JSON", + }); + }); + + test("Existing JSON file.", () => { + return expect( + checkFilePath("./src/__tests__/samples/passes.json")() + ).resolves.toBeRight(); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/checks/isHoppCLIError.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/checks/isHoppCLIError.spec.ts new file mode 100644 index 000000000..a12d127cb --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/checks/isHoppCLIError.spec.ts @@ -0,0 +1,19 @@ +import { isHoppCLIError } from "../../../utils/checks"; + +describe("isHoppCLIError", () => { + test("NULL error value.", () => { + expect(isHoppCLIError(null)).toBeFalsy(); + }); + + test("Non-existing code property.", () => { + expect(isHoppCLIError({ name: "name" })).toBeFalsy(); + }); + + test("Invalid code value.", () => { + expect(isHoppCLIError({ code: 2 })).toBeFalsy(); + }); + + test("Valid code value.", () => { + expect(isHoppCLIError({ code: "TEST_SCRIPT_ERROR" })).toBeTruthy(); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/checks/isHoppErrnoException.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/checks/isHoppErrnoException.spec.ts new file mode 100644 index 000000000..1c1716bb8 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/checks/isHoppErrnoException.spec.ts @@ -0,0 +1,19 @@ +import { isHoppErrnoException } from "../../../utils/checks"; + +describe("isHoppErrnoException", () => { + test("NULL exception value.", () => { + expect(isHoppErrnoException(null)).toBeFalsy(); + }); + + test("Non-existing name property.", () => { + expect(isHoppErrnoException({ what: "what" })).toBeFalsy(); + }); + + test("Invalid name value.", () => { + expect(isHoppErrnoException({ name: 3 })).toBeFalsy(); + }); + + test("Valid name value.", () => { + expect(isHoppErrnoException({ name: "name" })).toBeTruthy(); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/checks/isRESTCollection.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/checks/isRESTCollection.spec.ts new file mode 100644 index 000000000..b9b30e9ac --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/checks/isRESTCollection.spec.ts @@ -0,0 +1,84 @@ +import { isRESTCollection } from "../../../utils/checks"; + +describe("isRESTCollection", () => { + test("Undefined collection value.", () => { + expect(isRESTCollection(undefined)).toBeFalsy(); + }); + + test("Invalid id value.", () => { + expect( + isRESTCollection({ + v: 1, + name: "test", + id: 1, + }) + ).toBeFalsy(); + }); + + test("Invalid requests value.", () => { + expect( + isRESTCollection({ + v: 1, + name: "test", + id: "1", + requests: null, + }) + ).toBeFalsy(); + }); + + test("Invalid folders value.", () => { + expect( + isRESTCollection({ + v: 1, + name: "test", + id: "1", + requests: [], + folders: undefined, + }) + ).toBeFalsy(); + }); + + test("Invalid RESTCollection(s) in folders.", () => { + expect( + isRESTCollection({ + v: 1, + name: "test", + id: "1", + requests: [], + folders: [ + { + v: 1, + name: "test1", + id: "2", + requests: undefined, + folders: [], + }, + ], + }) + ).toBeFalsy(); + }); + + test("Invalid HoppRESTRequest(s) in requests.", () => { + expect( + isRESTCollection({ + v: 1, + name: "test", + id: "1", + requests: [{}], + folders: [], + }) + ).toBeFalsy(); + }); + + test("Valid RESTCollection.", () => { + expect( + isRESTCollection({ + v: 1, + name: "test", + id: "1", + requests: [], + folders: [], + }) + ).toBeTruthy(); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/collection/collectionsRunner.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/collection/collectionsRunner.spec.ts new file mode 100644 index 000000000..dcfe69dd6 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/collection/collectionsRunner.spec.ts @@ -0,0 +1,116 @@ +import { collectionsRunner } from "../../../utils/collections"; +import { HoppRESTRequest } from "@hoppscotch/data"; +import axios, { AxiosResponse } from "axios"; + +import "@relmify/jest-fp-ts"; + +jest.mock("axios"); + +const SAMPLE_HOPP_REQUEST = { + v: "1", + name: "request", + method: "GET", + endpoint: "https://example.com", + params: [], + headers: [], + preRequestScript: "", + testScript: "", + auth: { + authActive: false, + authType: "none", + }, + body: { + contentType: null, + body: null, + }, +}; + +const SAMPLE_RESOLVED_RESPONSE = { + data: { body: 1 }, + status: 200, + statusText: "OK", + config: { + url: "https://example.com", + supported: true, + method: "GET", + }, + headers: [], +}; + +describe("collectionsRunner", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + test("Empty HoppCollection.", () => { + return expect(collectionsRunner([])()).resolves.toStrictEqual([]); + }); + + test("Empty requests and folders in collection.", () => { + return expect( + collectionsRunner([ + { + v: 1, + name: "name", + folders: [], + requests: [], + }, + ])() + ).resolves.toMatchObject([]); + }); + + test("Non-empty requests in collection.", () => { + (axios as unknown as jest.Mock).mockResolvedValue(SAMPLE_RESOLVED_RESPONSE); + + return expect( + collectionsRunner([ + { + v: 1, + name: "collection", + folders: [], + requests: [SAMPLE_HOPP_REQUEST], + }, + ])() + ).resolves.toMatchObject([ + { + path: "collection/request", + tests: [], + errors: [], + result: true, + }, + ]); + }); + + test("Non-empty folders in collection.", () => { + (axios as unknown as jest.Mock).mockResolvedValue(SAMPLE_RESOLVED_RESPONSE); + + return expect( + collectionsRunner([ + { + v: 1, + name: "collection", + folders: [ + { + v: 1, + name: "folder", + folders: [], + requests: [SAMPLE_HOPP_REQUEST], + }, + ], + requests: [], + }, + ])() + ).resolves.toMatchObject([ + { + path: "collection/folder/request", + tests: [], + errors: [], + result: true, + }, + ]); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/collection/collectionsRunnerResult.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/collection/collectionsRunnerResult.spec.ts new file mode 100644 index 000000000..fdc9a5494 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/collection/collectionsRunnerResult.spec.ts @@ -0,0 +1,35 @@ +import { collectionsRunnerResult } from "../../../utils/collections"; + +const FALSE_RESULT_REPORT = { + path: "some_path", + tests: [], + errors: [], + result: false, + duration: { test: 1, request: 1, preRequest: 1 }, +}; + +const TRUE_RESULT_REPORT = { + path: "some_path", + tests: [], + errors: [], + result: true, + duration: { test: 1, request: 1, preRequest: 1 }, +}; + +describe("collectionsRunnerResult", () => { + test("Empty request-report.", () => { + expect(collectionsRunnerResult([])).toBeTruthy(); + }); + + test("Atleast 1 false result in request-report.", () => { + expect( + collectionsRunnerResult([FALSE_RESULT_REPORT, TRUE_RESULT_REPORT]) + ).toBeFalsy(); + }); + + test("All true result(s) in request-report.", () => { + expect( + collectionsRunnerResult([TRUE_RESULT_REPORT, TRUE_RESULT_REPORT]) + ).toBeTruthy(); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/getters/getDurationInSeconds.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/getters/getDurationInSeconds.spec.ts new file mode 100644 index 000000000..edeb52c7b --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/getters/getDurationInSeconds.spec.ts @@ -0,0 +1,24 @@ +import { DEFAULT_DURATION_PRECISION } from "../../../utils/constants"; +import { getDurationInSeconds } from "../../../utils/getters"; + +describe("getDurationInSeconds", () => { + const testDurations = [ + { end: [1, 111111111], precision: 1, expected: 1.1 }, + { end: [2, 333333333], precision: 2, expected: 2.33 }, + { + end: [3, 555555555], + precision: DEFAULT_DURATION_PRECISION, + expected: 3.556, + }, + { end: [4, 777777777], precision: 4, expected: 4.7778 }, + ]; + + test.each(testDurations)( + "($end.0 s + $end.1 ns) rounded-off to $expected", + ({ end, precision, expected }) => { + expect(getDurationInSeconds(end as [number, number], precision)).toBe( + expected + ); + } + ); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/getters/getEffectiveFinalMetaData.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/getters/getEffectiveFinalMetaData.spec.ts new file mode 100644 index 000000000..637064bac --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/getters/getEffectiveFinalMetaData.spec.ts @@ -0,0 +1,42 @@ +import { Environment } from "@hoppscotch/data"; +import { getEffectiveFinalMetaData } from "../../../utils/getters"; + +const DEFAULT_ENV = { + name: "name", + variables: [{ key: "PARAM", value: "parsed_param" }], +}; + +describe("getEffectiveFinalMetaData", () => { + test("Empty list of meta-data.", () => { + expect(getEffectiveFinalMetaData([], DEFAULT_ENV)).toSubsetEqualRight([]); + }); + + test("Non-empty active list of meta-data with unavailable ENV.", () => { + expect( + getEffectiveFinalMetaData( + [{ active: true, key: "<>", value: "<>" }], + DEFAULT_ENV + ) + ).toSubsetEqualRight([{ active: true, key: "", value: "" }]); + }); + + test("Inactive list of meta-data.", () => { + expect( + getEffectiveFinalMetaData( + [{ active: false, key: "KEY", value: "<>" }], + DEFAULT_ENV + ) + ).toSubsetEqualRight([]); + }); + + test("Active list of meta-data.", () => { + expect( + getEffectiveFinalMetaData( + [{ active: true, key: "PARAM", value: "<>" }], + DEFAULT_ENV + ) + ).toSubsetEqualRight([ + { active: true, key: "PARAM", value: "parsed_param" }, + ]); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/mutators/parseCollectionData.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/mutators/parseCollectionData.spec.ts new file mode 100644 index 000000000..f1fc79eab --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/mutators/parseCollectionData.spec.ts @@ -0,0 +1,36 @@ +import { HoppCLIError } from "../../../types/errors"; +import { parseCollectionData } from "../../../utils/mutators"; + +describe("parseCollectionData", () => { + test("Reading non-existing file.", () => { + return expect( + parseCollectionData("./src/__tests__/samples/notexist.txt")() + ).resolves.toSubsetEqualLeft({ + code: "UNKNOWN_ERROR", + }); + }); + + test("Unparseable JSON contents.", () => { + return expect( + parseCollectionData("./src/__tests__/samples/malformed-collection.json")() + ).resolves.toSubsetEqualLeft({ + code: "MALFORMED_COLLECTION", + }); + }); + + test("Invalid HoppCollection.", () => { + return expect( + parseCollectionData( + "./src/__tests__/samples/malformed-collection2.json" + )() + ).resolves.toSubsetEqualLeft({ + code: "MALFORMED_COLLECTION", + }); + }); + + test("Valid HoppCollection.", () => { + return expect( + parseCollectionData("./src/__tests__/samples/passes.json")() + ).resolves.toBeRight(); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/pre-request/getEffectiveRESTRequest.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/pre-request/getEffectiveRESTRequest.spec.ts new file mode 100644 index 000000000..0aa46fcc9 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/pre-request/getEffectiveRESTRequest.spec.ts @@ -0,0 +1,148 @@ +import { Environment, HoppRESTRequest } from "@hoppscotch/data"; +import { EffectiveHoppRESTRequest } from "../../../interfaces/request"; +import { HoppCLIError } from "../../../types/errors"; +import { getEffectiveRESTRequest } from "../../../utils/pre-request"; + +const DEFAULT_ENV = { + name: "name", + variables: [ + { + key: "HEADER", + value: "parsed_header", + }, + { key: "PARAM", value: "parsed_param" }, + { key: "TOKEN", value: "parsed_token" }, + { key: "BODY_PROP", value: "parsed_body_prop" }, + { key: "ENDPOINT", value: "https://parsed-endpoint.com" }, + ], +}; + +const DEFAULT_REQUEST = { + v: "1", + name: "name", + method: "GET", + endpoint: "https://example.com", + params: [], + headers: [], + preRequestScript: "", + testScript: "", + auth: { + authActive: false, + authType: "none", + }, + body: { + contentType: null, + body: null, + }, +}; + +describe("getEffectiveRESTRequest", () => { + let SAMPLE_REQUEST = Object.assign({}, DEFAULT_REQUEST); + + beforeEach(() => { + SAMPLE_REQUEST = Object.assign({}, DEFAULT_REQUEST); + }); + + test("Endpoint, headers and params with unavailable ENV.", () => { + SAMPLE_REQUEST.headers = [ + { + key: "HEADER", + value: "<>", + active: true, + }, + ]; + SAMPLE_REQUEST.params = [ + { + key: "PARAM", + value: "<>", + active: true, + }, + ]; + SAMPLE_REQUEST.endpoint = "<>"; + + expect( + getEffectiveRESTRequest(SAMPLE_REQUEST, DEFAULT_ENV) + ).toSubsetEqualRight({ + effectiveFinalHeaders: [{ active: true, key: "HEADER", value: "" }], + effectiveFinalParams: [{ active: true, key: "PARAM", value: "" }], + effectiveFinalURL: "", + }); + }); + + test("Auth with unavailable ENV.", () => { + SAMPLE_REQUEST.auth = { + authActive: true, + authType: "bearer", + token: "<>", + }; + + expect( + getEffectiveRESTRequest(SAMPLE_REQUEST, DEFAULT_ENV) + ).toSubsetEqualRight({ + effectiveFinalHeaders: [ + { active: true, key: "Authorization", value: "Bearer " }, + ], + }); + }); + + test("Body with unavailable ENV.", () => { + SAMPLE_REQUEST.body = { + contentType: "text/plain", + body: "<>", + }; + + expect( + getEffectiveRESTRequest(SAMPLE_REQUEST, DEFAULT_ENV) + ).toSubsetEqualLeft({ + code: "PARSING_ERROR", + }); + }); + + test("Request meta-data with available ENVs.", () => { + SAMPLE_REQUEST.headers = [ + { + key: "HEADER", + value: "<
>", + active: true, + }, + ]; + SAMPLE_REQUEST.params = [ + { + key: "PARAM", + value: "<>", + active: true, + }, + ]; + SAMPLE_REQUEST.endpoint = "<>"; + SAMPLE_REQUEST.auth = { + authActive: true, + authType: "bearer", + token: "<>", + }; + SAMPLE_REQUEST.body = { + contentType: "text/plain", + body: "<>", + }; + + const vars = DEFAULT_ENV.variables; + + expect( + getEffectiveRESTRequest(SAMPLE_REQUEST, DEFAULT_ENV) + ).toSubsetEqualRight({ + effectiveFinalHeaders: [ + { active: true, key: "HEADER", value: vars[0].value }, + { + active: true, + key: "Authorization", + value: `Bearer ${vars[2].value}`, + }, + { active: true, key: "content-type", value: "text/plain" }, + ], + effectiveFinalParams: [ + { active: true, key: "PARAM", value: vars[1].value }, + ], + effectiveFinalURL: vars[4].value, + effectiveFinalBody: vars[3].value, + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/pre-request/getPreRequestMetrics.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/pre-request/getPreRequestMetrics.spec.ts new file mode 100644 index 000000000..2f28bd068 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/pre-request/getPreRequestMetrics.spec.ts @@ -0,0 +1,24 @@ +import { PreRequestMetrics, RequestMetrics } from "../../../types/response"; +import { getPreRequestMetrics } from "../../../utils/pre-request"; + +describe("getPreRequestMetrics", () => { + test("With empty errors.", () => { + expect(getPreRequestMetrics([], 1)).toMatchObject({ + scripts: { failed: 0, passed: 1 }, + }); + }); + + test("With non-empty errors.", () => { + expect( + getPreRequestMetrics( + [ + { code: "REQUEST_ERROR", data: {} }, + { code: "PRE_REQUEST_SCRIPT_ERROR", data: {} }, + ], + 1 + ) + ).toMatchObject({ + scripts: { failed: 1, passed: 0 }, + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/pre-request/preRequestScriptRunner.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/pre-request/preRequestScriptRunner.spec.ts new file mode 100644 index 000000000..f382f7f44 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/pre-request/preRequestScriptRunner.spec.ts @@ -0,0 +1,71 @@ +import { HoppRESTRequest } from "@hoppscotch/data"; +import { HoppEnvs } from "../../../types/request"; +import * as E from "fp-ts/Either"; +import { HoppCLIError } from "../../../types/errors"; +import { EffectiveHoppRESTRequest } from "../../../interfaces/request"; +import { preRequestScriptRunner } from "../../../utils/pre-request"; + +import "@relmify/jest-fp-ts"; + +const SAMPLE_ENVS: HoppEnvs = { + global: [], + selected: [], +}; +const VALID_PRE_REQUEST_SCRIPT = ` + pw.env.set("ENDPOINT","https://example.com"); +`; +const INVALID_PRE_REQUEST_SCRIPT = "d"; +const SAMPLE_REQUEST: HoppRESTRequest = { + v: "1", + name: "request", + method: "GET", + endpoint: "<>", + params: [], + headers: [], + preRequestScript: "", + testScript: "", + auth: { authActive: false, authType: "none" }, + body: { + contentType: null, + body: null, + }, +}; + +describe("preRequestScriptRunner", () => { + let SUCCESS_PRE_REQUEST_RUNNER: E.Either< + HoppCLIError, + EffectiveHoppRESTRequest + >, + FAILURE_PRE_REQUEST_RUNNER: E.Either< + HoppCLIError, + EffectiveHoppRESTRequest + >; + + beforeAll(async () => { + SAMPLE_REQUEST.preRequestScript = VALID_PRE_REQUEST_SCRIPT; + SUCCESS_PRE_REQUEST_RUNNER = await preRequestScriptRunner( + SAMPLE_REQUEST, + SAMPLE_ENVS + )(); + + SAMPLE_REQUEST.preRequestScript = INVALID_PRE_REQUEST_SCRIPT; + FAILURE_PRE_REQUEST_RUNNER = await preRequestScriptRunner( + SAMPLE_REQUEST, + SAMPLE_ENVS + )(); + }); + + test("Parsing of request endpoint with set ENV.", () => { + expect(SUCCESS_PRE_REQUEST_RUNNER).toSubsetEqualRight(< + EffectiveHoppRESTRequest + >{ + effectiveFinalURL: "https://example.com", + }); + }); + + test("Failed execution due to unknown variable error.", () => { + expect(FAILURE_PRE_REQUEST_RUNNER).toSubsetEqualLeft({ + code: "PRE_REQUEST_SCRIPT_ERROR", + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/request/getRequestMetrics.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/request/getRequestMetrics.spec.ts new file mode 100644 index 000000000..98c534232 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/request/getRequestMetrics.spec.ts @@ -0,0 +1,24 @@ +import { RequestMetrics } from "../../../types/response"; +import { getRequestMetrics } from "../../../utils/request"; + +describe("getRequestMetrics", () => { + test("With empty errors.", () => { + expect(getRequestMetrics([], 1)).toMatchObject({ + requests: { failed: 0, passed: 1 }, + }); + }); + + test("With non-empty errors.", () => { + expect( + getRequestMetrics( + [ + { code: "REQUEST_ERROR", data: {} }, + { code: "PARSING_ERROR", data: {} }, + ], + 1 + ) + ).toMatchObject({ + requests: { failed: 1, passed: 0 }, + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/request/processRequest.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/request/processRequest.spec.ts new file mode 100644 index 000000000..2cc73c6cd --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/request/processRequest.spec.ts @@ -0,0 +1,104 @@ +import { HoppRESTRequest } from "@hoppscotch/data"; +import axios, { AxiosResponse } from "axios"; +import { processRequest } from "../../../utils/request"; +import { HoppEnvs } from "../../../types/request"; + +import "@relmify/jest-fp-ts"; + +jest.mock("axios"); + +const DEFAULT_REQUEST = { + v: "1", + name: "name", + method: "POST", + endpoint: "https://example.com", + params: [], + headers: [], + preRequestScript: "", + testScript: "", + auth: { + authType: "none", + authActive: false, + }, + body: { + contentType: null, + body: null, + }, +}; + +const DEFAULT_RESPONSE = { + data: {}, + status: 200, + config: { + url: "https://example.com", + supported: true, + method: "POST", + }, + statusText: "OK", + headers: [], +}; + +const DEFAULT_ENVS = { + global: [], + selected: [], +}; + +describe("processRequest", () => { + let SAMPLE_REQUEST = DEFAULT_REQUEST; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + afterEach(() => { + SAMPLE_REQUEST = DEFAULT_REQUEST; + }); + + test("With empty envs for 'true' result.", () => { + (axios as unknown as jest.Mock).mockResolvedValue(DEFAULT_RESPONSE); + + return expect( + processRequest(SAMPLE_REQUEST, DEFAULT_ENVS, "fake/collection/path")() + ).resolves.toMatchObject({ + report: { + result: true, + }, + }); + }); + + test("With non-empty envs, pre-request-script and test-script.", () => { + SAMPLE_REQUEST.preRequestScript = ` + pw.env.set("ENDPOINT", "https://example.com"); + `; + SAMPLE_REQUEST.testScript = ` + pw.test("check status.", () => { + pw.expect(pw.response.status).toBe(200); + }); + `; + + (axios as unknown as jest.Mock).mockResolvedValue(DEFAULT_RESPONSE); + + return expect( + processRequest(SAMPLE_REQUEST, DEFAULT_ENVS, "fake/collection/path")() + ).resolves.toMatchObject({ + envs: { + selected: [{ key: "ENDPOINT", value: "https://example.com" }], + }, + report: { + result: true, + }, + }); + }); + + test("With invalid-pre-request-script.", () => { + SAMPLE_REQUEST.preRequestScript = `invalid`; + + (axios as unknown as jest.Mock).mockResolvedValue(DEFAULT_RESPONSE); + + return expect( + processRequest(SAMPLE_REQUEST, DEFAULT_ENVS, "fake/request/path")() + ).resolves.toMatchObject({ + report: { result: false }, + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/request/requestRunner.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/request/requestRunner.spec.ts new file mode 100644 index 000000000..e1df4a2df --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/request/requestRunner.spec.ts @@ -0,0 +1,111 @@ +import axios, { AxiosError, AxiosResponse } from "axios"; +import { RequestConfig } from "../../../interfaces/request"; +import { requestRunner } from "../../../utils/request"; +import { RequestRunnerResponse } from "../../../interfaces/response"; + +import "@relmify/jest-fp-ts"; + +jest.mock("axios"); + +describe("requestRunner", () => { + let SAMPLE_REQUEST_CONFIG: RequestConfig = { + url: "https://example.com", + supported: false, + method: "GET", + }; + + beforeEach(() => { + SAMPLE_REQUEST_CONFIG.supported = false; + SAMPLE_REQUEST_CONFIG.url = "https://example.com"; + SAMPLE_REQUEST_CONFIG.method = "GET"; + jest.clearAllMocks(); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + it("Should handle axios-error with response info.", () => { + jest.spyOn(axios, "isAxiosError").mockReturnValue(true); + (axios as unknown as jest.Mock).mockRejectedValueOnce({ + name: "name", + message: "message", + config: SAMPLE_REQUEST_CONFIG, + isAxiosError: true, + response: { + data: "data", + status: 404, + statusText: "NOT FOUND", + headers: [], + config: SAMPLE_REQUEST_CONFIG, + }, + toJSON: () => Object({}), + }); + + return expect( + requestRunner(SAMPLE_REQUEST_CONFIG)() + ).resolves.toSubsetEqualRight({ + body: "data", + status: 404, + }); + }); + + it("Should handle axios-error for unsupported request.", () => { + jest.spyOn(axios, "isAxiosError").mockReturnValue(true); + (axios as unknown as jest.Mock).mockRejectedValueOnce({ + name: "name", + message: "message", + config: SAMPLE_REQUEST_CONFIG, + isAxiosError: true, + toJSON: () => Object({}), + }); + + return expect( + requestRunner(SAMPLE_REQUEST_CONFIG)() + ).resolves.toSubsetEqualRight({ + status: 501, + body: {}, + }); + }); + + it("Should handle axios-error with request info.", () => { + jest.spyOn(axios, "isAxiosError").mockReturnValue(true); + SAMPLE_REQUEST_CONFIG.supported = true; + (axios as unknown as jest.Mock).mockRejectedValueOnce({ + name: "name", + message: "message", + config: SAMPLE_REQUEST_CONFIG, + isAxiosError: true, + request: {}, + toJSON: () => Object({}), + }); + + return expect(requestRunner(SAMPLE_REQUEST_CONFIG)()).resolves.toBeLeft(); + }); + + it("Should handle unknown error.", () => { + jest.spyOn(axios, "isAxiosError").mockReturnValue(false); + (axios as unknown as jest.Mock).mockRejectedValueOnce({}); + + return expect(requestRunner(SAMPLE_REQUEST_CONFIG)()).resolves.toBeLeft(); + }); + + it("Should successfully execute.", () => { + SAMPLE_REQUEST_CONFIG.supported = true; + (axios as unknown as jest.Mock).mockResolvedValue({ + data: "data", + status: 200, + config: SAMPLE_REQUEST_CONFIG, + statusText: "OK", + headers: [], + }); + + return expect( + requestRunner(SAMPLE_REQUEST_CONFIG)() + ).resolves.toSubsetEqualRight({ + status: 200, + body: "data", + method: "GET", + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/test/getTestMetrics.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/test/getTestMetrics.spec.ts new file mode 100644 index 000000000..e0115f980 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/test/getTestMetrics.spec.ts @@ -0,0 +1,55 @@ +import { TestMetrics } from "../../../types/response"; +import { getTestMetrics } from "../../../utils/test"; + +describe("getTestMetrics", () => { + test("With empty test-reports and errors.", () => { + expect(getTestMetrics([], 1, [])).toMatchObject({ + tests: { passed: 0, failed: 0 }, + testSuites: { failed: 0, passed: 0 }, + duration: 1, + scripts: { failed: 0, passed: 1 }, + }); + }); + + test("With non-empty test-reports and no test-script-error.", () => { + expect( + getTestMetrics( + [ + { + descriptor: "descriptor", + expectResults: [], + failed: 0, + passed: 2, + }, + { + descriptor: "descriptor", + expectResults: [], + failed: 2, + passed: 1, + }, + ], + 5, + [] + ) + ).toMatchObject({ + tests: { failed: 2, passed: 3 }, + testSuites: { failed: 1, passed: 1 }, + scripts: { failed: 0, passed: 1 }, + duration: 5, + }); + }); + + test("With empty test-reports and some test-script-error.", () => { + expect( + getTestMetrics([], 5, [ + { code: "TEST_SCRIPT_ERROR", data: {} }, + { code: "PRE_REQUEST_SCRIPT_ERROR", data: {} }, + ]) + ).toMatchObject({ + tests: { failed: 0, passed: 0 }, + testSuites: { failed: 0, passed: 0 }, + scripts: { failed: 1, passed: 0 }, + duration: 5, + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/test/testDescriptorParser.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/test/testDescriptorParser.spec.ts new file mode 100644 index 000000000..21baa4faf --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/test/testDescriptorParser.spec.ts @@ -0,0 +1,63 @@ +import { TestDescriptor } from "@hoppscotch/js-sandbox"; +import { testDescriptorParser, getTestMetrics } from "../../../utils/test"; +import { TestReport } from "../../../interfaces/response"; +import { TestMetrics } from "../../../types/response"; + +import "@relmify/jest-fp-ts"; + +const SAMPLE_TEST_DESCRIPTOR: TestDescriptor = { + descriptor: "Status code is 200", + expectResults: [ + { + status: "error", + message: "some_message", + }, + ], + children: [ + { + descriptor: "Check JSON response property", + expectResults: [ + { + status: "pass", + message: "some_message", + }, + ], + children: [], + }, + { + descriptor: "Check header property", + expectResults: [ + { + status: "fail", + message: "some_message", + }, + ], + children: [], + }, + ], +}; + +describe("testDescriptorParser", () => { + let TEST_REPORT: TestReport[]; + beforeAll(async () => { + TEST_REPORT = await testDescriptorParser(SAMPLE_TEST_DESCRIPTOR)(); + }); + + it("Should have 3 tests-report.", () => { + expect(TEST_REPORT).toEqual(expect.any(Array)); + expect(TEST_REPORT.length).toStrictEqual(3); + }); + + it("Should have 1 passed, 2 failed test-cases; 1 passed, 2 failed test-suite.", () => { + expect(getTestMetrics(TEST_REPORT, 1, [])).toMatchObject({ + tests: { + failed: 2, + passed: 1, + }, + testSuites: { + failed: 2, + passed: 1, + }, + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/functions/test/testRunner.spec.ts b/packages/hoppscotch-cli/src/__tests__/functions/test/testRunner.spec.ts new file mode 100644 index 000000000..0eeafa913 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/functions/test/testRunner.spec.ts @@ -0,0 +1,73 @@ +import { TestResponse } from "@hoppscotch/js-sandbox"; +import * as E from "fp-ts/Either"; +import { TestRunnerRes } from "../../../types/response"; +import { HoppCLIError } from "../../../types/errors"; +import { getTestMetrics, testRunner } from "../../../utils/test"; +import { HoppEnvs } from "../../../types/request"; + +import "@relmify/jest-fp-ts"; + +const SAMPLE_ENVS: HoppEnvs = { + global: [], + selected: [ + { + key: "DEVBLIN", + value: "set-by-devblin", + }, + ], +}; +const SAMPLE_RESPONSE: TestResponse = { + status: 200, + headers: [], + body: {}, +}; + +describe("testRunner", () => { + let SUCCESS_TEST_RUNNER_RES: E.Either, + FAILURE_TEST_RUNNER_RES: E.Either; + + beforeAll(async () => { + SUCCESS_TEST_RUNNER_RES = await testRunner({ + testScript: ` + // Check status code is 200 + pw.test("Status code is 200", ()=> { + pw.expect(pw.response.status).toBe(200); + }); + + // Check JSON response property + pw.test("Check JSON response property", ()=> { + pw.expect(pw.response.body).toBeType("string") + pw.expect(pw.response.body).toBe("body"); + }); + `, + envs: SAMPLE_ENVS, + response: SAMPLE_RESPONSE, + })(); + + FAILURE_TEST_RUNNER_RES = await testRunner({ + testScript: "a", + envs: SAMPLE_ENVS, + response: SAMPLE_RESPONSE, + })(); + }); + + it("Should have 2 failed, 1 passed test-cases; 1 failed, 1 passed test-suites.", () => { + expect(SUCCESS_TEST_RUNNER_RES).toBeRight(); + + if (E.isRight(SUCCESS_TEST_RUNNER_RES)) { + const { duration, testsReport } = SUCCESS_TEST_RUNNER_RES.right; + const { tests, testSuites } = getTestMetrics(testsReport, duration, []); + + expect(tests.failed).toStrictEqual(2); + expect(tests.passed).toStrictEqual(1); + expect(testSuites.failed).toStrictEqual(1); + expect(testSuites.passed).toStrictEqual(1); + } + }); + + it("Should fail to execute with test-script-error.", () => { + expect(FAILURE_TEST_RUNNER_RES).toSubsetEqualLeft({ + code: "TEST_SCRIPT_ERROR", + }); + }); +}); diff --git a/packages/hoppscotch-cli/src/__tests__/samples/fails.json b/packages/hoppscotch-cli/src/__tests__/samples/fails.json new file mode 100644 index 000000000..bdc0657cf --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/fails.json @@ -0,0 +1,51 @@ +[ + { + "v": 1, + "name": "tests", + "folders": [], + "requests": [ + { + "v": "1", + "endpoint": "https://echo.hoppscotch.io/<>", + "name": "", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "Headers", + "key": "", + "value": "" + }, + "preRequestScript": "pw.env.set(\"HEADERS_TYPE1\", \"devblin_local1\");", + "testScript": "// Check status code is 200\npwd.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.method).toBe(\"GET\");\n pw.expect(pw.response.body.headers).toBeType(\"string\");\n});", + "body": { + "contentType": "application/json", + "body": "{\n\"test\": \"<>\"\n}" + } + }, + { + "v": "1", + "endpoint": "https://echo.hoppscotch.dio/<>", + "name": "success", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "Headers", + "key": "", + "value": "" + }, + "preRequestScript": "pw.env.setd(\"HEADERS_TYPE2\", \"devblin_local2\");", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(300);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.method).toBe(\"GET\");\n pw.expect(pw.response.body.headers).toBeType(\"object\");\n});", + "body": { + "contentType": "application/json", + "body": "{\n\"test\": \"<>\"\n}" + } + } + ] + } +] diff --git a/packages/hoppscotch-cli/src/__tests__/samples/malformed-collection.json b/packages/hoppscotch-cli/src/__tests__/samples/malformed-collection.json new file mode 100644 index 000000000..5e80935c2 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/malformed-collection.json @@ -0,0 +1,50 @@ +[ + { + "v": 1, + "folders": [], + "requests": + { + "v": "1", + "endpoint": "https://echo.hoppscotch.io/<>", + "name": "fail", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "Headers", + "key": "", + "value": "" + }, + "preRequestScript": "pw.env.set(\"HEADERS_TYPE1\", \"devblin_local1\");", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.method).toBe(\"GET\");\n pw.expect(pw.response.body.headers).toBeType(\"string\");\n});", + "body": { + "contentType": "application/json", + "body": "{\n\"test\": \"<>\"\n}" + } + }, + { + "v": "1", + "endpoint": "https://echo.hoppscotch.io/<>", + "name": "success", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "Headers", + "key": "", + "value": "" + }, + "preRequestScript": "pw.env.set(\"HEADERS_TYPE2\", \"devblin_local2\");", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(300);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.method).toBe(\"GET\");\n pw.expect(pw.response.body.headers).toBeType(\"object\");\n});", + "body": { + "contentType": "application/json", + "body": "{\n\"test\": \"<>\"\n}" + } + } + ] + } +] diff --git a/packages/hoppscotch-cli/src/__tests__/samples/malformed-collection2.json b/packages/hoppscotch-cli/src/__tests__/samples/malformed-collection2.json new file mode 100644 index 000000000..8df994d77 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/malformed-collection2.json @@ -0,0 +1,7 @@ +[ + { + "v": 1, + "name": "tests", + "folders": [] + } +] diff --git a/packages/hoppscotch-cli/src/__tests__/samples/notjson.txt b/packages/hoppscotch-cli/src/__tests__/samples/notjson.txt new file mode 100644 index 000000000..8081ea880 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/notjson.txt @@ -0,0 +1,29 @@ +[ + { + "v": 1, + "folders": [], + "requests": + { + "v": "1", + "endpoint": "https://echo.hoppscotch.io/<>", + "name": "fail", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "Headers", + "key": "", + "value": "" + }, + "preRequestScript": "pw.env.set(\"HEADERS_TYPE1\", \"devblin_local1\");", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.method).toBe(\"GET\");\n pw.expect(pw.response.body.headers).toBeType(\"string\");\n});", + "body": { + "contentType": "application/json", + "body": "{\n\"test\": \"<>\"\n}" + } + } + ] + } +] diff --git a/packages/hoppscotch-cli/src/__tests__/samples/passes.json b/packages/hoppscotch-cli/src/__tests__/samples/passes.json new file mode 100644 index 000000000..aea1fc356 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/samples/passes.json @@ -0,0 +1,51 @@ +[ + { + "v": 1, + "name": "tests", + "folders": [], + "requests": [ + { + "v": "1", + "endpoint": "https://echo.hoppscotch.io/<>", + "name": "", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "Headers", + "key": "", + "value": "" + }, + "preRequestScript": "pw.env.set(\"HEADERS_TYPE1\", \"devblin_local1\");", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.method).toBe(\"GET\");\n pw.expect(pw.response.body.headers).toBeType(\"object\");\n});", + "body": { + "contentType": "application/json", + "body": "{\n\"test\": \"<>\"\n}" + } + }, + { + "v": "1", + "endpoint": "https://echo.hoppscotch.io/<>", + "name": "success", + "params": [], + "headers": [], + "method": "GET", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "Headers", + "key": "", + "value": "" + }, + "preRequestScript": "pw.env.set(\"HEADERS_TYPE2\", \"devblin_local2\");", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.method).toBe(\"GET\");\n pw.expect(pw.response.body.headers).toBeType(\"object\");\n});", + "body": { + "contentType": "application/json", + "body": "{\n\"test\": \"<>\"\n}" + } + } + ] + } +] diff --git a/packages/hoppscotch-cli/src/__tests__/types.ts b/packages/hoppscotch-cli/src/__tests__/types.ts new file mode 100644 index 000000000..b84c6b7d4 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/types.ts @@ -0,0 +1,7 @@ +import { ExecException } from "child_process"; + +export type ExecResponse = { + error: ExecException | null; + stdout: string; + stderr: string; +}; diff --git a/packages/hoppscotch-cli/src/__tests__/utils.ts b/packages/hoppscotch-cli/src/__tests__/utils.ts new file mode 100644 index 000000000..75a2a79e2 --- /dev/null +++ b/packages/hoppscotch-cli/src/__tests__/utils.ts @@ -0,0 +1,26 @@ +import { exec } from "child_process"; +import { ExecResponse } from "./types"; + +export const execAsync = (command: string): Promise => + new Promise((resolve) => + exec(command, (error, stdout, stderr) => resolve({ error, stdout, stderr })) + ); + +export const trimAnsi = (target: string) => { + const ansiRegex = + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; + + return target.replace(ansiRegex, ""); +}; + +export const getErrorCode = (out: string) => { + const ansiTrimmedStr = trimAnsi(out); + + return ansiTrimmedStr.split(" ")[0]; +}; + +export const getTestJsonFilePath = (file: string) => { + const filePath = `${process.cwd()}/src/__tests__/samples/${file}`; + + return filePath; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 155afd12b..9e3b88c4d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -355,37 +355,51 @@ importers: specifiers: '@hoppscotch/data': workspace:^0.4.2 '@hoppscotch/js-sandbox': workspace:^2.0.0 - '@swc/core': ^1.2.163 + '@relmify/jest-fp-ts': ^2.0.2 + '@swc/core': ^1.2.181 '@types/axios': ^0.14.0 '@types/chalk': ^2.2.0 '@types/commander': ^2.12.2 + '@types/jest': ^27.4.1 + '@types/lodash': ^4.14.181 + '@types/qs': ^6.9.7 axios: ^0.21.4 chalk: ^4.1.1 commander: ^8.0.0 esm: ^3.2.25 - fp-ts: ^2.11.3 + fp-ts: ^2.12.1 + io-ts: ^2.2.16 + jest: ^27.5.1 lodash: ^4.17.21 prettier: ^2.6.2 qs: ^6.10.3 - tsup: ^5.12.4 - typescript: ^4.6.3 + ts-jest: ^27.1.4 + tsup: ^5.12.7 + typescript: ^4.6.4 devDependencies: '@hoppscotch/data': link:../hoppscotch-data '@hoppscotch/js-sandbox': link:../hoppscotch-js-sandbox - '@swc/core': 1.2.163 + '@relmify/jest-fp-ts': 2.0.2_fp-ts@2.12.1+io-ts@2.2.16 + '@swc/core': 1.2.181 '@types/axios': 0.14.0 '@types/chalk': 2.2.0 '@types/commander': 2.12.2 + '@types/jest': 27.4.1 + '@types/lodash': 4.14.181 + '@types/qs': 6.9.7 axios: 0.21.4 chalk: 4.1.2 commander: 8.3.0 esm: 3.2.25 - fp-ts: 2.11.9 + fp-ts: 2.12.1 + io-ts: 2.2.16_fp-ts@2.12.1 + jest: 27.5.1 lodash: 4.17.21 prettier: 2.6.2 qs: 6.10.3 - tsup: 5.12.4_typescript@4.6.3 - typescript: 4.6.3 + ts-jest: 27.1.4_53ggqi2i4rbcfjtktmjua6zili + tsup: 5.12.7_typescript@4.6.4 + typescript: 4.6.4 packages/hoppscotch-data: specifiers: @@ -580,14 +594,14 @@ packages: resolution: {integrity: sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 /@babel/helper-builder-binary-assignment-operator-visitor/7.16.7: resolution: {integrity: sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-explode-assignable-expression': 7.16.7 - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 /@babel/helper-compilation-targets/7.16.7_@babel+core@7.17.9: resolution: {integrity: sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==} @@ -702,7 +716,7 @@ packages: resolution: {integrity: sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 /@babel/helper-function-name/7.16.0: resolution: {integrity: sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==} @@ -755,7 +769,7 @@ packages: resolution: {integrity: sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 /@babel/helper-module-imports/7.16.0: resolution: {integrity: sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==} @@ -804,7 +818,7 @@ packages: resolution: {integrity: sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 /@babel/helper-plugin-utils/7.16.7: resolution: {integrity: sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==} @@ -816,7 +830,7 @@ packages: dependencies: '@babel/helper-annotate-as-pure': 7.16.7 '@babel/helper-wrap-function': 7.16.8 - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 transitivePeerDependencies: - supports-color @@ -828,7 +842,7 @@ packages: '@babel/helper-member-expression-to-functions': 7.16.7 '@babel/helper-optimise-call-expression': 7.16.7 '@babel/traverse': 7.17.9 - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 transitivePeerDependencies: - supports-color @@ -848,7 +862,7 @@ packages: resolution: {integrity: sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 /@babel/helper-split-export-declaration/7.16.0: resolution: {integrity: sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==} @@ -877,7 +891,7 @@ packages: '@babel/helper-function-name': 7.17.9 '@babel/template': 7.16.7 '@babel/traverse': 7.17.9 - '@babel/types': 7.16.8 + '@babel/types': 7.17.0 transitivePeerDependencies: - supports-color @@ -2204,10 +2218,10 @@ packages: '@types/node': 17.0.24 chalk: 4.1.2 cosmiconfig: 7.0.1 - cosmiconfig-typescript-loader: 1.0.9_c6ucwwwirjo6ockovr3lj3o2cm + cosmiconfig-typescript-loader: 1.0.9_kpiehm6dkipypriomyghl2wcpq lodash: 4.17.21 resolve-from: 5.0.0 - typescript: 4.6.3 + typescript: 4.6.4 transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -3504,7 +3518,7 @@ packages: chalk: 4.1.2 emittery: 0.8.1 exit: 0.1.2 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jest-changed-files: 27.5.1 jest-config: 27.5.1 jest-haste-map: 27.5.1 @@ -3518,7 +3532,7 @@ packages: jest-util: 27.5.1 jest-validate: 27.5.1 jest-watcher: 27.5.1 - micromatch: 4.0.4 + micromatch: 4.0.5 rimraf: 3.0.2 slash: 3.0.0 strip-ansi: 6.0.1 @@ -3540,6 +3554,13 @@ packages: jest-mock: 27.5.1 dev: true + /@jest/expect-utils/28.1.0: + resolution: {integrity: sha512-5BrG48dpC0sB80wpeIX5FU6kolDJI4K0n5BM9a5V38MGx0pyRvUBSS0u2aNTdDzmOrCjhOg8pGs6a20ivYkdmw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + jest-get-type: 28.0.2 + dev: true + /@jest/fake-timers/27.5.1: resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -3580,7 +3601,7 @@ packages: collect-v8-coverage: 1.0.1 exit: 0.1.2 glob: 7.2.0 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 istanbul-lib-coverage: 3.2.0 istanbul-lib-instrument: 5.1.0 istanbul-lib-report: 3.0.0 @@ -3599,6 +3620,13 @@ packages: - supports-color dev: true + /@jest/schemas/28.0.2: + resolution: {integrity: sha512-YVDJZjd4izeTDkij00vHHAymNXQ6WWsdChFRK86qck6Jpr3DCL5W3Is3vslviRlP+bLuMYRLbdp98amMvqudhA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@sinclair/typebox': 0.23.5 + dev: true + /@jest/source-map/27.5.1: resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -3640,11 +3668,11 @@ packages: chalk: 4.1.2 convert-source-map: 1.8.0 fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jest-haste-map: 27.5.1 jest-regex-util: 27.5.1 jest-util: 27.5.1 - micromatch: 4.0.4 + micromatch: 4.0.5 pirates: 4.0.5 slash: 3.0.0 source-map: 0.6.1 @@ -3664,6 +3692,18 @@ packages: chalk: 4.1.2 dev: true + /@jest/types/28.1.0: + resolution: {integrity: sha512-xmEggMPr317MIOjjDoZ4ejCSr9Lpbt/u34+dvc99t7DS8YirW5rwZEhzKPC2BMUFkUhI48qs6qLUSGw5FuL0GA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/schemas': 28.0.2 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 17.0.31 + '@types/yargs': 17.0.10 + chalk: 4.1.2 + dev: true + /@jridgewell/resolve-uri/3.0.5: resolution: {integrity: sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==} engines: {node: '>=6.0.0'} @@ -3919,11 +3959,11 @@ packages: ufo: 0.7.11 dev: false - /@nuxt/kit-edge/3.0.0-rc.2-27530889.9e5a3cd: - resolution: {integrity: sha512-D/Fm/7igO3/GE+5S8528frpJT25V+i4Y8ZcZ4MuO0i6karl4whwcSDkVOmrZBCtLa0IG+FD2f/6E4JJaB0BBHQ==} + /@nuxt/kit-edge/3.0.0-rc.3-27536597.3359b3b: + resolution: {integrity: sha512-vQWAzbsHk8sP0tGXLfKX6EfnR6cb6sG4CJLcj2sptuyKwnXrHjQTfdWxKkJLqVBKq4O/8Vej9Xm9r5VLGOpNPA==} engines: {node: ^14.16.0 || ^16.11.0 || ^17.0.0 || ^18.0.0} dependencies: - '@nuxt/schema': /@nuxt/schema-edge/3.0.0-rc.2-27530889.9e5a3cd + '@nuxt/schema': /@nuxt/schema-edge/3.0.0-rc.3-27536597.3359b3b c12: 0.2.7 consola: 2.15.3 defu: 6.0.0 @@ -3939,7 +3979,7 @@ packages: scule: 0.2.1 semver: 7.3.7 unctx: 1.1.4 - unimport: 0.1.8 + unimport: 0.2.1 untyped: 0.4.4 transitivePeerDependencies: - esbuild @@ -3971,8 +4011,8 @@ packages: - encoding dev: false - /@nuxt/schema-edge/3.0.0-rc.2-27530889.9e5a3cd: - resolution: {integrity: sha512-Ac8bXC8nXYZKGJPLIMPKZwA5i7X/oaWGeSIJ5zruUt9XwLRP2BOgOTTrM+vy/KK44pWeweCgZkYMM6CD/XewBw==} + /@nuxt/schema-edge/3.0.0-rc.3-27536597.3359b3b: + resolution: {integrity: sha512-+a/6Irx+EL2oxD+TX0Saervw3SXbr3SqFnAUE5QfTQppzG5Zf3Y9KBf+wzafB7qEDoQiCl0DWBuFVaPt+Ibt3g==} engines: {node: ^14.16.0 || ^16.11.0 || ^17.0.0 || ^18.0.0} dependencies: c12: 0.2.7 @@ -3984,7 +4024,7 @@ packages: scule: 0.2.1 std-env: 3.1.1 ufo: 0.8.4 - unimport: 0.1.8 + unimport: 0.2.1 transitivePeerDependencies: - esbuild - rollup @@ -4487,6 +4527,20 @@ packages: jest-matcher-utils: 27.5.1 dev: true + /@relmify/jest-fp-ts/2.0.2_fp-ts@2.12.1+io-ts@2.2.16: + resolution: {integrity: sha512-qhQOfn9q5YsylD9qNCiizrA8w/i3A015hDk3P46fYPe48yLSB6BJaBImSvECUY9nCjeiEp4YP2sPopNyqDYwjw==} + peerDependencies: + fp-ts: 2.x + io-ts: 2.x + dependencies: + '@jest/expect-utils': 28.1.0 + expect: 28.1.0 + fp-ts: 2.12.1 + io-ts: 2.2.16_fp-ts@2.12.1 + jest-get-type: 28.0.2 + jest-matcher-utils: 28.1.0 + dev: true + /@rollup/pluginutils/4.1.2: resolution: {integrity: sha512-ROn4qvkxP9SyPeHaf7uQC/GPFY6L/OWy9+bd9AwcjOAWQwxRscoEyAUD8qCY5o5iL4jqQwoLk2kaTKJPb/HwzQ==} engines: {node: '>= 8.0.0'} @@ -4674,6 +4728,10 @@ packages: - supports-color dev: false + /@sinclair/typebox/0.23.5: + resolution: {integrity: sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg==} + dev: true + /@sindresorhus/is/0.14.0: resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} engines: {node: '>=6'} @@ -4705,8 +4763,8 @@ packages: resolution: {integrity: sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==} dev: false - /@swc/core-android-arm-eabi/1.2.163: - resolution: {integrity: sha512-8tv6EMcL5zkgXikwkEMu4I2iy79gmCDe44a3PtQjAc7O/QxxyjJnEVxB6/1U1TnpyLBjcjVfLLzX572LnK9p4Q==} + /@swc/core-android-arm-eabi/1.2.181: + resolution: {integrity: sha512-H3HNf8j6M13uIbSruef8iMsCElJJDZOhp5qxwm/+P1jAG4eQ4vPfajIlTVdFJes8Adrbr4WQaVvl+m4BQw51JQ==} engines: {node: '>=10'} cpu: [arm] os: [android] @@ -4714,8 +4772,8 @@ packages: dev: true optional: true - /@swc/core-android-arm64/1.2.163: - resolution: {integrity: sha512-nDnogIlm1JMs+lSBNH5MIKf7h2h1JQ7kpZSIhnCrr8kajFP5YWQyz9pu2GMv2oc9iWp9TafRk2h2ov3rBJwzIg==} + /@swc/core-android-arm64/1.2.181: + resolution: {integrity: sha512-b1apYKeosBaXl28xE/By4QVHYrXaR2+nOdcP6rsDXg6nyLBArtoiS5YUFikFN/VQbSAQqNeJQ+rovT5zITrgSQ==} engines: {node: '>=10'} cpu: [arm64] os: [android] @@ -4723,8 +4781,8 @@ packages: dev: true optional: true - /@swc/core-darwin-arm64/1.2.163: - resolution: {integrity: sha512-5sxTJUfWK8hFZNdh3MNYJBB4X58WL8gRzr/U7rWMRpA9GKFcKNAsvPZICICXEVGVX7glC6LZX94ML2fGsW/NeA==} + /@swc/core-darwin-arm64/1.2.181: + resolution: {integrity: sha512-M3/PPeO6NTN7GYa1mOWPNMaAPxEQH8xd+X6FHMa7OBCi+Qxkarafu4DZRfzR88TcS3XikqFLgmmzSP7Z/tye2w==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] @@ -4732,8 +4790,8 @@ packages: dev: true optional: true - /@swc/core-darwin-x64/1.2.163: - resolution: {integrity: sha512-pVYtFi0Rfeh0JLZKD0HR1j4chcqEaxyk4lhx2DEMg2VvZYjZd1b6bRKB5gKJy1lLzpiUngqeSpIt14jnCIqWZg==} + /@swc/core-darwin-x64/1.2.181: + resolution: {integrity: sha512-8Uc6gx7YN5+eSnk3h7aHqp1f3RFoBJPDPeH9cURm4mfE4BTgkVgkctUm0IE5sS5AotazVbrOwhEFrl7TONSfPA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -4741,8 +4799,8 @@ packages: dev: true optional: true - /@swc/core-freebsd-x64/1.2.163: - resolution: {integrity: sha512-a8adSrD2d7WCdD+BqUt7hlmwujcYTYGpSq6i8OxfmxPSf50lCUoBaQ+B/tSyvLlvnusiURHfwhfzSld1vtg04Q==} + /@swc/core-freebsd-x64/1.2.181: + resolution: {integrity: sha512-SbnsbJHGFNY7VSTA5OhBh2PmLgQumIGerAxTCTYO1IgtbADCTL+gCjU0TK0viG/zpH4jnjaL965BI4JTo/bpRg==} engines: {node: '>=10'} cpu: [x64] os: [freebsd] @@ -4750,8 +4808,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm-gnueabihf/1.2.163: - resolution: {integrity: sha512-7pQZ7acveuX2p0Ysrg6AHVedTbWQBOjlBQ8AmJ3Wwri9h4o1GLzJkzJQ++Dyp+xGE+pI0ppK6dqaQZ1Zm8yPLQ==} + /@swc/core-linux-arm-gnueabihf/1.2.181: + resolution: {integrity: sha512-aWO6Lr9wea96Z7AEagzf4reKgDb3UWXZnClwJK7baScwF8KV+Mh99vVgkSe1nj2gKOZ31pBLp62RDJkc3gdlnA==} engines: {node: '>=10'} cpu: [arm] os: [linux] @@ -4759,8 +4817,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-gnu/1.2.163: - resolution: {integrity: sha512-yPG6iAN7OlhiNWPy8Ma9WrrQpQZIZE5huR1HVRzM5m8/svs65m3vCTAEHWa+45y1PX3/Bo+hd6tyjaHMAco/ew==} + /@swc/core-linux-arm64-gnu/1.2.181: + resolution: {integrity: sha512-+7fzDwsvcbhPafKdminMQrU3Ej1NHltXy7k+zgjj8BDPZbfi8hRzQcONeBV7sfl4xvw3d3roNHu2UMmKzLNs0w==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -4768,8 +4826,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-musl/1.2.163: - resolution: {integrity: sha512-sEBgJbaTixEEv8vvf7Udjc9d4HvAYnKtMBu354FszSwKBLmf75CJNUU4vJQRn+E/+WwMXxorSXdx1+wW4fpStg==} + /@swc/core-linux-arm64-musl/1.2.181: + resolution: {integrity: sha512-U9k8pv2oCxYYfu9DdOO1ZZgqbmF97NgJzSaIu3PsTedF4RcGIiPcuGOFqrUECsGUW2i6uKejE8onbXPj9UmaZQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -4777,8 +4835,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-gnu/1.2.163: - resolution: {integrity: sha512-69OjEhbxYaMVIhly2hrgDwiOG0MdPQJ3BU5lD/kStvNMnGS/bCNgVYnnsjJgJKfpvJBLtUd/t7QrdwZvrY+pSw==} + /@swc/core-linux-x64-gnu/1.2.181: + resolution: {integrity: sha512-9AQXrvZ9BFQJeqYMpKQZRf9h/DEwhfHIR39krehO+g594i+mkkp+UNTToez6Ifn+NBYl58xyEcQGwsYIqtdYVw==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -4786,8 +4844,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-musl/1.2.163: - resolution: {integrity: sha512-I2pvui8SaNE74vO1YHIJj7/R6IuR8qXfW5YNJ9NV2yR5G6Po2vPR/kMpJgd4rrt6gg16+FGw+XDpMue8+TGLcw==} + /@swc/core-linux-x64-musl/1.2.181: + resolution: {integrity: sha512-Pq/aBMj3F4CR4tXq85t7IW3piu76a677nIoo6QtBkAjrQ5QuJqpaez/5aewipG+kIXpiu/DNFIN+cISa1QeC8A==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -4795,8 +4853,8 @@ packages: dev: true optional: true - /@swc/core-win32-arm64-msvc/1.2.163: - resolution: {integrity: sha512-CaFr1xVfXrXywDDyNUfRSWh9vqhMmaOh9dpBGHDWPDT+EOwK0Rm78LZPh9G3iQakEq9/6ztqCmskPy7Go6gG6A==} + /@swc/core-win32-arm64-msvc/1.2.181: + resolution: {integrity: sha512-m24036tVFDE8ZJ3fBVBfsHw4tHd0BG6g3TvT2MLAiW2MezYeTdrGpmvPBz4Woz686I/06cWeSg7cZF1/ZcZMMA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] @@ -4804,8 +4862,8 @@ packages: dev: true optional: true - /@swc/core-win32-ia32-msvc/1.2.163: - resolution: {integrity: sha512-0HfuKu8eG1brQ13PVba5WJm187asQuHeVuuVD0WL1/d6TSp2dfsfXdOPt7gjPG0WVtVpYh8e4N0IEcE0gVpzxA==} + /@swc/core-win32-ia32-msvc/1.2.181: + resolution: {integrity: sha512-OPROzGapmr29qqwvB/aP9SA80r2eIukj+q7gghdQVptJrQU4GrTyzW1TpnGtpzj8rLZz4hEG6KtyPUh54bJZ/g==} engines: {node: '>=10'} cpu: [ia32] os: [win32] @@ -4813,8 +4871,8 @@ packages: dev: true optional: true - /@swc/core-win32-x64-msvc/1.2.163: - resolution: {integrity: sha512-BB1tioCAu2Wdt2efKOcbNR9vByzNUhiJqmBv1wWGR6wqV2k46jRMex2NzQESMBl8slIJG+ZPFLE8gUChl0crIg==} + /@swc/core-win32-x64-msvc/1.2.181: + resolution: {integrity: sha512-YrIaS63XsGiQ9AgxUVZ7Irt4pwQc3c2TPN7PyQP7ok9zBZxY5pBTwRTdLctlF4LNsSavlHE5+rvdPzcYAG0ekQ==} engines: {node: '>=10'} cpu: [x64] os: [win32] @@ -4822,24 +4880,24 @@ packages: dev: true optional: true - /@swc/core/1.2.163: - resolution: {integrity: sha512-8/ytlOhXSomPjfgOFgto6xnYR+UAj0eHJEIu2NbdHgpFjrDFti6C9XeW0zB6zheCq0fShP0RbE9qXlgYKoSZ8w==} + /@swc/core/1.2.181: + resolution: {integrity: sha512-evQX+Br/gC+FYLbUIF1dOQa7hUzBpowrcbgPkIRCEvi4HrCn7pGBZ2ZHBXmwEtBdLfOlyQvN/8USClApQKK4Rw==} engines: {node: '>=10'} hasBin: true optionalDependencies: - '@swc/core-android-arm-eabi': 1.2.163 - '@swc/core-android-arm64': 1.2.163 - '@swc/core-darwin-arm64': 1.2.163 - '@swc/core-darwin-x64': 1.2.163 - '@swc/core-freebsd-x64': 1.2.163 - '@swc/core-linux-arm-gnueabihf': 1.2.163 - '@swc/core-linux-arm64-gnu': 1.2.163 - '@swc/core-linux-arm64-musl': 1.2.163 - '@swc/core-linux-x64-gnu': 1.2.163 - '@swc/core-linux-x64-musl': 1.2.163 - '@swc/core-win32-arm64-msvc': 1.2.163 - '@swc/core-win32-ia32-msvc': 1.2.163 - '@swc/core-win32-x64-msvc': 1.2.163 + '@swc/core-android-arm-eabi': 1.2.181 + '@swc/core-android-arm64': 1.2.181 + '@swc/core-darwin-arm64': 1.2.181 + '@swc/core-darwin-x64': 1.2.181 + '@swc/core-freebsd-x64': 1.2.181 + '@swc/core-linux-arm-gnueabihf': 1.2.181 + '@swc/core-linux-arm64-gnu': 1.2.181 + '@swc/core-linux-arm64-musl': 1.2.181 + '@swc/core-linux-x64-gnu': 1.2.181 + '@swc/core-linux-x64-musl': 1.2.181 + '@swc/core-win32-arm64-msvc': 1.2.181 + '@swc/core-win32-ia32-msvc': 1.2.181 + '@swc/core-win32-x64-msvc': 1.2.181 dev: true /@szmarczak/http-timer/1.1.2: @@ -4955,14 +5013,14 @@ packages: /@types/babel__template/7.4.1: resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} dependencies: - '@babel/parser': 7.16.2 - '@babel/types': 7.16.0 + '@babel/parser': 7.17.9 + '@babel/types': 7.17.0 dev: true /@types/babel__traverse/7.14.2: resolution: {integrity: sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA==} dependencies: - '@babel/types': 7.16.0 + '@babel/types': 7.17.0 dev: true /@types/body-parser/1.19.1: @@ -5224,6 +5282,10 @@ packages: /@types/node/17.0.24: resolution: {integrity: sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==} + /@types/node/17.0.31: + resolution: {integrity: sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==} + dev: true + /@types/node/17.0.6: resolution: {integrity: sha512-+XBAjfZmmivILUzO0HwBJoYkAyyySSLg5KCGBDFLomJo0sV6szvVLAf4ANZZ0pfWzgEds5KmGLG9D5hfEqOhaA==} @@ -5456,6 +5518,12 @@ packages: '@types/yargs-parser': 21.0.0 dev: true + /@types/yargs/17.0.10: + resolution: {integrity: sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==} + dependencies: + '@types/yargs-parser': 21.0.0 + dev: true + /@typescript-eslint/eslint-plugin/5.14.0_c73qtwoqpvdfp6xojm7zfmehbu: resolution: {integrity: sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6604,7 +6672,7 @@ packages: babel-plugin-istanbul: 6.1.1 babel-preset-jest: 27.5.1_@babel+core@7.17.9 chalk: 4.1.2 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 slash: 3.0.0 transitivePeerDependencies: - supports-color @@ -7085,17 +7153,6 @@ packages: picocolors: 1.0.0 dev: true - /browserslist/4.19.1: - resolution: {integrity: sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001311 - electron-to-chromium: 1.4.68 - escalade: 3.1.1 - node-releases: 2.0.2 - picocolors: 1.0.0 - /browserslist/4.20.0: resolution: {integrity: sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -7398,9 +7455,6 @@ packages: /caniuse-lite/1.0.30001274: resolution: {integrity: sha512-+Nkvv0fHyhISkiMIjnyjmf5YJcQ1IQHZN6U9TLUMroWR38FNwpsC51Gb68yueafX1V6ifOisInSgP9WJFS13ew==} - /caniuse-lite/1.0.30001311: - resolution: {integrity: sha512-mleTFtFKfykEeW34EyfhGIFjGCqzhh38Y0LhdQ9aWF+HorZTtdgKV/1hEE0NlFkG2ubvisPV6l400tlbPys98A==} - /caniuse-lite/1.0.30001315: resolution: {integrity: sha512-5v7LFQU4Sb/qvkz7JcZkvtSH1Ko+1x2kgo3ocdBeMGZSOFpuE1kkm0kpTwLtWeFrw5qw08ulLxJjVIXIS8MkiQ==} @@ -8004,7 +8058,7 @@ packages: /core-js-compat/3.20.3: resolution: {integrity: sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw==} dependencies: - browserslist: 4.19.1 + browserslist: 4.20.2 semver: 7.0.0 /core-js/2.6.12: @@ -8030,7 +8084,7 @@ packages: dependencies: '@iarna/toml': 2.2.5 - /cosmiconfig-typescript-loader/1.0.9_c6ucwwwirjo6ockovr3lj3o2cm: + /cosmiconfig-typescript-loader/1.0.9_kpiehm6dkipypriomyghl2wcpq: resolution: {integrity: sha512-tRuMRhxN4m1Y8hP9SNYfz7jRwt8lZdWxdjg/ohg5esKmsndJIn4yT96oJVcf5x0eA11taXl+sIp+ielu529k6g==} engines: {node: '>=12', npm: '>=6'} peerDependencies: @@ -8039,8 +8093,8 @@ packages: dependencies: '@types/node': 17.0.24 cosmiconfig: 7.0.1 - ts-node: 10.7.0_c6ucwwwirjo6ockovr3lj3o2cm - typescript: 4.6.3 + ts-node: 10.7.0_kpiehm6dkipypriomyghl2wcpq + typescript: 4.6.4 transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -8744,6 +8798,11 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dev: true + /diff-sequences/28.0.2: + resolution: {integrity: sha512-YtEoNynLDFCRznv/XDalsKGSZDoj0U5kLnXvY0JSq3nBboRrZXjD81+eSiwi+nzcZDwedMmcowcxNwwgFW23mQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dev: true + /diff/4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -8962,9 +9021,6 @@ packages: /electron-to-chromium/1.4.111: resolution: {integrity: sha512-/s3+fwhKf1YK4k7btOImOzCQLpUjS6MaPf0ODTNuT4eTM1Bg4itBpLkydhOzJmpmH6Z9eXFyuuK5czsmzRzwtw==} - /electron-to-chromium/1.4.68: - resolution: {integrity: sha512-cId+QwWrV8R1UawO6b9BR1hnkJ4EJPCPAr4h315vliHUtVUJDk39Sg1PMNnaWKfj5x+93ssjeJ9LKL6r8LaMiA==} - /electron-to-chromium/1.4.82: resolution: {integrity: sha512-Ks+ANzLoIrFDUOJdjxYMH6CMKB8UQo5modAwvSZTxgF+vEs/U7G5IbWFUp6dS4klPkTDVdxbORuk8xAXXhMsWw==} @@ -10063,6 +10119,17 @@ packages: jest-message-util: 27.5.1 dev: true + /expect/28.1.0: + resolution: {integrity: sha512-qFXKl8Pmxk8TBGfaFKRtcQjfXEnKAs+dmlxdwvukJZorwrAabT7M3h8oLOG01I2utEhkmUTi17CHaPBovZsKdw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/expect-utils': 28.1.0 + jest-get-type: 28.0.2 + jest-matcher-utils: 28.1.0 + jest-message-util: 28.1.0 + jest-util: 28.1.0 + dev: true + /extend-shallow/2.0.1: resolution: {integrity: sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=} engines: {node: '>=0.10.0'} @@ -10500,8 +10567,8 @@ packages: resolution: {integrity: sha512-wtUo3eA0/+GZnT+dCjkSt5CuGCH5ZXjjrcZvYm/3BC5KGavuwgvME+eRRHYtCGYWD6I+fJ2uZ9en/JVqDEPrJw==} dev: false - /fp-ts/2.11.9: - resolution: {integrity: sha512-GhYlNKkCOfdjp71ocdtyaQGoqCswEoWDJLRr+2jClnBBq2dnSOtd6QxmJdALq8UhfqCyZZ0f0lxadU4OhwY9nw==} + /fp-ts/2.12.1: + resolution: {integrity: sha512-oxvgqUYR6O9VkKXrxkJ0NOyU0FrE705MeqgBUMEPWyTu6Pwn768cJbHChw2XOBlgFLKfIHxjr2OOBFpv2mUGZw==} dev: true /fragment-cache/0.2.1: @@ -11744,6 +11811,14 @@ packages: dependencies: fp-ts: 2.11.10 + /io-ts/2.2.16_fp-ts@2.12.1: + resolution: {integrity: sha512-y5TTSa6VP6le0hhmIyN0dqEXkrZeJLeC5KApJq6VLci3UEKF80lZ+KuoUs02RhBxNWlrqSNxzfI7otLX1Euv8Q==} + peerDependencies: + fp-ts: ^2.5.0 + dependencies: + fp-ts: 2.12.1 + dev: true + /ip/1.1.5: resolution: {integrity: sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=} dev: false @@ -12285,7 +12360,7 @@ packages: '@jest/types': 27.5.1 chalk: 4.1.2 exit: 0.1.2 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 import-local: 3.1.0 jest-config: 27.5.1 jest-util: 27.5.1 @@ -12317,7 +12392,7 @@ packages: ci-info: 3.3.0 deepmerge: 4.2.2 glob: 7.2.0 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jest-circus: 27.5.1 jest-environment-jsdom: 27.5.1 jest-environment-node: 27.5.1 @@ -12328,7 +12403,7 @@ packages: jest-runner: 27.5.1 jest-util: 27.5.1 jest-validate: 27.5.1 - micromatch: 4.0.4 + micromatch: 4.0.5 parse-json: 5.2.0 pretty-format: 27.5.1 slash: 3.0.0 @@ -12350,6 +12425,16 @@ packages: pretty-format: 27.5.1 dev: true + /jest-diff/28.1.0: + resolution: {integrity: sha512-8eFd3U3OkIKRtlasXfiAQfbovgFgRDb0Ngcs2E+FMeBZ4rUezqIaGjuyggJBp+llosQXNEWofk/Sz4Hr5gMUhA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 28.0.2 + jest-get-type: 28.0.2 + pretty-format: 28.1.0 + dev: true + /jest-docblock/27.5.1: resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -12403,6 +12488,11 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dev: true + /jest-get-type/28.0.2: + resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dev: true + /jest-haste-map/27.5.1: resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -12412,12 +12502,12 @@ packages: '@types/node': 17.0.24 anymatch: 3.1.2 fb-watchman: 2.0.1 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jest-regex-util: 27.5.1 jest-serializer: 27.5.1 jest-util: 27.5.1 jest-worker: 27.5.1 - micromatch: 4.0.4 + micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: fsevents: 2.3.2 @@ -12466,6 +12556,16 @@ packages: pretty-format: 27.5.1 dev: true + /jest-matcher-utils/28.1.0: + resolution: {integrity: sha512-onnax0n2uTLRQFKAjC7TuaxibrPSvZgKTcSCnNUz/tOjJ9UhxNm7ZmPpoQavmTDUjXvUQ8KesWk2/VdrxIFzTQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 28.1.0 + jest-get-type: 28.0.2 + pretty-format: 28.1.0 + dev: true + /jest-message-util/27.5.1: resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -12481,6 +12581,21 @@ packages: stack-utils: 2.0.5 dev: true + /jest-message-util/28.1.0: + resolution: {integrity: sha512-RpA8mpaJ/B2HphDMiDlrAZdDytkmwFqgjDZovM21F35lHGeUeCvYmm6W+sbQ0ydaLpg5bFAUuWG1cjqOl8vqrw==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@babel/code-frame': 7.16.7 + '@jest/types': 28.1.0 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.10 + micromatch: 4.0.5 + pretty-format: 28.1.0 + slash: 3.0.0 + stack-utils: 2.0.5 + dev: true + /jest-mock/27.5.1: resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -12523,7 +12638,7 @@ packages: dependencies: '@jest/types': 27.5.1 chalk: 4.1.2 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jest-haste-map: 27.5.1 jest-pnp-resolver: 1.2.2_jest-resolve@27.5.1 jest-util: 27.5.1 @@ -12545,7 +12660,7 @@ packages: '@types/node': 17.0.24 chalk: 4.1.2 emittery: 0.8.1 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jest-docblock: 27.5.1 jest-environment-jsdom: 27.5.1 jest-environment-node: 27.5.1 @@ -12581,7 +12696,7 @@ packages: collect-v8-coverage: 1.0.1 execa: 5.1.1 glob: 7.2.0 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jest-haste-map: 27.5.1 jest-message-util: 27.5.1 jest-mock: 27.5.1 @@ -12625,7 +12740,7 @@ packages: babel-preset-current-node-syntax: 1.0.1_@babel+core@7.17.9 chalk: 4.1.2 expect: 27.5.1 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 jest-diff: 27.5.1 jest-get-type: 27.5.1 jest-haste-map: 27.5.1 @@ -12647,7 +12762,19 @@ packages: '@types/node': 17.0.24 chalk: 4.1.2 ci-info: 3.3.0 - graceful-fs: 4.2.9 + graceful-fs: 4.2.10 + picomatch: 2.3.1 + dev: true + + /jest-util/28.1.0: + resolution: {integrity: sha512-qYdCKD77k4Hwkose2YBEqQk7PzUf/NSE+rutzceduFveQREeH6b+89Dc9+wjX9dAwHcgdx4yedGA3FQlU/qCTA==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/types': 28.1.0 + '@types/node': 17.0.31 + chalk: 4.1.2 + ci-info: 3.3.0 + graceful-fs: 4.2.10 picomatch: 2.3.1 dev: true @@ -14325,7 +14452,7 @@ packages: /nuxt-windicss/2.2.11: resolution: {integrity: sha512-xobq725D6vqpIgYOrLJ6CVlR4xLlFGwuq//gZikXKOdoVRpoK8C+NpHazPd4+f17urGQ4H0LqGBCIujTvV1V0g==} dependencies: - '@nuxt/kit': /@nuxt/kit-edge/3.0.0-rc.2-27530889.9e5a3cd + '@nuxt/kit': /@nuxt/kit-edge/3.0.0-rc.3-27536597.3359b3b '@windicss/plugin-utils': 1.8.4 consola: 2.15.3 defu: 6.0.0 @@ -15795,6 +15922,16 @@ packages: react-is: 17.0.2 dev: true + /pretty-format/28.1.0: + resolution: {integrity: sha512-79Z4wWOYCdvQkEoEuSlBhHJqWeZ8D8YRPiPctJFCtvuaClGpiwiQYSCUOE6IEKUbbFukKOTFIUAXE8N4EQTo1Q==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/schemas': 28.0.2 + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 18.1.0 + dev: true + /pretty-time/1.1.0: resolution: {integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==} engines: {node: '>=4'} @@ -16072,6 +16209,10 @@ packages: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true + /react-is/18.1.0: + resolution: {integrity: sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==} + dev: true + /read-cache/1.0.0: resolution: {integrity: sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=} dependencies: @@ -18229,11 +18370,45 @@ packages: json5: 2.2.1 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.3.5 + semver: 7.3.6 typescript: 4.6.3 yargs-parser: 20.2.9 dev: true + /ts-jest/27.1.4_53ggqi2i4rbcfjtktmjua6zili: + resolution: {integrity: sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@types/jest': ^27.0.0 + babel-jest: '>=27.0.0 <28' + esbuild: '*' + jest: ^27.0.0 + typescript: '>=3.8 <5.0' + peerDependenciesMeta: + '@babel/core': + optional: true + '@types/jest': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@types/jest': 27.4.1 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 27.5.1 + jest-util: 27.5.1 + json5: 2.2.1 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.6 + typescript: 4.6.4 + yargs-parser: 20.2.9 + dev: true + /ts-jest/27.1.4_tgc6da2oqazvrn56dzwolsqo5i: resolution: {integrity: sha512-qjkZlVPWVctAezwsOD1OPzbZ+k7zA5z3oxII4dGdZo5ggX/PL7kvwTM0pXTr10fAtbiVpJaL3bWd502zAhpgSQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -18263,7 +18438,7 @@ packages: json5: 2.2.1 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.3.5 + semver: 7.3.6 typescript: 4.6.3 yargs-parser: 20.2.9 dev: true @@ -18287,7 +18462,7 @@ packages: resolution: {integrity: sha512-DEQrfv6l7IvN2jlzc/VTdZJYsWUnQNCsueYjMkC/iXoEoi5fNan6MjeDqkvhfzbmHgdz9UxDUluX3V5HdjTydQ==} dev: true - /ts-node/10.7.0_c6ucwwwirjo6ockovr3lj3o2cm: + /ts-node/10.7.0_kpiehm6dkipypriomyghl2wcpq: resolution: {integrity: sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==} hasBin: true peerDependencies: @@ -18313,7 +18488,7 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.6.3 + typescript: 4.6.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -18369,36 +18544,6 @@ packages: /tslib/2.3.1: resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} - /tsup/5.12.4_typescript@4.6.3: - resolution: {integrity: sha512-uUraITfIj2h6rXAdeaVUYrZ2Is9joLFyEGZN5mGAke874JojCizb2MCUcE0wGdcERtyob5mbbFUKkMgal8SlFw==} - hasBin: true - peerDependencies: - typescript: ^4.1.0 - peerDependenciesMeta: - typescript: - optional: true - dependencies: - bundle-require: 3.0.4_esbuild@0.14.34 - cac: 6.7.12 - chokidar: 3.5.3 - debug: 4.3.4 - esbuild: 0.14.34 - execa: 5.1.1 - globby: 11.1.0 - joycon: 3.1.1 - postcss-load-config: 3.1.4 - resolve-from: 5.0.0 - rollup: 2.70.1 - source-map: 0.7.3 - sucrase: 3.21.0 - tree-kill: 1.2.2 - typescript: 4.6.3 - transitivePeerDependencies: - - postcss - - supports-color - - ts-node - dev: true - /tsup/5.12.5_typescript@4.6.3: resolution: {integrity: sha512-lKwzJsB49sDto51QjqOB4SdiBLKRvgTymEBuBCovcksdDwFEz3esrkbf3m497PXntUKVTzcgOfPdTgknMtvufw==} hasBin: true @@ -18428,6 +18573,36 @@ packages: - supports-color - ts-node + /tsup/5.12.7_typescript@4.6.4: + resolution: {integrity: sha512-+OxYroGLByY0Fm8DLZaB4nVMlD59VsQoNXdhnO9wOG+cOsKXUwN3ER9gaKOjZJG26eKUXebmDme0Cy3emfRvOQ==} + hasBin: true + peerDependencies: + typescript: ^4.1.0 + peerDependenciesMeta: + typescript: + optional: true + dependencies: + bundle-require: 3.0.4_esbuild@0.14.34 + cac: 6.7.12 + chokidar: 3.5.3 + debug: 4.3.4 + esbuild: 0.14.34 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 3.1.4 + resolve-from: 5.0.0 + rollup: 2.70.1 + source-map: 0.7.3 + sucrase: 3.21.0 + tree-kill: 1.2.2 + typescript: 4.6.4 + transitivePeerDependencies: + - postcss + - supports-color + - ts-node + dev: true + /tsutils/3.21.0_typescript@4.6.3: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -18506,6 +18681,12 @@ packages: hasBin: true dev: true + /typescript/4.6.4: + resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + /ua-parser-js/0.7.31: resolution: {integrity: sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==} @@ -18598,8 +18779,8 @@ packages: engines: {node: '>= 0.4.12'} dev: true - /unimport/0.1.8: - resolution: {integrity: sha512-Eynu+yyVVZ76te5DbE3744z1YjqoES+muNlNP03kVURHC/S2IR65mjzdmT6wpWvU78q488dK0fQ+tUK9UEndZA==} + /unimport/0.2.1: + resolution: {integrity: sha512-6u1uY6GFIi9WslI7fPun0I0ZrIGJ0yAn92n/9rQv4MAEEnxg8zMO+Tj7q/6Z/7XxTn8qFCJIqEM/3Io81Zwmmw==} dependencies: '@rollup/pluginutils': 4.2.1 escape-string-regexp: 5.0.0 @@ -18607,9 +18788,9 @@ packages: local-pkg: 0.4.1 magic-string: 0.26.1 mlly: 0.5.2 - pathe: 0.2.0 + pathe: 0.3.0 scule: 0.2.1 - unplugin: 0.6.2 + unplugin: 0.6.3 transitivePeerDependencies: - esbuild - rollup @@ -18742,6 +18923,28 @@ packages: webpack-virtual-modules: 0.4.3 dev: true + /unplugin/0.6.3: + resolution: {integrity: sha512-CoW88FQfCW/yabVc4bLrjikN9HC8dEvMU4O7B6K2jsYMPK0l6iAnd9dpJwqGcmXJKRCU9vwSsy653qg+RK0G6A==} + peerDependencies: + esbuild: '>=0.13' + rollup: ^2.50.0 + vite: ^2.3.0 + webpack: 4 || 5 + peerDependenciesMeta: + esbuild: + optional: true + rollup: + optional: true + vite: + optional: true + webpack: + optional: true + dependencies: + chokidar: 3.5.3 + webpack-sources: 3.2.3 + webpack-virtual-modules: 0.4.3 + dev: true + /unquote/1.1.1: resolution: {integrity: sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=} @@ -19731,11 +19934,11 @@ packages: /yargs-parser/20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} engines: {node: '>=10'} + dev: true /yargs-parser/20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} - dev: true /yargs-parser/21.0.1: resolution: {integrity: sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==} @@ -19778,7 +19981,7 @@ packages: require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 - yargs-parser: 20.2.4 + yargs-parser: 20.2.9 /yargs/17.3.1: resolution: {integrity: sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==}