Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion package/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,14 @@ function clientTests() {
}

// Before Meteor calls the `start` function, app tests will be parsed and loaded by Mocha
function start() {
async function start() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The returned Promise from start() is silently discarded in packages/meteor/test_environment.js:59-61

if (typeof testDriverPackage.start === "function") {
  testDriverPackage.start(); // ← no await, no .catch()
}

Making start() async means it now returns a Promise. The Meteor framework does not await it. Any rejection inside start() becomes an unhandled Promise rejection (which in Node 18+ terminates the process with a cryptic error, not the actual stack trace).

I reccommend you to handle it in test_enviroment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to avoid the async/await snallbal, try to handle it like

function start() {
  new Promise(resolve => Meteor.startup(resolve))
    .then(() => { /* move current start() body here */ })
    .catch(err => {
      console.error('meteor-mocha: failed to start', err);
      process.exit(1);
    });
}

// Wait for all Meteor.startup() callbacks (including async ones) to complete.
// In Meteor 3.x, async startup callbacks and top-level await can cause the
// startup queue to still be draining when the test driver's start() is called.
// Adding a callback at the end of the queue ensures it runs after all prior
// callbacks have finished. See: https://github.com/Meteor-Community-Packages/meteor-mocha/issues/176
await new Promise(resolve => Meteor.startup(resolve));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if any previously-registered Meteor.startup() callback throws, the behavior depends on Meteor's internal error handling in the startup queue, but the await inside start() won't catch those errors. More importantly, if the await itself rejects for any reason, there's no error handling in start() to produce a meaningful message


const args = setArgs();
runnerOptions = args.runnerOptions;
coverageOptions = args.coverageOptions;
Expand Down
21 changes: 21 additions & 0 deletions tests/dummy_app/server/async-startup.tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* eslint-env mocha */
import { Meteor } from 'meteor/meteor';
import assert from 'assert';

// Simulate async startup work (e.g. ensuring MongoDB indices, initializing
// collections). Without the fix in server.js, mocha.run() can fire before
// this callback completes, causing the test below to fail.
let startupCompleted = false;

Meteor.startup(async () => {
await new Promise(resolve => setTimeout(resolve, 50));
startupCompleted = true;
});

describe('async Meteor.startup()', function () {
it('should complete before tests run', function () {
assert.strictEqual(startupCompleted, true,
'Async Meteor.startup() callback did not complete before tests ran. ' +
'See https://github.com/Meteor-Community-Packages/meteor-mocha/issues/176');
});
});
Loading