From 9b5fec7b323857f30861e4feafbf7e59c71df7a1 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Tue, 5 Apr 2022 03:27:14 -0700 Subject: [PATCH] feat: add toInclude() function (#2234) Co-authored-by: Andrew Bastin --- .../helpers/terndoc/pw-test.json | 3 +- packages/hoppscotch-cli/package.json | 2 +- packages/hoppscotch-js-sandbox/package.json | 2 +- .../testing/expect/toInclude.spec.ts | 183 ++++++++++++++++++ .../hoppscotch-js-sandbox/src/test-runner.ts | 57 ++++++ 5 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toInclude.spec.ts diff --git a/packages/hoppscotch-app/helpers/terndoc/pw-test.json b/packages/hoppscotch-app/helpers/terndoc/pw-test.json index 7c8616cdc..21137a65a 100644 --- a/packages/hoppscotch-app/helpers/terndoc/pw-test.json +++ b/packages/hoppscotch-app/helpers/terndoc/pw-test.json @@ -9,7 +9,8 @@ "toBeLevel4xx": "fn()", "toBeLevel5xx": "fn()", "toBeType": "fn(type: string)", - "toHaveLength": "fn(length: number)" + "toHaveLength": "fn(length: number)", + "toInclude": "fn(value: ?)" } }, "pw": { diff --git a/packages/hoppscotch-cli/package.json b/packages/hoppscotch-cli/package.json index da4f130a1..75764476d 100644 --- a/packages/hoppscotch-cli/package.json +++ b/packages/hoppscotch-cli/package.json @@ -1,6 +1,6 @@ { "name": "@hoppscotch/cli", - "version": "0.1.12", + "version": "0.1.13", "description": "A CLI to run Hoppscotch test scripts in CI environments.", "homepage": "https://hoppscotch.io", "main": "dist/index.js", diff --git a/packages/hoppscotch-js-sandbox/package.json b/packages/hoppscotch-js-sandbox/package.json index 9a1ef12c6..d5d29f3b0 100644 --- a/packages/hoppscotch-js-sandbox/package.json +++ b/packages/hoppscotch-js-sandbox/package.json @@ -1,6 +1,6 @@ { "name": "@hoppscotch/js-sandbox", - "version": "2.0.0", + "version": "2.1.0", "description": "JavaScript sandboxes for running external scripts used by Hoppscotch clients", "main": "./lib/index.js", "module": "./lib/index.mjs", diff --git a/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toInclude.spec.ts b/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toInclude.spec.ts new file mode 100644 index 000000000..431173969 --- /dev/null +++ b/packages/hoppscotch-js-sandbox/src/__tests__/testing/expect/toInclude.spec.ts @@ -0,0 +1,183 @@ +import * as TE from "fp-ts/TaskEither" +import { pipe } from "fp-ts/function" +import { execTestScript, TestResponse } from "../../../test-runner" + +const fakeResponse: TestResponse = { + status: 200, + body: "hoi", + headers: [], +} + +const func = (script: string, res: TestResponse) => + pipe( + execTestScript(script, { global: [], selected: [] }, res), + TE.map((x) => x.tests) + ) + +describe("toInclude", () => { + test("asserts true for collections with matching values", () => { + return expect( + func( + ` + pw.expect([1, 2, 3]).toInclude(1) + pw.expect("123").toInclude(1) + `, + fakeResponse + )() + ).resolves.toEqualRight([ + expect.objectContaining({ + expectResults: [ + { status: "pass", message: "Expected [1,2,3] to include 1" }, + { status: "pass", message: 'Expected "123" to include 1' }, + ], + }), + ]) + }) + + test("asserts false for collections without matching values", () => { + return expect( + func( + ` + pw.expect([1, 2, 3]).toInclude(4) + pw.expect("123").toInclude(4) + `, + fakeResponse + )() + ).resolves.toEqualRight([ + expect.objectContaining({ + expectResults: [ + { status: "fail", message: "Expected [1,2,3] to include 4" }, + { status: "fail", message: 'Expected "123" to include 4' }, + ], + }), + ]) + }) + + test("asserts false for empty collections", () => { + return expect( + func( + ` + pw.expect([]).not.toInclude(0) + pw.expect("").not.toInclude(0) + `, + fakeResponse + )() + ).resolves.toEqualRight([ + expect.objectContaining({ + expectResults: [ + { + status: "pass", + message: "Expected [] to not include 0", + }, + { + status: "pass", + message: 'Expected "" to not include 0', + }, + ], + }), + ]) + }) + + test("asserts false for [number array].includes(string)", () => { + return expect( + func( + ` + pw.expect([1]).not.toInclude("1") + `, + fakeResponse + )() + ).resolves.toEqualRight([ + expect.objectContaining({ + expectResults: [ + { + status: "pass", + message: 'Expected [1] to not include "1"', + }, + ], + }), + ]) + }) + + test("asserts true for [string].includes(number)", () => { + // This is a Node.js quirk. + // (`"123".includes(123)` returns `True` in Node.js v14.19.1) + // See https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.includes + return expect( + func(`pw.expect("123").toInclude(123)`, fakeResponse)() + ).resolves.toEqualRight([ + expect.objectContaining({ + expectResults: [ + { + status: "pass", + message: 'Expected "123" to include 123', + }, + ], + }), + ]) + }) + + test("gives error if not called on an array or string", () => { + return expect( + func( + ` + pw.expect(5).not.toInclude(0) + pw.expect(true).not.toInclude(0) + `, + fakeResponse + )() + ).resolves.toEqualRight([ + expect.objectContaining({ + expectResults: [ + { + status: "error", + message: "Expected toInclude to be called for an array or string", + }, + { + status: "error", + message: "Expected toInclude to be called for an array or string", + }, + ], + }), + ]) + }) + + test("gives an error if toInclude parameter is null", () => { + return expect( + func( + ` + pw.expect([1, 2, 3, 4]).not.toInclude(null) + `, + fakeResponse + )() + ).resolves.toEqualRight([ + expect.objectContaining({ + expectResults: [ + { + status: "error", + message: "Argument for toInclude should not be null", + }, + ], + }), + ]) + }) + + test("gives an error if toInclude parameter is undefined", () => { + return expect( + func( + ` + pw.expect([1, 2, 3, 4]).not.toInclude(undefined) + `, + fakeResponse + )() + ).resolves.toEqualRight([ + expect.objectContaining({ + expectResults: [ + { + status: "error", + message: "Argument for toInclude should not be undefined", + }, + ], + }), + ]) + }) +}) diff --git a/packages/hoppscotch-js-sandbox/src/test-runner.ts b/packages/hoppscotch-js-sandbox/src/test-runner.ts index 9967266f8..720e1fa9d 100644 --- a/packages/hoppscotch-js-sandbox/src/test-runner.ts +++ b/packages/hoppscotch-js-sandbox/src/test-runner.ts @@ -312,6 +312,61 @@ function createExpectation( } ) + const toIncludeHandle = vm.newFunction("toInclude", (needleHandle) => { + const expectedVal = vm.dump(needleHandle) + + if (!(Array.isArray(expectVal) || typeof expectVal === "string")) { + currTestStack[currTestStack.length - 1].expectResults.push({ + status: "error", + message: `Expected toInclude to be called for an array or string`, + }) + + return { value: vm.undefined } + } + + if (expectedVal === null) { + currTestStack[currTestStack.length - 1].expectResults.push({ + status: "error", + message: `Argument for toInclude should not be null`, + }) + + return { value: vm.undefined } + } + + if (expectedVal === undefined) { + currTestStack[currTestStack.length - 1].expectResults.push({ + status: "error", + message: `Argument for toInclude should not be undefined`, + }) + + return { value: vm.undefined } + } + + let assertion = expectVal.includes(expectedVal) + if (negated) assertion = !assertion + + const expectValPretty = JSON.stringify(expectVal) + const expectedValPretty = JSON.stringify(expectedVal) + + if (assertion) { + currTestStack[currTestStack.length - 1].expectResults.push({ + status: "pass", + message: `Expected ${expectValPretty} to${ + negated ? " not" : "" + } include ${expectedValPretty}`, + }) + } else { + currTestStack[currTestStack.length - 1].expectResults.push({ + status: "fail", + message: `Expected ${expectValPretty} to${ + negated ? " not" : "" + } include ${expectedValPretty}`, + }) + } + + return { value: vm.undefined } + }) + vm.setProp(resultHandle, "toBe", toBeFnHandle) vm.setProp(resultHandle, "toBeLevel2xx", toBeLevel2xxHandle) vm.setProp(resultHandle, "toBeLevel3xx", toBeLevel3xxHandle) @@ -319,6 +374,7 @@ function createExpectation( vm.setProp(resultHandle, "toBeLevel5xx", toBeLevel5xxHandle) vm.setProp(resultHandle, "toBeType", toBeTypeHandle) vm.setProp(resultHandle, "toHaveLength", toHaveLengthHandle) + vm.setProp(resultHandle, "toInclude", toIncludeHandle) vm.defineProp(resultHandle, "not", { get: () => { @@ -333,6 +389,7 @@ function createExpectation( toBeLevel5xxHandle.dispose() toBeTypeHandle.dispose() toHaveLengthHandle.dispose() + toIncludeHandle.dispose() return resultHandle }