@jskit-ai/shell-web 0.1.152 → 0.1.154

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.
@@ -1,110 +1,23 @@
1
1
  import assert from "node:assert/strict";
2
- import { spawn } from "node:child_process";
3
- import net from "node:net";
4
2
  import path from "node:path";
5
3
  import process from "node:process";
6
4
  import test from "node:test";
7
5
  import { fileURLToPath } from "node:url";
8
6
  import { chromium, expect } from "@playwright/test";
7
+ import {
8
+ createChromiumLaunchOptions,
9
+ startViteFixture,
10
+ stopProcess
11
+ } from "../../../tooling/testUtils/browserFixture.mjs";
9
12
  import {
10
13
  DEFAULT_VIEWPORTS,
11
14
  runAdaptiveShellSmokeCase
12
15
  } from "../src/test/adaptiveShellSmoke.js";
13
16
 
14
17
  const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
15
- const REPOSITORY_ROOT = path.resolve(TEST_DIRECTORY, "../../..");
16
18
  const FIXTURE_ROOT = path.resolve(TEST_DIRECTORY, "../fixtures/adaptive-shell");
17
- const VITE_CLI = path.join(REPOSITORY_ROOT, "node_modules", "vite", "bin", "vite.js");
18
19
  const RUN_BROWSER_TEST = process.env.JSKIT_SHELL_WEB_BROWSER_INTEGRATION === "1";
19
20
 
20
- function startVite(port) {
21
- const child = spawn(process.execPath, [
22
- VITE_CLI,
23
- "--config",
24
- path.join(FIXTURE_ROOT, "vite.config.mjs"),
25
- "--port",
26
- String(port),
27
- "--clearScreen",
28
- "false"
29
- ], {
30
- cwd: FIXTURE_ROOT,
31
- stdio: ["ignore", "pipe", "pipe"],
32
- env: {
33
- ...process.env,
34
- NO_COLOR: "1",
35
- FORCE_COLOR: "0"
36
- }
37
- });
38
- let output = "";
39
- child.stdout.on("data", collectOutput);
40
- child.stderr.on("data", collectOutput);
41
-
42
- function collectOutput(chunk) {
43
- output += chunk.toString("utf8");
44
- }
45
-
46
- async function waitUntilReady() {
47
- await new Promise((resolve, reject) => {
48
- const startedAt = Date.now();
49
- const timer = setInterval(checkReadiness, 50);
50
-
51
- function checkReadiness() {
52
- if (new RegExp(`http://127\\.0\\.0\\.1:${port}/`, "u").test(output)) {
53
- clearInterval(timer);
54
- resolve();
55
- return;
56
- }
57
- if (child.exitCode !== null) {
58
- clearInterval(timer);
59
- reject(new Error(`Vite exited before becoming ready.\n${output}`));
60
- return;
61
- }
62
- if (Date.now() - startedAt >= 30_000) {
63
- clearInterval(timer);
64
- reject(new Error(`Timed out waiting for Vite.\n${output}`));
65
- }
66
- }
67
- });
68
- }
69
-
70
- return Object.freeze({ child, waitUntilReady });
71
- }
72
-
73
- async function stopVite(runtime) {
74
- const child = runtime?.child;
75
- if (!child || child.exitCode !== null || child.signalCode !== null) {
76
- return;
77
- }
78
- await new Promise((resolve) => {
79
- const forceTimer = setTimeout(forceStop, 5_000);
80
- child.once("exit", finishStop);
81
- child.kill("SIGTERM");
82
-
83
- function forceStop() {
84
- child.kill("SIGKILL");
85
- }
86
-
87
- function finishStop() {
88
- clearTimeout(forceTimer);
89
- resolve();
90
- }
91
- });
92
- }
93
-
94
- async function reservePort() {
95
- const server = net.createServer();
96
- await new Promise((resolve, reject) => {
97
- server.once("error", reject);
98
- server.listen(0, "127.0.0.1", resolve);
99
- });
100
- const address = server.address();
101
- assert.ok(address && typeof address === "object");
102
- await new Promise((resolve, reject) => {
103
- server.close((error) => error ? reject(error) : resolve());
104
- });
105
- return address.port;
106
- }
107
-
108
21
  function parseRgb(color) {
109
22
  const channels = String(color || "").match(/[\d.]+/gu)?.map(Number) || [];
110
23
  return {
@@ -260,25 +173,22 @@ test("shell-web adaptive navigation passes package-owned browser contracts", {
260
173
  : "set JSKIT_SHELL_WEB_BROWSER_INTEGRATION=1 to run shell-web browser integration",
261
174
  timeout: 180_000
262
175
  }, async () => {
263
- const port = await reservePort();
264
- const vite = startVite(port);
176
+ const vite = await startViteFixture({ fixtureRoot: FIXTURE_ROOT });
265
177
  let browser = null;
266
178
 
267
179
  try {
268
- await vite.waitUntilReady();
269
- const executablePath = String(process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || "").trim();
270
- browser = await chromium.launch({
271
- headless: true,
272
- ...(executablePath ? { executablePath } : {})
273
- });
180
+ browser = await chromium.launch(createChromiumLaunchOptions());
274
181
  const context = await browser.newContext({
275
- baseURL: `http://127.0.0.1:${port}`,
182
+ baseURL: vite.baseURL,
276
183
  locale: "en-US"
277
184
  });
278
185
 
279
186
  for (const viewport of DEFAULT_VIEWPORTS) {
280
187
  const page = await context.newPage();
281
188
  await runAdaptiveShellSmokeCase({ page, expect, viewport });
189
+ if (viewport.name !== "compact") {
190
+ await expect(page).toHaveURL(/\/home\/settings\/general$/u);
191
+ }
282
192
  await page.close();
283
193
  }
284
194
 
@@ -324,6 +234,6 @@ test("shell-web adaptive navigation passes package-owned browser contracts", {
324
234
  await context.close();
325
235
  } finally {
326
236
  await browser?.close();
327
- await stopVite(vite);
237
+ await stopProcess(vite);
328
238
  }
329
239
  });
@@ -3,14 +3,16 @@ import path from "node:path";
3
3
  import test from "node:test";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { fileURLToPath } from "node:url";
6
- import descriptor from "../package.descriptor.mjs";
6
+ import packageJson from "../package.json" with { type: "json" };
7
+
8
+ const packageMetadata = packageJson.jskit;
7
9
 
8
10
  const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
9
11
  const PACKAGE_DIR = path.resolve(TEST_DIRECTORY, "..");
10
12
  const CREATE_APP_TEMPLATE_DIR = path.resolve(PACKAGE_DIR, "..", "..", "tooling", "create-app", "templates", "minimal-shell");
11
13
 
12
14
  function findFileMutation(id) {
13
- const files = descriptor?.mutations?.files;
15
+ const files = packageMetadata?.mutations?.files;
14
16
  return Array.isArray(files)
15
17
  ? files.find((entry) => String(entry?.id || "").trim() === id) || null
16
18
  : null;
@@ -3,7 +3,9 @@ import path from "node:path";
3
3
  import test from "node:test";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { fileURLToPath } from "node:url";
6
- import descriptor from "../package.descriptor.mjs";
6
+ import packageJson from "../package.json" with { type: "json" };
7
+
8
+ const packageMetadata = packageJson.jskit;
7
9
  import {
8
10
  LOCAL_LINK_ITEM_COMPONENT_DEFINITIONS,
9
11
  findLocalLinkItemDefinition,
@@ -14,14 +16,14 @@ const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
14
16
  const PACKAGE_DIR = path.resolve(TEST_DIRECTORY, "..");
15
17
 
16
18
  function findFileMutation(id) {
17
- const files = descriptor?.mutations?.files;
19
+ const files = packageMetadata?.mutations?.files;
18
20
  return Array.isArray(files)
19
21
  ? files.find((entry) => String(entry?.id || "").trim() === id) || null
20
22
  : null;
21
23
  }
22
24
 
23
25
  function findSourceMutation(id) {
24
- const sourceMutations = descriptor?.mutations?.source;
26
+ const sourceMutations = packageMetadata?.mutations?.source;
25
27
  return Array.isArray(sourceMutations)
26
28
  ? sourceMutations.find((entry) => String(entry?.id || "").trim() === id) || null
27
29
  : null;
@@ -3,16 +3,19 @@ import { readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import test from "node:test";
5
5
  import { fileURLToPath } from "node:url";
6
+ import {
7
+ isRequestedOrDescendantLocation
8
+ } from "../src/test/adaptiveShellSmoke.js";
6
9
 
7
10
  const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
11
 
9
12
  test("shell-web owns the exact Playwright dependency used by its generated smoke", async () => {
10
- const descriptor = (await import(`${path.join(PACKAGE_ROOT, "package.descriptor.mjs")}?test=${Date.now()}`)).default;
13
+ const packageJson = JSON.parse(await readFile(path.join(PACKAGE_ROOT, "package.json"), "utf8"));
11
14
  const rootPackage = JSON.parse(await readFile(path.resolve(PACKAGE_ROOT, "../../package.json"), "utf8"));
12
15
 
13
- assert.equal(descriptor.mutations.dependencies.dev["@playwright/test"], "1.61.1");
16
+ assert.equal(packageJson.jskit.mutations.dependencies.dev["@playwright/test"], "1.61.1");
14
17
  assert.equal(
15
- descriptor.mutations.dependencies.dev["@playwright/test"],
18
+ packageJson.jskit.mutations.dependencies.dev["@playwright/test"],
16
19
  rootPackage.devDependencies["@playwright/test"]
17
20
  );
18
21
  });
@@ -25,6 +28,39 @@ test("adaptive shell smoke navigates through Playwright baseURL", async () => {
25
28
  assert.doesNotMatch(source, /http:\/\/127\.0\.0\.1/u);
26
29
  });
27
30
 
31
+ test("adaptive shell navigation accepts canonical descendants without accepting prefix siblings", () => {
32
+ const requested = new URL("https://example.test/home/settings?tab=profile");
33
+
34
+ assert.equal(
35
+ isRequestedOrDescendantLocation(
36
+ new URL("https://example.test/home/settings?tab=profile"),
37
+ requested
38
+ ),
39
+ true
40
+ );
41
+ assert.equal(
42
+ isRequestedOrDescendantLocation(
43
+ new URL("https://example.test/home/settings/general?tab=profile"),
44
+ requested
45
+ ),
46
+ true
47
+ );
48
+ assert.equal(
49
+ isRequestedOrDescendantLocation(
50
+ new URL("https://example.test/home/settings-other?tab=profile"),
51
+ requested
52
+ ),
53
+ false
54
+ );
55
+ assert.equal(
56
+ isRequestedOrDescendantLocation(
57
+ new URL("https://example.test/home/settings/general?tab=security"),
58
+ requested
59
+ ),
60
+ false
61
+ );
62
+ });
63
+
28
64
  test("adaptive shell smoke follows rendered layout state and waits for drawer transitions", async () => {
29
65
  const source = await readFile(path.join(PACKAGE_ROOT, "src/test/adaptiveShellSmoke.js"), "utf8");
30
66
 
@@ -4,14 +4,16 @@ import test from "node:test";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { assertGeneratedUiSourceContract } from "@jskit-ai/kernel/shared/support/generatedUiContract";
7
- import descriptor from "../package.descriptor.mjs";
7
+ import packageJson from "../package.json" with { type: "json" };
8
+
9
+ const packageMetadata = packageJson.jskit;
8
10
  import { resolveShellRouteTransitionKey } from "../src/client/support/routeTransitionKey.js";
9
11
 
10
12
  const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
11
13
  const PACKAGE_DIR = path.resolve(TEST_DIRECTORY, "..");
12
14
 
13
15
  function readOutlets(target = "") {
14
- const outlets = descriptor?.metadata?.ui?.placements?.outlets;
16
+ const outlets = packageMetadata?.metadata?.ui?.placements?.outlets;
15
17
  const normalizedTarget = String(target || "").trim();
16
18
  return Array.isArray(outlets)
17
19
  ? outlets.filter((entry) => String(entry?.target || "").trim() === normalizedTarget)
@@ -19,7 +21,7 @@ function readOutlets(target = "") {
19
21
  }
20
22
 
21
23
  function readContributions(target = "") {
22
- const contributions = descriptor?.metadata?.ui?.placements?.contributions;
24
+ const contributions = packageMetadata?.metadata?.ui?.placements?.contributions;
23
25
  const normalizedTarget = String(target || "").trim();
24
26
  return Array.isArray(contributions)
25
27
  ? contributions.filter((entry) => String(entry?.target || "").trim() === normalizedTarget)
@@ -27,7 +29,7 @@ function readContributions(target = "") {
27
29
  }
28
30
 
29
31
  function readTopology(id = "", owner = "") {
30
- const placements = descriptor?.metadata?.ui?.placements?.topology?.placements;
32
+ const placements = packageMetadata?.metadata?.ui?.placements?.topology?.placements;
31
33
  const normalizedId = String(id || "").trim();
32
34
  const normalizedOwner = String(owner || "").trim();
33
35
  return Array.isArray(placements)
@@ -46,12 +48,12 @@ function readPackageImportSpecifiers(source = "") {
46
48
  }
47
49
 
48
50
  function readClientContainerTokens() {
49
- const tokens = descriptor?.metadata?.apiSummary?.containerTokens?.client;
51
+ const tokens = packageMetadata?.metadata?.apiSummary?.containerTokens?.client;
50
52
  return Array.isArray(tokens) ? tokens : [];
51
53
  }
52
54
 
53
55
  function findFileMutation(id) {
54
- const files = descriptor?.mutations?.files;
56
+ const files = packageMetadata?.mutations?.files;
55
57
  return Array.isArray(files)
56
58
  ? files.find((entry) => String(entry?.id || "").trim() === id) || null
57
59
  : null;
@@ -351,7 +353,7 @@ test("shell-web placement topology seeds global actions as a semantic shell plac
351
353
  assert.match(source, /outlet: "shell-layout:supporting-side-panel"/);
352
354
  });
353
355
 
354
- test("shell-web descriptor pre-optimizes package subpaths reached only through dynamic base-shell modules", async () => {
356
+ test("shell-web packageMetadata pre-optimizes package subpaths reached only through dynamic base-shell modules", async () => {
355
357
  const [providerSource, placementSource, placementTopologySource, errorSource] = await Promise.all([
356
358
  readFile(path.join(PACKAGE_DIR, "src", "client", "providers", "ShellWebClientProvider.js"), "utf8"),
357
359
  readFile(path.join(PACKAGE_DIR, "templates", "src", "placement.js"), "utf8"),
@@ -378,16 +380,16 @@ test("shell-web descriptor pre-optimizes package subpaths reached only through d
378
380
  "@jskit-ai/shell-web/client/error"
379
381
  ]);
380
382
 
381
- assert.deepEqual(descriptor?.metadata?.client?.optimizeDeps?.include, [
383
+ assert.deepEqual(packageMetadata?.metadata?.client?.optimizeDeps?.include, [
382
384
  "@jskit-ai/shell-web/client/placement",
383
385
  "@jskit-ai/shell-web/client/error"
384
386
  ]);
385
- assert.deepEqual(descriptor?.metadata?.client?.optimizeDeps?.exclude, [
387
+ assert.deepEqual(packageMetadata?.metadata?.client?.optimizeDeps?.exclude, [
386
388
  "@jskit-ai/shell-web/client"
387
389
  ]);
388
390
  });
389
391
 
390
- test("shell-web descriptor metadata advertises adaptive shell outlets, default links, and installs the scaffold page", () => {
392
+ test("shell-web packageMetadata metadata advertises adaptive shell outlets, default links, and installs the scaffold page", () => {
391
393
  const homePageMutationIds = [
392
394
  "shell-web-page-home-wrapper",
393
395
  "shell-web-page-home",