The MOCHA_GREP env var isn't working with this Meteor 3.4 + rspack setup

Claude Code is trying to run selected tests only. Seems the Meteor implementation of rspack isn’t allowing grep, if it does please advise on how to achieve this, thanks!

FYI - using Mocha for testing.

UPDATE:

The background research on the Meteor rspack grep issue completed. Here's the key finding:

  MOCHA_GREP + Rspack

  MOCHA_GREP is not a Meteor feature — it's from the meteortesting:mocha Atmosphere package. The Meteor 3.4 docs don't mention it at all. The docs do acknowledge that rspack
  testing flows are still being stabilized in the 3.4.x release cycle (Phase 5 roadmap).

  The likely cause: rspack takes over bundling and the process.env.MOCHA_GREP reference in meteortesting:mocha's runtimeArgs.js is either being replaced at build time with an
  empty value, or the env var isn't propagating to the rspack runtime context.

  Workarounds:
  1. meteor test-packages --filter "name" — Meteor's built-in filtering (but only for package tests, not app tests)
  2. Remove rspack temporarily for test runs: meteor remove rspack to fall back to the traditional bundler where MOCHA_GREP works
  3. Wait for Meteor 3.4.x to stabilize test flows with rspack

  For now, running the full suite and filtering output (as we did) is the pragmatic approach.

FYI - I have 1,300 tests for this repo so this is a big downside that I didn’t know before switching to rspack. Might be that this is a known problem/shortcoming and I just didn’t read about it but IMO it’s something that hopefully is fixed soon. Hope the Meteor team agrees.

@a4xrbj1
Hey! I think the issue is that with rspack, the process.env.MOCHA_GREP might not propagate correctly to the test runner :thinking:

Have you tried passing it via Meteor settings instead? Something like:

// settings-test.json
{
  "public": {
    "mochaRuntimeArgs": {
      "mochaOptions": {
        "grep": "your pattern here"
      }
    }
  }
}

Then:

meteor test --driver-package meteortesting:mocha --settings settings-test.json

Under the hood, meteortesting:mocha reads MOCHA_GREP on the server and bridges it to the client via Meteor.settings.public.mochaRuntimeArgs anyway — so passing it directly through settings should bypass whatever rspack does to the env var

1 Like

I didn’t have a test for this scenario, so I’m going to check it myself.

For now, could you try what @dupontbertrand suggested?

Another workaround would be to teach the AI to tag .only on the proper describe/it blocks it acts on, at least until we confirm this is a regression we can fix in 3.4.1.

By the way, next week we will release a beta for Meteor 3.4.1 including many fixes for reports already made. I keep getting reports, and I have two right now including this one to work until next week, so this is a good time to report anything else you see that we can improve. Thanks.

I can’t reproduce this issue. I created two Meteor apps, one with Meteor 3.3.2 (Meteor bundler) and the other with Meteor 3.4 (Rspack bundler), and added simple mocha tests. When running the tests with the meteortesting:mocha driver using MOCHA_GREP, both scenarios behave the same.

I would need a minimal repository that reproduces the exact problem so I can see the edge case that is not supported and where the env spreading is being missed.

The grep filter in settings didn't work — all tests ran.

That’s what Claude Code reports. Additional info:

... uses mainModule, test files must be explicitly imported.

Still showing other test failures. The .only didn't work — other test files' tests are still running. That's because describe.only only scopes within a single file when Mocha has multiple files loaded eagerly.

describe.only isn't filtering. Mocha in the meteor test context may not support .only across files, or the other test files are loaded outside Mocha's scope.

Could you ask Claude to reproduce the issue and create a Meteor app in a repo that it can share with us for a proper check?

It would be good to ask it because, with your project context where the issue is reproduced, it may understand the problem and abstract it enough to build a minimal reproduction app on its own.

On my side, everything works: I explicitly imported with mainModule, .only works for me, and the same happens in the common app skeletons used as examples.

I’ve traced the full execution chain and I believe this is not an rspack issue at all. MOCHA_GREP works correctly — it filters which tests Mocha runs. The problem is specific
to projects where:

  1. mainModule is set but testModule is NOT set in package.json’s meteor section
  2. Test files have side effects on load (DB connections, queue setup, cross-service imports)

Here’s what happens:

When meteor test runs with mainModule but no testModule, the compiler’s _isLazy() (in compiler-plugin.js) still eagerly loads ALL *.test.js files via isTestFilePath(),
regardless of the mainModule restriction. This is correct behavior — test files should be eager during test runs.

The issue is that MOCHA_GREP / mochaInstance.grep() operates at the Mocha runner level (filtering during run()), not at the file loading level. By the time start() calls
mochaInstance.grep(pattern), every test file has already been loaded and evaluated. In a simple app, this is invisible — test files load cleanly even if their tests won’t run.
But in a larger app with test files that import heavy infrastructure (BullMQ workers, MongoDB sessions, S3 clients, cross-service fixtures), the loading itself can fail or
produce errors.

This is why you can’t reproduce it with simple test apps — the loading is silent and clean. The issue only surfaces when test file evaluation has meaningful side effects.

What would actually solve the problem is a testModule entry point (analogous to mainModule but for meteor test), so that only explicitly imported test files are loaded.
Without it, every *.test.js file is eagerly evaluated regardless of grep. This would give projects with mainModule the same control over test loading that mainModule gives
them for production code.

I can create a minimal reproduction repo that demonstrates this — a Meteor 3.4 app with mainModule, no testModule, and two test files where one has a side effect on load
(e.g., throws if a required service isn’t available). Setting MOCHA_GREP to match only the other test file will still fail because the problematic file loads regardless. Want
me to put that together?

============
Additional info:

Why nachocodoner Can’t Reproduce It

His test apps likely have:

  1. Lightweight test files — test files that don’t fail on load. A simple describe(‘foo’, function() { it(‘works’, function() { … }) }) loads cleanly even if grep skips it.
  2. No cross-service dependencies — his test files don’t depend on a running backend, MongoDB replica set, BullMQ queues, or baseline data being loaded.
  3. Possibly using testModule — which would make only the declared entry point eager, not all *.test.js files.

Thanks for the analysis. This is related (but probably not the same) as this issue that I reported and submitted a PR for in February. No response yet from the maintainers, unfortunately.

I will try another round to reproduce it with your info. If you get a repo with the steps, let me know. That will be helpful to speed up the possible fix to your use case.

As part of this, I will also look into your issue @permb, thanks for the reminder.

Repo is live at GitHub - a4xrbj1/meteor-mocha-grep-repro: Minimal reproduction: MOCHA_GREP cannot prevent eager test file loading when mainModule is set without testModule (Meteor 3.4) · GitHub.

The test run confirmed the reproduction — here’s what the output proves:

bad.test.js is being LOADED even though MOCHA_GREP should have excluded it. <<<

Error: LOAD-TIME SIDE EFFECT: bad.test.js was evaluated at load time.
at imports/test/bad.test.js:23:7
at evaluateNextModule (packages/core-runtime.js:167:26)
at runEagerModules (packages/core-runtime.js:206:3)

The stack trace shows runEagerModules → fileEvaluate → bad.test.js — Meteor’s eager module loader is evaluating the file during startup, long before meteortesting:mocha’s
start() function calls mochaInstance.grep().

You can share this repo link directly in your forum reply to @nachocodoner. The README has full reproduction steps (npm run test:grep) and explains the root cause + the
testModule workaround.

I pushed a new PR to allow the testing package startup to be async - that might help in this case

1 Like

It’s still not working and quite frankly I’m starting to regret switching to rspack with such a fundamental feature not working properly. It’s seriously affecting my workflow, running tests that are touched by code editing is a fundamental principle of coding and with over 1000 tests it’s a serious problem. Claude Code (Opus 4.6) tried it’s best last night again, trying different options but in the end all failed. Here’s is its summary (which quite frankly I have no time to look into the details as a Solo Entrepreneur):

Meteor 3 + rspack + meteortesting:mocha — Dual Mocha Instance Problem

Environment

  • Meteor 3.x with @meteorjs/rspack bundler
  • meteortesting:mocha@3.3.0 / meteortesting:mocha-core@8.2.0 (Mocha 10.2.0)
  • package.json has mainModule set:
    “meteor”: {
    “mainModule”: {
    “client”: “client/main.js”,
    “server”: “server/main.js”
    }
    }

How tests are initiated

MONGO_URL=“mongodb://…”
TEST_CLIENT=0
MOCHA_TIMEOUT=60000
meteor test --once
–driver-package meteortesting:mocha
–settings settings-development.json
–port 4001

The meteortesting:mocha package must first be added via meteor add meteortesting:mocha. The run_tests.sh script in the repo automates starting the MongoDB replica set and
running this command.

Build architecture (how rspack structures the test bundle)

When meteor test runs with rspack, the build produces these files in _build/test/:

server-entry.js → Entry point (imports app code + test files)
server-rspack.js → Bundled output from rspack (38MB, IIFE-wrapped)
server-meteor.js → Meteor runtime file that imports server-rspack.js

The load chain at runtime:

  1. Meteor boots — loads all Meteor packages (including meteortesting:mocha-core and meteortesting:mocha)
  2. Meteor loads server-meteor.js — which contains import ‘./server-rspack.js’
  3. server-rspack.js evaluates — all app code and test files execute inside the rspack IIFE
  4. Meteor.startup callbacks fire — including the test driver’s start() function
  5. start() calls mochaInstance.run() — runs the mocha test suite

The problem: two separate Mocha instances

Instance 1: Meteor package space (mocha-core)

In meteortesting:mocha-core/server.js:
import Mocha from ‘mocha’
import config from ‘./loadConfig’

const mochaInstance = new Mocha({ ui: ‘bdd’, ignoreLeaks: true, …config })
setupGlobals(mochaInstance) // sets global.describe, global.it, etc.

export { mochaInstance }

This mochaInstance lives in Meteor’s package module system. The test driver (meteortesting:mocha/server.js) imports it and calls mochaInstance.run() to execute tests.

Instance 2: rspack bundle space

When a test file inside the rspack bundle does:
import { mochaInstance } from ‘meteor/meteortesting:mocha-core’;

rspack resolves this as an external:
// In server-rspack.js:
“meteor/meteortesting:mocha-core”(module) {
module.exports = require(“meteor/meteortesting:mocha-core”);
}

This require(“meteor/meteortesting:mocha-core”) call at runtime goes through Node’s require system. However, the Meteor package system’s module resolution for packages loaded
from app code (inside rspack) returns a different module instance than the one loaded from package code (inside meteortesting:mocha).

Evidence

I verified this step by step:

Step 1: Inside server/tests.js (which runs inside the rspack bundle), I imported mochaInstance and called describe:

import { mochaInstance } from ‘meteor/meteortesting:mocha-core’;

console.log(‘suites BEFORE:’, mochaInstance.suite.suites.length); // 0
describe(‘Smoke test’, function () {
it(‘should pass’, function () {});
});
console.log(‘suites AFTER:’, mochaInstance.suite.suites.length); // 1
console.log(‘total tests:’, mochaInstance.suite.total()); // 1

Output:
suites BEFORE: 0
suites AFTER: 1
total tests: 1
----- RUNNING SERVER TESTS -----
0 passing (0ms)

The suite registers correctly on the mochaInstance visible inside the rspack bundle. But when the test driver calls mochaInstance.run(), it gets 0 tests — because it’s running
on a different mochaInstance.

Step 2: I checked from inside Meteor.startup (which fires during the test run):

Meteor.startup(function () {
console.log(‘suites at startup:’, mochaInstance.suite.suites.length); // 0
console.log(‘total tests:’, mochaInstance.suite.total()); // 0
});

Output:
----- RUNNING SERVER TESTS -----
suites at startup: 0
total tests: 0
0 passing (0ms)

By Meteor.startup time, the mochaInstance visible to the rspack bundle shows 0 suites. This proves the module is being re-evaluated (a fresh new Mocha() is created) when
accessed from different contexts.

Approaches tried

Approach 1: testModule in package.json

“testModule”: { “server”: “server/tests.js” }

Result: rspack’s @meteorjs/rspack plugin does not read testModule from package.json. The debug log I added to rspack.config.js showed:
isTestModule=false testEntry=undefined testServerEntry=undefined

The rspack plugin receives these values from the Meteor build tool’s Meteor object passed to defineConfig(). Meteor does NOT populate Meteor.testServerEntry when testModule is
set in package.json — this is a disconnect between Meteor’s package.json schema and what it passes to the rspack plugin.

However, Meteor does auto-generate _build/test/server-entry.js with import ‘…/…/server/tests.js’ when testModule.server is set. So the file IS loaded. But the tests inside it
still can’t register on the correct Mocha instance.

Approach 2: BannerPlugin to inject mocha globals

new BannerPlugin({
banner: [
‘var describe = global.describe;’,
‘var it = global.it;’,
‘var before = global.before;’,
// …
].join(‘\n’),
raw: true,
entryOnly: true,
})

Result: The banner correctly injected var describe = global.describe at the top of server-rspack.js, OUTSIDE the IIFE. I verified:

  • typeof describe === ‘function’ ✓
  • describe() doesn’t throw ✓
  • The suite registers on mochaInstance inside the bundle ✓

But it’s the wrong instance — the test driver’s mochaInstance.run() operates on a different object.

Approach 3: Re-running setupGlobals from inside the bundle

import { mochaInstance, setupGlobals } from ‘meteor/meteortesting:mocha-core’;
setupGlobals(mochaInstance);

Result: Same problem. The mochaInstance imported inside the rspack bundle is a separate instance from the one the test driver uses. Re-running setupGlobals on it just sets
global.describe/global.it to point to this separate instance’s suite. Tests register on it, but the driver’s run() still finds 0 tests.

Approach 4: Eager test mode (no testModule, let rspack auto-discover)

Without testModule, rspack sets isTestEager=true and generates .meteor/local/test/server-eager-tests.mjs:
const ctx = import.meta.webpackContext(‘/’, {
recursive: true,
regExp: /.(?:test|spec)s?.[^.]+$/,
exclude: /…node_modules|.meteor|client…/,
mode: ‘eager’,
});
ctx.keys().forEach(ctx);

Result: This auto-discovers all *.test.js files and they DO get compiled into the 38MB bundle (I found describe calls at lines 128922, 722301, etc.). But the same dual-instance
problem means 0 tests run.

Root cause

The Meteor package module system creates separate module evaluation contexts for:

  • Package-to-package imports: When meteortesting:mocha imports from meteortesting:mocha-core, it gets the canonical module instance
  • App-to-package imports: When rspack-bundled app code does require(“meteor/meteortesting:mocha-core”), it gets a different evaluation of the same module

Since mocha-core/server.js creates const mochaInstance = new Mocha(options) at the top level, each evaluation creates a FRESH Mocha instance with its own root suite. Tests
register on the app-space instance, but run() executes on the package-space instance.

Suggested fix directions @nachocodoner

  1. In @meteorjs/rspack: Ensure that require(“meteor/…”) calls inside the rspack bundle go through the SAME module cache as the Meteor package system. This would make
    require(“meteor/meteortesting:mocha-core”) return the identical mochaInstance object.
  2. In meteortesting:mocha-core: Instead of exporting mochaInstance directly, store it on global (e.g., global.__mochaInstance) and have both the test driver and the app code
    reference it from there. This bypasses module system differences.
  3. In meteortesting:mocha: In the start() function, before calling mochaInstance.run(), merge suites from global.__rspackMochaInstance (if set) into the driver’s mochaInstance.
    This would be a compatibility shim.

Good news, I’ve reproduced this thanks to your repository. I’m going to take a look at it today. I’m replying here since you provided useful feedback in your last message, and now with a reproduction I can work on fixes one by one, starting from the earliest issue. Just wanted to let you know.

I’m about to deploy a new beta for 3.4.1. If the change turns out to be easy to fix and backward compatible, I’ll include it so we can at least unblock one of the problems, and hopefully the others cascade.

Any other issue you get you can share a reproduction in a repository as done before, these are really useful to understand the configuration and fix later.

It’s curious to see that the example repository you shared reproduces the problem without Rspack at all. This seems to point, at the very least, to an issue in the Meteor bundler and testing infrastructure, and your example repository proves that: no Rspack setup there, just a simple Meteor app.

I tested your app from a checkout with the core await change needed: https://github.com/meteor/meteor/pull/14317, and also by applying locally the meteor-mocha changes from @permb here: https://github.com/Meteor-Community-Packages/meteor-mocha/pull/177/changes. The app still doesn’t get fixed either. So this issue is definitely not related to those changes, although those changes will fix others.

I have tried to understand the issue, but I still don’t see what issue you mean. It is not clearly reproduced, and the steps and context are not clear enough to act on.

First of all, the repository of your app reproducing the problem, https://github.com/a4xrbj1/meteor-mocha-grep-repro, does not add Rspack, so I can’t see how the issue mentioned there and what is being claimed here match. It is said in this forum post that Rspack introduced a regression on MOCHA_GREP, but the repository does not provide a reproducible path using Rspack. It only uses the Meteor bundler in Meteor 3.4, with no Rspack at all.

The description in the reproducible app is so vague. The issue shown there is that a specific test, bad.test.js, in an app that loads tests in eager mode, with no testModule defined in package.json, crashes and should not crash. Adding the text that describes the problem:

Repo claim: Minimal reproduction: MOCHA_GREP cannot prevent eager test file loading when mainModule is set without testModule (Meteor 3.4)

Meteor test startup
  ├─ Compiler: isTestFilePath("imports/test/good.test.js") → true → EAGER
  ├─ Compiler: isTestFilePath("imports/test/bad.test.js")  → true → EAGER
  │
  ├─ Load good.test.js → describe('good suite') registered ✓
  ├─ Load bad.test.js  → throw Error("LOAD-TIME SIDE EFFECT...") ✗ CRASH
  │                       ^^^^ grep cannot prevent this
  │
  └─ meteortesting:mocha start() never reached
      └─ mochaInstance.grep("good suite")  ← never called
      └─ mochaInstance.run()               ← never called

The crash happens because of the eager mechanism used to discover and load files, and because bad.test.js explicitly throws an error at the root of the file. To save time on your end, I verified this across Meteor 3.x with and without the bundler. This happens regardless of whether we use Meteor 3.x, the Rspack bundler, or anything else. That is the expected behavior. There is no change there. Grep will never run because the error is thrown first at load time.

I took my time to show that, no matter which Meteor 3.x version or bundler you use, you get the same error output.

So MOCHA_GREP behaves the same in Meteor 3.0.4 and Meteor 3.4, with or without Rspack or testModule. It is true that with Rspack the thrown error does not block other tests from running, but I would consider that a gain anyway.

If we avoid throwing an error in the eagerly loaded bad.test.js removing these lines, I also tested that across the Meteor 3.x cases mentioned before.

Now three of them behave the same. MOCHA_GREP='good suite' runs the describe and it from good.test.js, and ignores the describes from bad.test.js. But in every case, regardless of Meteor 3.x version, bundler or testModule, it still eagerly loads the file. MOCHA_GREP works consistently across versions and bundlers based on the evidence shown. This has been the behavior all time. No regression yet found.


It’s still not working and quite frankly I’m starting to regret switching to rspack with such a fundamental feature not working properly.

Rspack adoption is a win and many of the community has expressed it already. You can’t opt out anyway. I would encourage everybody to migrate, and when they hit an issue, as has happened through the betas, 3.4.1 work, this forum post, github issues tracker and others, to dig into it, test it manually, understand it, and provide a fix. We are open to fixing issues and we care and are actively taking our time to attend each, with more or less reproduction evidences, as already shown.

However, in return, we ask for patience and proper explanations and reproductions. If you can get a reproduction that actually shows a regression of MOCHA_GREP between Meteor 3.4 with and without Rspack, please share it and we will take a look. Also, please review AI output carefully and challenge it with manual checks, because in this case the repository you provided does not show evidence of a regression after the checks already done. I’m not saying the issue does not exist, only that it needs to be properly reproduced. So far, every test case has shown that the issue does not appear as described here.

Hi @nachocodoner ,

appreciate the time you spent on trying to solve this. A couple of comments from my side for clarification:

  1. You’ve asked for a simple repo but it obviously can’t reproduce the complex architecture I have with multiple apps interacting with each other, several 3rd party systems etc.

To give you some idea, the backend system (which is what we’re most talking about here) has > 1,000 tests. I personally have never used grep before so I can’t comment on it if it worked or not. I usually mark the tests with .only which is much easier IMO. The tests are running against an empty MongoDb database, as they otherwise would remove our 2 Gb+ big database we use in DEV (old copy of PROD db).

However, for the on-prem app we’re using a totally different setup, everything runs inside a Docker image (in Colima), it starts the backend server, the on-prem machine, Redis, MongoDb. The backend and on-prem (which is another backend service) communicate work via Redis, specifically via a job queue (BullMQ). That goes back and forth. So a completely different setup, it’s also still using Babel :wink: We need to pass “MOCHA_GREP” as an ENV variable due to the different test setup.

Meaning to say, every single case is different. We’re also using a serverless repo which uses Jest for testing. We also use Playwright for our E2E tests. So yeah, it never gets boring and you need to constantly mentally jump from one topic to the next, which is why AI helps as a SME.

  1. My main “frustration” with rspack comes with the “meteor-desktop” project. It was so difficult to make it work from the old “meteor-desktop” which was under the meteor-community as rspack is quite different than babel. Still, rspack is having some teething issues when we just want to use a certain npm package and it doesn’t work. But as you said, one package at a time, we’re making good progress on eliminating such roadblocks. I see the benefit of faster build times, faster deploys, less memory used in PROD. That’s great and the best of all is that we’re modernizing MeteorJS piece by piece (finally). There’s nothing better and nothing more important for MeteorJS than this modernization projects!

So I’m sorry if the minimum repo wasn’t much helpful to narrow down where the problem comes from.

My statement about blaming rspack was wrong it seems and more coming from the other issues I had with rspack (like I wrote, it’s a bumpy love/hate relationship so far, you miss the steady Babel never gives any problems one LOL). Sorry for that.

And yes, grep is also failing in that Docker image with Babel. So it’s not rspack (but you already verified that).

I’m probably done with this problem. I have instructed the AI agent to use .only() instead, let’s hope it will remember it and not try for the 1000th times to use grep and endlessly try to fix something that seems to be broken for quite a while.

1 Like