File size: 2,556 Bytes
c0af099
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { describe, expect, it } from "vitest";

/**
 * Regression test for the `ReferenceError: ProgressEvent is not defined`
 * unhandled rejection that intermittently failed whole CI runs
 * (all tests green, exit code 1).
 *
 * MSW's XMLHttpRequest interceptor evaluates the bare `ProgressEvent`
 * identifier inside async response callbacks. Vitest's jsdom teardown does
 * `keys.forEach((key) => delete global[key])`, so any *own* property named
 * `ProgressEvent` — jsdom's, or a polyfill a setup file installed — is gone
 * once the environment for a test file is torn down. A callback that settles
 * after that point then throws.
 *
 * `vitest.setup.ts` therefore keeps a fallback on `globalThis`'s prototype
 * chain, which `delete` cannot reach. This test performs exactly the deletion
 * teardown performs and asserts the identifier still resolves.
 */
describe("ProgressEvent fallback in vitest.setup.ts", () => {
  it("resolves the bare identifier after teardown deletes the own property", () => {
    const live = Object.getOwnPropertyDescriptor(globalThis, "ProgressEvent");
    expect(live).toBeDefined();

    // What vitest's jsdom teardown does to every jsdom key.
    delete (globalThis as { ProgressEvent?: unknown }).ProgressEvent;

    try {
      // Before the fix this is "undefined", and the construction below throws
      // ReferenceError — the exact failure seen in CI.
      expect(typeof ProgressEvent).toBe("function");

      const event = new ProgressEvent("error", {
        lengthComputable: true,
        loaded: 3,
        total: 7,
      });

      expect(event).toBeInstanceOf(Event);
      expect(event.type).toBe("error");
      expect(event.lengthComputable).toBe(true);
      expect(event.loaded).toBe(3);
      expect(event.total).toBe(7);
    } finally {
      if (live) Object.defineProperty(globalThis, "ProgressEvent", live);
    }
  });

  it("prefers jsdom's own ProgressEvent while the environment is alive", () => {
    // The own property shadows the prototype fallback, so nothing observes the
    // stand-in until teardown removes jsdom's class.
    expect(
      Object.getOwnPropertyDescriptor(globalThis, "ProgressEvent"),
    ).toBeDefined();
    expect(new ProgressEvent("progress").type).toBe("progress");
  });

  it("does not add ProgressEvent to plain objects", () => {
    // The fallback lives on globalThis's own prototype chain, which in Node is
    // not Object.prototype — so it must not leak onto ordinary objects.
    expect("ProgressEvent" in {}).toBe(false);
  });
});