@jskit-ai/workspaces-web 0.1.41 → 0.1.43

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.
@@ -3,6 +3,7 @@ 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 { assertGeneratedUiSourceContract } from "@jskit-ai/kernel/shared/support/generatedUiContract";
6
7
  import descriptor from "../package.descriptor.mjs";
7
8
 
8
9
  const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
@@ -16,6 +17,19 @@ function readOutlets(target = "") {
16
17
  : [];
17
18
  }
18
19
 
20
+ function findTopology(id, owner = "") {
21
+ const placements = descriptor?.metadata?.ui?.placements?.topology?.placements;
22
+ const normalizedId = String(id || "").trim();
23
+ const normalizedOwner = String(owner || "").trim();
24
+ return Array.isArray(placements)
25
+ ? placements.find((entry) => {
26
+ const entryId = String(entry?.id || "").trim();
27
+ const entryOwner = String(entry?.owner || "").trim();
28
+ return entryId === normalizedId && entryOwner === normalizedOwner;
29
+ }) || null
30
+ : null;
31
+ }
32
+
19
33
  function findContribution(id) {
20
34
  const contributions = descriptor?.metadata?.ui?.placements?.contributions;
21
35
  return Array.isArray(contributions)
@@ -43,8 +57,25 @@ test("workspaces-web admin settings template exposes surface-derived settings ou
43
57
  "utf8"
44
58
  );
45
59
 
60
+ assertGeneratedUiSourceContract(source, {
61
+ forbidCardShell: true,
62
+ sourceName: "admin/workspace/settings.vue",
63
+ requiredPatterns: [
64
+ {
65
+ id: "admin-settings-outlet",
66
+ pattern: /target="admin-settings:primary-menu"/,
67
+ message: "Admin settings shell needs the semantic settings outlet host."
68
+ },
69
+ {
70
+ id: "admin-settings-router-view",
71
+ pattern: /<RouterView \/>/,
72
+ message: "Admin settings shell needs to host child settings routes."
73
+ }
74
+ ]
75
+ });
46
76
  assert.match(source, /target="admin-settings:primary-menu"/);
47
- assert.match(source, /default-link-component-token="local\.main\.ui\.surface-aware-menu-link-item"/);
77
+ assert.doesNotMatch(source, /default-link-component-token/);
78
+ assert.doesNotMatch(source, /<v-card\b/);
48
79
  assert.match(source, /<RouterView \/>/);
49
80
  });
50
81
 
@@ -67,6 +98,38 @@ test("workspaces-web installs an app-owned account invites section wrapper", asy
67
98
  });
68
99
  });
69
100
 
101
+ test("workspaces-web settings components use direct panels instead of card scaffolds", async () => {
102
+ for (const relativePath of [
103
+ path.join("templates", "src", "components", "WorkspaceNotFoundCard.vue"),
104
+ path.join("src", "client", "components", "WorkspaceProfileClientElement.vue"),
105
+ path.join("src", "client", "components", "WorkspaceSettingsFieldsClientElement.vue"),
106
+ path.join("src", "client", "components", "WorkspacesClientElement.vue"),
107
+ path.join("src", "client", "components", "AccountSettingsInvitesSection.vue")
108
+ ]) {
109
+ const source = await readFile(path.join(PACKAGE_DIR, relativePath), "utf8");
110
+
111
+ assertGeneratedUiSourceContract(source, {
112
+ forbidCardShell: true,
113
+ sourceName: relativePath
114
+ });
115
+ assert.doesNotMatch(source, /<v-card\b|v-card-title|v-card-subtitle/);
116
+ }
117
+ });
118
+
119
+ test("workspaces-web resource load states expose local retry actions", async () => {
120
+ const expectations = new Map([
121
+ ["src/client/components/WorkspaceProfileClientElement.vue", /addEdit\.canRetryLoad[\s\S]*@click="addEdit\.refresh"/],
122
+ ["src/client/components/WorkspaceSettingsFieldsClientElement.vue", /addEdit\.canRetryLoad[\s\S]*@click="addEdit\.refresh"/],
123
+ ["src/client/components/WorkspacesClientElement.vue", /bootstrapLoadError[\s\S]*@click="refreshBootstrap"/],
124
+ ["src/client/components/WorkspaceMembersClientElement.vue", /canRetryLoad[\s\S]*@click="refreshLoad"/]
125
+ ]);
126
+
127
+ for (const [relativePath, pattern] of expectations) {
128
+ const source = await readFile(path.join(PACKAGE_DIR, relativePath), "utf8");
129
+ assert.match(source, pattern, `${relativePath} must expose a local retry action for load errors.`);
130
+ }
131
+ });
132
+
70
133
  test("workspaces-web installs an account invites cue scaffold that reads placement runtime state", async () => {
71
134
  const source = await readFile(
72
135
  path.join(PACKAGE_DIR, "templates", "packages", "main", "src", "client", "components", "AccountPendingInvitesCue.vue"),
@@ -86,14 +149,60 @@ test("workspaces-web installs an account invites cue scaffold that reads placeme
86
149
  });
87
150
  });
88
151
 
89
- test("workspaces-web admin settings index template is a simple developer-owned stub", async () => {
152
+ test("workspaces-web admin settings index template renders real workspace settings controls", async () => {
90
153
  const source = await readFile(
91
154
  path.join(PACKAGE_DIR, "templates", "src", "pages", "admin", "workspace", "settings", "index.vue"),
92
155
  "utf8"
93
156
  );
94
157
 
95
- assert.match(source, /definePage/);
96
- assert.match(source, /your_child_segment/);
158
+ assert.match(source, /@jskit-ai\/workspaces-web\/client\/components\/WorkspaceSettingsClientElement/);
159
+ assert.match(source, /<WorkspaceSettingsClientElement \/>/);
160
+ assert.doesNotMatch(source, /your_child_segment|To redirect this settings shell/);
161
+ });
162
+
163
+ test("workspaces-web starter surfaces avoid instructional placeholder copy", async () => {
164
+ const appSource = await readFile(
165
+ path.join(PACKAGE_DIR, "templates", "src", "surfaces", "app", "index.vue"),
166
+ "utf8"
167
+ );
168
+ const adminSource = await readFile(
169
+ path.join(PACKAGE_DIR, "templates", "src", "surfaces", "admin", "index.vue"),
170
+ "utf8"
171
+ );
172
+
173
+ assertGeneratedUiSourceContract(appSource, {
174
+ forbidCardShell: true,
175
+ sourceName: "workspaces app surface",
176
+ requiredPatterns: [
177
+ {
178
+ id: "workspace-app-empty-state",
179
+ pattern: /No workspace activity yet/,
180
+ message: "Workspace app surface needs a product-shaped empty state."
181
+ }
182
+ ]
183
+ });
184
+ assertGeneratedUiSourceContract(adminSource, {
185
+ forbidCardShell: true,
186
+ sourceName: "workspaces admin surface",
187
+ requiredPatterns: [
188
+ {
189
+ id: "workspace-admin-member-link",
190
+ pattern: /to="\.\/members"/,
191
+ message: "Workspace admin surface needs a direct members action."
192
+ },
193
+ {
194
+ id: "workspace-admin-settings-link",
195
+ pattern: /to="\.\/workspace\/settings"/,
196
+ message: "Workspace admin surface needs a direct settings action."
197
+ }
198
+ ]
199
+ });
200
+ assert.match(appSource, /No workspace activity yet/);
201
+ assert.match(adminSource, /Manage members and workspace settings/);
202
+ assert.match(adminSource, /to="\.\/members"/);
203
+ assert.match(adminSource, /to="\.\/workspace\/settings"/);
204
+ assert.doesNotMatch(appSource, /Replace this page|Primary in-workspace surface/);
205
+ assert.doesNotMatch(adminSource, /Use this area|Privileged workspace workflows/);
97
206
  });
98
207
 
99
208
  test("workspaces-web descriptor metadata advertises admin settings outlets", () => {
@@ -102,7 +211,6 @@ test("workspaces-web descriptor metadata advertises admin settings outlets", ()
102
211
  [
103
212
  {
104
213
  target: "admin-settings:primary-menu",
105
- defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
106
214
  surfaces: ["admin"],
107
215
  source: "templates/src/pages/admin/workspace/settings.vue"
108
216
  }
@@ -113,28 +221,78 @@ test("workspaces-web descriptor metadata advertises admin settings outlets", ()
113
221
  [
114
222
  {
115
223
  target: "admin-cog:primary-menu",
116
- defaultLinkComponentToken: "local.main.ui.surface-aware-menu-link-item",
117
224
  surfaces: ["admin"],
118
225
  source: "src/client/components/UsersWorkspaceToolsWidget.vue"
119
226
  }
120
227
  ]
121
228
  );
229
+ assert.deepEqual(findTopology("page.section-nav", "admin-settings"), {
230
+ id: "page.section-nav",
231
+ owner: "admin-settings",
232
+ description: "Navigation between workspace admin settings child pages.",
233
+ surfaces: ["admin"],
234
+ variants: {
235
+ compact: {
236
+ outlet: "admin-settings:primary-menu",
237
+ renderers: {
238
+ link: "local.main.ui.surface-aware-menu-link-item"
239
+ }
240
+ },
241
+ medium: {
242
+ outlet: "admin-settings:primary-menu",
243
+ renderers: {
244
+ link: "local.main.ui.surface-aware-menu-link-item"
245
+ }
246
+ },
247
+ expanded: {
248
+ outlet: "admin-settings:primary-menu",
249
+ renderers: {
250
+ link: "local.main.ui.surface-aware-menu-link-item"
251
+ }
252
+ }
253
+ }
254
+ });
255
+ assert.deepEqual(findTopology("admin.tools-menu"), {
256
+ id: "admin.tools-menu",
257
+ description: "Admin surface tools menu actions.",
258
+ surfaces: ["admin"],
259
+ variants: {
260
+ compact: {
261
+ outlet: "admin-cog:primary-menu",
262
+ renderers: {
263
+ link: "local.main.ui.surface-aware-menu-link-item"
264
+ }
265
+ },
266
+ medium: {
267
+ outlet: "admin-cog:primary-menu",
268
+ renderers: {
269
+ link: "local.main.ui.surface-aware-menu-link-item"
270
+ }
271
+ },
272
+ expanded: {
273
+ outlet: "admin-cog:primary-menu",
274
+ renderers: {
275
+ link: "local.main.ui.surface-aware-menu-link-item"
276
+ }
277
+ }
278
+ }
279
+ });
122
280
  assert.equal(findContribution("workspaces.workspace.settings.general"), null);
123
281
  assert.deepEqual(findContribution("workspaces.workspace.menu.app"), {
124
282
  id: "workspaces.workspace.menu.app",
125
- target: "shell-layout:primary-menu",
283
+ target: "shell.primary-nav",
284
+ kind: "link",
126
285
  surfaces: ["app"],
127
286
  order: 50,
128
- componentToken: "local.main.ui.surface-aware-menu-link-item",
129
287
  when: "auth.authenticated === true",
130
288
  source: "mutations.text#workspaces-web-placement-block"
131
289
  });
132
290
  assert.deepEqual(findContribution("workspaces.workspace.menu.admin"), {
133
291
  id: "workspaces.workspace.menu.admin",
134
- target: "shell-layout:primary-menu",
292
+ target: "shell.primary-nav",
293
+ kind: "link",
135
294
  surfaces: ["admin"],
136
295
  order: 60,
137
- componentToken: "local.main.ui.surface-aware-menu-link-item",
138
296
  when: "auth.authenticated === true",
139
297
  source: "mutations.text#workspaces-web-placement-block"
140
298
  });
@@ -142,7 +300,8 @@ test("workspaces-web descriptor metadata advertises admin settings outlets", ()
142
300
  assert.match(findTextMutation("workspaces-web-placement-block")?.value || "", /id: "workspaces\.workspace\.menu\.admin"[\s\S]*surfaces: \["admin"\][\s\S]*label: "Home"/);
143
301
  assert.deepEqual(findContribution("workspaces.profile.menu.surface-switch"), {
144
302
  id: "workspaces.profile.menu.surface-switch",
145
- target: "auth-profile-menu:primary-menu",
303
+ target: "auth.profile-menu",
304
+ kind: "component",
146
305
  surfaces: ["*"],
147
306
  order: 100,
148
307
  componentToken: "workspaces.web.profile.menu.surface-switch-item",
@@ -151,7 +310,8 @@ test("workspaces-web descriptor metadata advertises admin settings outlets", ()
151
310
  });
152
311
  assert.deepEqual(findContribution("workspaces.workspace.selector"), {
153
312
  id: "workspaces.workspace.selector",
154
- target: "shell-layout:top-left",
313
+ target: "shell.identity",
314
+ kind: "component",
155
315
  surfaces: ["*"],
156
316
  order: 200,
157
317
  componentToken: "workspaces.web.workspace.selector",
@@ -160,7 +320,9 @@ test("workspaces-web descriptor metadata advertises admin settings outlets", ()
160
320
  });
161
321
  assert.deepEqual(findContribution("workspaces.account.settings.invites"), {
162
322
  id: "workspaces.account.settings.invites",
163
- target: "account-settings:sections",
323
+ target: "settings.sections",
324
+ owner: "account-settings",
325
+ kind: "component",
164
326
  surfaces: ["account"],
165
327
  order: 400,
166
328
  componentToken: "local.main.account-settings.section.invites",
@@ -168,8 +330,15 @@ test("workspaces-web descriptor metadata advertises admin settings outlets", ()
168
330
  source: "mutations.text#workspaces-web-account-settings-placement"
169
331
  });
170
332
  assert.match(findTextMutation("workspaces-web-account-settings-placement")?.value || "", /id: "workspaces\.account\.settings\.invites"/);
171
- assert.match(findTextMutation("workspaces-web-account-settings-placement")?.value || "", /target: "account-settings:sections"/);
333
+ assert.match(findTextMutation("workspaces-web-account-settings-placement")?.value || "", /target: "settings\.sections"/);
334
+ assert.match(findTextMutation("workspaces-web-account-settings-placement")?.value || "", /owner: "account-settings"/);
172
335
  assert.match(findTextMutation("workspaces-web-account-settings-placement")?.value || "", /componentToken: "local\.main\.account-settings\.section\.invites"/);
336
+ assert.equal(findTextMutation("workspaces-web-admin-placement-topology")?.file, "src/placementTopology.js");
337
+ assert.match(findTextMutation("workspaces-web-admin-placement-topology")?.value || "", /id: "page\.section-nav"/);
338
+ assert.match(findTextMutation("workspaces-web-admin-placement-topology")?.value || "", /owner: "admin-settings"/);
339
+ assert.match(findTextMutation("workspaces-web-admin-placement-topology")?.value || "", /outlet: "admin-settings:primary-menu"/);
340
+ assert.match(findTextMutation("workspaces-web-admin-placement-topology")?.value || "", /id: "admin\.tools-menu"/);
341
+ assert.match(findTextMutation("workspaces-web-admin-placement-topology")?.value || "", /outlet: "admin-cog:primary-menu"/);
173
342
  assert.deepEqual(findFileMutation("users-web-page-admin-workspace-settings"), {
174
343
  from: "templates/src/pages/admin/workspace/settings/index.vue",
175
344
  toSurface: "admin",