Skip to content

Commit 11406ee

Browse files
committed
feat!: Remove testRunner middleware
The QUnit TestRunner (`testrunner.html`, `testrunner.css`, `TestRunner.js`) is now provided by the UI5 framework (`sap.ui.core`). All supported UI5 releases ship the relevant testrunner resources, so the dedicated middleware is no longer needed. JIRA: CPOUI5FOUNDATION-1158
1 parent 451be32 commit 11406ee

16 files changed

Lines changed: 261 additions & 1369 deletions

File tree

internal/documentation/docs/pages/Server.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ Please be aware of the following risks when using the server:
2828
::: info Removed Middleware
2929
The `serveThemes` middleware has been removed in UI5 CLI v5. Theme compilation is now handled by the `buildThemes` build task, which pre-compiles all theme CSS files. The resulting CSS files (including `library.css`, `library-RTL.css`, `library-parameters.json`, and CSS Variables resources) are served via the `serveResources` middleware, providing the same functionality with better performance through build-time compilation and caching.
3030

31-
Custom middleware previously referencing `serveThemes` via `beforeMiddleware` or `afterMiddleware` will continue to work with automatic remapping and a deprecation warning. See the [v5 migration guide](../updates/migrate-v5.md) for details.
31+
The `testRunner` middleware has also been removed in UI5 CLI v5. The QUnit TestRunner (`testrunner.html`, `testrunner.css`, `TestRunner.js`) is now provided by the UI5 framework (`sap.ui.core`) and is served via the `serveResources` middleware.
32+
33+
Custom middleware previously referencing `serveThemes` or `testRunner` via `beforeMiddleware` or `afterMiddleware` will continue to work with automatic remapping and a deprecation warning. See the [v5 migration guide](../updates/migrate-v5.md) for details.
3234
:::
3335

3436
All available standard middleware are listed below in the order of their execution.
@@ -43,7 +45,6 @@ A project can also add custom middleware to the server by using the [Custom Serv
4345
| `liveReloadClient` | See chapter [liveReload](#livereload) |
4446
| `discovery` | See chapter [discovery](#discovery) |
4547
| `serveResources` | See chapter [serveResources](#serveresources) |
46-
| `testRunner` | See chapter [testRunner](#testrunner) |
4748
| `versionInfo` | See chapter [versionInfo](#versioninfo) |
4849
| `nonReadRequests` | See chapter [nonReadRequests](#nonreadrequests) |
4950
| `serveIndex` | See chapter [serveIndex](#serveindex) |
@@ -105,9 +106,6 @@ The following file content transformations are executed:
105106
- Escaping non-ASCII characters in `.properties` translation files based on a project's [configuration](./Configuration.md#encoding-of-properties-files)
106107
- Enhancing the `manifest.json` with supported locales determined by available `.properties` [translation files](./Builder.md#generation-of-supported-locales)
107108

108-
### testRunner
109-
Serves a static version of the UI5 QUnit TestRunner at `/test-resources/sap/ui/qunit/testrunner.html`.
110-
111109
### versionInfo
112110
Generates and serves the version info file `/resources/sap-ui-version.json`, which is required for several framework functionalities.
113111

internal/documentation/docs/updates/migrate-v5.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Or update your global install via: `npm i --global @ui5/cli@next`
1919

2020
- **@ui5/server: Live Reload is enabled by default for `ui5 serve`**
2121

22+
- **@ui5/server: Standard middleware `serveThemes` and `testRunner` have been removed**
2223

2324
## Node.js and npm Version Support
2425

@@ -268,21 +269,23 @@ If your project uses a custom middleware that provides live reload functionality
268269
The following middleware has been removed from the [standard middlewares list](../pages/Server.md#standard-middleware):
269270

270271
* `serveThemes` — The `buildThemes` build task now handles theme compilation (LESS to CSS). Because server sessions now also perform builds, this task runs during a server start instead of on demand during runtime. The resulting CSS files are served by the `serveResources` middleware. This change improves performance through build-time compilation and caching while maintaining the same functionality.
272+
* `testRunner` — The UI5 QUnit TestRunner resources (`testrunner.html`, `testrunner.css`, `TestRunner.js`) are now provided by the UI5 framework (`sap.ui.core`) and served via the `serveResources` middleware. All supported UI5 releases ship the relevant testrunner resources, so the dedicated middleware is no longer needed.
271273

272274
**Backward Compatibility:**
273-
If your project or any custom middleware references a removed middleware via `beforeMiddleware` or `afterMiddleware`, UI5 CLI will automatically remap the reference to the nearest remaining middleware and log a deprecation warning. Your custom middleware will still be executed in the expected order.
275+
If your project or any custom middleware references a removed middleware via `beforeMiddleware` or `afterMiddleware`, UI5 CLI keeps a no-op placeholder in the middleware execution order at the original slot. The custom middleware is executed in the same position as before and a deprecation warning is logged.
274276

275277
**What Changed:**
276278
- Theme CSS files (`library.css`, `library-RTL.css`, etc.) are now **pre-built**
277-
- Files are served via `serveResources` instead of being compiled on demand
278-
- The same CSS files are available at the same URLs as before
279+
- Theme files are served via `serveResources` instead of being compiled on demand
280+
- TestRunner resources are served via `serveResources` from the UI5 framework instead of being shipped with UI5 CLI
279281

280282
**Recommended Action:**
281283
Update your `ui5.yaml` configuration to reference an existing middleware instead.
282284

283285
| Removed Middleware | Replacement Behavior | Recommended `afterMiddleware` |
284286
| ------------------ | -------------------- | ----------------------------- |
285-
| `serveThemes` | CSS files pre-built by `buildThemes` task and served via `serveResources` | `testRunner` |
287+
| `serveThemes` | CSS files pre-built by `buildThemes` task and served via `serveResources` | `serveResources` |
288+
| `testRunner` | TestRunner resources served via `serveResources` from the UI5 framework | `serveResources` |
286289

287290
## Learn More
288291

packages/server/REUSE.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,3 @@ precedence = "aggregate"
1616
SPDX-FileCopyrightText = ["2010 Sencha Inc.", "2011 LearnBoost", "2011 TJ Holowaychuk", "2014-2015 Douglas Christopher Wilson"]
1717
SPDX-License-Identifier = "MIT"
1818

19-
[[annotations]]
20-
path = "lib/middleware/testRunner/**"
21-
precedence = "aggregate"
22-
SPDX-FileCopyrightText = "2026 SAP SE or an SAP affiliate company and OpenUI5 contributors"
23-
SPDX-License-Identifier = "Apache-2.0"

packages/server/eslint.config.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,6 @@ import eslintCommonConfig from "../../eslint.common.config.js";
33

44
export default [
55
...eslintCommonConfig, // Load common ESLint config
6-
{
7-
// Add project-specific ESLint config rules here
8-
// in order to override common config
9-
ignores: [
10-
"lib/middleware/testRunner/",
11-
]
12-
},
136
{
147
// Live reload client script runs in the browser, not Node.js
158
files: ["lib/liveReload/client.js"],

packages/server/lib/middleware/MiddlewareManager.js

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,6 @@ import {getLogger} from "@ui5/logger";
44
const hasOwn = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
55
const log = getLogger("server:MiddlewareManager");
66

7-
/**
8-
* Mapping of removed standard middleware names to their predecessor and successor
9-
* in the original execution order. Used to remap custom middleware references
10-
* to the nearest remaining middleware when a removed middleware is referenced.
11-
*/
12-
const LEGACY_MIDDLEWARE_MAPPING = {
13-
serveThemes: {before: "testRunner", after: "versionInfo"}
14-
};
15-
167
/**
178
* @private
189
* @typedef {object} MiddlewareResources
@@ -49,6 +40,7 @@ class MiddlewareManager {
4940

5041
this.middleware = Object.create(null);
5142
this.middlewareExecutionOrder = [];
43+
this.legacyMiddlewarePlaceholders = new Set();
5244
this.middlewareUtil = new MiddlewareUtil({graph, project: rootProject});
5345
}
5446

@@ -64,10 +56,15 @@ class MiddlewareManager {
6456
await this.addStandardMiddleware();
6557
await this.addCustomMiddleware();
6658

67-
return this.middlewareExecutionOrder.map((name) => {
68-
const m = this.middleware[name];
69-
app.use(m.mountPath, m.middleware);
70-
});
59+
// Strip legacy placeholders for removed standard middlewares — they only existed
60+
// in middlewareExecutionOrder to preserve slots for custom beforeMiddleware /
61+
// afterMiddleware references and are not backed by an actual middleware.
62+
return this.middlewareExecutionOrder
63+
.filter((name) => !this.legacyMiddlewarePlaceholders.has(name))
64+
.map((name) => {
65+
const m = this.middleware[name];
66+
app.use(m.mountPath, m.middleware);
67+
});
7168
}
7269

7370
/**
@@ -86,7 +83,7 @@ class MiddlewareManager {
8683
customMiddleware, wrapperCallback, mountPath = "/",
8784
beforeMiddleware, afterMiddleware
8885
} = {}) {
89-
if (this.middleware[middlewareName]) {
86+
if (this.#isMiddlewareNameKnown(middlewareName)) {
9087
throw new Error(`A middleware with the name ${middlewareName} has already been added`);
9188
}
9289

@@ -107,8 +104,7 @@ class MiddlewareManager {
107104
}
108105

109106
if (beforeMiddleware || afterMiddleware) {
110-
let refMiddlewareName = beforeMiddleware || afterMiddleware;
111-
const originalRefMiddlewareName = refMiddlewareName; // Store original before any remapping
107+
const refMiddlewareName = beforeMiddleware || afterMiddleware;
112108
let refMiddlewareIdx = this.middlewareExecutionOrder.indexOf(refMiddlewareName);
113109

114110
if (refMiddlewareName === "connectUi5Proxy") {
@@ -119,33 +115,23 @@ class MiddlewareManager {
119115
`Please see the migration guide at https://ui5.github.io/cli/updates/migrate-v3/`);
120116
}
121117

122-
// Handle legacy middleware with graceful fallback
123-
const legacyMapping = LEGACY_MIDDLEWARE_MAPPING[refMiddlewareName];
124-
if (legacyMapping) {
125-
// Replace with the appropriate fallback based on reference type
126-
refMiddlewareName = afterMiddleware ? legacyMapping.before : legacyMapping.after;
127-
118+
// Warn when a removed standard middleware is referenced. The reference still resolves
119+
// because the removed middleware is kept as a placeholder in middlewareExecutionOrder
120+
// (see #addLegacyMiddlewarePlaceholder), preserving the original execution slot.
121+
if (this.legacyMiddlewarePlaceholders.has(refMiddlewareName)) {
128122
log.warn(
129-
`Standard middleware "${originalRefMiddlewareName}" has been removed. ` +
123+
`Standard middleware "${refMiddlewareName}" has been removed. ` +
130124
`Custom middleware "${middlewareName}" defined in project ` +
131-
`"${this.middlewareUtil.getProject()}" references it and ` +
132-
`is now placed ${afterMiddleware ? "after" : "before"} ` +
133-
`"${refMiddlewareName}" instead. ` +
125+
`"${this.middlewareUtil.getProject()}" still references it. ` +
126+
`The custom middleware will be executed in the slot the removed middleware ` +
127+
`originally occupied, but the reference should be updated. ` +
134128
`For details, see the migration guide at ` +
135129
`https://ui5.github.io/cli/next/updates/migrate-v5`);
136130
}
137131

138-
refMiddlewareIdx = this.middlewareExecutionOrder.indexOf(refMiddlewareName);
139-
140132
if (refMiddlewareIdx === -1) {
141-
// Provide clear error message, including remapping context if applicable
142-
const errorMsg = legacyMapping ?
143-
`Could not find fallback middleware "${refMiddlewareName}" ` +
144-
`(mapped from removed middleware "${originalRefMiddlewareName}"), ` +
145-
`referenced by custom middleware "${middlewareName}"` :
146-
`Could not find middleware ${refMiddlewareName}, referenced by custom ` +
147-
`middleware ${middlewareName}`;
148-
throw new Error(errorMsg);
133+
throw new Error(`Could not find middleware ${refMiddlewareName}, referenced by custom ` +
134+
`middleware ${middlewareName}`);
149135
}
150136
if (afterMiddleware) {
151137
// Insert after index of referenced middleware
@@ -272,7 +258,8 @@ class MiddlewareManager {
272258
});
273259
}
274260
});
275-
await this.addMiddleware("testRunner");
261+
this.#addLegacyMiddlewarePlaceholder("testRunner");
262+
this.#addLegacyMiddlewarePlaceholder("serveThemes");
276263
await this.addMiddleware("versionInfo", {
277264
mountPath: "/resources/sap-ui-version.json"
278265
});
@@ -327,11 +314,11 @@ class MiddlewareManager {
327314
}
328315

329316
let middlewareName = middlewareDef.name;
330-
if (this.middleware[middlewareName]) {
317+
if (this.#isMiddlewareNameKnown(middlewareName)) {
331318
// Middleware is already known
332319
// => add a suffix to allow for multiple configurations of the same middleware
333320
let suffixCounter = 0;
334-
while (this.middleware[middlewareName]) {
321+
while (this.#isMiddlewareNameKnown(middlewareName)) {
335322
suffixCounter++; // Start at 1
336323
middlewareName = `${middlewareDef.name}--${suffixCounter}`;
337324
}
@@ -363,6 +350,35 @@ class MiddlewareManager {
363350
});
364351
}
365352
}
353+
354+
/**
355+
* Inserts a no-op placeholder for a removed standard middleware into the execution order.
356+
* Placeholders preserve the original slot so custom middlewares referencing the removed
357+
* middleware via beforeMiddleware / afterMiddleware keep their intended position.
358+
* They are stripped in applyMiddleware() before mounting on the express app.
359+
*
360+
* @private
361+
* @param {string} middlewareName Name of the removed standard middleware
362+
*/
363+
#addLegacyMiddlewarePlaceholder(middlewareName) {
364+
this.legacyMiddlewarePlaceholders.add(middlewareName);
365+
this.middlewareExecutionOrder.push(middlewareName);
366+
}
367+
368+
/**
369+
* Returns whether the given middleware name is already known — either registered
370+
* in <code>this.middleware</code> or occupying a legacy placeholder slot.
371+
* Placeholder names are treated as known to avoid collisions in the execution order:
372+
* a custom middleware named e.g. "testRunner" would otherwise clash with the
373+
* placeholder inserted to preserve its original slot.
374+
*
375+
* @private
376+
* @param {string} middlewareName Middleware name to check
377+
* @returns {boolean}
378+
*/
379+
#isMiddlewareNameKnown(middlewareName) {
380+
return !!(this.middleware[middlewareName] || this.legacyMiddlewarePlaceholders.has(middlewareName));
381+
}
366382
}
367383

368384
export default MiddlewareManager;

packages/server/lib/middleware/middlewareRepository.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const middlewareInfos = {
77
serveIndex: {path: "./serveIndex.js"},
88
discovery: {path: "./discovery.js"},
99
versionInfo: {path: "./versionInfo.js"},
10-
testRunner: {path: "./testRunner.js"},
1110
nonReadRequests: {path: "./nonReadRequests.js"}
1211
};
1312

packages/server/lib/middleware/testRunner.js

Lines changed: 0 additions & 62 deletions
This file was deleted.

0 commit comments

Comments
 (0)