Skip to content

Commit b55a7a3

Browse files
fix: log value of object when logged even if it changes (#379)
e.g. xs = [] console.log(xs) xs.push(1) console.log(xs) should log [], [ 1 ] not [ 1 ], [ 1 ]
1 parent fdeee96 commit b55a7a3

6 files changed

Lines changed: 59 additions & 46 deletions

File tree

packages/dom-evaluator/src/dom-test-evaluator.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
TEST_EVALUATOR_HOOKS_ID,
2121
} from "../../shared/src/ids";
2222
import { MockLocalStorage } from "./mock-local-storage";
23-
import { createLogFlusher, ProxyConsole } from "../../shared/src/proxy-console";
23+
import { ProxyConsole } from "../../shared/src/proxy-console";
2424
import { format } from "../../shared/src/format";
2525

2626
const READY_MESSAGE: ReadyEvent["data"] = { type: "ready" };
@@ -50,13 +50,11 @@ const removeTestScripts = () => {
5050
export class DOMTestEvaluator implements TestEvaluator {
5151
#runTest?: TestEvaluator["runTest"];
5252
#proxyConsole: ProxyConsole;
53-
#flushLogs: ReturnType<typeof createLogFlusher>;
5453

5554
constructor(
56-
proxyConsole: ProxyConsole = new ProxyConsole(globalThis.console),
55+
proxyConsole: ProxyConsole = new ProxyConsole(globalThis.console, format),
5756
) {
5857
this.#proxyConsole = proxyConsole;
59-
this.#flushLogs = createLogFlusher(this.#proxyConsole, format);
6058
}
6159

6260
async init(opts: InitTestFrameOptions) {
@@ -130,7 +128,7 @@ ${testString}`);
130128

131129
if (opts.hooks?.afterEach) eval(opts.hooks.afterEach);
132130

133-
return { pass: true, ...this.#flushLogs() };
131+
return { pass: true, ...this.#proxyConsole.flush() };
134132
} catch (err) {
135133
this.#proxyConsole.off();
136134
console.error(err);
@@ -154,7 +152,7 @@ ${testString}`);
154152
...(!!error.expected && { expected: error.expected }),
155153
...(!!error.actual && { actual: error.actual }),
156154
},
157-
...this.#flushLogs(),
155+
...this.#proxyConsole.flush(),
158156
};
159157
} finally {
160158
this.#proxyConsole.off();

packages/javascript-evaluator/src/javascript-test-evaluator.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type {
1313
import type { ReadyEvent } from "../../shared/src/interfaces/test-runner";
1414
import { postCloneableMessage } from "../../shared/src/messages";
1515
import { format } from "../../shared/src/format";
16-
import { ProxyConsole, createLogFlusher } from "../../shared/src/proxy-console";
16+
import { ProxyConsole } from "../../shared/src/proxy-console";
1717

1818
const READY_MESSAGE: ReadyEvent["data"] = { type: "ready" };
1919
declare global {
@@ -38,13 +38,11 @@ const wrapCode = (code: string) => `(async () => {${code};
3838
export class JavascriptTestEvaluator implements TestEvaluator {
3939
#runTest?: TestEvaluator["runTest"];
4040
#proxyConsole: ProxyConsole;
41-
#flushLogs: ReturnType<typeof createLogFlusher>;
4241

4342
constructor(
44-
proxyConsole: ProxyConsole = new ProxyConsole(globalThis.console),
43+
proxyConsole: ProxyConsole = new ProxyConsole(globalThis.console, format),
4544
) {
4645
this.#proxyConsole = proxyConsole;
47-
this.#flushLogs = createLogFlusher(this.#proxyConsole, format);
4846
}
4947

5048
init(opts: InitWorkerOptions) {
@@ -79,7 +77,7 @@ ${test};`);
7977

8078
if (opts.hooks?.afterEach) eval(opts.hooks.afterEach);
8179

82-
return { pass: true, ...this.#flushLogs() };
80+
return { pass: true, ...this.#proxyConsole.flush() };
8381
} catch (err: unknown) {
8482
this.#proxyConsole.off();
8583
console.error(err);
@@ -101,7 +99,7 @@ ${test};`);
10199
...(!!error.expected && { expected: error.expected }),
102100
...(!!error.actual && { actual: error.actual }),
103101
},
104-
...this.#flushLogs(),
102+
...this.#proxyConsole.flush(),
105103
};
106104
} finally {
107105
this.#proxyConsole.off();

packages/python-evaluator/src/python-test-evaluator.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import { ReadyEvent } from "../../shared/src/interfaces/test-runner";
1818
import { postCloneableMessage } from "../../shared/src/messages";
1919
import { format } from "../../shared/src/format";
20-
import { ProxyConsole, createLogFlusher } from "../../shared/src/proxy-console";
20+
import { ProxyConsole } from "../../shared/src/proxy-console";
2121

2222
type EvaluatedTeststring = {
2323
input?: string[];
@@ -38,13 +38,11 @@ class PythonTestEvaluator implements TestEvaluator {
3838
#pyodide?: PyodideInterface;
3939
#runTest?: TestEvaluator["runTest"];
4040
#proxyConsole: ProxyConsole;
41-
#flushLogs: ReturnType<typeof createLogFlusher>;
4241

4342
constructor(
44-
proxyConsole: ProxyConsole = new ProxyConsole(globalThis.console),
43+
proxyConsole: ProxyConsole = new ProxyConsole(globalThis.console, format),
4544
) {
4645
this.#proxyConsole = proxyConsole;
47-
this.#flushLogs = createLogFlusher(this.#proxyConsole, format);
4846
}
4947

5048
async init(opts: InitWorkerOptions) {
@@ -108,7 +106,7 @@ class PythonTestEvaluator implements TestEvaluator {
108106
// Execute afterEach hook if it exists
109107
if (opts.hooks?.afterEach) eval(opts.hooks.afterEach);
110108

111-
return { pass: true, ...this.#flushLogs() };
109+
return { pass: true, ...this.#proxyConsole.flush() };
112110
}
113111

114112
if (!evaluatedTestString || !("test" in evaluatedTestString)) {
@@ -141,7 +139,7 @@ class PythonTestEvaluator implements TestEvaluator {
141139

142140
if (opts.hooks?.afterEach) eval(opts.hooks.afterEach);
143141

144-
return { pass: true, ...this.#flushLogs() };
142+
return { pass: true, ...this.#proxyConsole.flush() };
145143
} catch (err) {
146144
this.#proxyConsole.off();
147145
console.error(err);
@@ -170,7 +168,7 @@ class PythonTestEvaluator implements TestEvaluator {
170168
...(!!actual && { actual }),
171169
type: error.type,
172170
},
173-
...this.#flushLogs(),
171+
...this.#proxyConsole.flush(),
174172
};
175173
} finally {
176174
this.#proxyConsole.off();

packages/shared/src/proxy-console.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ export class ProxyConsole {
1313
#originalConsole: Console;
1414
#proxyConsole: Console;
1515
#isOn: boolean = false;
16-
#calls: { level: Level; args: unknown[] }[] = [];
16+
#calls: { level: Level; msg: string }[] = [];
17+
#format: (x: unknown) => string;
1718

18-
constructor(originalConsole: Console) {
19+
constructor(originalConsole: Console, format: (x: unknown) => string) {
1920
this.#originalConsole = { ...originalConsole };
2021
this.#proxyConsole = originalConsole;
22+
this.#format = format;
2123
}
2224

2325
on() {
@@ -30,7 +32,8 @@ export class ProxyConsole {
3032
for (const level of LEVELS) {
3133
this.#proxyConsole[level] = (...args: unknown[]) => {
3234
this.#originalConsole[level](...args);
33-
this.#calls.push({ level, args });
35+
const msg = args.map((arg) => this.#format(arg)).join(" ");
36+
this.#calls.push({ level, msg });
3437
};
3538
}
3639
}
@@ -45,18 +48,8 @@ export class ProxyConsole {
4548
}
4649

4750
flush() {
48-
const out = this.#calls;
51+
const logs = this.#calls;
4952
this.#calls = [];
50-
return out;
53+
return logs.length ? { logs } : {};
5154
}
5255
}
53-
54-
export const createLogFlusher =
55-
(proxy: ProxyConsole, format: (x: unknown) => string) => () => {
56-
const rawLogs = proxy.flush();
57-
const logs = rawLogs.map(({ level, args }) => ({
58-
level,
59-
msg: args.map((arg) => format(arg)).join(" "),
60-
}));
61-
return logs.length ? { logs } : {};
62-
};

packages/tests/integration-tests/index.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,30 @@ describe("Test Runner", () => {
216216
});
217217
});
218218

219+
it("should evaluate the logged value at the time it was logged", async () => {
220+
const result = await page.evaluate(async (type) => {
221+
const runner = await window.FCCTestRunner.createTestRunner({
222+
type,
223+
});
224+
return runner.runTest(`
225+
const arr = [];
226+
for(let i = 0; i < 3; i++) {
227+
console.log(arr);
228+
arr.push(i);
229+
}
230+
`);
231+
}, type);
232+
233+
expect(result).toEqual({
234+
pass: true,
235+
logs: [
236+
{ level: "log", msg: "[]" },
237+
{ level: "log", msg: "[ 0 ]" },
238+
{ level: "log", msg: "[ 0, 1 ]" },
239+
],
240+
});
241+
});
242+
219243
it("should console.error non-chai errors thrown in the test", async () => {
220244
const spy = vi.fn();
221245
page.once("console", (msg) => {

packages/tests/proxy-console.test.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const levelsForEach = LEVELS.map((level) => ({
88
level,
99
}));
1010

11+
const simpleFormat = (x: string) => x;
12+
1113
describe("proxy-console", () => {
1214
let originalConsole: typeof console;
1315
beforeAll(() => {
@@ -24,7 +26,7 @@ describe("proxy-console", () => {
2426
});
2527

2628
it("should replace console methods when .on is called", () => {
27-
const proxy = new ProxyConsole(window.console);
29+
const proxy = new ProxyConsole(window.console, simpleFormat);
2830

2931
proxy.on();
3032

@@ -34,7 +36,7 @@ describe("proxy-console", () => {
3436
});
3537

3638
it("should restore console methods when .off is called", () => {
37-
const proxy = new ProxyConsole(window.console);
39+
const proxy = new ProxyConsole(window.console, simpleFormat);
3840

3941
proxy.on();
4042
proxy.off();
@@ -50,7 +52,7 @@ describe("proxy-console", () => {
5052
const logSpy = vi
5153
.spyOn(window.console, level)
5254
.mockImplementation(vi.fn());
53-
const proxy = new ProxyConsole(window.console);
55+
const proxy = new ProxyConsole(window.console, simpleFormat);
5456

5557
proxy.on();
5658
window.console[level]("test message");
@@ -60,41 +62,41 @@ describe("proxy-console", () => {
6062
);
6163

6264
describe("flush", () => {
63-
it("should return an empty array if no calls were recorded", () => {
64-
const proxy = new ProxyConsole(window.console);
65+
it("should not have a logs property if no calls were recorded", () => {
66+
const proxy = new ProxyConsole(window.console, simpleFormat);
6567

6668
proxy.on();
6769
const results = proxy.flush();
6870

69-
expect(results).toEqual([]);
71+
expect(results).toEqual({});
7072
});
7173

7274
it("should return all the calls recorded while proxying", () => {
7375
vi.spyOn(window.console, "log").mockImplementation(vi.fn());
7476
vi.spyOn(window.console, "warn").mockImplementation(vi.fn());
75-
const proxy = new ProxyConsole(window.console);
77+
const proxy = new ProxyConsole(window.console, simpleFormat);
7678
proxy.on();
7779

7880
window.console.log("test message 1");
7981
window.console.warn("test message 2");
8082
const results = proxy.flush();
8183

82-
expect(results).toEqual([
83-
{ level: "log", args: ["test message 1"] },
84-
{ level: "warn", args: ["test message 2"] },
84+
expect(results.logs).toEqual([
85+
{ level: "log", msg: "test message 1" },
86+
{ level: "warn", msg: "test message 2" },
8587
]);
8688
});
8789

8890
it("should clear the calls after flushing", () => {
8991
vi.spyOn(window.console, "log").mockImplementation(vi.fn());
90-
const proxy = new ProxyConsole(window.console);
92+
const proxy = new ProxyConsole(window.console, simpleFormat);
9193
proxy.on();
9294

9395
window.console.log("test message 1");
9496
proxy.flush();
9597
const results = proxy.flush();
9698

97-
expect(results).toEqual([]);
99+
expect(results).toEqual({});
98100
});
99101
});
100102
});

0 commit comments

Comments
 (0)