wallabyjs / public

Repository for Wallaby.js questions and issues
http://wallabyjs.com
759 stars 45 forks source link

Running mocha's global fixtures #2907

Closed bhvngt closed 2 years ago

bhvngt commented 2 years ago

Issue description or question

My project is currently using mocha's Global Fixtures to do one time setup. I have a mono repo. Hence I have multiple such fixtures configured for each of the sub-projects. I have tried following configurations

        setup: wallaby => {
                const mocha = wallaby.testFramework;
                mocha.suite.on('pre-require', function () {
                    require(wallaby.localProjectDir + '/libs/store/tests/helpers/setupSignupUser.js');
                    require(wallaby.localProjectDir + '/libs/service/tests/helpers/setupSignupUser.js');
                });
                 }

Here it throws [ERR_REQUIRE_ESM]: require() of ES Module since my globalSetup.js files are ES modules. Dynamic import using await import and making setup function async does not compile.

        setup: wallaby => {
                const mocha = wallaby.testFramework;
                mocha.require([
                    wallaby.localProjectDir + '/libs/store/tests/helpers/setupSignupUser.js',
                    wallaby.localProjectDir + '/libs/service/tests/helpers/setupSignupUser.js'
                ])
                 }

Here mocha.require is not a method. Setting mocha.options.require also does not work.

Appreciate your help here.

Wallaby diagnostics report

{
  editorVersion: 'IntelliJ IDEA 2021.3.1',
  pluginVersion: '1.0.230',
  editorType: 'IntelliJ',
  osVersion: 'darwin 21.2.0',
  nodeVersion: 'v16.13.1',
  coreVersion: '1.0.1209',
  checksum: 'MmUxNzQ4YTIxNGUwZjZhODg5YWRhNzc5Y2E0OWY3MzgsMTY2OTU5MzYwMDAwMCww',
  config: {
    files: [
      { pattern: 'package.json', ignore: false, trigger: true, load: true, instrument: true, order: 1 },
      { pattern: '**/{src,tests,types}/**/!(*.+(test|spec)).ts', ignore: false, trigger: true, load: true, instrument: true, order: 2 },
      { pattern: '{temp,apps,tools,utils}/**/*', ignore: true, trigger: true, load: true, instrument: true },
      { pattern: 'libs/ui/**/*', ignore: true, trigger: true, load: true, instrument: true },
      { pattern: '**/node_modules/**', ignore: true, trigger: true, load: true, instrument: true },
      { pattern: '.env', instrument: false, ignore: false, trigger: true, load: true, order: 3 }
    ],
    tests: [
      { pattern: '**/tests/**/*.+(test|spec).ts', ignore: false, trigger: true, load: true, test: true, order: 4 },
      { pattern: '{temp,apps,tools,utils}/**/*', ignore: true, trigger: true, load: true, test: true },
      { pattern: 'libs/ui/**/*', ignore: true, trigger: true, load: true, test: true },
      { pattern: '**/node_modules/**', ignore: true, trigger: true, load: true, test: true }
    ],
    testFramework: { version: 'mocha@2.1.0', configurator: 'mocha@2.1.0', reporter: 'mocha@2.1.0', starter: 'mocha@2.1.0' },
    filesWithNoCoverageCalculated: [ '**/node_modules/**', '**/tests/**/*.+(test|spec).{js,ts}' ],
    env: {
      type: 'node',
      params: { runner: '--experimental-specifier-resolution=node' },
      DOTENV_CONFIG_PATH: '.env',
      runner: '<homeDir>/.fnm/node-versions/v16.13.1/installation/bin/node',
      viewportSize: { width: 800, height: 600 },
      options: { width: 800, height: 600 },
      bundle: true
    },
    workers: { restart: true, initial: 0, regular: 0, recycle: true },
    debug: true,
    reportConsoleErrorAsError: true,
    diagnostics: {},
    runAllTestsInAffectedTestFile: false,
    updateNoMoreThanOneSnapshotPerTestFileRun: false,
    addModifiedTestFileToExclusiveTestRun: true,
    compilers: { '**/*.?(lit)coffee?(.md)': [Function (anonymous)] },
    preprocessors: {},
    maxConsoleMessagesPerTest: 100,
    autoConsoleLog: true,
    delays: { run: 0, edit: 100, update: 0 },
    teardown: undefined,
    hints: {
      ignoreCoverage: '__REGEXP /ignore coverage|istanbul ignore/',
      ignoreCoverageForFile: '__REGEXP /ignore file coverage/',
      commentAutoLog: '?',
      testFileSelection: { include: '__REGEXP /file\\.only/', exclude: '__REGEXP /file\\.skip/' }
    },
    automaticTestFileSelection: true,
    runSelectedTestsOnly: false,
    mapConsoleMessagesStackTrace: false,
    extensions: {},
    reportUnhandledPromises: true,
    slowTestThreshold: 75,
    lowCoverageThreshold: 80,
    loose: undefined,
    symlinkNodeModules: true,
    configCode: 'const filterCategories = ["temp", "apps", "tools", "utils"].join(",");\n' +
      'global._ranSetup = false;\n' +
      'module.exports = function(wallaby) {\n' +
      '\treturn {\n' +
      '\t\tfiles: [\n' +
      '\t\t\t"package.json",\n' +
      '\t\t\t"**/{src,tests,types}/**/!(*.+(test|spec)).ts",\n' +
      '\t\t\t`!{${filterCategories}}/**/*`,\n' +
      '\t\t\t"!libs/ui/**/*",\n' +
      '\t\t\t"!**/node_modules/**",\n' +
      '\t\t\t{\n' +
      '\t\t\t\tpattern: ".env",\n' +
      '\t\t\t\tinstrument: false\n' +
      '\t\t\t}\n' +
      '\t\t],\n' +
      '\n' +
      '\t\ttests: [\n' +
      '\t\t\t"**/tests/**/*.+(test|spec).ts",\n' +
      '\t\t\t`!{${filterCategories}}/**/*`,\n' +
      '\t\t\t"!libs/ui/**/*",\n' +
      '\t\t\t"!**/node_modules/**"\n' +
      '\t\t],\n' +
      '\n' +
      '\t\ttestFramework: "mocha",\n' +
      '\t\tfilesWithNoCoverageCalculated: ["**/node_modules/**", "**/tests/**/*.+(test|spec).{js,ts}"],\n' +
      '\n' +
      '\t\tenv: {\n' +
      '\t\t\ttype: "node",\n' +
      '\t\t\tparams: {\n' +
      '\t\t\t\trunner: "--experimental-specifier-resolution=node"\n' +
      '\t\t\t},\n' +
      '\t\t\t"DOTENV_CONFIG_PATH": ".env"\n' +
      '\t\t},\n' +
      '\n' +
      '\t\tworkers: { restart: true },\n' +
      '\t\tdebug: true,\n' +
      '\t\treportConsoleErrorAsError: true,\n' +
      '\t\tsetup: (wallaby) => {\n' +
      '\t\t\tif (!global._ranSetup) {\n' +
      '\t\t\t\tglobal._ranSetup = true;\n' +
      '\t\t\t\trequire("dotenv/config");\n' +
      "\t\t\t\trequire('chai').should();\n" +
      '\t\t\t\tconst mocha = wallaby.testFramework;\n' +
      "\t\t\t\tmocha.suite.on('pre-require', function () {\n" +
      "\t\t\t\t\trequire(wallaby.localProjectDir + '/libs/store/tests/helpers/setupSignupUser.js');\n" +
      "\t\t\t\t\trequire(wallaby.localProjectDir + '/libs/service/tests/helpers/setupSignupUser.js');\n" +
      '\t\t\t\t});\n' +
      '\t\t\t\tconst fs = require("fs");\n' +
      '\t\t\t\tconst json5 = require("json5");\n' +
      '\t\t\t\tconst rushJson = json5.parse(fs.readFileSync(`${wallaby.localProjectDir}/rush.json`, "utf-8"));\n' +
      '\t\t\t\tconsole.log("Sym linking rush modules");\n' +
      '\t\t\t\trushJson.projects.map(project => {\n' +
      '\t\t\t\t\tconst [packageCategory, packageName] = project.packageName.split("/");\n' +
      '\t\t\t\t\tconst categoryDir = `./node_modules/${packageCategory}`;\n' +
      '\t\t\t\t\tif (!fs.existsSync(categoryDir)) fs.mkdirSync(categoryDir);\n' +
      '\t\t\t\t\tif (!fs.existsSync(`${categoryDir}/${packageName}`)) {\n' +
      '\t\t\t\t\t\tconsole.log("Sym linking", project.packageName);\n' +
      '\t\t\t\t\t\tconst sourcePath = `${wallaby.localProjectDir}/${project.projectFolder}`;\n' +
      '\t\t\t\t\t\tconst targetPath = `${categoryDir}/${packageName}`;\n' +
      '\t\t\t\t\t\tconsole.log(`Sym linking ${sourcePath} with ${targetPath}`);\n' +
      '\t\t\t\t\t\tfs.symlinkSync(sourcePath, targetPath);\n' +
      '\t\t\t\t\t}\n' +
      '\t\t\t\t});\n' +
      '\t\t\t}\n' +
      '\t\t}\n' +
      '\t};\n' +
      '};\n'
  },
  packageJSON: { dependencies: undefined, devDependencies: undefined },
  fs: { numberOfFiles: 31 },
  debug: [
    '2022-01-18T14:37:36.872Z project Wallaby Node version: v16.13.1\n',
    '2022-01-18T14:37:36.872Z project Wallaby config: <homeDir>/Projects/lab/innerview/wallaby.cjs\n',
    '2022-01-18T14:37:46.012Z fs File system scan has finished by timeout\n',
    '2022-01-18T14:37:46.030Z project File cache: <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/projects/57e7e640d3458563\n',
    '2022-01-18T14:37:46.240Z uiService Listening port 51235\n',
    '2022-01-18T14:37:46.428Z project package.json file change detected, invalidating local cache\n',
    '2022-01-18T14:37:47.347Z workers Parallelism for initial run: 2, for regular run: 2\n',
    '2022-01-18T14:37:47.347Z workers Starting run worker instance #0\n',
    '2022-01-18T14:37:47.347Z workers Starting run worker instance #1\n',
    '2022-01-18T14:37:47.348Z workers Web server is listening at 51675\n',
    '2022-01-18T14:37:47.349Z project File cache requires some updates, waiting required files from IDE\n',
    '2022-01-18T14:37:48.972Z workers Started run worker instance (delayed) #0\n',
    '2022-01-18T14:37:48.973Z workers Started run worker instance (delayed) #1\n',
    '2022-01-18T14:37:50.791Z project Stopping process pool\n',
    '2022-01-18T14:37:50.793Z project Running postprocessor\n',
    '2022-01-18T14:37:50.798Z postprocessor New TypeScript language service is required\n',
    '2022-01-18T14:37:52.538Z project Postprocessor execution finished\n',
    '2022-01-18T14:37:52.538Z project Test run started; run priority: 3\n',
    '2022-01-18T14:37:52.539Z project Running all tests\n',
    '2022-01-18T14:37:52.541Z workers Starting test run, priority: 3\n',
    '2022-01-18T14:37:52.541Z workers Distributing tests between 2 workers\n',
    '2022-01-18T14:37:52.542Z workers Running tests in parallel\n',
    '2022-01-18T14:37:52.542Z nodeRunner Starting sandbox [worker #0, session #nfcqm]\n',
    '2022-01-18T14:37:52.542Z nodeRunner Starting sandbox [worker #1, session #rteet]\n',
    '2022-01-18T14:37:52.542Z nodeRunner Preparing sandbox [worker #0, session #nfcqm]\n',
    '2022-01-18T14:37:52.542Z nodeRunner Preparing sandbox [worker #1, session #rteet]\n',
    '2022-01-18T14:37:52.542Z nodeRunner Prepared sandbox [worker #0, session #nfcqm]\n',
    '2022-01-18T14:37:52.542Z nodeRunner Prepared sandbox [worker #1, session #rteet]\n',
    '2022-01-18T14:37:52.542Z workers [worker #0, session #nfcqm] Running tests in sandbox\n',
    '2022-01-18T14:37:52.543Z workers [worker #1, session #rteet] Running tests in sandbox\n',
    '2022-01-18T14:37:52.800Z workers Sandbox (active) [rteet] error: require() of ES Module <homeDir>/Projects/lab/innerview/libs/store/tests/helpers/setupSignupUser.js from <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js not supported.\n' +
      'Instead change the require of setupSignupUser.js in <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js to a dynamic import() which is available in all CommonJS modules.\n',
    '2022-01-18T14:37:52.802Z workers Failed to map the stack to user code, entry message: require() of ES Module <homeDir>/Projects/lab/innerview/libs/store/tests/helpers/setupSignupUser.js from <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js not supported.\n' +
      'Instead change the require of setupSignupUser.js in <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js to a dynamic import() which is available in all CommonJS modules., stack: Error [ERR_REQUIRE_ESM]: require() of ES Module <homeDir>/Projects/lab/innerview/libs/store/tests/helpers/setupSignupUser.js from <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js not supported.\n' +
      'Instead change the require of setupSignupUser.js in <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js to a dynamic import() which is available in all CommonJS modules.\n' +
      '    at Module.r.require (<homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/server.js:30:10058)\n' +
      '    at Module.n.require (<homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/server.js:30:8616)\n' +
      '    at Module.n.require (<homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/server.js:30:8616)\n' +
      '    at Suite.eval (eval at module.exports (<homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js:14:44), <a\n',
    '2022-01-18T14:37:52.803Z workers Sandbox (active) [nfcqm] error: require() of ES Module <homeDir>/Projects/lab/innerview/libs/store/tests/helpers/setupSignupUser.js from <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js not supported.\n' +
      'Instead change the require of setupSignupUser.js in <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js to a dynamic import() which is available in all CommonJS modules.\n',
    '2022-01-18T14:37:52.803Z workers Failed to map the stack to user code, entry message: require() of ES Module <homeDir>/Projects/lab/innerview/libs/store/tests/helpers/setupSignupUser.js from <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js not supported.\n' +
      'Instead change the require of setupSignupUser.js in <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js to a dynamic import() which is available in all CommonJS modules., stack: Error [ERR_REQUIRE_ESM]: require() of ES Module <homeDir>/Projects/lab/innerview/libs/store/tests/helpers/setupSignupUser.js from <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js not supported.\n' +
      'Instead change the require of setupSignupUser.js in <homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js to a dynamic import() which is available in all CommonJS modules.\n' +
      '    at Module.r.require (<homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/server.js:30:10058)\n' +
      '    at Module.n.require (<homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/server.js:30:8616)\n' +
      '    at Module.n.require (<homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/server.js:30:8616)\n' +
      '    at Suite.eval (eval at module.exports (<homeDir>/Library/Caches/JetBrains/IntelliJIdea2021.3/wallaby/wallaby/runners/node/bootstrap.js:14:44), <a\n',
    '2022-01-18T14:37:52.936Z workers [rteet] Run 0 test(s), skipped 0 test(s)\n',
    '2022-01-18T14:37:52.936Z workers [nfcqm] Run 0 test(s), skipped 0 test(s)\n',
    '2022-01-18T14:37:53.179Z workers [rteet] Sandbox is responsive, closing it\n',
    '2022-01-18T14:37:53.179Z workers Starting run worker instance #1\n',
    '2022-01-18T14:37:53.180Z workers [nfcqm] Sandbox is responsive, closing it\n',
    '2022-01-18T14:37:53.180Z workers Starting run worker instance #0\n',
    '2022-01-18T14:37:53.180Z workers Merging parallel test run results\n',
    '2022-01-18T14:37:53.182Z project Test run finished\n',
    '2022-01-18T14:37:53.183Z project Processed console.log entries\n',
    '2022-01-18T14:37:53.183Z project Processed loading sequences\n',
    '2022-01-18T14:37:53.183Z project Processed executed tests\n',
    '2022-01-18T14:37:53.193Z project Processed code coverage\n',
    '2022-01-18T14:37:53.220Z project Test run result processed and sent to IDE\n',
    '2022-01-18T14:37:54.068Z workers Started run worker instance (delayed) #0\n',
    '2022-01-18T14:37:54.070Z workers Started run worker instance (delayed) #1\n'
  ]
}
smcenlly commented 2 years ago

Can you please describe the intent of your global setup when running in Wallaby context?

As you are no doubt aware, Wallaby runs your tests multiple times. In your case, you have Wallaby configured to start a new process for each test execution (because you're using mocha with esm).

Can you describe what global setup is doing? Do you need it to run for each process when it starts? Or is the code in your global setup OK to run just once when Wallaby starts? How you solve the problem may differ a little depending on what you're looking to do.


Regardless of when you need the code to run, we also put together some sample code that should help:

package.json In our project root package.json, we had type set to module. I assume you also have this.

setup.js Sample setup file in project root that is an ES module (doesn't actually do anything in our case)

import { readFileSync } from 'fs';

console.log(readFileSync);

wallaby.cjs Make sure Wallaby configuration file has .cjs extension instead of .js extension (because of package.json). Make setup() function async and then import your setup file.

module.exports = function (wallaby) {
  return {
...
    setup: async function (wallaby) {
      const path = require("path");
      await import(path.join(wallaby.localProjectDir, "setup.js"));
    },
  };
};

If you're still having problems getting it working, please let us know what you need to execute and when. We should be able to help.

bhvngt commented 2 years ago

Hi @smcenlly I am seeding my local db with few records before my tests began. So thats the code that is running within global fixture. It needs to run only once and need not run for each test run or for each process.

Code within the globalFixture is as follows

import pg from "pg";
import { AUTH_USER_EMAIL, AUTH_PASSWORD } from "./_constants";

export async function mochaGlobalSetup() {
   // inserting few records using pg.
}

Typically this fixture is called by mocha using its require configuration flag.

I was able to make it run within the wallaby setup using dynamic import based on your example.

For my understanding, does the setup code runs for each test process spawned by wallaby or does it run only once per wallaby instance? If it is former then Is there a way to run a code only once per wallaby instance?

smcenlly commented 2 years ago

The setup code is run every time in the context of the process running your tests. By default, you should have multiple processes running your tests at the same time (assuming you have more than one test file).

If you only want to initialise the database once on Wallaby startup then you may instead change your module.export to return an async function. For example:

module.exports = async function (wallaby) {
  if (!process.env.DB_INITIALIZED) {
    await someAsynchronousSetupFunction();
    process.env.DB_INITIALIZED = true;
  }
  return {
...
  }
};

In the example above, we use a process environment variable that is passed to all child worker processes to ensure that the initialisation code only runs once.

If your tests have not been designed to run in multiple processes at the same time and you run into problems, you can use Wallaby's workers setting to limit the concurrency.