@jskit-ai/users-web 0.1.72 → 0.1.74
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 +107 -19
- package/package.json +8 -8
- package/src/client/account-settings/sections.js +14 -10
- package/{templates/src/components/account/settings → src/client/components}/AccountSettingsClientElement.vue +14 -41
- package/src/client/composables/crud/crudJsonApiTransportSupport.js +137 -0
- package/src/client/composables/crud/crudLookupFieldLabelSupport.js +13 -1
- package/src/client/composables/crud/crudLookupFieldRuntime.js +6 -0
- package/src/client/composables/crud/crudSchemaFormHelpers.js +30 -0
- package/src/client/composables/internal/crudListParentTitleSupport.js +4 -0
- package/src/client/composables/records/useCrudAddEdit.js +11 -1
- package/src/client/composables/records/useCrudList.js +4 -0
- package/src/client/composables/records/useCrudView.js +7 -0
- package/src/client/composables/runtime/addEditUiRuntime.js +7 -6
- package/src/client/composables/runtime/useListCore.js +8 -0
- package/src/client/composables/useCrudListFilterLookups.js +5 -0
- package/src/client/composables/useCrudListParentTitle.js +21 -1
- package/src/client/index.js +1 -0
- package/templates/src/components/account/settings/vibe-coding-todo.todo +20 -0
- package/templates/src/pages/account/index.vue +1 -1
- package/test/accountSettingsSections.test.js +16 -5
- package/test/addEditUiRuntime.test.js +17 -0
- package/test/crudJsonApiTransportSupport.test.js +166 -0
- package/test/crudLookupFieldRuntime.test.js +25 -0
- package/test/exportsContract.test.js +1 -0
- package/test/requestTransportOptions.test.js +35 -0
- package/test/settingsPlacementContract.test.js +153 -1
- package/test/useCrudAddEdit.test.js +50 -0
- package/test/useCrudListParentTitle.test.js +106 -0
|
@@ -80,6 +80,28 @@ test("users-web home tools widget exposes home-cog outlet", async () => {
|
|
|
80
80
|
assert.match(source, /:default-link-component-token="HOME_COG_OUTLET\.defaultLinkComponentToken"/);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
+
test("users-web account page template uses the package-owned account settings host", async () => {
|
|
84
|
+
const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "pages", "account", "index.vue"), "utf8");
|
|
85
|
+
|
|
86
|
+
assert.match(
|
|
87
|
+
source,
|
|
88
|
+
/import AccountSettingsClientElement from "@jskit-ai\/users-web\/client\/components\/AccountSettingsClientElement";/
|
|
89
|
+
);
|
|
90
|
+
assert.doesNotMatch(source, /components\/account\/settings\/AccountSettingsClientElement\.vue/);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("users-web package-owned account settings host is fully placement-backed", async () => {
|
|
94
|
+
const source = await readFile(
|
|
95
|
+
path.join(PACKAGE_DIR, "src", "client", "components", "AccountSettingsClientElement.vue"),
|
|
96
|
+
"utf8"
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
assert.match(source, /useAccountSettingsSections/);
|
|
100
|
+
assert.doesNotMatch(source, /AccountSettingsProfileSection/);
|
|
101
|
+
assert.doesNotMatch(source, /AccountSettingsPreferencesSection/);
|
|
102
|
+
assert.doesNotMatch(source, /AccountSettingsNotificationsSection/);
|
|
103
|
+
});
|
|
104
|
+
|
|
83
105
|
test("users-web descriptor metadata advertises home cog outlet and standard home settings placements", () => {
|
|
84
106
|
assert.deepEqual(
|
|
85
107
|
readOutlets("home-cog:primary-menu"),
|
|
@@ -98,7 +120,7 @@ test("users-web descriptor metadata advertises home cog outlet and standard home
|
|
|
98
120
|
{
|
|
99
121
|
target: "account-settings:sections",
|
|
100
122
|
surfaces: ["account"],
|
|
101
|
-
source: "
|
|
123
|
+
source: "src/client/components/AccountSettingsClientElement.vue"
|
|
102
124
|
}
|
|
103
125
|
]
|
|
104
126
|
);
|
|
@@ -130,6 +152,27 @@ test("users-web descriptor metadata advertises home cog outlet and standard home
|
|
|
130
152
|
source: "mutations.text#users-web-home-tools-placement"
|
|
131
153
|
});
|
|
132
154
|
assert.equal(findContribution("users.home.settings.general"), null);
|
|
155
|
+
expectContribution("users.account.settings.profile", {
|
|
156
|
+
target: "account-settings:sections",
|
|
157
|
+
surfaces: ["account"],
|
|
158
|
+
order: 100,
|
|
159
|
+
componentToken: "local.main.account-settings.section.profile",
|
|
160
|
+
source: "mutations.text#users-web-account-settings-sections-placement"
|
|
161
|
+
});
|
|
162
|
+
expectContribution("users.account.settings.preferences", {
|
|
163
|
+
target: "account-settings:sections",
|
|
164
|
+
surfaces: ["account"],
|
|
165
|
+
order: 200,
|
|
166
|
+
componentToken: "local.main.account-settings.section.preferences",
|
|
167
|
+
source: "mutations.text#users-web-account-settings-sections-placement"
|
|
168
|
+
});
|
|
169
|
+
expectContribution("users.account.settings.notifications", {
|
|
170
|
+
target: "account-settings:sections",
|
|
171
|
+
surfaces: ["account"],
|
|
172
|
+
order: 300,
|
|
173
|
+
componentToken: "local.main.account-settings.section.notifications",
|
|
174
|
+
source: "mutations.text#users-web-account-settings-sections-placement"
|
|
175
|
+
});
|
|
133
176
|
|
|
134
177
|
expectTextMutation("users-web-home-tools-placement", {
|
|
135
178
|
reason: "Append users-web home tools widget and settings menu placements into app-owned placement registry.",
|
|
@@ -145,6 +188,22 @@ test("users-web descriptor metadata advertises home cog outlet and standard home
|
|
|
145
188
|
'unscopedSuffix: "/settings"'
|
|
146
189
|
]
|
|
147
190
|
});
|
|
191
|
+
expectTextMutation("users-web-account-settings-sections-placement", {
|
|
192
|
+
reason: "Append users-web account settings section placements into the app-owned placement registry.",
|
|
193
|
+
category: "users-web",
|
|
194
|
+
skipIfContains: 'id: "users.account.settings.profile"',
|
|
195
|
+
snippets: [
|
|
196
|
+
'id: "users.account.settings.profile"',
|
|
197
|
+
'componentToken: "local.main.account-settings.section.profile"',
|
|
198
|
+
'value: "profile"',
|
|
199
|
+
'id: "users.account.settings.preferences"',
|
|
200
|
+
'componentToken: "local.main.account-settings.section.preferences"',
|
|
201
|
+
'value: "preferences"',
|
|
202
|
+
'id: "users.account.settings.notifications"',
|
|
203
|
+
'componentToken: "local.main.account-settings.section.notifications"',
|
|
204
|
+
'value: "notifications"'
|
|
205
|
+
]
|
|
206
|
+
});
|
|
148
207
|
|
|
149
208
|
expectTextMutation("users-web-profile-settings-placement", {
|
|
150
209
|
reason: "Append users-web profile settings menu placement into app-owned placement registry.",
|
|
@@ -159,6 +218,99 @@ test("users-web descriptor metadata advertises home cog outlet and standard home
|
|
|
159
218
|
]
|
|
160
219
|
});
|
|
161
220
|
|
|
221
|
+
assert.equal(findFileMutation("users-web-component-account-settings-root"), null);
|
|
162
222
|
assert.equal(findFileMutation("users-web-component-account-settings-invites"), null);
|
|
223
|
+
assert.deepEqual(findFileMutation("users-web-component-account-settings-profile"), {
|
|
224
|
+
from: "templates/src/components/account/settings/AccountSettingsProfileSection.vue",
|
|
225
|
+
to: "src/components/account/settings/AccountSettingsProfileSection.vue",
|
|
226
|
+
reason: "Install app-owned account settings profile section scaffold.",
|
|
227
|
+
category: "users-web",
|
|
228
|
+
id: "users-web-component-account-settings-profile"
|
|
229
|
+
});
|
|
230
|
+
assert.deepEqual(findFileMutation("users-web-component-account-settings-preferences"), {
|
|
231
|
+
from: "templates/src/components/account/settings/AccountSettingsPreferencesSection.vue",
|
|
232
|
+
to: "src/components/account/settings/AccountSettingsPreferencesSection.vue",
|
|
233
|
+
reason: "Install app-owned account settings preferences section scaffold.",
|
|
234
|
+
category: "users-web",
|
|
235
|
+
id: "users-web-component-account-settings-preferences"
|
|
236
|
+
});
|
|
237
|
+
assert.deepEqual(findFileMutation("users-web-component-account-settings-notifications"), {
|
|
238
|
+
from: "templates/src/components/account/settings/AccountSettingsNotificationsSection.vue",
|
|
239
|
+
to: "src/components/account/settings/AccountSettingsNotificationsSection.vue",
|
|
240
|
+
reason: "Install app-owned account settings notifications section scaffold.",
|
|
241
|
+
category: "users-web",
|
|
242
|
+
id: "users-web-component-account-settings-notifications"
|
|
243
|
+
});
|
|
244
|
+
assert.deepEqual(findTextMutation("users-web-main-client-provider-account-settings-profile-import"), {
|
|
245
|
+
op: "append-text",
|
|
246
|
+
file: "packages/main/src/client/providers/MainClientProvider.js",
|
|
247
|
+
position: "top",
|
|
248
|
+
skipIfContains:
|
|
249
|
+
"import AccountSettingsProfileSection from \"/src/components/account/settings/AccountSettingsProfileSection.vue\";",
|
|
250
|
+
value: "import AccountSettingsProfileSection from \"/src/components/account/settings/AccountSettingsProfileSection.vue\";\n",
|
|
251
|
+
reason: "Bind the app-owned account profile settings section into local main client provider imports.",
|
|
252
|
+
category: "users-web",
|
|
253
|
+
id: "users-web-main-client-provider-account-settings-profile-import"
|
|
254
|
+
});
|
|
255
|
+
assert.deepEqual(findTextMutation("users-web-main-client-provider-account-settings-preferences-import"), {
|
|
256
|
+
op: "append-text",
|
|
257
|
+
file: "packages/main/src/client/providers/MainClientProvider.js",
|
|
258
|
+
position: "top",
|
|
259
|
+
skipIfContains:
|
|
260
|
+
"import AccountSettingsPreferencesSection from \"/src/components/account/settings/AccountSettingsPreferencesSection.vue\";",
|
|
261
|
+
value:
|
|
262
|
+
"import AccountSettingsPreferencesSection from \"/src/components/account/settings/AccountSettingsPreferencesSection.vue\";\n",
|
|
263
|
+
reason: "Bind the app-owned account preferences settings section into local main client provider imports.",
|
|
264
|
+
category: "users-web",
|
|
265
|
+
id: "users-web-main-client-provider-account-settings-preferences-import"
|
|
266
|
+
});
|
|
267
|
+
assert.deepEqual(findTextMutation("users-web-main-client-provider-account-settings-notifications-import"), {
|
|
268
|
+
op: "append-text",
|
|
269
|
+
file: "packages/main/src/client/providers/MainClientProvider.js",
|
|
270
|
+
position: "top",
|
|
271
|
+
skipIfContains:
|
|
272
|
+
"import AccountSettingsNotificationsSection from \"/src/components/account/settings/AccountSettingsNotificationsSection.vue\";",
|
|
273
|
+
value:
|
|
274
|
+
"import AccountSettingsNotificationsSection from \"/src/components/account/settings/AccountSettingsNotificationsSection.vue\";\n",
|
|
275
|
+
reason: "Bind the app-owned account notifications settings section into local main client provider imports.",
|
|
276
|
+
category: "users-web",
|
|
277
|
+
id: "users-web-main-client-provider-account-settings-notifications-import"
|
|
278
|
+
});
|
|
279
|
+
assert.deepEqual(findTextMutation("users-web-main-client-provider-account-settings-profile-register"), {
|
|
280
|
+
op: "append-text",
|
|
281
|
+
file: "packages/main/src/client/providers/MainClientProvider.js",
|
|
282
|
+
position: "bottom",
|
|
283
|
+
skipIfContains:
|
|
284
|
+
"registerMainClientComponent(\"local.main.account-settings.section.profile\", () => AccountSettingsProfileSection);",
|
|
285
|
+
value:
|
|
286
|
+
"\nregisterMainClientComponent(\"local.main.account-settings.section.profile\", () => AccountSettingsProfileSection);\n",
|
|
287
|
+
reason: "Bind the app-owned account profile settings section token into local main client provider registry.",
|
|
288
|
+
category: "users-web",
|
|
289
|
+
id: "users-web-main-client-provider-account-settings-profile-register"
|
|
290
|
+
});
|
|
291
|
+
assert.deepEqual(findTextMutation("users-web-main-client-provider-account-settings-preferences-register"), {
|
|
292
|
+
op: "append-text",
|
|
293
|
+
file: "packages/main/src/client/providers/MainClientProvider.js",
|
|
294
|
+
position: "bottom",
|
|
295
|
+
skipIfContains:
|
|
296
|
+
"registerMainClientComponent(\"local.main.account-settings.section.preferences\", () => AccountSettingsPreferencesSection);",
|
|
297
|
+
value:
|
|
298
|
+
"\nregisterMainClientComponent(\"local.main.account-settings.section.preferences\", () => AccountSettingsPreferencesSection);\n",
|
|
299
|
+
reason: "Bind the app-owned account preferences settings section token into local main client provider registry.",
|
|
300
|
+
category: "users-web",
|
|
301
|
+
id: "users-web-main-client-provider-account-settings-preferences-register"
|
|
302
|
+
});
|
|
303
|
+
assert.deepEqual(findTextMutation("users-web-main-client-provider-account-settings-notifications-register"), {
|
|
304
|
+
op: "append-text",
|
|
305
|
+
file: "packages/main/src/client/providers/MainClientProvider.js",
|
|
306
|
+
position: "bottom",
|
|
307
|
+
skipIfContains:
|
|
308
|
+
"registerMainClientComponent(\"local.main.account-settings.section.notifications\", () => AccountSettingsNotificationsSection);",
|
|
309
|
+
value:
|
|
310
|
+
"\nregisterMainClientComponent(\"local.main.account-settings.section.notifications\", () => AccountSettingsNotificationsSection);\n",
|
|
311
|
+
reason: "Bind the app-owned account notifications settings section token into local main client provider registry.",
|
|
312
|
+
category: "users-web",
|
|
313
|
+
id: "users-web-main-client-provider-account-settings-notifications-register"
|
|
314
|
+
});
|
|
163
315
|
|
|
164
316
|
});
|
|
@@ -51,6 +51,22 @@ test("createCrudFormModel preserves explicit boolean defaults for nullable field
|
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
test("createCrudFormModel initializes nullable lookup fields as null", () => {
|
|
55
|
+
const model = createCrudFormModel([
|
|
56
|
+
{
|
|
57
|
+
key: "serviceId",
|
|
58
|
+
type: "string",
|
|
59
|
+
nullable: true,
|
|
60
|
+
component: "lookup",
|
|
61
|
+
relation: { kind: "lookup", namespace: "services" }
|
|
62
|
+
}
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
assert.deepEqual(model, {
|
|
66
|
+
serviceId: null
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
54
70
|
test("buildCrudFormPayload normalizes booleans and numbers while skipping empty numeric values", () => {
|
|
55
71
|
const payload = buildCrudFormPayload(
|
|
56
72
|
[
|
|
@@ -114,6 +130,13 @@ test("buildCrudFormPayload serializes cleared nullable typed fields as null", ()
|
|
|
114
130
|
[
|
|
115
131
|
{ key: "reviewed", type: "boolean", nullable: true },
|
|
116
132
|
{ key: "serviceId", type: "integer", nullable: true },
|
|
133
|
+
{
|
|
134
|
+
key: "selectedServiceId",
|
|
135
|
+
type: "string",
|
|
136
|
+
nullable: true,
|
|
137
|
+
component: "lookup",
|
|
138
|
+
relation: { kind: "lookup", namespace: "services" }
|
|
139
|
+
},
|
|
117
140
|
{ key: "fromDate", type: "string", format: "date", nullable: true },
|
|
118
141
|
{ key: "scheduledAt", type: "string", format: "date-time", nullable: true },
|
|
119
142
|
{ key: "fromTime", type: "string", format: "time", nullable: true }
|
|
@@ -121,6 +144,7 @@ test("buildCrudFormPayload serializes cleared nullable typed fields as null", ()
|
|
|
121
144
|
{
|
|
122
145
|
reviewed: null,
|
|
123
146
|
serviceId: null,
|
|
147
|
+
selectedServiceId: "",
|
|
124
148
|
fromDate: "",
|
|
125
149
|
scheduledAt: "",
|
|
126
150
|
fromTime: ""
|
|
@@ -130,6 +154,7 @@ test("buildCrudFormPayload serializes cleared nullable typed fields as null", ()
|
|
|
130
154
|
assert.deepEqual(payload, {
|
|
131
155
|
reviewed: null,
|
|
132
156
|
serviceId: null,
|
|
157
|
+
selectedServiceId: null,
|
|
133
158
|
fromDate: null,
|
|
134
159
|
scheduledAt: null,
|
|
135
160
|
fromTime: null
|
|
@@ -247,6 +272,31 @@ test("applyCrudPayloadToForm preserves nullable boolean payload values", () => {
|
|
|
247
272
|
});
|
|
248
273
|
});
|
|
249
274
|
|
|
275
|
+
test("applyCrudPayloadToForm preserves null lookup payload values", () => {
|
|
276
|
+
const fields = [
|
|
277
|
+
{
|
|
278
|
+
key: "serviceId",
|
|
279
|
+
type: "string",
|
|
280
|
+
nullable: true,
|
|
281
|
+
component: "lookup",
|
|
282
|
+
relation: { kind: "lookup", namespace: "services" }
|
|
283
|
+
}
|
|
284
|
+
];
|
|
285
|
+
const form = reactive({
|
|
286
|
+
serviceId: "existing"
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
applyCrudPayloadToForm(fields, form, {
|
|
290
|
+
serviceId: null
|
|
291
|
+
});
|
|
292
|
+
assert.equal(form.serviceId, null);
|
|
293
|
+
|
|
294
|
+
applyCrudPayloadToForm(fields, form, {
|
|
295
|
+
serviceId: 42
|
|
296
|
+
});
|
|
297
|
+
assert.equal(form.serviceId, "42");
|
|
298
|
+
});
|
|
299
|
+
|
|
250
300
|
test("resolveCrudRouteBoundFieldValues maps route params for route-bound form fields", () => {
|
|
251
301
|
const values = resolveCrudRouteBoundFieldValues(
|
|
252
302
|
[
|
|
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import { createSchema } from "json-rest-schema";
|
|
4
4
|
import { resolveCrudListParentDescriptor, resolveCrudListParentRecordTitle, resolveCrudListParentTitleFromItems } from "../src/client/composables/internal/crudListParentTitleSupport.js";
|
|
5
|
+
import { useCrudListParentTitle } from "../src/client/composables/useCrudListParentTitle.js";
|
|
5
6
|
|
|
6
7
|
const contactChildResource = Object.freeze({
|
|
7
8
|
contract: {
|
|
@@ -106,6 +107,32 @@ test("resolveCrudListParentTitleFromItems uses the hydrated lookup label", () =>
|
|
|
106
107
|
assert.equal(title, "Jessica Dickinson");
|
|
107
108
|
});
|
|
108
109
|
|
|
110
|
+
test("resolveCrudListParentTitleFromItems ignores raw lookup ids when no hydrated label is present", () => {
|
|
111
|
+
const descriptor = resolveCrudListParentDescriptor({
|
|
112
|
+
resource: contactChildResource,
|
|
113
|
+
route: {
|
|
114
|
+
matched: [{ path: "/w/:workspaceSlug/admin/contacts/:contactId/availabilities" }],
|
|
115
|
+
params: {
|
|
116
|
+
workspaceSlug: "dogandgroom",
|
|
117
|
+
contactId: "538779"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
recordIdParam: "availabilityRuleId"
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const title = resolveCrudListParentTitleFromItems(
|
|
124
|
+
[
|
|
125
|
+
{
|
|
126
|
+
id: 1,
|
|
127
|
+
contactId: 538779
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
descriptor
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
assert.equal(title, "");
|
|
134
|
+
});
|
|
135
|
+
|
|
109
136
|
test("resolveCrudListParentRecordTitle falls back to entity label plus id", () => {
|
|
110
137
|
const title = resolveCrudListParentRecordTitle(
|
|
111
138
|
{
|
|
@@ -154,3 +181,82 @@ test("resolveCrudListParentDescriptor supports parentRouteParamKey aliases", ()
|
|
|
154
181
|
assert.equal(descriptor?.fieldKey, "staffContactId");
|
|
155
182
|
assert.equal(descriptor?.routeParamKey, "contactId");
|
|
156
183
|
});
|
|
184
|
+
|
|
185
|
+
test("useCrudListParentTitle loads the parent record when child rows only expose the raw parent id", () => {
|
|
186
|
+
const runtime = useCrudListParentTitle({
|
|
187
|
+
listRuntime: {
|
|
188
|
+
items: [
|
|
189
|
+
{
|
|
190
|
+
id: 1,
|
|
191
|
+
contactId: 538779
|
|
192
|
+
}
|
|
193
|
+
],
|
|
194
|
+
isInitialLoading: false,
|
|
195
|
+
loadError: ""
|
|
196
|
+
},
|
|
197
|
+
resource: contactChildResource,
|
|
198
|
+
recordIdParam: "availabilityRuleId",
|
|
199
|
+
route: {
|
|
200
|
+
matched: [{ path: "/w/:workspaceSlug/admin/contacts/:contactId/availabilities" }],
|
|
201
|
+
params: {
|
|
202
|
+
workspaceSlug: "dogandgroom",
|
|
203
|
+
contactId: "538779"
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
viewRuntimeFactory: () => ({
|
|
207
|
+
record: {
|
|
208
|
+
id: 538779,
|
|
209
|
+
firstName: "Jessica",
|
|
210
|
+
lastName: "Dickinson"
|
|
211
|
+
},
|
|
212
|
+
isLoading: false,
|
|
213
|
+
loadError: ""
|
|
214
|
+
})
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
assert.equal(runtime.shouldLoadParentRecord, true);
|
|
218
|
+
assert.equal(runtime.title, "Jessica Dickinson");
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("useCrudListParentTitle requests the parent through JSON:API record transport", () => {
|
|
222
|
+
let capturedTransport = null;
|
|
223
|
+
|
|
224
|
+
useCrudListParentTitle({
|
|
225
|
+
listRuntime: {
|
|
226
|
+
items: [
|
|
227
|
+
{
|
|
228
|
+
id: 1,
|
|
229
|
+
contactId: 538779
|
|
230
|
+
}
|
|
231
|
+
],
|
|
232
|
+
isInitialLoading: false,
|
|
233
|
+
loadError: ""
|
|
234
|
+
},
|
|
235
|
+
resource: contactChildResource,
|
|
236
|
+
recordIdParam: "availabilityRuleId",
|
|
237
|
+
route: {
|
|
238
|
+
matched: [{ path: "/w/:workspaceSlug/admin/contacts/:contactId/availabilities" }],
|
|
239
|
+
params: {
|
|
240
|
+
workspaceSlug: "dogandgroom",
|
|
241
|
+
contactId: "538779"
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
viewRuntimeFactory: (options = {}) => {
|
|
245
|
+
capturedTransport = options.transport;
|
|
246
|
+
return {
|
|
247
|
+
record: {
|
|
248
|
+
id: 538779,
|
|
249
|
+
fullName: "Jessica Dickinson"
|
|
250
|
+
},
|
|
251
|
+
isLoading: false,
|
|
252
|
+
loadError: ""
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
assert.deepEqual(capturedTransport, {
|
|
258
|
+
kind: "jsonapi-resource",
|
|
259
|
+
responseType: "contacts",
|
|
260
|
+
responseKind: "record"
|
|
261
|
+
});
|
|
262
|
+
});
|