@ui5/webcomponents-base 2.10.0 → 2.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ import { getTheme } from "../../src/config/Theme.js";
7
7
  import { getAnimationMode } from "../../src/config/AnimationMode.js";
8
8
  import AnimationMode from "../../src/types/AnimationMode.js";
9
9
  import { getThemeRoot } from "../../src/config/ThemeRoot.js";
10
+ import applyTheme from "../../src/theming/applyTheme.js";
10
11
 
11
12
  describe("Some settings can be set via SAP UI URL params", () => {
12
13
  before(() => {
@@ -53,88 +54,409 @@ describe("Some settings can be set via SAP UI URL params", () => {
53
54
  });
54
55
 
55
56
  describe("Different themeRoot configurations", () => {
56
- it("Allowed theme root", () => {
57
- const searchParams = "sap-ui-theme=sap_horizon_hcb@https://example.com";
57
+ describe("Allowed theme root", () => {
58
+ before(() => {
59
+ const searchParams = "sap-ui-theme=sap_horizon_hcb@https://example.com";
58
60
 
59
- // All allowed theme roots need to be described inside the meta tag.
60
- cy.window()
61
- .then($el => {
62
- const metaTag = document.createElement("meta");
63
- metaTag.name = "sap-allowedThemeOrigins";
64
- metaTag.content = "https://example.com";
61
+ // All allowed theme roots need to be described inside the meta tag.
62
+ cy.window()
63
+ .then($el => {
64
+ const metaTag = document.createElement("meta");
65
+ metaTag.name = "sap-allowed-theme-origins";
66
+ metaTag.content = "https://example.com";
65
67
 
66
- $el.document.head.append(metaTag);
67
- })
68
+ $el.document.head.append(metaTag);
69
+ });
68
70
 
69
- cy.stub(internals, "search", () => {
70
- return searchParams;
71
+ cy.stub(internals, "search").callsFake(() => {
72
+ return searchParams;
73
+ });
74
+
75
+ cy.wrap({ resetConfiguration })
76
+ .invoke("resetConfiguration", true);
77
+
78
+ cy.wrap(internals)
79
+ .invoke("search")
80
+ .should("be.equal", searchParams);
81
+
82
+ cy.mount(<TestGeneric />);
71
83
  });
72
84
 
73
- cy.wrap({ resetConfiguration })
74
- .invoke("resetConfiguration", true);
85
+ afterEach(() => {
86
+ cy.window()
87
+ .then($el => {
88
+ const link = $el.document.head.querySelector("link[sap-ui-webcomponents-theme]");
89
+ link?.remove();
90
+ });
91
+ });
75
92
 
76
- cy.wrap(internals)
77
- .invoke("search")
78
- .should("be.equal", searchParams);
93
+ after(() => {
94
+ cy.window()
95
+ .then($el => {
96
+ const metaTag = $el.document.head.querySelector("[name='sap-allowed-theme-origins']");
97
+ metaTag?.remove();
98
+ });
99
+ });
79
100
 
80
- cy.mount(<TestGeneric />);
101
+ it("should return raw themeRoot from URL parameter", () => {
102
+ cy.wrap({ getThemeRoot })
103
+ .invoke("getThemeRoot")
104
+ .should("equal", "https://example.com");
105
+ });
81
106
 
82
- cy.wrap({ getThemeRoot })
83
- .invoke("getThemeRoot")
84
- .should("equal", "https://example.com/UI5/");
107
+ it("should extract theme without themeRoot part", () => {
108
+ cy.wrap({ getTheme })
109
+ .invoke("getTheme")
110
+ .should("equal", "sap_horizon_hcb");
111
+ });
112
+
113
+ it("should create link in DOM with validated URL", () => {
114
+ // Apply theme to trigger link creation
115
+ cy.wrap({ applyTheme, getTheme })
116
+ .invoke("getTheme")
117
+ .then(theme => {
118
+ return cy.wrap({ applyTheme }).invoke("applyTheme", theme);
119
+ });
120
+
121
+ cy.get("link[sap-ui-webcomponents-theme='sap_horizon_hcb']")
122
+ .should("exist")
123
+ .and("have.attr", "href")
124
+ .and("equal", "https://example.com/UI5/Base/baseLib/sap_horizon_hcb/css_variables.css");
125
+ });
126
+ });
127
+
128
+ describe("Unallowed theme root", () => {
129
+ before(() => {
130
+ const searchParams = "sap-ui-theme=sap_horizon_hcb@https://another-example.com";
131
+
132
+ cy.stub(internals, "search").callsFake(() => {
133
+ return searchParams;
134
+ });
135
+
136
+ cy.wrap({ resetConfiguration })
137
+ .invoke("resetConfiguration", true);
85
138
 
86
- // All allowed theme roots need to be described inside the meta tag.
87
- cy.window()
88
- .then($el => {
89
- const metaTag = $el.document.head.querySelector("[name='sap-allowedThemeOrigins']");
139
+ cy.wrap(internals)
140
+ .invoke("search")
141
+ .should("be.equal", searchParams);
142
+
143
+ cy.mount(<TestGeneric />);
144
+ });
145
+
146
+ afterEach(() => {
147
+ cy.window()
148
+ .then($el => {
149
+ const link = $el.document.head.querySelector("link[sap-ui-webcomponents-theme]");
150
+ link?.remove();
151
+ });
152
+ });
153
+
154
+ it("should return raw themeRoot even if not allowed", () => {
155
+ cy.wrap({ getThemeRoot })
156
+ .invoke("getThemeRoot")
157
+ .should("equal", "https://another-example.com");
158
+ });
90
159
 
91
- metaTag?.remove();
92
- })
160
+ it("should not create link in DOM due to validation failure", () => {
161
+ cy.get("link[sap-ui-webcomponents-theme='sap_horizon_hcb']")
162
+ .should("not.exist");
163
+ });
93
164
  });
94
165
 
95
- it("Unallowed theme root", () => {
96
- const searchParams = "sap-ui-theme=sap_horizon_hcb@https://another-example.com";
166
+ describe("Relative theme root", () => {
167
+ before(() => {
168
+ const searchParams = "sap-ui-theme=sap_horizon_hcb@./test";
97
169
 
98
- cy.stub(internals, "search", () => {
99
- return searchParams;
170
+ cy.window()
171
+ .then($el => {
172
+ const metaTag = document.createElement("meta");
173
+ metaTag.name = "sap-allowed-theme-origins";
174
+ metaTag.content = "*";
175
+ $el.document.head.append(metaTag);
176
+ });
177
+
178
+ cy.stub(internals, "search").callsFake(() => {
179
+ return searchParams;
180
+ });
181
+
182
+ cy.wrap({ resetConfiguration })
183
+ .invoke("resetConfiguration", true);
184
+
185
+ cy.wrap(internals)
186
+ .invoke("search")
187
+ .should("be.equal", searchParams);
188
+
189
+ cy.mount(<TestGeneric />);
100
190
  });
101
191
 
102
- cy.wrap({ resetConfiguration })
103
- .invoke("resetConfiguration", true);
192
+ afterEach(() => {
193
+ cy.window()
194
+ .then($el => {
195
+ const link = $el.document.head.querySelector("link[sap-ui-webcomponents-theme]");
196
+ link?.remove();
197
+ });
198
+ });
104
199
 
105
- cy.wrap(internals)
106
- .invoke("search")
107
- .should("be.equal", searchParams);
200
+ after(() => {
201
+ cy.window()
202
+ .then($el => {
203
+ const metaTag = $el.document.head.querySelector("[name='sap-allowed-theme-origins']");
204
+ metaTag?.remove();
205
+ });
206
+ });
108
207
 
109
- cy.mount(<TestGeneric />);
208
+ it("should return raw relative themeRoot", () => {
209
+ cy.wrap({ getThemeRoot })
210
+ .invoke("getThemeRoot")
211
+ .should("equal", "./test");
212
+ });
110
213
 
111
- cy.wrap({ getThemeRoot })
112
- .invoke("getThemeRoot")
113
- .should("equal", `${window.location.origin}/UI5/`);
214
+ it("should create link with resolved URL", () => {
215
+ // Apply theme to trigger link creation
216
+ cy.wrap({ applyTheme, getTheme })
217
+ .invoke("getTheme")
218
+ .then(theme => {
219
+ return cy.wrap({ applyTheme }).invoke("applyTheme", theme);
220
+ });
221
+
222
+ cy.get("link[sap-ui-webcomponents-theme='sap_horizon_hcb']")
223
+ .should("exist")
224
+ .and("have.attr", "href")
225
+ .then(href => {
226
+ return href.endsWith("/test/UI5/Base/baseLib/sap_horizon_hcb/css_variables.css");
227
+ })
228
+ .should("be.true");
229
+ });
114
230
  });
115
231
 
116
- it("Relative theme root", () => {
117
- const searchParams = "sap-ui-theme=sap_horizon_hcb@./test";
232
+ describe("Absolute path theme root", () => {
233
+ before(() => {
234
+ const searchParams = "sap-ui-theme=custom_theme@/absolute/path";
118
235
 
119
- cy.stub(internals, "search", () => {
120
- return searchParams;
236
+ cy.window()
237
+ .then($el => {
238
+ const metaTag = document.createElement("meta");
239
+ metaTag.name = "sap-allowed-theme-origins";
240
+ metaTag.content = "*";
241
+ $el.document.head.append(metaTag);
242
+ });
243
+
244
+ cy.stub(internals, "search").callsFake(() => {
245
+ return searchParams;
246
+ });
247
+
248
+ cy.wrap({ resetConfiguration })
249
+ .invoke("resetConfiguration", true);
250
+
251
+ cy.wrap(internals)
252
+ .invoke("search")
253
+ .should("be.equal", searchParams);
254
+
255
+ cy.mount(<TestGeneric />);
121
256
  });
122
257
 
123
- cy.wrap({ resetConfiguration })
124
- .invoke("resetConfiguration", true);
258
+ afterEach(() => {
259
+ cy.window()
260
+ .then($el => {
261
+ const link = $el.document.head.querySelector("link[sap-ui-webcomponents-theme]");
262
+ link?.remove();
263
+ });
264
+ });
125
265
 
126
- cy.wrap(internals)
127
- .invoke("search")
128
- .should("be.equal", searchParams);
266
+ after(() => {
267
+ cy.window()
268
+ .then($el => {
269
+ const metaTag = $el.document.head.querySelector("[name='sap-allowed-theme-origins']");
270
+ metaTag?.remove();
271
+ });
272
+ });
129
273
 
130
- cy.mount(<TestGeneric />);
274
+ it("should return raw absolute path", () => {
275
+ cy.wrap({ getThemeRoot })
276
+ .invoke("getThemeRoot")
277
+ .should("equal", "/absolute/path");
278
+ });
279
+
280
+ it("should create link with resolved URL", () => {
281
+ // Apply theme to trigger link creation
282
+ cy.wrap({ applyTheme, getTheme })
283
+ .invoke("getTheme")
284
+ .then(theme => {
285
+ return cy.wrap({ applyTheme }).invoke("applyTheme", theme);
286
+ });
287
+
288
+ cy.get("link[sap-ui-webcomponents-theme='custom_theme']")
289
+ .should("exist")
290
+ .and("have.attr", "href")
291
+ .then(href => {
292
+ return href.endsWith("/absolute/path/UI5/Base/baseLib/custom_theme/css_variables.css");
293
+ })
294
+ .should("be.true");
295
+ });
296
+ });
297
+
298
+ describe("ThemeRoot with trailing slash", () => {
299
+ before(() => {
300
+ const searchParams = "sap-ui-theme=custom_theme@https://cdn.example.com/themes/";
301
+
302
+ cy.window()
303
+ .then($el => {
304
+ const metaTag = document.createElement("meta");
305
+ metaTag.name = "sap-allowed-theme-origins";
306
+ metaTag.content = "https://cdn.example.com";
307
+ $el.document.head.append(metaTag);
308
+ });
309
+
310
+ cy.stub(internals, "search").callsFake(() => {
311
+ return searchParams;
312
+ });
313
+
314
+ cy.wrap({ resetConfiguration })
315
+ .invoke("resetConfiguration", true);
316
+
317
+ cy.wrap(internals)
318
+ .invoke("search")
319
+ .should("be.equal", searchParams);
320
+
321
+ cy.mount(<TestGeneric />);
322
+ });
323
+
324
+ afterEach(() => {
325
+ cy.window()
326
+ .then($el => {
327
+ const link = $el.document.head.querySelector("link[sap-ui-webcomponents-theme]");
328
+ link?.remove();
329
+ });
330
+ });
331
+
332
+ after(() => {
333
+ cy.window()
334
+ .then($el => {
335
+ const metaTag = $el.document.head.querySelector("[name='sap-allowed-theme-origins']");
336
+ metaTag?.remove();
337
+ });
338
+ });
339
+
340
+ it("should return raw themeRoot with trailing slash", () => {
341
+ cy.wrap({ getThemeRoot })
342
+ .invoke("getThemeRoot")
343
+ .should("equal", "https://cdn.example.com/themes/");
344
+ });
345
+
346
+ it("should create link normalizing trailing slash", () => {
347
+ // Apply theme to trigger link creation
348
+ cy.wrap({ applyTheme, getTheme })
349
+ .invoke("getTheme")
350
+ .then(theme => {
351
+ return cy.wrap({ applyTheme }).invoke("applyTheme", theme);
352
+ });
353
+
354
+ cy.get("link[sap-ui-webcomponents-theme='custom_theme']")
355
+ .should("exist")
356
+ .and("have.attr", "href")
357
+ .and("equal", "https://cdn.example.com/themes/UI5/Base/baseLib/custom_theme/css_variables.css");
358
+ });
359
+ });
360
+
361
+ describe("Invalid URL format", () => {
362
+ before(() => {
363
+ const searchParams = "sap-ui-theme=custom_theme@not-a-valid-url";
131
364
 
132
- cy.wrap({ getThemeRoot })
133
- .invoke("getThemeRoot")
134
- .then(themeRoot => {
135
- return themeRoot?.endsWith("/test/UI5/");
136
- })
137
- .should("be.true");
365
+ cy.stub(internals, "search").callsFake(() => {
366
+ return searchParams;
367
+ });
368
+
369
+ cy.wrap({ resetConfiguration })
370
+ .invoke("resetConfiguration", true);
371
+
372
+ cy.wrap(internals)
373
+ .invoke("search")
374
+ .should("be.equal", searchParams);
375
+
376
+ cy.mount(<TestGeneric />);
377
+ });
378
+
379
+ afterEach(() => {
380
+ cy.window()
381
+ .then($el => {
382
+ const link = $el.document.head.querySelector("link[sap-ui-webcomponents-theme]");
383
+ link?.remove();
384
+ });
385
+ });
386
+
387
+ it("should return raw invalid URL value", () => {
388
+ cy.wrap({ getThemeRoot })
389
+ .invoke("getThemeRoot")
390
+ .should("equal", "not-a-valid-url");
391
+ });
392
+
393
+ it("should not create link due to invalid URL", () => {
394
+ cy.get("link[sap-ui-webcomponents-theme='custom_theme']")
395
+ .should("not.exist");
396
+ });
397
+ });
398
+
399
+ describe("Legacy meta tag name support", () => {
400
+ before(() => {
401
+ const searchParams = "sap-ui-theme=custom_theme@https://legacy.example.com";
402
+
403
+ cy.window()
404
+ .then($el => {
405
+ const metaTag = document.createElement("meta");
406
+ metaTag.name = "sap-allowedThemeOrigins"; // Old camelCase format
407
+ metaTag.content = "https://legacy.example.com";
408
+ $el.document.head.append(metaTag);
409
+ });
410
+
411
+ cy.stub(internals, "search").callsFake(() => {
412
+ return searchParams;
413
+ });
414
+
415
+ cy.wrap({ resetConfiguration })
416
+ .invoke("resetConfiguration", true);
417
+
418
+ cy.wrap(internals)
419
+ .invoke("search")
420
+ .should("be.equal", searchParams);
421
+
422
+ cy.mount(<TestGeneric />);
423
+ });
424
+
425
+ afterEach(() => {
426
+ cy.window()
427
+ .then($el => {
428
+ const link = $el.document.head.querySelector("link[sap-ui-webcomponents-theme]");
429
+ link?.remove();
430
+ });
431
+ });
432
+
433
+ after(() => {
434
+ cy.window()
435
+ .then($el => {
436
+ const metaTag = $el.document.head.querySelector("[name='sap-allowedThemeOrigins']");
437
+ metaTag?.remove();
438
+ });
439
+ });
440
+
441
+ it("should return raw themeRoot", () => {
442
+ cy.wrap({ getThemeRoot })
443
+ .invoke("getThemeRoot")
444
+ .should("equal", "https://legacy.example.com");
445
+ });
446
+
447
+ it("should support legacy sap-allowedThemeOrigins meta tag", () => {
448
+ // Apply theme to trigger link creation
449
+ cy.wrap({ applyTheme, getTheme })
450
+ .invoke("getTheme")
451
+ .then(theme => {
452
+ return cy.wrap({ applyTheme }).invoke("applyTheme", theme);
453
+ });
454
+
455
+ cy.get("link[sap-ui-webcomponents-theme='custom_theme']")
456
+ .should("exist")
457
+ .and("have.attr", "href")
458
+ .and("equal", "https://legacy.example.com/UI5/Base/baseLib/custom_theme/css_variables.css");
459
+ });
138
460
  });
139
461
  });
140
462
 
@@ -0,0 +1,180 @@
1
+ import { internals } from "../../src/Location.js";
2
+ import TestGeneric from "../../test/test-elements/Generic.js";
3
+ import { resetConfiguration } from "../../src/InitialConfiguration.js";
4
+ import { getTheme } from "../../src/config/Theme.js";
5
+ import { getThemeRoot } from "../../src/config/ThemeRoot.js";
6
+ import applyTheme from "../../src/theming/applyTheme.js";
7
+
8
+ const THEME = "sap_horizon";
9
+
10
+ const addMetaTag = (content: string) => {
11
+ cy.window().then($win => {
12
+ const metaTag = $win.document.createElement("meta");
13
+ metaTag.name = "sap-allowed-theme-origins";
14
+ metaTag.content = content;
15
+ $win.document.head.append(metaTag);
16
+ });
17
+ };
18
+
19
+ const removeMetaTag = () => {
20
+ cy.window().then($win => {
21
+ $win.document.head.querySelector("[name='sap-allowed-theme-origins']")?.remove();
22
+ });
23
+ };
24
+
25
+ const removeThemeLink = () => {
26
+ cy.window().then($win => {
27
+ $win.document.head.querySelector("link[sap-ui-webcomponents-theme]")?.remove();
28
+ });
29
+ };
30
+
31
+ const applyAndCheckLink = (theme: string) => {
32
+ cy.wrap({ applyTheme, getTheme })
33
+ .invoke("getTheme")
34
+ .then(() => {
35
+ return cy.wrap({ applyTheme }).invoke("applyTheme", theme);
36
+ });
37
+ };
38
+
39
+ // ─── Without meta tag ────────────────────────────────────────────────────────
40
+
41
+ describe("ThemeRoot via URL param — without meta tag", () => {
42
+ afterEach(() => {
43
+ removeThemeLink();
44
+ });
45
+
46
+ [
47
+ { label: "absolute URL (different origin)", themeRoot: "http://example2.com/themes/", blocked: true },
48
+ { label: "absolute URL (different protocol)", themeRoot: "https://example.com/themes/", blocked: true },
49
+ { label: "absolute URL (different port)", themeRoot: "http://example:9090.com/themes/", blocked: true },
50
+ { label: "absolute URL (same host, no meta tag)", themeRoot: "http://example.com/themes/", blocked: true },
51
+ { label: "protocol-relative (different origin)", themeRoot: "//example2.com/themes/", blocked: true },
52
+ { label: "protocol-relative (different port)", themeRoot: "//example:9090.com/themes/", blocked: true },
53
+ { label: "protocol-relative (same host, no meta tag)", themeRoot: "//example.com/themes/", blocked: true },
54
+ { label: "root-relative", themeRoot: "/themes/", blocked: false },
55
+ { label: "relative (current dir)", themeRoot: "./themes/", blocked: false },
56
+ { label: "relative (parent dir)", themeRoot: "../themes/", blocked: false },
57
+ ].forEach(({ label, themeRoot, blocked }) => {
58
+ describe(`${label}: ${themeRoot}`, () => {
59
+ before(() => {
60
+ cy.stub(internals, "search").callsFake(() => {
61
+ return `sap-ui-theme=${THEME}@${themeRoot}`;
62
+ });
63
+
64
+ cy.wrap({ resetConfiguration }).invoke("resetConfiguration", true);
65
+ cy.mount(<TestGeneric />);
66
+ });
67
+
68
+ it(blocked ? "should not create a link element" : "should create a link element (same-origin, no meta tag needed)", () => {
69
+ applyAndCheckLink(THEME);
70
+ if (blocked) {
71
+ cy.get(`link[sap-ui-webcomponents-theme='${THEME}']`).should("not.exist");
72
+ } else {
73
+ cy.get(`link[sap-ui-webcomponents-theme='${THEME}']`)
74
+ .should("exist")
75
+ .and("have.attr", "href")
76
+ .and("include", `UI5/Base/baseLib/${THEME}/css_variables.css`);
77
+ }
78
+ });
79
+ });
80
+ });
81
+ });
82
+
83
+ // ─── With meta tag ────────────────────────────────────────────────────────────
84
+
85
+ describe("ThemeRoot via URL param — with meta tag (allowed: http://example.com)", () => {
86
+ afterEach(() => {
87
+ removeThemeLink();
88
+ });
89
+
90
+ after(() => {
91
+ removeMetaTag();
92
+ });
93
+
94
+ before(() => {
95
+ addMetaTag("http://example.com");
96
+ });
97
+
98
+ const blocked = [
99
+ { label: "absolute URL (different origin)", themeRoot: "http://example2.com/themes/" },
100
+ { label: "absolute URL (different protocol)", themeRoot: "https://example.com/themes/" },
101
+ { label: "absolute URL (different port)", themeRoot: "http://example:9090.com/themes/" },
102
+ { label: "protocol-relative (different origin)", themeRoot: "//example2.com/themes/" },
103
+ { label: "protocol-relative (different port)", themeRoot: "//example:9090.com/themes/" },
104
+ ];
105
+
106
+ const allowed = [
107
+ {
108
+ label: "absolute URL (matches allowed origin)",
109
+ themeRoot: "http://example.com/themes/",
110
+ expectedHref: "http://example.com/themes/UI5/Base/baseLib/sap_horizon/css_variables.css",
111
+ },
112
+ {
113
+ label: "protocol-relative (resolves to allowed origin)",
114
+ themeRoot: "//example.com/themes/",
115
+ expectedHref: "http://example.com/themes/UI5/Base/baseLib/sap_horizon/css_variables.css",
116
+ },
117
+ ];
118
+
119
+ const sameOriginAllowed = [
120
+ { label: "root-relative", themeRoot: "/themes/" },
121
+ { label: "relative (current dir)", themeRoot: "./themes/" },
122
+ { label: "relative (parent dir)", themeRoot: "../themes/" },
123
+ ];
124
+
125
+ blocked.forEach(({ label, themeRoot }) => {
126
+ describe(`blocked — ${label}: ${themeRoot}`, () => {
127
+ before(() => {
128
+ cy.stub(internals, "search").callsFake(() => {
129
+ return `sap-ui-theme=${THEME}@${themeRoot}`;
130
+ });
131
+ cy.wrap({ resetConfiguration }).invoke("resetConfiguration", true);
132
+ cy.mount(<TestGeneric />);
133
+ });
134
+
135
+ it("should not create a link element", () => {
136
+ applyAndCheckLink(THEME);
137
+ cy.get(`link[sap-ui-webcomponents-theme='${THEME}']`).should("not.exist");
138
+ });
139
+ });
140
+ });
141
+
142
+ allowed.forEach(({ label, themeRoot, expectedHref }) => {
143
+ describe(`allowed — ${label}: ${themeRoot}`, () => {
144
+ before(() => {
145
+ cy.stub(internals, "search").callsFake(() => {
146
+ return `sap-ui-theme=${THEME}@${themeRoot}`;
147
+ });
148
+ cy.wrap({ resetConfiguration }).invoke("resetConfiguration", true);
149
+ cy.mount(<TestGeneric />);
150
+ });
151
+
152
+ it("should create a link element with correct href", () => {
153
+ applyAndCheckLink(THEME);
154
+ cy.get(`link[sap-ui-webcomponents-theme='${THEME}']`)
155
+ .should("exist")
156
+ .and("have.attr", "href", expectedHref);
157
+ });
158
+ });
159
+ });
160
+
161
+ sameOriginAllowed.forEach(({ label, themeRoot }) => {
162
+ describe(`allowed (same-origin) — ${label}: ${themeRoot}`, () => {
163
+ before(() => {
164
+ cy.stub(internals, "search").callsFake(() => {
165
+ return `sap-ui-theme=${THEME}@${themeRoot}`;
166
+ });
167
+ cy.wrap({ resetConfiguration }).invoke("resetConfiguration", true);
168
+ cy.mount(<TestGeneric />);
169
+ });
170
+
171
+ it("should create a link element", () => {
172
+ applyAndCheckLink(THEME);
173
+ cy.get(`link[sap-ui-webcomponents-theme='${THEME}']`)
174
+ .should("exist")
175
+ .and("have.attr", "href")
176
+ .and("include", `UI5/Base/baseLib/${THEME}/css_variables.css`);
177
+ });
178
+ });
179
+ });
180
+ });