@object-ui/data-objectstack 17.5.0 → 17.6.0
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/CHANGELOG.md +219 -0
- package/dist/index.cjs +262 -357
- package/dist/index.d.cts +385 -448
- package/dist/index.d.ts +385 -448
- package/dist/index.js +258 -350
- package/package.json +5 -6
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/aggregate-capability.test.ts +0 -177
- package/src/apiAccessDenied.test.ts +0 -233
- package/src/appAccessProbe.test.ts +0 -147
- package/src/bulkDelete.test.ts +0 -75
- package/src/bulkUpdate.test.ts +0 -93
- package/src/cache/MetadataCache.test.ts +0 -477
- package/src/cache/MetadataCache.ts +0 -289
- package/src/cloud-surface-retired-4152.pin.test.ts +0 -171
- package/src/connection.test.ts +0 -188
- package/src/contracts.ts +0 -115
- package/src/deleteView.homes.test.ts +0 -389
- package/src/deleteViewContract.types.test.ts +0 -246
- package/src/errors.test.ts +0 -426
- package/src/errors.ts +0 -293
- package/src/exportDownload.test.ts +0 -141
- package/src/filter-entry-translation.test.ts +0 -360
- package/src/filter-operator-ast-parity.test.ts +0 -135
- package/src/getObjectSchema.test.ts +0 -145
- package/src/httpFailureLogging.test.ts +0 -109
- package/src/importJob.test.ts +0 -71
- package/src/index.ts +0 -4442
- package/src/integration.ts +0 -200
- package/src/listViewOverrides.test.ts +0 -241
- package/src/listViews.test.ts +0 -175
- package/src/metadata-client.get-envelope.test.ts +0 -171
- package/src/metadata-client.saveAdvisories.test.ts +0 -226
- package/src/metadata-client.test.ts +0 -303
- package/src/metadata-client.ts +0 -1046
- package/src/occ.test.ts +0 -101
- package/src/onMutation.test.ts +0 -589
- package/src/onSaveAdvisory.test.ts +0 -311
- package/src/onWriteWarning.test.ts +0 -273
- package/src/orderby-serialization.test.ts +0 -115
- package/src/queryDataset.test.ts +0 -543
- package/src/searchAll.test.ts +0 -120
- package/src/security.ts +0 -243
- package/src/spec-symbol-batch6.test.ts +0 -326
- package/src/studio.ts +0 -152
- package/src/updateView.draft.test.ts +0 -242
- package/src/userState.test.ts +0 -315
- package/src/userState.ts +0 -238
- package/src/v3-compat.test.ts +0 -229
- package/src/validation-error.test.ts +0 -94
- package/src/viewCacheInvalidation.pin.test.ts +0 -260
- package/src/viewInvalidationSeam.pin.test.ts +0 -162
package/src/studio.ts
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Studio module integration for @objectstack/spec v3.0.0
|
|
11
|
-
* Provides visual designer schema improvements: canvas, property editors, theme builder.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export interface StudioCanvasConfig {
|
|
15
|
-
/** Canvas width */
|
|
16
|
-
width: number;
|
|
17
|
-
/** Canvas height */
|
|
18
|
-
height: number;
|
|
19
|
-
/** Background type */
|
|
20
|
-
background: 'grid' | 'dots' | 'lines' | 'none';
|
|
21
|
-
/** Grid size in pixels */
|
|
22
|
-
gridSize: number;
|
|
23
|
-
/** Snap to grid */
|
|
24
|
-
snapToGrid: boolean;
|
|
25
|
-
/** Zoom range */
|
|
26
|
-
zoom: {
|
|
27
|
-
min: number;
|
|
28
|
-
max: number;
|
|
29
|
-
step: number;
|
|
30
|
-
current: number;
|
|
31
|
-
};
|
|
32
|
-
/** Pan offset */
|
|
33
|
-
panOffset: { x: number; y: number };
|
|
34
|
-
/** Show minimap */
|
|
35
|
-
showMinimap: boolean;
|
|
36
|
-
/** Minimap position */
|
|
37
|
-
minimapPosition: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface StudioPropertyEditor {
|
|
41
|
-
/** Property name */
|
|
42
|
-
name: string;
|
|
43
|
-
/** Display label */
|
|
44
|
-
label: string;
|
|
45
|
-
/** Editor type */
|
|
46
|
-
type: 'text' | 'number' | 'boolean' | 'select' | 'color' | 'code' | 'expression';
|
|
47
|
-
/** Default value */
|
|
48
|
-
defaultValue?: unknown;
|
|
49
|
-
/** Options for select type */
|
|
50
|
-
options?: Array<{ label: string; value: string }>;
|
|
51
|
-
/** Group/category */
|
|
52
|
-
group?: string;
|
|
53
|
-
/** Description */
|
|
54
|
-
description?: string;
|
|
55
|
-
/** Whether the property supports live preview */
|
|
56
|
-
livePreview?: boolean;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface StudioThemeBuilderConfig {
|
|
60
|
-
/** Available color palettes */
|
|
61
|
-
palettes: StudioColorPalette[];
|
|
62
|
-
/** Typography presets */
|
|
63
|
-
typography: StudioTypographyPreset[];
|
|
64
|
-
/** Spacing scale */
|
|
65
|
-
spacing: number[];
|
|
66
|
-
/** Border radius options */
|
|
67
|
-
borderRadius: number[];
|
|
68
|
-
/** Shadow presets */
|
|
69
|
-
shadows: StudioShadowPreset[];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface StudioColorPalette {
|
|
73
|
-
name: string;
|
|
74
|
-
colors: Record<string, string>;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface StudioTypographyPreset {
|
|
78
|
-
name: string;
|
|
79
|
-
fontFamily: string;
|
|
80
|
-
fontSize: Record<string, string>;
|
|
81
|
-
fontWeight: Record<string, number>;
|
|
82
|
-
lineHeight: Record<string, string>;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export interface StudioShadowPreset {
|
|
86
|
-
name: string;
|
|
87
|
-
value: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Default canvas configuration for designers.
|
|
92
|
-
*/
|
|
93
|
-
export function createDefaultCanvasConfig(overrides?: Partial<StudioCanvasConfig>): StudioCanvasConfig {
|
|
94
|
-
return {
|
|
95
|
-
width: 1200,
|
|
96
|
-
height: 800,
|
|
97
|
-
background: 'grid',
|
|
98
|
-
gridSize: 8,
|
|
99
|
-
snapToGrid: true,
|
|
100
|
-
zoom: {
|
|
101
|
-
min: 0.25,
|
|
102
|
-
max: 3,
|
|
103
|
-
step: 0.1,
|
|
104
|
-
current: 1,
|
|
105
|
-
},
|
|
106
|
-
panOffset: { x: 0, y: 0 },
|
|
107
|
-
showMinimap: false,
|
|
108
|
-
minimapPosition: 'bottom-right',
|
|
109
|
-
...overrides,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Snap a position to the grid.
|
|
115
|
-
*/
|
|
116
|
-
export function snapToGrid(x: number, y: number, gridSize: number): { x: number; y: number } {
|
|
117
|
-
return {
|
|
118
|
-
x: Math.round(x / gridSize) * gridSize,
|
|
119
|
-
y: Math.round(y / gridSize) * gridSize,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Calculate auto-layout positions for a set of items.
|
|
125
|
-
* Uses a simple grid-based layout algorithm.
|
|
126
|
-
*/
|
|
127
|
-
export function calculateAutoLayout(
|
|
128
|
-
items: Array<{ id: string; width: number; height: number }>,
|
|
129
|
-
canvasWidth: number,
|
|
130
|
-
padding: number = 40,
|
|
131
|
-
gap: number = 40,
|
|
132
|
-
): Array<{ id: string; x: number; y: number }> {
|
|
133
|
-
const positions: Array<{ id: string; x: number; y: number }> = [];
|
|
134
|
-
let currentX = padding;
|
|
135
|
-
let currentY = padding;
|
|
136
|
-
let rowMaxHeight = 0;
|
|
137
|
-
|
|
138
|
-
for (const item of items) {
|
|
139
|
-
// Wrap to next row if exceeds canvas width
|
|
140
|
-
if (currentX + item.width + padding > canvasWidth) {
|
|
141
|
-
currentX = padding;
|
|
142
|
-
currentY += rowMaxHeight + gap;
|
|
143
|
-
rowMaxHeight = 0;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
positions.push({ id: item.id, x: currentX, y: currentY });
|
|
147
|
-
currentX += item.width + gap;
|
|
148
|
-
rowMaxHeight = Math.max(rowMaxHeight, item.height);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return positions;
|
|
152
|
-
}
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectUI
|
|
3
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the MIT license found in the
|
|
6
|
-
* LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
10
|
-
import { ObjectStackAdapter } from './index';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* `updateView` read/write addressing (#4139).
|
|
14
|
-
*
|
|
15
|
-
* ADR-0034 routes a runtime-created view through the per-item DRAFT seam
|
|
16
|
-
* (`createRuntimeMetadata` → `metadataClient.save(..., { mode: 'draft' })`),
|
|
17
|
-
* so a view made from the `+` tab exists ONLY as a draft row until an
|
|
18
|
-
* explicit Publish. `updateView` used to read *and* write the published
|
|
19
|
-
* overlay on both halves: the read 404ed, a `catch {}` labelled
|
|
20
|
-
* "treat missing as create-equivalent" substituted `current = {}`, and the
|
|
21
|
-
* merge degenerated into a `{label, name, object}` partial that the server
|
|
22
|
-
* rejects (422) — while the draft row the UI actually reads back through
|
|
23
|
-
* `?preview=draft` kept the old label. The rename was silently lost.
|
|
24
|
-
*
|
|
25
|
-
* These pins fix the addressing on BOTH halves: whichever row the read
|
|
26
|
-
* resolved is the row the write updates.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/** The canonical ViewItem envelope `viewEnvelope()` stages as a draft. */
|
|
30
|
-
const DRAFT_VIEW = {
|
|
31
|
-
name: 'crm_activity.kanban_board',
|
|
32
|
-
object: 'crm_activity',
|
|
33
|
-
viewKind: 'list',
|
|
34
|
-
label: 'Kanban Board',
|
|
35
|
-
config: {
|
|
36
|
-
type: 'kanban',
|
|
37
|
-
columns: ['subject', 'status', 'due_date'],
|
|
38
|
-
kanban: { columns: ['subject', 'status', 'due_date'], groupBy: 'status' },
|
|
39
|
-
data: { provider: 'object', object: 'crm_activity' },
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
interface Harness {
|
|
44
|
-
ds: any;
|
|
45
|
-
/** Published-overlay writes (`client.meta.saveItem`) — the wrong address for a draft. */
|
|
46
|
-
saveItem: ReturnType<typeof vi.fn>;
|
|
47
|
-
/** Draft-addressed writes: `[url, parsedBody]` per `PUT ...?mode=draft`. */
|
|
48
|
-
draftPuts: Array<[string, any]>;
|
|
49
|
-
/** Every metadata URL the adapter fetched, in order. */
|
|
50
|
-
urls: string[];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Build an adapter whose two metadata addresses answer independently.
|
|
55
|
-
*
|
|
56
|
-
* @param opts.draft body served at `GET /meta/view/:name?state=draft`
|
|
57
|
-
* (`null` → 404, i.e. nothing pending), wrapped in the
|
|
58
|
-
* `{type,name,item}` envelope the framework sends for
|
|
59
|
-
* draft reads.
|
|
60
|
-
* @param opts.published what `client.meta.getItem` (the published read) does:
|
|
61
|
-
* a body to resolve, or an Error to throw.
|
|
62
|
-
*/
|
|
63
|
-
function makeDS(opts: { draft: any | null; published: any | Error }): Harness {
|
|
64
|
-
const draftPuts: Array<[string, any]> = [];
|
|
65
|
-
const urls: string[] = [];
|
|
66
|
-
|
|
67
|
-
const fetchImpl = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
68
|
-
const url = String(input);
|
|
69
|
-
const json = (body: unknown, status = 200) =>
|
|
70
|
-
new Response(JSON.stringify(body), {
|
|
71
|
-
status,
|
|
72
|
-
headers: { 'Content-Type': 'application/json' },
|
|
73
|
-
});
|
|
74
|
-
if (url.includes('/meta/view/')) {
|
|
75
|
-
urls.push(url);
|
|
76
|
-
if ((init?.method ?? 'GET') === 'PUT') {
|
|
77
|
-
draftPuts.push([url, JSON.parse(String(init?.body ?? '{}'))]);
|
|
78
|
-
return json({ success: true, version: 2 });
|
|
79
|
-
}
|
|
80
|
-
if (url.includes('state=draft')) {
|
|
81
|
-
if (opts.draft == null) return json({ error: 'not found' }, 404);
|
|
82
|
-
// Draft reads answer the `{type,name,item}` envelope (unlike the
|
|
83
|
-
// bare-body published read) — the asymmetry `unwrapDraftBody` exists for.
|
|
84
|
-
return json({ type: 'view', name: opts.draft.name, item: opts.draft });
|
|
85
|
-
}
|
|
86
|
-
return json({ error: 'not found' }, 404);
|
|
87
|
-
}
|
|
88
|
-
return json({ success: true, data: { capabilities: {}, routes: {} } });
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const ds: any = new ObjectStackAdapter({ baseUrl: 'http://test.local', fetch: fetchImpl });
|
|
92
|
-
ds.connected = true;
|
|
93
|
-
ds.connectionState = 'connected';
|
|
94
|
-
const saveItem = vi.fn(async () => ({ success: true }));
|
|
95
|
-
ds.client = {
|
|
96
|
-
meta: {
|
|
97
|
-
getItem: vi.fn(async () => {
|
|
98
|
-
if (opts.published instanceof Error) throw opts.published;
|
|
99
|
-
return { item: opts.published };
|
|
100
|
-
}),
|
|
101
|
-
saveItem,
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
return { ds, saveItem, draftPuts, urls };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** A published read that 404s, decorated the way the SDK client decorates. */
|
|
108
|
-
function notFound(): Error {
|
|
109
|
-
return Object.assign(new Error('Metadata item not found'), { httpStatus: 404 });
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
describe('ObjectStackDataSource.updateView — draft addressing (#4139)', () => {
|
|
113
|
-
it('renames a draft-only view by merging onto the DRAFT body and writing the draft back', async () => {
|
|
114
|
-
const { ds, saveItem, draftPuts } = makeDS({ draft: DRAFT_VIEW, published: notFound() });
|
|
115
|
-
|
|
116
|
-
await ds.updateView('crm_activity', DRAFT_VIEW.name, { label: 'Pipeline Board' });
|
|
117
|
-
|
|
118
|
-
// The write must be draft-addressed — the published overlay is the wrong
|
|
119
|
-
// row, and writing it would publish a view the user never published.
|
|
120
|
-
expect(saveItem).not.toHaveBeenCalled();
|
|
121
|
-
expect(draftPuts).toHaveLength(1);
|
|
122
|
-
const [url, body] = draftPuts[0];
|
|
123
|
-
expect(url).toContain('mode=draft');
|
|
124
|
-
expect(url).toContain(encodeURIComponent(DRAFT_VIEW.name));
|
|
125
|
-
|
|
126
|
-
// …and it must carry the FULL merged document, not the
|
|
127
|
-
// `{label, name, object}` partial the create-equivalent path produced.
|
|
128
|
-
expect(body.label).toBe('Pipeline Board');
|
|
129
|
-
expect(body.viewKind).toBe('list');
|
|
130
|
-
expect(body.name).toBe(DRAFT_VIEW.name);
|
|
131
|
-
expect(body.object).toBe('crm_activity');
|
|
132
|
-
expect(body.config).toEqual(DRAFT_VIEW.config);
|
|
133
|
-
// The pre-fix partial had exactly these three keys — pin the shape so a
|
|
134
|
-
// regression cannot pass by carrying the label alone.
|
|
135
|
-
expect(Object.keys(body).sort()).toEqual(
|
|
136
|
-
['config', 'label', 'name', 'object', 'viewKind'].sort(),
|
|
137
|
-
);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('probes the draft BEFORE the published overlay, so a pending draft is never shadowed', async () => {
|
|
141
|
-
// A view with both a published overlay and a pending draft: the edit
|
|
142
|
-
// belongs to the draft. Writing the published row instead would be
|
|
143
|
-
// overwritten at publish time — the rename would vanish on Publish.
|
|
144
|
-
const published = { ...DRAFT_VIEW, label: 'Old Published Label' };
|
|
145
|
-
const { ds, saveItem, draftPuts } = makeDS({ draft: DRAFT_VIEW, published });
|
|
146
|
-
|
|
147
|
-
await ds.updateView('crm_activity', DRAFT_VIEW.name, { label: 'Pipeline Board' });
|
|
148
|
-
|
|
149
|
-
expect(saveItem).not.toHaveBeenCalled();
|
|
150
|
-
expect(draftPuts).toHaveLength(1);
|
|
151
|
-
expect(draftPuts[0][1].label).toBe('Pipeline Board');
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('carries the full document for a set-default patch too, not just renames', async () => {
|
|
155
|
-
// `handleSetDefaultView` drives the same read-merge-write cycle with
|
|
156
|
-
// `{isDefault}`, so it degraded into the same partial write (#4139).
|
|
157
|
-
const { ds, draftPuts } = makeDS({ draft: DRAFT_VIEW, published: notFound() });
|
|
158
|
-
|
|
159
|
-
await ds.updateView('crm_activity', DRAFT_VIEW.name, { isDefault: true });
|
|
160
|
-
|
|
161
|
-
expect(draftPuts).toHaveLength(1);
|
|
162
|
-
const body = draftPuts[0][1];
|
|
163
|
-
expect(body.isDefault).toBe(true);
|
|
164
|
-
expect(body.label).toBe('Kanban Board');
|
|
165
|
-
expect(body.config).toEqual(DRAFT_VIEW.config);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('CONTROL — a published view with no pending draft still updates the published overlay', async () => {
|
|
169
|
-
const published = {
|
|
170
|
-
name: 'crm_activity.all',
|
|
171
|
-
object: 'crm_activity',
|
|
172
|
-
viewKind: 'list',
|
|
173
|
-
label: 'All Activities',
|
|
174
|
-
config: { type: 'grid', data: { object: 'crm_activity' } },
|
|
175
|
-
};
|
|
176
|
-
const { ds, saveItem, draftPuts } = makeDS({ draft: null, published });
|
|
177
|
-
|
|
178
|
-
await ds.updateView('crm_activity', 'crm_activity.all', { label: 'Everything' });
|
|
179
|
-
|
|
180
|
-
// No draft row → the published path is unchanged.
|
|
181
|
-
expect(draftPuts).toHaveLength(0);
|
|
182
|
-
expect(saveItem).toHaveBeenCalledTimes(1);
|
|
183
|
-
const [type, name, body] = saveItem.mock.calls[0];
|
|
184
|
-
expect(type).toBe('view');
|
|
185
|
-
expect(name).toBe('crm_activity.all');
|
|
186
|
-
expect(body.label).toBe('Everything');
|
|
187
|
-
expect(body.viewKind).toBe('list');
|
|
188
|
-
expect(body.config).toEqual(published.config);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('surfaces a genuinely-missing view instead of writing a create-equivalent partial', async () => {
|
|
192
|
-
const { ds, saveItem, draftPuts } = makeDS({ draft: null, published: notFound() });
|
|
193
|
-
|
|
194
|
-
await expect(
|
|
195
|
-
ds.updateView('crm_activity', 'crm_activity.ghost', { label: 'Nope' }),
|
|
196
|
-
).rejects.toThrow(/crm_activity\.ghost/);
|
|
197
|
-
|
|
198
|
-
// The whole point: no partial write is emitted on the way out.
|
|
199
|
-
expect(saveItem).not.toHaveBeenCalled();
|
|
200
|
-
expect(draftPuts).toHaveLength(0);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('surfaces a transport failure on the published read rather than degrading to a partial write', async () => {
|
|
204
|
-
const boom = Object.assign(new Error('Internal Server Error'), { httpStatus: 500 });
|
|
205
|
-
const { ds, saveItem, draftPuts } = makeDS({ draft: null, published: boom });
|
|
206
|
-
|
|
207
|
-
await expect(
|
|
208
|
-
ds.updateView('crm_activity', 'crm_activity.all', { label: 'Nope' }),
|
|
209
|
-
).rejects.toThrow('Internal Server Error');
|
|
210
|
-
|
|
211
|
-
expect(saveItem).not.toHaveBeenCalled();
|
|
212
|
-
expect(draftPuts).toHaveLength(0);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('surfaces a transport failure on the DRAFT probe rather than falling through to the published row', async () => {
|
|
216
|
-
// A 500 on the draft probe is not "no draft pending" — falling through
|
|
217
|
-
// would write the published overlay while a draft may still shadow it.
|
|
218
|
-
const fetchImpl = vi.fn(async (input: RequestInfo | URL) => {
|
|
219
|
-
const url = String(input);
|
|
220
|
-
if (url.includes('/meta/view/') && url.includes('state=draft')) {
|
|
221
|
-
return new Response(JSON.stringify({ error: 'boom' }), {
|
|
222
|
-
status: 503,
|
|
223
|
-
headers: { 'Content-Type': 'application/json' },
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
return new Response(JSON.stringify({ success: true, data: {} }), {
|
|
227
|
-
status: 200,
|
|
228
|
-
headers: { 'Content-Type': 'application/json' },
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
const ds: any = new ObjectStackAdapter({ baseUrl: 'http://test.local', fetch: fetchImpl });
|
|
232
|
-
ds.connected = true;
|
|
233
|
-
ds.connectionState = 'connected';
|
|
234
|
-
const saveItem = vi.fn(async () => ({ success: true }));
|
|
235
|
-
ds.client = { meta: { getItem: vi.fn(async () => ({ item: DRAFT_VIEW })), saveItem } };
|
|
236
|
-
|
|
237
|
-
await expect(
|
|
238
|
-
ds.updateView('crm_activity', DRAFT_VIEW.name, { label: 'Nope' }),
|
|
239
|
-
).rejects.toThrow();
|
|
240
|
-
expect(saveItem).not.toHaveBeenCalled();
|
|
241
|
-
});
|
|
242
|
-
});
|