@jskit-ai/users-web 0.1.173 → 0.1.175

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.
Files changed (113) hide show
  1. package/package.json +18 -149
  2. package/src/client/composables/account-settings/accountSettingsAvatarUploadRuntime.js +2 -2
  3. package/src/client/composables/useAccountSettingsRuntime.js +3 -3
  4. package/src/client/index.js +0 -11
  5. package/src/client/providers/UsersWebClientProvider.js +0 -2
  6. package/test/exportsContract.test.js +3 -29
  7. package/test/settingsPlacementContract.test.js +10 -0
  8. package/fixtures/crud-list-date-filters/App.vue +0 -107
  9. package/fixtures/crud-list-date-filters/index.html +0 -12
  10. package/fixtures/crud-list-date-filters/main.js +0 -24
  11. package/fixtures/crud-list-date-filters/vite.config.mjs +0 -18
  12. package/src/client/bulkActions.js +0 -47
  13. package/src/client/components/CrudAddEditScreen.vue +0 -186
  14. package/src/client/components/CrudDeleteAction.vue +0 -93
  15. package/src/client/components/CrudListBulkActionSurface.vue +0 -126
  16. package/src/client/components/CrudListDateFilterControl.vue +0 -188
  17. package/src/client/components/CrudListFilterSurface.vue +0 -388
  18. package/src/client/components/CrudListRecordActionMenu.vue +0 -120
  19. package/src/client/components/CrudListScreen.vue +0 -501
  20. package/src/client/components/CrudViewScreen.vue +0 -197
  21. package/src/client/composables/crud/crudBindingSupport.js +0 -75
  22. package/src/client/composables/crud/crudHttpClientSupport.js +0 -57
  23. package/src/client/composables/crud/crudJsonApiTransportSupport.js +0 -145
  24. package/src/client/composables/crud/crudLookupFieldLabelSupport.js +0 -151
  25. package/src/client/composables/crud/crudLookupFieldRuntime.js +0 -251
  26. package/src/client/composables/crud/crudSchemaFormHelpers.js +0 -464
  27. package/src/client/composables/internal/crudListFilterLookupSupport.js +0 -161
  28. package/src/client/composables/internal/crudListParentTitleSupport.js +0 -171
  29. package/src/client/composables/internal/useOperationScope.js +0 -144
  30. package/src/client/composables/records/useAddEdit.js +0 -224
  31. package/src/client/composables/records/useCrudAddEdit.js +0 -227
  32. package/src/client/composables/records/useCrudList.js +0 -101
  33. package/src/client/composables/records/useCrudView.js +0 -47
  34. package/src/client/composables/records/useList.js +0 -500
  35. package/src/client/composables/records/useView.js +0 -164
  36. package/src/client/composables/runtime/addEditUiRuntime.js +0 -130
  37. package/src/client/composables/runtime/listUiRuntime.js +0 -123
  38. package/src/client/composables/runtime/modelStateHelpers.js +0 -90
  39. package/src/client/composables/runtime/operationAdapters.js +0 -34
  40. package/src/client/composables/runtime/operationUiHelpers.js +0 -125
  41. package/src/client/composables/runtime/operationValidationHelpers.js +0 -51
  42. package/src/client/composables/runtime/useAddEditCore.js +0 -112
  43. package/src/client/composables/runtime/useCommandCore.js +0 -123
  44. package/src/client/composables/runtime/useEndpointResource.js +0 -199
  45. package/src/client/composables/runtime/useFieldErrorBag.js +0 -61
  46. package/src/client/composables/runtime/useListCore.js +0 -112
  47. package/src/client/composables/runtime/useUiFeedback.js +0 -98
  48. package/src/client/composables/runtime/useViewCore.js +0 -93
  49. package/src/client/composables/runtime/viewUiRuntime.js +0 -96
  50. package/src/client/composables/support/errorMessageHelpers.js +0 -66
  51. package/src/client/composables/support/listQueryParamSupport.js +0 -473
  52. package/src/client/composables/support/listSearchSupport.js +0 -70
  53. package/src/client/composables/support/refValueHelpers.js +0 -19
  54. package/src/client/composables/support/requestQueryRuntimeSupport.js +0 -141
  55. package/src/client/composables/support/resourceLoadStateHelpers.js +0 -208
  56. package/src/client/composables/support/routeTemplateHelpers.js +0 -285
  57. package/src/client/composables/support/scopeHelpers.js +0 -145
  58. package/src/client/composables/useAccess.js +0 -109
  59. package/src/client/composables/useCommand.js +0 -118
  60. package/src/client/composables/useCrudAddEditScreen.js +0 -88
  61. package/src/client/composables/useCrudDeleteAction.js +0 -160
  62. package/src/client/composables/useCrudListBulkActions.js +0 -147
  63. package/src/client/composables/useCrudListFilterLookups.js +0 -146
  64. package/src/client/composables/useCrudListFilters.js +0 -339
  65. package/src/client/composables/useCrudListParentTitle.js +0 -151
  66. package/src/client/composables/useCrudListRowActions.js +0 -144
  67. package/src/client/composables/useCrudListScreen.js +0 -237
  68. package/src/client/composables/useCrudViewScreen.js +0 -77
  69. package/src/client/composables/usePagedCollection.js +0 -181
  70. package/src/client/composables/useRealtimeQueryInvalidation.js +0 -156
  71. package/src/client/composables/useScopeRuntime.js +0 -135
  72. package/src/client/filters.js +0 -15
  73. package/src/client/lib/httpClient.js +0 -47
  74. package/src/client/lib/permissions.js +0 -34
  75. package/src/client/rowActions.js +0 -47
  76. package/src/client/support/contractGuards.js +0 -34
  77. package/src/client/support/crudListDateFilterSupport.js +0 -91
  78. package/test/addEditUiRuntime.test.js +0 -110
  79. package/test/crudBindingSupport.test.js +0 -110
  80. package/test/crudHttpClientSupport.test.js +0 -77
  81. package/test/crudJsonApiTransportSupport.test.js +0 -179
  82. package/test/crudListBulkActionSurface.test.js +0 -27
  83. package/test/crudListDateFilterSupport.test.js +0 -92
  84. package/test/crudListDateFilterSurface.browser.test.js +0 -182
  85. package/test/crudListFilterSurface.test.js +0 -90
  86. package/test/crudLookupFieldRuntime.test.js +0 -264
  87. package/test/crudScreenComponents.test.js +0 -117
  88. package/test/errorIntentContract.test.js +0 -31
  89. package/test/errorMessageHelpers.test.js +0 -28
  90. package/test/listQueryParamSupport.test.js +0 -223
  91. package/test/listUiRuntime.test.js +0 -102
  92. package/test/operationValidationHelpers.test.js +0 -64
  93. package/test/permissions.test.js +0 -35
  94. package/test/refValueHelpers.test.js +0 -14
  95. package/test/requestTransportOptions.test.js +0 -457
  96. package/test/resourceLoadStateHelpers.test.js +0 -189
  97. package/test/routeTemplateHelpers.test.js +0 -85
  98. package/test/scopeHelpers.test.js +0 -57
  99. package/test/useAddEditCore.test.js +0 -124
  100. package/test/useAddEditRequestQueryParams.test.js +0 -190
  101. package/test/useCrudAddEdit.test.js +0 -415
  102. package/test/useCrudDeleteAction.test.js +0 -139
  103. package/test/useCrudListBulkActions.test.js +0 -65
  104. package/test/useCrudListFilterLookups.test.js +0 -114
  105. package/test/useCrudListFilters.test.js +0 -293
  106. package/test/useCrudListParentTitle.test.js +0 -262
  107. package/test/useCrudListRowActions.test.js +0 -77
  108. package/test/useListRouteInitialization.test.js +0 -337
  109. package/test/useListSearchSupport.test.js +0 -61
  110. package/test/usePagedCollection.test.js +0 -53
  111. package/test/useViewRequestQueryParams.test.js +0 -20
  112. package/test/viewCoreLoading.test.js +0 -44
  113. package/test/viewUiRuntime.test.js +0 -95
@@ -1,77 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
-
4
- test("useCrudListRowActions manages per-record state and executes action context", async () => {
5
- const { defineCrudListRowActions } = await import("@jskit-ai/users-web/client/rowActions");
6
- const { useCrudListRowActions } = await import("@jskit-ai/users-web/client/composables/useCrudListRowActions");
7
- const calls = [];
8
- let finishRun = null;
9
- const actions = defineCrudListRowActions([
10
- {
11
- key: "delete",
12
- label: "Delete",
13
- color: "error",
14
- visible: ({ record }) => record.isOwnerRow !== true,
15
- disabled: ({ record }) => record.locked === true,
16
- loading: ({ record }) => record.pending === true,
17
- async run(context) {
18
- calls.push(context);
19
- await new Promise((resolve) => {
20
- finishRun = resolve;
21
- });
22
- return "deleted";
23
- }
24
- }
25
- ]);
26
- const runtime = useCrudListRowActions(actions, {
27
- resolveRecordId: (record) => record.id,
28
- resolveContext: () => ({
29
- records: "records-token",
30
- reload: "reload-token"
31
- })
32
- });
33
-
34
- assert.equal(runtime.hasActions.value, true);
35
- assert.equal(runtime.hasVisibleActionsFor({ id: "owner", isOwnerRow: true }, 0), false);
36
- assert.equal(runtime.isActionDisabled("delete", { id: "locked", locked: true }, 1), true);
37
- assert.equal(runtime.isActionLoading("delete", { id: "pending", pending: true }, 2), true);
38
-
39
- const record = { id: "10", name: "Worker" };
40
- assert.equal(runtime.isActionVisible("delete", record, 3), true);
41
- assert.equal(runtime.isActionDisabled("delete", record, 3), false);
42
-
43
- const result = runtime.execute("delete", record, 3);
44
- assert.equal(runtime.isActionExecuting("delete", record, 3), true);
45
- assert.equal(runtime.isActionLoading("delete", record, 3), true);
46
- finishRun();
47
-
48
- assert.equal(await result, "deleted");
49
- assert.equal(runtime.isActionExecuting("delete", record, 3), false);
50
- assert.equal(calls.length, 1);
51
- assert.equal(calls[0].action.key, "delete");
52
- assert.equal(calls[0].record, record);
53
- assert.equal(calls[0].index, 3);
54
- assert.equal(calls[0].recordId, "10");
55
- assert.equal(calls[0].records, "records-token");
56
- assert.equal(calls[0].reload, "reload-token");
57
- });
58
-
59
- test("defineCrudListRowActions skips malformed and duplicate actions", async () => {
60
- const { defineCrudListRowActions } = await import("@jskit-ai/users-web/client/rowActions");
61
-
62
- const actions = defineCrudListRowActions([
63
- null,
64
- { key: "delete", label: "Delete", color: "error" },
65
- { key: "delete", label: "Delete again" },
66
- { key: "missing-label" },
67
- { label: "Generated key" }
68
- ]);
69
-
70
- assert.deepEqual(
71
- actions.map((action) => [action.key, action.label, action.color]),
72
- [
73
- ["delete", "Delete", "error"],
74
- ["action-5", "Generated key", "primary"]
75
- ]
76
- );
77
- });
@@ -1,337 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
4
- import {
5
- computed,
6
- createRenderer,
7
- h,
8
- unref
9
- } from "vue";
10
- import {
11
- createMemoryHistory,
12
- createRouter,
13
- useRoute
14
- } from "vue-router";
15
-
16
- import { useList } from "../src/client/composables/records/useList.js";
17
- import { useCrudListFilters } from "../src/client/composables/useCrudListFilters.js";
18
-
19
- const CURRENTNESS_FILTERS = Object.freeze({
20
- currentness: Object.freeze({
21
- type: "enum",
22
- defaultValue: "active",
23
- options: Object.freeze([
24
- Object.freeze({
25
- label: "Active",
26
- value: "active"
27
- }),
28
- Object.freeze({
29
- label: "Archived",
30
- value: "archived"
31
- })
32
- ])
33
- })
34
- });
35
-
36
- async function waitFor(check, message = "Expected condition was not met.") {
37
- for (let attempt = 0; attempt < 100; attempt += 1) {
38
- if (check()) {
39
- return;
40
- }
41
- await new Promise((resolve) => setTimeout(resolve, 0));
42
- }
43
-
44
- assert.fail(message);
45
- }
46
-
47
- function createTestRenderer() {
48
- function insert(child, parent, anchor = null) {
49
- const siblings = parent.children || (parent.children = []);
50
- const index = anchor ? siblings.indexOf(anchor) : -1;
51
- if (index < 0) {
52
- siblings.push(child);
53
- } else {
54
- siblings.splice(index, 0, child);
55
- }
56
- child.parent = parent;
57
- }
58
-
59
- function remove(child) {
60
- const parent = child?.parent;
61
- const index = parent?.children?.indexOf(child) ?? -1;
62
- if (index >= 0) {
63
- parent.children.splice(index, 1);
64
- }
65
- child.parent = null;
66
- }
67
-
68
- return createRenderer({
69
- patchProp(element, key, _previousValue, nextValue) {
70
- element.properties[key] = nextValue;
71
- },
72
- insert,
73
- remove,
74
- createElement(type) {
75
- return {
76
- type,
77
- properties: {},
78
- children: [],
79
- parent: null
80
- };
81
- },
82
- createText(text) {
83
- return {
84
- type: "text",
85
- text,
86
- parent: null
87
- };
88
- },
89
- createComment(text) {
90
- return {
91
- type: "comment",
92
- text,
93
- parent: null
94
- };
95
- },
96
- setText(node, text) {
97
- node.text = text;
98
- },
99
- setElementText(element, text) {
100
- element.children = [{
101
- type: "text",
102
- text,
103
- parent: element
104
- }];
105
- },
106
- parentNode(node) {
107
- return node?.parent || null;
108
- },
109
- nextSibling(node) {
110
- const parent = node?.parent;
111
- const index = parent?.children?.indexOf(node) ?? -1;
112
- return index >= 0 ? parent.children[index + 1] || null : null;
113
- },
114
- querySelector() {
115
- return null;
116
- },
117
- setScopeId() {},
118
- cloneNode(node) {
119
- return {
120
- ...node,
121
- properties: {
122
- ...node.properties
123
- },
124
- children: [...(node.children || [])],
125
- parent: null
126
- };
127
- },
128
- insertStaticContent(content, parent, anchor) {
129
- const node = {
130
- type: "static",
131
- text: content,
132
- parent: null
133
- };
134
- insert(node, parent, anchor);
135
- return [node, node];
136
- }
137
- });
138
- }
139
-
140
- function createListTestAdapter() {
141
- return Object.freeze({
142
- useOperationScope() {
143
- const route = useRoute();
144
-
145
- return Object.freeze({
146
- routeContext: Object.freeze({
147
- route,
148
- currentSurfaceId: computed(() => "admin")
149
- }),
150
- scopeParamValue: computed(() => ""),
151
- normalizedOwnershipFilter: "none",
152
- apiPath: computed(() => "/contacts"),
153
- queryKey: computed(() => ["contacts"]),
154
- queryCanRun(gate = true) {
155
- return computed(() => Boolean(unref(gate)));
156
- },
157
- permissionGate() {
158
- return computed(() => true);
159
- },
160
- loadError(error = "") {
161
- return computed(() => String(unref(error) || ""));
162
- },
163
- isLoading(loading = false) {
164
- return computed(() => Boolean(unref(loading)));
165
- }
166
- });
167
- }
168
- });
169
- }
170
-
171
- async function mountRouteSynchronizedList(initialLocation) {
172
- const calls = [];
173
- const client = {
174
- async request(path, options) {
175
- calls.push({
176
- path,
177
- options
178
- });
179
- return {
180
- items: [],
181
- nextCursor: null
182
- };
183
- }
184
- };
185
- const queryClient = new QueryClient({
186
- defaultOptions: {
187
- queries: {
188
- retry: false
189
- }
190
- }
191
- });
192
- const router = createRouter({
193
- history: createMemoryHistory(),
194
- routes: [{
195
- path: "/contacts",
196
- component: {
197
- render() {
198
- return h("div");
199
- }
200
- }
201
- }]
202
- });
203
- const renderer = createTestRenderer();
204
- let filterRuntime = null;
205
- const app = renderer.createApp({
206
- setup() {
207
- filterRuntime = useCrudListFilters(CURRENTNESS_FILTERS);
208
- useList({
209
- adapter: createListTestAdapter(),
210
- client,
211
- queryKeyFactory: () => ["contacts"],
212
- queryParams: filterRuntime.queryParams,
213
- routeQueryValueResolvers: filterRuntime.routeQueryValueResolvers,
214
- syncToRoute: {
215
- enabled: true,
216
- search: false,
217
- queryParams: true
218
- }
219
- });
220
-
221
- return () => h("div");
222
- }
223
- });
224
- app.use(router);
225
- app.use(VueQueryPlugin, {
226
- queryClient
227
- });
228
-
229
- await router.push(initialLocation);
230
- await router.isReady();
231
- app.mount({
232
- type: "root",
233
- children: []
234
- });
235
- await waitFor(
236
- () => calls.length > 0,
237
- `Expected a list request for ${JSON.stringify(initialLocation)}.`
238
- );
239
- await new Promise((resolve) => setImmediate(resolve));
240
-
241
- return {
242
- app,
243
- calls,
244
- filterRuntime,
245
- queryClient,
246
- router
247
- };
248
- }
249
-
250
- test("route-synchronized lists make one initial request with the hydrated route filter", async () => {
251
- const runtime = await mountRouteSynchronizedList({
252
- path: "/contacts",
253
- query: {
254
- currentness: "archived"
255
- }
256
- });
257
-
258
- assert.equal(runtime.calls.length, 1);
259
- assert.deepEqual(runtime.calls[0], {
260
- path: "/contacts",
261
- options: {
262
- method: "GET",
263
- query: {
264
- currentness: "archived",
265
- limit: 20
266
- }
267
- }
268
- });
269
- runtime.app.unmount();
270
- runtime.queryClient.clear();
271
- });
272
-
273
- test("route-synchronized lists make one initial request with a declarative default", async () => {
274
- const runtime = await mountRouteSynchronizedList("/contacts");
275
-
276
- assert.equal(runtime.calls.length, 1);
277
- assert.equal(runtime.calls[0].options.query.currentness, "active");
278
- runtime.app.unmount();
279
- runtime.queryClient.clear();
280
- });
281
-
282
- test("invalid initial route filters fall back before the first list request", async () => {
283
- const runtime = await mountRouteSynchronizedList({
284
- path: "/contacts",
285
- query: {
286
- currentness: "not-a-valid-option"
287
- }
288
- });
289
-
290
- assert.equal(runtime.calls.length, 1);
291
- assert.equal(runtime.calls[0].options.query.currentness, "active");
292
- runtime.app.unmount();
293
- runtime.queryClient.clear();
294
- });
295
-
296
- test("route navigation rehydrates filters and a later clear stays cleared", async () => {
297
- const runtime = await mountRouteSynchronizedList({
298
- path: "/contacts",
299
- query: {
300
- currentness: "archived"
301
- }
302
- });
303
-
304
- await runtime.router.push({
305
- path: "/contacts",
306
- query: {
307
- currentness: "active"
308
- }
309
- });
310
- await waitFor(() => runtime.filterRuntime.values.currentness === "active");
311
- await waitFor(() => runtime.calls.length === 2);
312
-
313
- runtime.router.back();
314
- await waitFor(() => runtime.router.currentRoute.value.query.currentness === "archived");
315
- await waitFor(() => runtime.filterRuntime.values.currentness === "archived");
316
- await waitFor(() => runtime.calls.length === 3);
317
-
318
- runtime.router.forward();
319
- await waitFor(() => runtime.router.currentRoute.value.query.currentness === "active");
320
- await waitFor(() => runtime.filterRuntime.values.currentness === "active");
321
- await waitFor(() => runtime.calls.length === 4);
322
-
323
- runtime.filterRuntime.clearFilter("currentness");
324
- await waitFor(() => !Object.hasOwn(runtime.router.currentRoute.value.query, "currentness"));
325
- await waitFor(
326
- () => runtime.calls.length >= 5,
327
- `Expected one request after clearing the filter; received ${runtime.calls.length}.`
328
- );
329
-
330
- assert.equal(runtime.calls.length, 5, JSON.stringify(runtime.calls, null, 2));
331
- assert.equal(runtime.filterRuntime.values.currentness, "");
332
- assert.deepEqual(runtime.calls.at(-1).options.query, {
333
- limit: 20
334
- });
335
- runtime.app.unmount();
336
- runtime.queryClient.clear();
337
- });
@@ -1,61 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import {
4
- normalizeListSearchConfig,
5
- matchesLocalSearch
6
- } from "../src/client/composables/support/listSearchSupport.js";
7
-
8
- test("normalizeListSearchConfig defaults to disabled query search", () => {
9
- const config = normalizeListSearchConfig();
10
- assert.equal(config.enabled, false);
11
- assert.equal(config.mode, "query");
12
- assert.equal(config.queryParam, "q");
13
- assert.equal(config.label, "Search");
14
- assert.equal(config.minLength, 1);
15
- });
16
-
17
- test("normalizeListSearchConfig accepts query mode and fields", () => {
18
- const config = normalizeListSearchConfig({
19
- enabled: true,
20
- mode: "query",
21
- queryParam: "search",
22
- fields: ["name", "name", "email"]
23
- });
24
- assert.equal(config.enabled, true);
25
- assert.equal(config.mode, "query");
26
- assert.equal(config.queryParam, "search");
27
- assert.deepEqual(config.fields, ["name", "email"]);
28
- });
29
-
30
- test("normalizeListSearchConfig accepts explicit local mode", () => {
31
- const config = normalizeListSearchConfig({
32
- enabled: true,
33
- mode: "local"
34
- });
35
- assert.equal(config.mode, "local");
36
- });
37
-
38
- test("matchesLocalSearch checks configured fields", () => {
39
- const result = matchesLocalSearch(
40
- {
41
- firstName: "Ana",
42
- surname: "Marin",
43
- phone: "0400"
44
- },
45
- "mar",
46
- ["surname"]
47
- );
48
- assert.equal(result, true);
49
- });
50
-
51
- test("matchesLocalSearch scans scalar values when fields are not configured", () => {
52
- const result = matchesLocalSearch(
53
- {
54
- firstName: "Ana",
55
- surname: "Marin",
56
- count: 42
57
- },
58
- "42"
59
- );
60
- assert.equal(result, true);
61
- });
@@ -1,53 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { trimInfinitePagesToFirst } from "../src/client/composables/usePagedCollection.js";
4
-
5
- test("trimInfinitePagesToFirst leaves non-object payloads untouched", () => {
6
- assert.equal(trimInfinitePagesToFirst(null), null);
7
- assert.equal(trimInfinitePagesToFirst(""), "");
8
- assert.equal(trimInfinitePagesToFirst(12), 12);
9
- });
10
-
11
- test("trimInfinitePagesToFirst leaves single-page payload unchanged", () => {
12
- const payload = Object.freeze({
13
- pages: Object.freeze([{ items: [1, 2] }]),
14
- pageParams: Object.freeze([null])
15
- });
16
-
17
- assert.equal(trimInfinitePagesToFirst(payload), payload);
18
- });
19
-
20
- test("trimInfinitePagesToFirst truncates to the first page and first pageParam", () => {
21
- const payload = {
22
- pages: [
23
- { items: [1, 2] },
24
- { items: [3, 4] },
25
- { items: [5, 6] }
26
- ],
27
- pageParams: [null, "cursor-1", "cursor-2"],
28
- extra: true
29
- };
30
-
31
- assert.deepEqual(
32
- trimInfinitePagesToFirst(payload),
33
- {
34
- pages: [{ items: [1, 2] }],
35
- pageParams: [null],
36
- extra: true
37
- }
38
- );
39
- });
40
-
41
- test("trimInfinitePagesToFirst injects null pageParam when missing", () => {
42
- const payload = {
43
- pages: [{ items: [1] }, { items: [2] }]
44
- };
45
-
46
- assert.deepEqual(
47
- trimInfinitePagesToFirst(payload),
48
- {
49
- pages: [{ items: [1] }],
50
- pageParams: [null]
51
- }
52
- );
53
- });
@@ -1,20 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { buildRequestQueryObject } from "../src/client/composables/support/requestQueryRuntimeSupport.js";
4
-
5
- test("buildRequestQueryObject preserves repeated keys as arrays deterministically", () => {
6
- assert.deepEqual(
7
- buildRequestQueryObject([
8
- { key: "include", values: ["vetId", "linkedUserId", "pets", "pets.breedId"] },
9
- { key: "limit", values: ["10"] }
10
- ]),
11
- {
12
- include: ["vetId", "linkedUserId", "pets", "pets.breedId"],
13
- limit: "10"
14
- }
15
- );
16
- });
17
-
18
- test("buildRequestQueryObject returns an empty object when no request params are active", () => {
19
- assert.deepEqual(buildRequestQueryObject([]), {});
20
- });
@@ -1,44 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { ref } from "vue";
4
- import { useViewCore } from "../src/client/composables/runtime/useViewCore.js";
5
-
6
- test("useViewCore prefers resource isInitialLoading/isFetching signals when provided", () => {
7
- const resource = {
8
- data: ref({ id: 42 }),
9
- isInitialLoading: ref(false),
10
- isFetching: ref(true),
11
- query: {
12
- isPending: ref(true),
13
- isFetching: ref(true),
14
- error: ref(null)
15
- },
16
- loadError: ref("")
17
- };
18
-
19
- const view = useViewCore({
20
- resource
21
- });
22
-
23
- assert.equal(view.isLoading.value, false);
24
- assert.equal(view.isFetching.value, true);
25
- });
26
-
27
- test("useViewCore falls back to query pending/fetching when resource-level loading refs are absent", () => {
28
- const resource = {
29
- data: ref(null),
30
- query: {
31
- isPending: ref(true),
32
- isFetching: ref(false),
33
- error: ref(null)
34
- },
35
- loadError: ref("")
36
- };
37
-
38
- const view = useViewCore({
39
- resource
40
- });
41
-
42
- assert.equal(view.isLoading.value, true);
43
- assert.equal(view.isFetching.value, false);
44
- });
@@ -1,95 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { ref } from "vue";
4
- import { createViewUiRuntime } from "../src/client/composables/runtime/viewUiRuntime.js";
5
-
6
- test("createViewUiRuntime resolves api/list/edit paths with nested params", () => {
7
- const runtime = createViewUiRuntime({
8
- recordIdParam: "addressId",
9
- routeParams: ref({
10
- userId: "user-7",
11
- addressId: "addr-42"
12
- }),
13
- routePath: ref("/users/user-7/addresses/addr-42"),
14
- apiUrlTemplate: "/crud/users/:userId/addresses/:addressId",
15
- listUrlTemplate: "..",
16
- editUrlTemplate: "./edit"
17
- });
18
-
19
- assert.equal(runtime.recordId.value, "addr-42");
20
- assert.equal(runtime.apiSuffix.value, "/crud/users/user-7/addresses/addr-42");
21
- assert.equal(runtime.listUrl.value, "/users/user-7/addresses");
22
- assert.equal(runtime.editUrl.value, "/users/user-7/addresses/addr-42/edit");
23
- assert.equal(runtime.resolveParams("./edit"), "/users/user-7/addresses/addr-42/edit");
24
- });
25
-
26
- test("createViewUiRuntime resolves view links from the record pathname when route is nested", () => {
27
- const runtime = createViewUiRuntime({
28
- recordIdParam: "contactId",
29
- routeParams: ref({
30
- workspaceSlug: "dogandgroom",
31
- contactId: "541841",
32
- petId: "715528"
33
- }),
34
- routeParamNames: ref(["workspaceSlug", "contactId", "petId"]),
35
- routePath: ref("/w/dogandgroom/admin/contacts/541841/pets/715528"),
36
- listUrlTemplate: "..",
37
- editUrlTemplate: "./edit"
38
- });
39
-
40
- assert.equal(runtime.listUrl.value, "/w/dogandgroom/admin/contacts");
41
- assert.equal(runtime.editUrl.value, "/w/dogandgroom/admin/contacts/541841/edit");
42
- assert.equal(runtime.resolveParams("./edit"), "/w/dogandgroom/admin/contacts/541841/edit");
43
- });
44
-
45
- test("createViewUiRuntime uses route param order when repeated values are present", () => {
46
- const runtime = createViewUiRuntime({
47
- recordIdParam: "contactId",
48
- routeParams: ref({
49
- workspaceSlug: "123",
50
- contactId: "123",
51
- petId: "123"
52
- }),
53
- routeParamNames: ref(["workspaceSlug", "contactId", "petId"]),
54
- routePath: ref("/w/123/admin/contacts/123/pets/123"),
55
- editUrlTemplate: "./edit"
56
- });
57
-
58
- assert.equal(runtime.editUrl.value, "/w/123/admin/contacts/123/edit");
59
- });
60
-
61
- test("createViewUiRuntime uses explicit routeRecordId when route params do not include id", () => {
62
- const runtime = createViewUiRuntime({
63
- recordIdParam: "addressId",
64
- routeParams: ref({
65
- userId: "user-7"
66
- }),
67
- routeRecordId: ref("new-id"),
68
- apiUrlTemplate: "/crud/users/:userId/addresses/:addressId"
69
- });
70
-
71
- assert.equal(runtime.recordId.value, "new-id");
72
- assert.equal(runtime.apiSuffix.value, "/crud/users/user-7/addresses/new-id");
73
- });
74
-
75
- test("createViewUiRuntime returns empty api suffix when required params are missing", () => {
76
- const runtime = createViewUiRuntime({
77
- recordIdParam: "addressId",
78
- routeParams: ref({
79
- addressId: "42"
80
- }),
81
- apiUrlTemplate: "/crud/users/:userId/addresses/:addressId"
82
- });
83
-
84
- assert.equal(runtime.apiSuffix.value, "");
85
- });
86
-
87
- test("createViewUiRuntime validates route parameter names", () => {
88
- assert.throws(
89
- () =>
90
- createViewUiRuntime({
91
- recordIdParam: "record-id"
92
- }),
93
- /recordIdParam "record-id" is invalid/
94
- );
95
- });