Skip to content

Commit 9643cd0

Browse files
authored
fix: move qunit config autostart out of require/define (#134)
In case of using QUnit.config.autostart to configure the globally available QUnit, it must be moved out from the sap.ui.require or sap.ui.define block so that it is applied synchronously when loading the module. If QUnit is required locally, then QUnit.config.autostart will not be moved. The move can also be supressed by using the configuration option noWrapQUnitConfigAutostart.
1 parent a8b271e commit 9643cd0

File tree

4 files changed

+98
-8
lines changed

4 files changed

+98
-8
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,11 +362,11 @@ void Promise.all([import("unit/controller/App.qunit")]).then(() => {
362362
will be converted to:
363363
364364
```js
365-
"sap.ui.require([], function () {
366-
"use strict";
365+
"use strict";
367366

367+
QUnit.config.autostart = false;
368+
"sap.ui.require([], function () {
368369
function __ui5_require_async(path) { /* ... */ }
369-
QUnit.config.autostart = false;
370370
void Promise.all([__ui5_require_async("unit/controller/App.qunit")]).then(() => {
371371
QUnit.start();
372372
});
@@ -375,6 +375,8 @@ will be converted to:
375375
376376
> :warning: Although `sap.ui.define` and `sap.ui.require` may appear similar from an API perspective, they have different behaviors. To understand these differences, please read the section titled "Using sap.ui.require instead of sap.ui.define on the top level" in the [Troubleshooting for Loading Modules](https://ui5.sap.com/#/topic/4363b3fe3561414ca1b030afc8cd30ce).
377377
378+
> :bulb: The plugin detects the global usage of `QUnit.config.autostart` and moves this out of the `sap.ui.require` or `sap.ui.define` block automatically to ensure that the config is applied sychronously when loading the module. The move can be supressed with the configuration option `noWrapQUnitConfigAutostart`. If `QUnit` is imported, e.g. `import QUnit from "qunit";` then this is detected and the autostart config is not moved as it must apply locally.
379+
378380
### Converting ES classes into Control.extend(..) syntax
379381
380382
By default, the plugin converts ES classes to `Control.extend(..)` syntax if the class extends from a class which has been imported.
@@ -795,6 +797,7 @@ In general, comments are preserved, but for each class property/method whose pos
795797
### Wrapping
796798

797799
- `noWrapBeforeImport` (Default: false) Does not wrap code before the first import (if there are imports).
800+
- `noWrapQUnitConfigAutostart` (Default: true) Does not wrap the `QUnit.config.autostart` in the `sap.ui.require` or `sap.ui.define` block.
798801

799802
### Class Conversion
800803

packages/plugin/__test__/__snapshots__/test.js.snap

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,9 +1647,40 @@ exports[`sap-ui-require othermodule-noannotation.js 1`] = `
16471647
`;
16481648
16491649
exports[`sap-ui-require testsuite-annotation.qunit.js 1`] = `
1650-
"sap.ui.require([], function () {
1650+
""use strict";
1651+
1652+
QUnit.config.autostart = false;
1653+
sap.ui.require([], function () {
1654+
function __ui5_require_async(path) {
1655+
return new Promise(function (resolve, reject) {
1656+
sap.ui.require([path], function (module) {
1657+
if (!(module && module.__esModule)) {
1658+
module = module === null || !(typeof module === "object" && path.endsWith("/library")) ? {
1659+
default: module
1660+
} : module;
1661+
Object.defineProperty(module, "__esModule", {
1662+
value: true
1663+
});
1664+
}
1665+
resolve(module);
1666+
}, function (err) {
1667+
reject(err);
1668+
});
1669+
});
1670+
}
1671+
void Promise.all([__ui5_require_async("unit/controller/App.qunit")]).then(() => {
1672+
QUnit.start();
1673+
});
1674+
});"
1675+
`;
1676+
1677+
exports[`sap-ui-require testsuite-annotation-with-qunit-import.qunit.js 1`] = `
1678+
"sap.ui.require(["qunit"], function (__QUnit) {
16511679
"use strict";
16521680
1681+
function _interopRequireDefault(obj) {
1682+
return obj && obj.__esModule && typeof obj.default !== "undefined" ? obj.default : obj;
1683+
}
16531684
function __ui5_require_async(path) {
16541685
return new Promise(function (resolve, reject) {
16551686
sap.ui.require([path], function (module) {
@@ -1667,6 +1698,7 @@ exports[`sap-ui-require testsuite-annotation.qunit.js 1`] = `
16671698
});
16681699
});
16691700
}
1701+
const QUnit = _interopRequireDefault(__QUnit);
16701702
QUnit.config.autostart = false;
16711703
void Promise.all([__ui5_require_async("unit/controller/App.qunit")]).then(() => {
16721704
QUnit.start();
@@ -1675,9 +1707,10 @@ exports[`sap-ui-require testsuite-annotation.qunit.js 1`] = `
16751707
`;
16761708
16771709
exports[`sap-ui-require testsuite-noannotation.qunit.js 1`] = `
1678-
"sap.ui.define([], function () {
1679-
"use strict";
1710+
""use strict";
16801711
1712+
QUnit.config.autostart = false;
1713+
sap.ui.define([], function () {
16811714
function __ui5_require_async(path) {
16821715
return new Promise(function (resolve, reject) {
16831716
sap.ui.require([path], function (module) {
@@ -1695,7 +1728,6 @@ exports[`sap-ui-require testsuite-noannotation.qunit.js 1`] = `
16951728
});
16961729
});
16971730
}
1698-
QUnit.config.autostart = false;
16991731
void Promise.all([__ui5_require_async("unit/controller/App.qunit")]).then(() => {
17001732
QUnit.start();
17011733
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* @sapUiRequire */
2+
import QUnit from "qunit";
3+
4+
// https://api.qunitjs.com/config/autostart/
5+
QUnit.config.autostart = false;
6+
7+
// import all your QUnit tests here
8+
void Promise.all([import("unit/controller/App.qunit")]).then(() => {
9+
QUnit.start();
10+
});

packages/plugin/src/modules/helpers/wrapper.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export function wrap(visitor, programNode, opts) {
8383
}
8484
}
8585

86+
let moveUseStrictIfNeeded = false;
8687
const preDefine = [...ignoredImports];
8788
// If the noWrapBeforeImport opt is set, split any code before the first import and afterwards into separate arrays.
8889
// This should be done before any interops or other vars are injected.
@@ -101,6 +102,25 @@ export function wrap(visitor, programNode, opts) {
101102
reachedFirstImport = true;
102103
}
103104
}
105+
moveUseStrictIfNeeded = true;
106+
body = newBody;
107+
}
108+
109+
// If the QUnit.config.autostart is found it needs to be moved to the top of the program
110+
if (
111+
opts.noWrapQUnitConfigAutostart === undefined ||
112+
opts.noWrapQUnitConfigAutostart
113+
) {
114+
const qunitConfigAutostart = findQUnitConfigAutostart(body, imports);
115+
if (qunitConfigAutostart) {
116+
preDefine.push(qunitConfigAutostart);
117+
body = body.filter((node) => node !== qunitConfigAutostart);
118+
moveUseStrictIfNeeded = true;
119+
}
120+
}
121+
122+
// if code has been moved to preDefine, we need to move the "use strict" directive
123+
if (moveUseStrictIfNeeded) {
104124
if (
105125
!opts.neverUseStrict &&
106126
preDefine.length &&
@@ -111,7 +131,6 @@ export function wrap(visitor, programNode, opts) {
111131
...(programNode.directives || []),
112132
];
113133
}
114-
body = newBody;
115134
}
116135

117136
if (injectDynamicImportHelper) {
@@ -220,6 +239,32 @@ function hasUseSapUiRequire(comments, body, remove) {
220239
});
221240
}
222241

242+
function findQUnitConfigAutostart(body, imports) {
243+
// if one imports QUnit, we don't need to move the QUnit.config.autostart
244+
// as the configuration should apply to the local QUnit module
245+
if (imports?.some((imp) => imp.name === "QUnit")) {
246+
return undefined;
247+
}
248+
// find the QUnit.config.autostart
249+
return body?.find((node) => {
250+
return (
251+
t.isExpressionStatement(node) &&
252+
t.isAssignmentExpression(node.expression) &&
253+
t.isMemberExpression(node.expression.left) &&
254+
t.isMemberExpression(node.expression.left.object) &&
255+
t.isIdentifier(node.expression.left.object.object) &&
256+
node.expression.left.object.object.name === "QUnit" &&
257+
t.isIdentifier(node.expression.left.object.property) &&
258+
node.expression.left.object.property.name === "config" &&
259+
t.isIdentifier(node.expression.left.property) &&
260+
node.expression.left.property.name === "autostart" &&
261+
node.expression.operator === "=" /* &&
262+
t.isBooleanLiteral(node.expression.right) &&
263+
node.expression.right.value === false */
264+
);
265+
});
266+
}
267+
223268
function generateDefineOrRequire(body, imports, exportGlobal, useRequire) {
224269
const defineOpts = {
225270
SOURCES: t.arrayExpression(imports.map((i) => t.stringLiteral(i.src))),

0 commit comments

Comments
 (0)