176 lines
5.5 KiB
JavaScript
176 lines
5.5 KiB
JavaScript
const PASS = "PASS";
|
|
const FAIL = "FAIL";
|
|
const ERROR = "ERROR";
|
|
|
|
const styles = {
|
|
[PASS]: { icon: "check", class: "success-response" },
|
|
[FAIL]: { icon: "close", class: "cl-error-response" },
|
|
[ERROR]: { icon: "close", class: "cl-error-response" },
|
|
none: { icon: "", class: "" }
|
|
};
|
|
|
|
// TODO: probably have to use a more global state for `test`
|
|
|
|
export default function runTestScriptWithVariables(script, variables) {
|
|
let pw = {
|
|
_errors: [],
|
|
_testReports: [],
|
|
_report: "",
|
|
expect(value) {
|
|
try {
|
|
return expect(value, this._testReports);
|
|
} catch (e) {
|
|
pw._testReports.push({ result: ERROR, message: e });
|
|
}
|
|
},
|
|
test: (descriptor, func) => test(descriptor, func, pw._testReports)
|
|
// globals that the script is allowed to have access to.
|
|
};
|
|
Object.assign(pw, variables);
|
|
|
|
// run pre-request script within this function so that it has access to the pw object.
|
|
new Function("pw", script)(pw);
|
|
//
|
|
const testReports = pw._testReports.map(item => {
|
|
if (item.result) {
|
|
item.styles = styles[item.result];
|
|
} else {
|
|
item.styles = styles.none;
|
|
}
|
|
return item;
|
|
});
|
|
return { report: pw._report, errors: pw._errors, testResults: testReports };
|
|
}
|
|
|
|
function test(descriptor, func, _testReports) {
|
|
_testReports.push({ startBlock: descriptor });
|
|
try {
|
|
func();
|
|
} catch (e) {
|
|
_testReports.push({ result: ERROR, message: e });
|
|
}
|
|
_testReports.push({ endBlock: true });
|
|
|
|
// TODO: Organize and generate text report of each {descriptor: true} section in testReports.
|
|
// add checkmark or x depending on if each testReport is pass=true or pass=false
|
|
}
|
|
|
|
function expect(expectValue, _testReports) {
|
|
return new Expectation(expectValue, null, _testReports);
|
|
}
|
|
|
|
class Expectation {
|
|
constructor(expectValue, _not, _testReports) {
|
|
this.expectValue = expectValue;
|
|
this.not = _not || new Expectation(this.expectValue, true, _testReports);
|
|
this._testReports = _testReports; // this values is used within Test.it, which wraps Expectation and passes _testReports value.
|
|
this._satisfies = function(expectValue, targetValue) {
|
|
// Used for testing if two values match the expectation, which could be === OR !==, depending on if not
|
|
// was used. Expectation#_satisfies prevents the need to have an if(this.not) branch in every test method.
|
|
// Signature is _satisfies([expectValue,] targetValue): if only one argument is given, it is assumed the targetValue, and expectValue is set to this.expectValue
|
|
if (!targetValue) {
|
|
targetValue = expectValue;
|
|
expectValue = this.expectValue;
|
|
}
|
|
if (this.not === true) {
|
|
// test the inverse. this.not is always truthly, but an Expectation that is inverted will always be strictly `true`
|
|
return expectValue !== targetValue;
|
|
} else {
|
|
return expectValue === targetValue;
|
|
}
|
|
};
|
|
}
|
|
_fmtNot(message) {
|
|
// given a string with "(not)" in it, replaces with "not" or "", depending if the expectation is expecting the positive or inverse (this._not)
|
|
if (this.not === true) {
|
|
return message.replace("(not)", "not ");
|
|
} else {
|
|
return message.replace("(not)", "");
|
|
}
|
|
}
|
|
_fail(message) {
|
|
this._testReports.push({ result: FAIL, message });
|
|
}
|
|
_pass(message) {
|
|
this._testReports.push({ result: PASS });
|
|
}
|
|
// TEST METHODS DEFINED BELOW
|
|
// these are the usual methods that would follow expect(...)
|
|
toBe(value) {
|
|
return this._satisfies(value)
|
|
? this._pass()
|
|
: this._fail(
|
|
this._fmtNot(`Expected ${this.expectValue} (not)to be ${value}`)
|
|
);
|
|
}
|
|
toHaveProperty(value) {
|
|
return this._satisfies(this.expectValue.hasOwnProperty(value), true)
|
|
? this._pass()
|
|
: this._fail(
|
|
this._fmtNot(
|
|
`Expected object ${this.expectValue} to (not)have property ${value}`
|
|
)
|
|
);
|
|
}
|
|
toBeLevel2xx() {
|
|
const code = parseInt(this.expectValue);
|
|
if (Number.isNaN(code)) {
|
|
return this._fail(
|
|
`Expected 200-level status but could not parse value ${this.expectValue}`
|
|
);
|
|
}
|
|
return this._satisfies(code >= 200 && code < 300)
|
|
? this._pass()
|
|
: this._fail(
|
|
this._fmtNot(
|
|
`Expected ${this.expectValue} to (not)be 200-level status`
|
|
)
|
|
);
|
|
}
|
|
toBeLevel3xx() {
|
|
const code = parseInt(this.expectValue);
|
|
if (Number.isNaN(code)) {
|
|
return this._fail(
|
|
`Expected 300-level status but could not parse value ${this.expectValue}`
|
|
);
|
|
}
|
|
return this._satisfies(code >= 300 && code < 400)
|
|
? this._pass()
|
|
: this._fail(
|
|
this._fmtNot(
|
|
`Expected ${this.expectValue} to (not)be 300-level status`
|
|
)
|
|
);
|
|
}
|
|
toBeLevel4xx() {
|
|
const code = parseInt(this.expectValue);
|
|
if (Number.isNaN(code)) {
|
|
return this._fail(
|
|
`Expected 400-level status but could not parse value ${this.expectValue}`
|
|
);
|
|
}
|
|
return this._satisfies(code >= 400 && code < 500)
|
|
? this._pass()
|
|
: this._fail(
|
|
this._fmtNot(
|
|
`Expected ${this.expectValue} to (not)be 400-level status`
|
|
)
|
|
);
|
|
}
|
|
toBeLevel5xx() {
|
|
const code = parseInt(this.expectValue);
|
|
if (Number.isNaN(code)) {
|
|
return this._fail(
|
|
`Expected 500-level status but could not parse value ${this.expectValue}`
|
|
);
|
|
}
|
|
return this._satisfies(code >= 500 && code < 600)
|
|
? this._pass()
|
|
: this._fail(
|
|
this._fmtNot(
|
|
`Expected ${this.expectValue} to (not)be 500-level status`
|
|
)
|
|
);
|
|
}
|
|
}
|