File size: 4,429 Bytes
baac5bb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
/**
* @fileoverview A set of methods that mimic a bit of the Jasmine testing library, but simpler and
* more succinct for manipulating a comfy integration test.
*
* Tests are not bundled by default, to test build with "--with-tests" and then invoke from the
* dev console like `rgthree_tests.TestDescribeLabel()`. The output is in the test itself.
*/
import { wait } from "rgthree/common/shared_utils.js";
declare global {
interface Window {
rgthree_tests: {
[key: string]: any;
};
}
}
window.rgthree_tests = window.rgthree_tests || {};
type TestContext = {
label?: string;
beforeEach?: Function[];
};
let contexts: TestContext[] = [];
export function describe(label: string, fn: Function) {
if (!label.startsWith('Test')) {
throw new Error('Test labels should start with "Test"');
}
window.rgthree_tests[label] = async () => {
await describeRun(label, fn);
};
return window.rgthree_tests[label];
}
export async function describeRun(label: string, fn: Function) {
await wait();
contexts.push({ label });
console.group(`[Start] ${contexts[contexts.length - 1]!.label}`);
await fn();
contexts.pop();
console.groupEnd();
}
export async function should(declaration: string, fn: Function) {
if (!contexts[contexts.length - 1]) {
throw Error("Called should outside of a describe.");
}
console.group(`...should ${declaration}`);
try {
for (const context of contexts) {
for (const beforeEachFn of context?.beforeEach || []) {
await beforeEachFn();
}
}
await fn();
} catch (e: any) {
fail(e);
}
console.groupEnd();
}
export async function beforeEach(fn: Function) {
if (!contexts[contexts.length - 1]) {
throw Error("Called beforeEach outside of a describe.");
}
const last = contexts[contexts.length - 1]!;
last.beforeEach = last?.beforeEach || [];
last.beforeEach.push(fn);
}
export function fail(e: Error) {
log(`X Failure: ${e}`, "color:#600; background:#fdd; padding: 2px 6px;");
}
function log(msg: string, styles: string) {
if (styles) {
console.log(`%c ${msg}`, styles);
} else {
console.log(msg);
}
}
class Expectation {
private propertyLabel: string | null = "";
private expectedLabel: string | null = "";
private expectedFn!: (v: any) => boolean;
private value: any;
constructor(value: any) {
this.value = value;
}
toBe(labelOrExpected: any, maybeExpected?: any) {
const expected = maybeExpected !== undefined ? maybeExpected : labelOrExpected;
this.propertyLabel = maybeExpected !== undefined ? labelOrExpected : null;
this.expectedLabel = JSON.stringify(expected);
this.expectedFn = (v) => v == expected;
return this.toBeEval();
}
toBeUndefined(propertyLabel: string) {
this.expectedFn = (v) => v === undefined;
this.propertyLabel = propertyLabel || "";
this.expectedLabel = "undefined";
return this.toBeEval(true);
}
toBeNullOrUndefined(propertyLabel: string) {
this.expectedFn = (v) => v == null;
this.propertyLabel = propertyLabel || "";
this.expectedLabel = "null or undefined";
return this.toBeEval(true);
}
toBeTruthy(propertyLabel: string) {
this.expectedFn = (v) => !v;
this.propertyLabel = propertyLabel || "";
this.expectedLabel = "truthy";
return this.toBeEval(false);
}
toBeANumber(propertyLabel: string) {
this.expectedFn = (v) => typeof v === "number";
this.propertyLabel = propertyLabel || "";
this.expectedLabel = "a number";
return this.toBeEval();
}
toBeEval(strict = false) {
let evaluation = this.expectedFn(this.value);
let msg = `Expected ${this.propertyLabel ? this.propertyLabel + " to be " : ""}${
this.expectedLabel
}`;
msg += evaluation ? "." : `, but was ${JSON.stringify(this.value)}`;
this.log(evaluation, msg);
return evaluation;
}
log(value: boolean, msg: string) {
if (value) {
log(`🗸 ${msg}`, "color:#060; background:#cec; padding: 2px 6px;");
} else {
log(`X ${msg}`, "color:#600; background:#fdd; padding: 2px 6px;");
}
}
}
export function expect(value: any, msg?: string) {
const expectation = new Expectation(value);
if (msg) {
expectation.log(value, msg);
}
return expectation;
}
|