@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.
- package/package.descriptor.mjs +98 -20
- package/package.json +11 -10
- package/src/client/components/AccountSettingsInvitesSection.vue +36 -9
- package/src/client/components/UsersWorkspaceToolsWidget.vue +0 -1
- package/src/client/components/WorkspaceMembersClientElement.vue +62 -45
- package/src/client/components/WorkspaceProfileClientElement.vue +55 -12
- package/src/client/components/WorkspaceSettingsClientElement.vue +1 -1
- package/src/client/components/WorkspaceSettingsFieldsClientElement.vue +55 -12
- package/src/client/components/WorkspacesClientElement.vue +67 -17
- package/src/shared/toolsOutletContracts.js +1 -4
- package/templates/src/components/WorkspaceNotFoundCard.vue +32 -13
- package/templates/src/pages/admin/workspace/settings/index.vue +2 -6
- package/templates/src/pages/admin/workspace/settings.vue +61 -23
- package/templates/src/surfaces/admin/index.vue +62 -14
- package/templates/src/surfaces/app/index.vue +32 -13
- package/test/exportsContract.test.js +1 -0
- package/test/settingsPlacementContract.test.js +183 -14
|
@@ -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.
|
|
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
|
|
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, /
|
|
96
|
-
assert.match(source,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
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: "
|
|
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",
|