typescriptjestjschaitsconfightml-components

Out of nowhere in an unrelated file when testing: ReferenceError: HTMLElement is not defined


My project's configuration is a bit complex, so I think it's more appropriate if I shared the commit state link. But, in short, somehow, in the utils.test.ts file, starting today, almost anything I add related to it makes the whole project so finicky.

It starts complaing about ReferenceError: HTMLElement is not defined in a totally unrelated file, which is also not referenced in the test files. Here is the whole error message:

 PASS  dist/test/elo.test.js
 FAIL  dist/test/utils.test.js
  ● Test suite failed to run

    ReferenceError: HTMLElement is not defined

      1 | import { Globals as g } from "../../infra/globals";
      2 |
    > 3 | export default class AboutView extends HTMLElement {
        |                                        ^
      4 |   static readonly tag: string = "about-view";
      5 |
      6 |   async connectedCallback(): Promise<void> {

      at Object.HTMLElement (src/ui/views/about-view.ts:3:40)
      at Object.<anonymous> (src/routing/switcher.ts:4:1)
      at Object.<anonymous> (src/routing/router.ts:3:1)
      at Object.<anonymous> (src/infra/setup.ts:13:1)
      at Object.<anonymous> (src/infra/globals.ts:2:1)
      at Object.<anonymous> (src/infra/utils.ts:3:1)
      at Object.<anonymous> (src/test/utils.test.ts:3:1)

Test Suites: 1 failed, 1 passed, 2 total
Tests:       6 passed, 6 total
Snapshots:   0 total
Time:        0.653 s, estimated 1 s
Ran all test suites matching /.\/dist\//i.

I did try to add stuff to my "lib": [] configuration in my tsconfig.json, as mentioned in this answer, but it didn't work.

The even weirder part is that, for example, the dateSorter function, which is very simple and doesn't depend on anything only runs without error if I copy-paste it into the file (I know, I know, terrible). Here is the utils.test.ts file for reference:

import * as chai from "chai";

import { paginationSlicer } from "../infra/utils";

import { GameEventTypes, TournamentOrLeague } from "../models/game_event";

describe("Pagination", () => {
  it("Pagination Slicer", () => {
    const events: TournamentOrLeague[] = [
      {
        type: GameEventTypes.tournament,
        name: "Comp 1",
        dates: [new Date(2022, 0, 29).getTime()],
      },
      {
        type: GameEventTypes.league,
        name: "Comp 2",
        dates: [new Date(2022, 0, 29).getTime()],
      },
      {
        type: GameEventTypes.league,
        name: "Comp 3",
        dates: [new Date(2022, 0, 29).getTime()],
      },
      {
        type: GameEventTypes.league,
        name: "Comp 4",
        dates: [new Date(2022, 0, 29).getTime()],
      },
      {
        type: GameEventTypes.league,
        name: "Comp 5",
        dates: [new Date(2022, 0, 29).getTime()],
      },
      {
        type: GameEventTypes.league,
        name: "Comp 6",
        dates: [new Date(2022, 0, 29).getTime()],
      },
    ];
    
    // With any of the lines below, we get the aforementioned error.
    // expect(paginationSlicer(0, events)).toEqual(5)
    // chai.expect(paginationSlicer(0, events).length).equal(5);
  });
});

// Zero idea why, but having this specific function in only one place
// doesn't work, otherwise the whole project errors in a weird fashion. This is
// currently copied in 3 different places.
const dateSorter = <T extends { date: number }>(
  dateAble1: T,
  dateAble2: T,
  desc: boolean = false
): number => {
  const [d1, d2] = [new Date(dateAble1.date), new Date(dateAble2.date)];
  const coeff = d1 > d2 ? 1 : d1 < d2 ? -1 : 0;
  return desc ? -coeff : coeff;
};

describe("Date Sorter", () => {
  it("Positive Dates", () => {
    const dateAbles = [
      { date: 1507593600003 },
      { date: 1507593600001 },
      { date: 1507593600000 },
    ];

    dateAbles.sort(dateSorter);

    chai
      .expect(dateAbles)
      .to.deep.equal([
        { date: 1507593600000 },
        { date: 1507593600001 },
        { date: 1507593600003 },
      ]);
  });

  it("Positive Descending Dates", () => {
    const dateAbles = [
      { date: 1507593600003 },
      { date: 1507593600001 },
      { date: 1507593600000 },
    ];

    dateAbles.sort((d1, d2) => dateSorter(d1, d2, true));

    chai
      .expect(dateAbles)
      .to.deep.equal([
        { date: 1507593600003 },
        { date: 1507593600001 },
        { date: 1507593600000 },
      ]);
  });

  it("Negative Dates", () => {
    const dateAbles = [
      { date: -1507593600003 },
      { date: -1507593600001 },
      { date: -1507593600000 },
    ];

    dateAbles.sort(dateSorter);

    chai
      .expect(dateAbles)
      .to.deep.equal([
        { date: -1507593600003 },
        { date: -1507593600001 },
        { date: -1507593600000 },
      ]);
  });
});

My project is organized in this way:

├── frontend/
│   ├── src/
│   │   ├── infra/
│   │   │   ├── utils.ts
│   │   ├── test/
│   │   │   ├── utils.test.ts
└── functions/

I use the frontend folder as a base and reference for the functions project. For that, in the functions' tsconfig added a reference path, and I added "composite": true.

I ended up also creating an issue on TypeScript's Github repo.


Solution

  • Here is what I had to do in order to solve this issue:

    1. Get rid of the Globals namespace I was using, for global variables to the whole project.
    2. Install a bunch of babel packages and core-js in the functions package.json:
      "devDependencies": {
        "@babel/cli": "^7.19.3",
        "@babel/core": "^7.19.3",
        "@babel/preset-env": "^7.19.4",
        "@babel/preset-flow": "^7.18.6",
        "@babel/preset-typescript": "^7.18.6",
        "core-js": "^3.25.5",
        ...
      }
      
    3. Add a Babel configuration file to the functions folder:
      {
          "presets": [
              "@babel/preset-typescript",
              "@babel/preset-flow",
              [
                  "@babel/preset-env",
                  {
                      "targets": {
                          "edge": "17",
                          "firefox": "60",
                          "chrome": "67",
                          "safari": "11.1"
                      },
                      "useBuiltIns": "usage",
                      "corejs": "3.6.5"
                  }
              ]
          ]
      }
      
    4. Install core-js to the frontend folder/project.

    I don't know if there was something simpler, but that's what worked.