File size: 3,907 Bytes
4450790
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/**

 * @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.

 */
import { wait } from "rgthree/common/shared_utils.js";

type TestContext = {
  label?: string;
  beforeEach?: Function[];
};

let contexts: TestContext[] = [];

export function describe(label: string, fn: Function) {
  return async () => {
    await describeRun(label, fn);
  };
}

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;
}