Skip to content

Commit

Permalink
Core: Always define globalThis.QUnit
Browse files Browse the repository at this point in the history
== Background ==

In QUnit 2.x, we always set the QUnit global in browser contexts, but
outside browsers, we only set the global if CJS/AMD wasn't detected.
Except in in the QUnit CLI we still set the global anyway, because
that's how most QUnit tests are written, incliuding for Node.js. So
really the only case where the QUnit global is missing is:

* AMD context, where QUnit would be a local argument name in the
  define/require() callback in each file.
* Custom test runners that 1) run in Node.js, and 2) require qunit
  directly and 3) don't also export it as a global.

I'm aware a growing proportion of users import 'qunit' directly in
each test file, to aid TypeScript for example. That's a great way
to avoid needing to rely on globals. But, it's not a requirement,
and is not always an option, especially in a browser context with
a simple no-build-step project.

== Why ==

1. Improve portability between test runners.

Make it QUnit's responsiblity to define this reliably, as indeed
it almost always already does, instead of requiring specific test
runners to patch this up on their end.

Esp in light of Karma deprecation, and emergence of more general
purpose TAP runners, I'd like to remove this as factor that might
make/break QUnit support in those.

2. Prepare for native ESM build.

We don't natively support ESM exports yet, but once we do this will
become a problem. To prevent split-brain problems around test registry
and other state, standardise internally on whichever globalThis.QUnit
was defined first, and then reliably export that to any importers,
regardless of import method.

Ref #1551.
  • Loading branch information
Krinkle committed Jun 18, 2024
1 parent 57c2dbc commit aa5b21a
Showing 1 changed file with 32 additions and 23 deletions.
55 changes: 32 additions & 23 deletions src/export.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
/* global module, exports, define */
import { window, document, globalThis } from './globals';

/**
* Available exports:
*
* globalThis:
* - browser (globalThis === window)
* - Web Worker (globalThis === self)
* - Node.js
* - SpiderMonkey (mozjs)
* - Rhino (since 7.14)
* - any other embedded JS engine
*
* CommonJS module.exports (commonjs2):
* - Node.js
*
* CommonJS exports (commonjs, https://wiki.commonjs.org/wiki/Modules):
* - Rhino
*
* AMD (RequireJS):
* - Browser
*/
export default function exportQUnit (QUnit) {
let exportedModule = false;

if (window && document) {
// QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
if (window.QUnit && window.QUnit.version) {
throw new Error('QUnit has already been defined.');
}

window.QUnit = QUnit;

exportedModule = true;
// If there is a placeholder QUnit global for preconfiguration,
// we must replace it with the real one. Such placeholder can be recognised
// by not having QUnit.version (it should only contain a QUnit.config property).
//
// If a real QUnit global is already defined, then replace our reference with that one.
// Since QUnit 3.0, we no longer throw an error if QUnit is loaded twice.
// This enables supporting mixed use of ESM import and CJS require() in a project,
// in a way that avoids split-brain problems for QUnit state.
if (globalThis.QUnit && globalThis.QUnit.version) {
QUnit = globalThis.QUnit;
} else {
globalThis.QUnit = QUnit;
}

// For Node.js
Expand All @@ -21,15 +42,11 @@ export default function exportQUnit (QUnit) {

// For consistency with CommonJS environments' exports
module.exports.QUnit = QUnit;

exportedModule = true;
}

// For CommonJS with exports, but without module.exports, like Rhino
if (typeof exports !== 'undefined' && exports) {
exports.QUnit = QUnit;

exportedModule = true;
}

// For AMD
Expand All @@ -38,13 +55,5 @@ export default function exportQUnit (QUnit) {
return QUnit;
});
QUnit.config.autostart = false;

exportedModule = true;
}

// For other environments, including Web Workers (globalThis === self),
// SpiderMonkey (mozjs), and other embedded JavaScript engines
if (!exportedModule) {
globalThis.QUnit = QUnit;
}
}

0 comments on commit aa5b21a

Please sign in to comment.