typescriptjestjspuppeteeres6-moduleslighthouse

"Jest failed to parse a file" when trying to import startFlow function from lighthouse library


I tried to wrap typescript app (lighthouse+puppeteer) in jest. I created jest environment, setup and teardown like is it in the documentation. All steps from setup work correctly and I see the result in the console but when I try to import startFlow inside __tests__/test1.test.ts

import { UserFlow, startFlow } from "lighthouse";

I get error

Jest encountered an unexpected token                                                                                                                  
                                                                                                                                                          
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.                                                                                                                                    
                                                                                                                                                          
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

By default "node_modules" folder is ignored by transformers.

Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

Details:

    C:\Users\malgorzata.kowal\Accenture Project\PMI\dce20-pmi-automation-performance-tool\node_modules\lighthouse\core\index.js:7
    import Trace from './gather/gatherers/trace.js';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      1 | import { afterAll, beforeAll, describe, test } from "@jest/globals";
      2 | import * as fs from "fs";
    > 3 | import { UserFlow, startFlow } from "lighthouse";
        | ^

my test1.test.ts

import { afterAll, beforeAll, describe, test } from "@jest/globals";
import * as fs from "fs";
import { UserFlow, startFlow } from "lighthouse";
import { Page } from "puppeteer";
import appConfig from "../config.ts";

describe("e2e lighthouse tests", () => {
  const customTimeout = 200_000;
  let page: Page;
  let flow: UserFlow;

  beforeAll(async () => {
    page = await globalThis.__BROWSER_GLOBAL__.newPage();
    flow = await startFlow(page, { name: "Test1" });
  });

  afterAll(async () => {
    await page.close();
    fs.writeFileSync(
      `${appConfig.directories.reports}/test1.html`,
      await flow.generateReport(),
    );
    fs.writeFileSync(
      `${appConfig.directories.reports}/test1.json`,
      JSON.stringify(await flow.createFlowResult(), null, 2),
    );
  });

  test(
    "test onet: test2 ",
    async () => {
      await page.goto("https://www.onet.pl/");
    },
    customTimeout,
  );
});

puppeteer-environment.ts

import { TestEnvironment as NodeEnvironment } from "jest-environment-node";

import { EnvironmentContext, JestEnvironmentConfig } from "@jest/environment";

import { Browser } from "puppeteer";
import PuppeteerDriver from "./puppeter.ts";

class PuppeteerEnvironment extends NodeEnvironment {
  puppeteerDriver = PuppeteerDriver;

  constructor(config: JestEnvironmentConfig, context: EnvironmentContext) {
    super(config, context);
  }

  async setup() {
    await super.setup();
    const browser = await this.puppeteerDriver.setup();
    this.global.__BROWSER_GLOBAL__ = browser;
  }

  async teardown() {
    (this.global.__BROWSER_GLOBAL__ as Browser).close();
    await super.teardown();
  }

  getVmContext() {
    return super.getVmContext();
  }
}

export default PuppeteerEnvironment;

jest.config.ts

import type { Config } from "jest";
const config: Config = {
  preset: "ts-jest",
  testEnvironment: "./src/puppeteer-environment.ts",
  extensionsToTreatAsEsm: [".ts", ".mts"],
  transform: {
    "^.+\\.(ts|mts)?$": [
      "ts-jest",
      {
        useESM: true,
      },
    ],
  },
  globalSetup: "./src/setup.js",
  globalTeardown: "./src/teardown.js",
  transformIgnorePatterns: ["node_modules/*"],
  moduleFileExtensions: ["ts", "js", "json", "node"],
};

export default config;

I run jest test

node --loader ts-node/esm node_modules/jest/bin/jest.js

And it doesn't have problems with importing modules until startFlow from lighthouse.

I tried to add extra babel transform to jest.config but it didn't help.


Solution

  • The problem occurred because I did not run it in --experimental-vm-modules mode. The problem does not occur when I run the test this way: node --experimental-vm-modules --experimental-loader ts-node/esm node_modules/jest/bin/jest.js