"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "packages/hoppscotch-js-sandbox/src/test-runner.ts" between
hoppscotch-2.2.1.tar.gz and hoppscotch-3.0.0.tar.gz

About: Hoppscotch is a light-weight, web based API development suite.

test-runner.ts  (hoppscotch-2.2.1):test-runner.ts  (hoppscotch-3.0.0)
import { isLeft } from "fp-ts/lib/Either" import * as O from "fp-ts/Option"
import { pipe } from "fp-ts/lib/function" import * as E from "fp-ts/Either"
import { TaskEither, tryCatch, chain, right, left } from "fp-ts/lib/TaskEither" import * as TE from "fp-ts/TaskEither"
import { pipe } from "fp-ts/function"
import * as qjs from "quickjs-emscripten" import * as qjs from "quickjs-emscripten"
import { marshalObjectToVM } from "./utils" import { Environment, parseTemplateStringE } from "@hoppscotch/data"
import cloneDeep from "lodash/cloneDeep"
import { getEnv, marshalObjectToVM, setEnv } from "./utils"
/** /**
* The response object structure exposed to the test script * The response object structure exposed to the test script
*/ */
export type TestResponse = { export type TestResponse = {
/** Status Code of the response */ /** Status Code of the response */
status: number status: number
/** List of headers returned */ /** List of headers returned */
headers: { key: string; value: string }[] headers: { key: string; value: string }[]
/** /**
skipping to change at line 48 skipping to change at line 51
*/ */
expectResults: ExpectResult[] expectResults: ExpectResult[]
/** /**
* Children test blocks (test blocks inside the test block) * Children test blocks (test blocks inside the test block)
*/ */
children: TestDescriptor[] children: TestDescriptor[]
} }
/** /**
* Defines the result of a test script execution
*/
export type TestResult = {
tests: TestDescriptor[]
envs: {
global: Environment["variables"]
selected: Environment["variables"]
}
}
/**
* Creates an Expectation object for use inside the sandbox * Creates an Expectation object for use inside the sandbox
* @param vm The QuickJS sandbox VM instance * @param vm The QuickJS sandbox VM instance
* @param expectVal The expecting value of the expectation * @param expectVal The expecting value of the expectation
* @param negated Whether the expectation is negated (negative) * @param negated Whether the expectation is negated (negative)
* @param currTestStack The current state of the test execution stack * @param currTestStack The current state of the test execution stack
* @returns Handle to the expectation object in VM * @returns Handle to the expectation object in VM
*/ */
function createExpectation( function createExpectation(
vm: qjs.QuickJSVm, vm: qjs.QuickJSVm,
expectVal: any, expectVal: any,
skipping to change at line 301 skipping to change at line 315
currTestStack[currTestStack.length - 1].expectResults.push({ currTestStack[currTestStack.length - 1].expectResults.push({
status: "error", status: "error",
message: `Argument for toHaveLength should be a number`, message: `Argument for toHaveLength should be a number`,
}) })
} }
return { value: vm.undefined } return { value: vm.undefined }
} }
) )
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, "toBe", toBeFnHandle)
vm.setProp(resultHandle, "toBeLevel2xx", toBeLevel2xxHandle) vm.setProp(resultHandle, "toBeLevel2xx", toBeLevel2xxHandle)
vm.setProp(resultHandle, "toBeLevel3xx", toBeLevel3xxHandle) vm.setProp(resultHandle, "toBeLevel3xx", toBeLevel3xxHandle)
vm.setProp(resultHandle, "toBeLevel4xx", toBeLevel4xxHandle) vm.setProp(resultHandle, "toBeLevel4xx", toBeLevel4xxHandle)
vm.setProp(resultHandle, "toBeLevel5xx", toBeLevel5xxHandle) vm.setProp(resultHandle, "toBeLevel5xx", toBeLevel5xxHandle)
vm.setProp(resultHandle, "toBeType", toBeTypeHandle) vm.setProp(resultHandle, "toBeType", toBeTypeHandle)
vm.setProp(resultHandle, "toHaveLength", toHaveLengthHandle) vm.setProp(resultHandle, "toHaveLength", toHaveLengthHandle)
vm.setProp(resultHandle, "toInclude", toIncludeHandle)
vm.defineProp(resultHandle, "not", { vm.defineProp(resultHandle, "not", {
get: () => { get: () => {
return createExpectation(vm, expectVal, !negated, currTestStack) return createExpectation(vm, expectVal, !negated, currTestStack)
}, },
}) })
toBeFnHandle.dispose() toBeFnHandle.dispose()
toBeLevel2xxHandle.dispose() toBeLevel2xxHandle.dispose()
toBeLevel3xxHandle.dispose() toBeLevel3xxHandle.dispose()
toBeLevel4xxHandle.dispose() toBeLevel4xxHandle.dispose()
toBeLevel5xxHandle.dispose() toBeLevel5xxHandle.dispose()
toBeTypeHandle.dispose() toBeTypeHandle.dispose()
toHaveLengthHandle.dispose() toHaveLengthHandle.dispose()
toIncludeHandle.dispose()
return resultHandle return resultHandle
} }
export const execTestScript = ( export const execTestScript = (
testScript: string, testScript: string,
envs: TestResult["envs"],
response: TestResponse response: TestResponse
): TaskEither<string, TestDescriptor[]> => ): TE.TaskEither<string, TestResult> =>
pipe( pipe(
tryCatch( TE.tryCatch(
async () => await qjs.getQuickJS(), async () => await qjs.getQuickJS(),
(reason) => `QuickJS initialization failed: ${reason}` (reason) => `QuickJS initialization failed: ${reason}`
), ),
chain( TE.chain(
// TODO: Make this more functional ? // TODO: Make this more functional ?
(QuickJS) => { (QuickJS) => {
let currentEnvs = cloneDeep(envs)
const vm = QuickJS.createVm() const vm = QuickJS.createVm()
const pwHandle = vm.newObject() const pwHandle = vm.newObject()
const testRunStack: TestDescriptor[] = [ const testRunStack: TestDescriptor[] = [
{ descriptor: "root", expectResults: [], children: [] }, { descriptor: "root", expectResults: [], children: [] },
] ]
const testFuncHandle = vm.newFunction( const testFuncHandle = vm.newFunction(
"test", "test",
skipping to change at line 377 skipping to change at line 451
const expectFnHandle = vm.newFunction("expect", (expectValueHandle) => { const expectFnHandle = vm.newFunction("expect", (expectValueHandle) => {
const expectVal = vm.dump(expectValueHandle) const expectVal = vm.dump(expectValueHandle)
return { return {
value: createExpectation(vm, expectVal, false, testRunStack), value: createExpectation(vm, expectVal, false, testRunStack),
} }
}) })
// Marshal response object // Marshal response object
const responseObjHandle = marshalObjectToVM(vm, response) const responseObjHandle = marshalObjectToVM(vm, response)
if (isLeft(responseObjHandle)) if (E.isLeft(responseObjHandle))
return left(`Response marshalling failed: ${responseObjHandle.left}`) return TE.left(
`Response marshalling failed: ${responseObjHandle.left}`
)
vm.setProp(pwHandle, "response", responseObjHandle.right) vm.setProp(pwHandle, "response", responseObjHandle.right)
responseObjHandle.right.dispose() responseObjHandle.right.dispose()
vm.setProp(pwHandle, "expect", expectFnHandle) vm.setProp(pwHandle, "expect", expectFnHandle)
expectFnHandle.dispose() expectFnHandle.dispose()
vm.setProp(pwHandle, "test", testFuncHandle) vm.setProp(pwHandle, "test", testFuncHandle)
testFuncHandle.dispose() testFuncHandle.dispose()
// Environment management APIs
// TODO: Unified Implementation
const envHandle = vm.newObject()
const envGetHandle = vm.newFunction("get", (keyHandle) => {
const key: unknown = vm.dump(keyHandle)
if (typeof key !== "string") {
return {
error: vm.newString("Expected key to be a string"),
}
}
const result = pipe(
getEnv(key, currentEnvs),
O.match(
() => vm.undefined,
({ value }) => vm.newString(value)
)
)
return {
value: result,
}
})
const envGetResolveHandle = vm.newFunction(
"getResolve",
(keyHandle) => {
const key: unknown = vm.dump(keyHandle)
if (typeof key !== "string") {
return {
error: vm.newString("Expected key to be a string"),
}
}
const result = pipe(
getEnv(key, currentEnvs),
E.fromOption(() => "INVALID_KEY" as const),
E.map(({ value }) =>
pipe(
parseTemplateStringE(value, [
...envs.selected,
...envs.global,
]),
// If the recursive resolution failed, return the unresolved v
alue
E.getOrElse(() => value)
)
),
// Create a new VM String
// NOTE: Do not shorten this to map(vm.newString) apparently it br
eaks it
E.map((x) => vm.newString(x)),
E.getOrElse(() => vm.undefined)
)
return {
value: result,
}
}
)
const envSetHandle = vm.newFunction("set", (keyHandle, valueHandle) => {
const key: unknown = vm.dump(keyHandle)
const value: unknown = vm.dump(valueHandle)
if (typeof key !== "string") {
return {
error: vm.newString("Expected key to be a string"),
}
}
if (typeof value !== "string") {
return {
error: vm.newString("Expected value to be a string"),
}
}
currentEnvs = setEnv(key, value, currentEnvs)
return {
value: vm.undefined,
}
})
const envResolveHandle = vm.newFunction("resolve", (valueHandle) => {
const value: unknown = vm.dump(valueHandle)
if (typeof value !== "string") {
return {
error: vm.newString("Expected value to be a string"),
}
}
const result = pipe(
parseTemplateStringE(value, [
...currentEnvs.selected,
...currentEnvs.global,
]),
E.getOrElse(() => value)
)
return {
value: vm.newString(result),
}
})
vm.setProp(envHandle, "resolve", envResolveHandle)
envResolveHandle.dispose()
vm.setProp(envHandle, "set", envSetHandle)
envSetHandle.dispose()
vm.setProp(envHandle, "getResolve", envGetResolveHandle)
envGetResolveHandle.dispose()
vm.setProp(envHandle, "get", envGetHandle)
envGetHandle.dispose()
vm.setProp(pwHandle, "env", envHandle)
envHandle.dispose()
vm.setProp(vm.global, "pw", pwHandle) vm.setProp(vm.global, "pw", pwHandle)
pwHandle.dispose() pwHandle.dispose()
const evalRes = vm.evalCode(testScript) const evalRes = vm.evalCode(testScript)
if (evalRes.error) { if (evalRes.error) {
const errorData = vm.dump(evalRes.error) const errorData = vm.dump(evalRes.error)
evalRes.error.dispose() evalRes.error.dispose()
return left(`Script evaluation failed: ${errorData}`) return TE.left(`Script evaluation failed: ${errorData}`)
} }
vm.dispose() vm.dispose()
return right(testRunStack) return TE.right({
tests: testRunStack,
envs: currentEnvs,
})
} }
) )
) )
 End of changes. 15 change blocks. 
11 lines changed or deleted 217 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)