@burojs/test-helpers 0.2.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/dist/index.js ADDED
@@ -0,0 +1,311 @@
1
+ import { validateMutationResult, resolveDataCapability, isDataError, validateBatchResult } from '@burojs/core';
2
+ import { describe, it, expect } from 'vitest';
3
+
4
+ // src/form-view.ts
5
+ function createFormView(page, resource, view) {
6
+ return {
7
+ field: (fieldName) => {
8
+ page.getByTestId(`form-field:${resource}:${view}:${fieldName}`);
9
+ return {
10
+ input: () => page.getByTestId(`form-field-input:${resource}:${view}:${fieldName}`),
11
+ error: () => page.getByTestId(`form-field-error:${resource}:${view}:${fieldName}`),
12
+ help: () => page.getByTestId(`form-field-help:${resource}:${view}:${fieldName}`)
13
+ };
14
+ },
15
+ section: (sectionKey) => page.getByTestId(`form-section:${resource}:${view}:${sectionKey}`),
16
+ submit: () => page.getByTestId(`form-submit:${resource}:${view}`),
17
+ cancel: () => page.getByTestId(`form-cancel:${resource}:${view}`),
18
+ action: (actionName) => page.getByTestId(`form-action:${resource}:${view}:${actionName}`)
19
+ };
20
+ }
21
+
22
+ // src/list-view.ts
23
+ function pathStr(path) {
24
+ return path.join("-");
25
+ }
26
+ function createListView(page, resource, options = {}) {
27
+ const path = options.basePath ?? `/${resource}`;
28
+ const filterBase = `list-filter:${resource}`;
29
+ const rowBase = (rowPath) => `${filterBase}:row:${pathStr(rowPath)}`;
30
+ const groupBase = (groupPath) => `${filterBase}:group:${pathStr(groupPath)}`;
31
+ return {
32
+ goto: async () => {
33
+ await page.goto(path);
34
+ },
35
+ row: (recordId) => createRowObject(page, resource, recordId),
36
+ search: () => page.getByTestId(`list-search:${resource}`),
37
+ filtersContainer: () => page.getByTestId(`list-filters:${resource}`),
38
+ filterButton: () => page.getByTestId(`${filterBase}:button`),
39
+ filterPanel: () => page.getByTestId(`${filterBase}:panel`),
40
+ filterAddRow: (groupPath = []) => page.getByTestId(`${groupBase(groupPath)}:add-row`),
41
+ filterRowField: (rowPath) => page.getByTestId(`${rowBase(rowPath)}:field`),
42
+ filterRowFieldOption: (rowPath, field) => page.getByTestId(`${rowBase(rowPath)}:field:option:${field}`),
43
+ filterRowOp: (rowPath) => page.getByTestId(`${rowBase(rowPath)}:op`),
44
+ filterRowOpOption: (rowPath, op) => page.getByTestId(`${rowBase(rowPath)}:op:option:${op}`),
45
+ filterRowValue: (rowPath) => page.getByTestId(`${rowBase(rowPath)}:value`),
46
+ filterRowRemove: (rowPath) => page.getByTestId(`${rowBase(rowPath)}:remove`),
47
+ filterApply: () => page.getByTestId(`${filterBase}:apply`),
48
+ filterReset: () => page.getByTestId(`${filterBase}:reset`),
49
+ filterBadge: (index) => page.getByTestId(`${filterBase}:badge:${index}`),
50
+ filterBadgeRemove: (index) => page.getByTestId(`${filterBase}:badge:${index}:remove`),
51
+ filterClearAll: () => page.getByTestId(`${filterBase}:clear-all`),
52
+ filterPinned: (field) => page.getByTestId(`${filterBase}:pinned:${field}`),
53
+ paginationPrev: () => page.getByTestId(`list-pagination-prev:${resource}`),
54
+ paginationNext: () => page.getByTestId(`list-pagination-next:${resource}`),
55
+ empty: () => page.getByTestId(`list-empty:${resource}`),
56
+ toolbar: () => page.getByTestId(`list-toolbar:${resource}`),
57
+ table: () => page.getByTestId(`list-table:${resource}`)
58
+ };
59
+ }
60
+ function createRowObject(page, resource, recordId) {
61
+ const rowId = `list-row:${resource}:${recordId}`;
62
+ return {
63
+ cell: (fieldName) => page.getByTestId(`list-cell:${resource}:${recordId}:${fieldName}`),
64
+ action: (actionName) => page.getByTestId(`list-row-action:${resource}:${recordId}:${actionName}`),
65
+ checkbox: () => page.getByTestId(`list-row-checkbox:${resource}:${recordId}`),
66
+ open: async () => {
67
+ await page.getByTestId(rowId).click();
68
+ }
69
+ };
70
+ }
71
+
72
+ // src/picker-object.ts
73
+ function createPickerObject(page, targetResource) {
74
+ return {
75
+ dialog: () => page.getByTestId(`picker-dialog:${targetResource}`),
76
+ popover: () => page.getByTestId(`picker-popover:${targetResource}`),
77
+ option: (value) => page.getByTestId(`picker-option:${targetResource}:${value}`),
78
+ search: () => page.getByTestId(`picker-search:${targetResource}`),
79
+ clear: () => page.getByTestId(`picker-clear:${targetResource}`),
80
+ quickCreate: () => page.getByTestId(`picker-quickcreate:${targetResource}`)
81
+ };
82
+ }
83
+
84
+ // src/show-view.ts
85
+ function createShowView(page, resource) {
86
+ return createFormView(page, resource, "show");
87
+ }
88
+
89
+ // src/resource-object.ts
90
+ function createResourceObject(page, name) {
91
+ return {
92
+ name,
93
+ list: (options) => createListView(page, name, options),
94
+ edit: (_recordId) => createFormView(page, name, "edit"),
95
+ show: (_recordId) => createShowView(page, name),
96
+ create: () => createFormView(page, name, "create"),
97
+ picker: () => createPickerObject(page, name)
98
+ };
99
+ }
100
+
101
+ // src/buro-page.ts
102
+ function createBuroPage(page) {
103
+ return {
104
+ resource: (name) => createResourceObject(page, name),
105
+ contextPicker: (sliceName) => page.getByTestId(`context-picker:${sliceName}`),
106
+ notification: (type) => page.getByTestId(`notification:${type}`),
107
+ actionConfirm: (resource, scope, actionName) => page.getByTestId(`action-confirm-dialog:${resource}:${scope}:${actionName}`),
108
+ page
109
+ };
110
+ }
111
+
112
+ // src/confirm-dialog.ts
113
+ function createConfirmDialog(page, resource, scope, actionName) {
114
+ return {
115
+ dialog: () => page.getByTestId(`action-confirm-dialog:${resource}:${scope}:${actionName}`),
116
+ yes: () => page.getByTestId(`action-confirm-yes:${resource}:${scope}:${actionName}`),
117
+ no: () => page.getByTestId(`action-confirm-no:${resource}:${scope}:${actionName}`)
118
+ };
119
+ }
120
+ var SESSION = Object.freeze({
121
+ sessionKey: "contract-session",
122
+ scopeKey: "contract-scope",
123
+ generation: 1
124
+ });
125
+ var REQUEST_CONTEXT = Object.freeze({ contract: "provider" });
126
+ function context(signal) {
127
+ return Object.freeze({
128
+ signal,
129
+ requestContext: REQUEST_CONTEXT,
130
+ session: SESSION,
131
+ providerServiceKey: "contract-provider"
132
+ });
133
+ }
134
+ function queryShape(kind, value) {
135
+ if (kind === "custom") return;
136
+ expect(value).toBeTypeOf("object");
137
+ if (kind === "list" || kind === "many")
138
+ expect(value.data).toBeInstanceOf(Array);
139
+ if (kind === "one") expect(value).toHaveProperty("data");
140
+ if (kind === "count")
141
+ expect([null, "number"]).toContain(typeof value.total);
142
+ }
143
+ function observed(fixture, expected, signal) {
144
+ expect(fixture.observeCalls()).toContainEqual(expect.objectContaining({ ...expected, signal }));
145
+ }
146
+ async function assertBatch(provider, fixture, operations, expected) {
147
+ expect(provider.batch?.queries).toBeTypeOf("function");
148
+ const controller = new AbortController();
149
+ const result = await provider.batch.queries(operations, context(controller.signal));
150
+ validateBatchResult(operations, result);
151
+ if (expected !== void 0) expect(result).toEqual(expected);
152
+ for (const entry of result.results) if (entry.status === "error") dataError(entry.error);
153
+ observed(
154
+ fixture,
155
+ { lifecycle: "batch", descriptors: operations.map((operation) => operation.descriptor) },
156
+ controller.signal
157
+ );
158
+ }
159
+ function dataError(value) {
160
+ expect(isDataError(value)).toBe(true);
161
+ }
162
+ function livePlan(value) {
163
+ expect(value, "descriptors.live was supplied, so live.open must return a plan").not.toBeNull();
164
+ expect(value).toBeTypeOf("object");
165
+ expect(value?.subscribe).toBeTypeOf("function");
166
+ }
167
+ function liveDescriptorRequired(hasLiveSlot, descriptor) {
168
+ if (!hasLiveSlot) return;
169
+ expect(
170
+ descriptor,
171
+ "the provider exposes a `live` slot, so the fixture must supply `descriptors.live` \u2014 omitting it silently skips the whole live cell"
172
+ ).toBeDefined();
173
+ }
174
+ function dataProviderContract(name, fixture) {
175
+ describe(`${name} data provider contract`, () => {
176
+ for (const kind of ["list", "one", "many", "count", "custom"]) {
177
+ it(`preserves the ${kind} query descriptor and AbortSignal`, async () => {
178
+ const provider = await fixture.create();
179
+ const descriptor = fixture.descriptors.queries?.[kind];
180
+ const handler = provider.queries?.[kind];
181
+ if (descriptor === void 0 || handler === void 0) return;
182
+ const controller = new AbortController();
183
+ const result = await handler(descriptor, context(controller.signal));
184
+ queryShape(kind, result);
185
+ const expected = fixture.descriptors.expected?.queries?.[kind];
186
+ if (expected !== void 0) expect(result).toEqual(expected);
187
+ observed(fixture, { lifecycle: "query", kind, descriptor }, controller.signal);
188
+ });
189
+ }
190
+ for (const kind of [
191
+ "createOne",
192
+ "createMany",
193
+ "updateOne",
194
+ "updateMany",
195
+ "deleteOne",
196
+ "deleteMany",
197
+ "custom"
198
+ ]) {
199
+ it(`validates ${kind} mutation output and preserves its request`, async () => {
200
+ const provider = await fixture.create();
201
+ const descriptor = fixture.descriptors.mutations?.[kind];
202
+ const handler = provider.mutations?.[kind];
203
+ if (descriptor === void 0 || handler === void 0) return;
204
+ const controller = new AbortController();
205
+ const result = await handler(descriptor, context(controller.signal));
206
+ validateMutationResult(descriptor, result);
207
+ const expected = fixture.descriptors.expected?.mutations?.[kind];
208
+ if (expected !== void 0) expect(result).toEqual(expected);
209
+ observed(fixture, { lifecycle: "mutation", kind, descriptor }, controller.signal);
210
+ });
211
+ }
212
+ for (const specimen of fixture.descriptors.errors ?? []) {
213
+ it(`returns a structured error for the ${specimen.lifecycle}.${specimen.kind} error specimen`, async () => {
214
+ const provider = await fixture.create();
215
+ const controller = new AbortController();
216
+ const handler = specimen.lifecycle === "query" ? provider.queries?.[specimen.kind] : provider.mutations?.[specimen.kind];
217
+ expect(handler).toBeTypeOf("function");
218
+ try {
219
+ await handler(specimen.descriptor, context(controller.signal));
220
+ throw new Error("Provider error specimen resolved instead of rejecting.");
221
+ } catch (error) {
222
+ dataError(error);
223
+ }
224
+ observed(
225
+ fixture,
226
+ { lifecycle: specimen.lifecycle, kind: specimen.kind, descriptor: specimen.descriptor },
227
+ controller.signal
228
+ );
229
+ });
230
+ }
231
+ it("reports absent optional capabilities without invoking fallback handlers", async () => {
232
+ const provider = await fixture.create();
233
+ const before = fixture.observeCalls().length;
234
+ for (const kind of ["list", "one", "many", "count", "custom"]) {
235
+ if (provider.queries?.[kind] === void 0) {
236
+ const capability = resolveDataCapability(provider, { lifecycle: "query", kind });
237
+ expect(capability).toMatchObject({ kind: "capability", code: "missing-data-capability" });
238
+ }
239
+ }
240
+ for (const kind of [
241
+ "createOne",
242
+ "createMany",
243
+ "updateOne",
244
+ "updateMany",
245
+ "deleteOne",
246
+ "deleteMany",
247
+ "custom"
248
+ ]) {
249
+ if (provider.mutations?.[kind] === void 0) {
250
+ const capability = resolveDataCapability(provider, { lifecycle: "mutation", kind });
251
+ expect(capability).toMatchObject({ kind: "capability", code: "missing-data-capability" });
252
+ }
253
+ }
254
+ expect(fixture.observeCalls()).toHaveLength(before);
255
+ });
256
+ it("validates batch IDs, kinds, partial errors, and the shared AbortSignal", async () => {
257
+ const provider = await fixture.create();
258
+ const operations = fixture.descriptors.batch;
259
+ if (provider.batch?.queries === void 0 || operations === void 0) return;
260
+ await assertBatch(provider, fixture, operations, fixture.descriptors.expected?.batch);
261
+ });
262
+ for (const [index, operations] of (fixture.descriptors.batchGroups ?? []).entries()) {
263
+ it(`keeps batch group ${index + 1} isolated and validates its partial results`, async () => {
264
+ const provider = await fixture.create();
265
+ await assertBatch(provider, fixture, operations, void 0);
266
+ });
267
+ }
268
+ for (const kind of ["offset", "cursor"]) {
269
+ it(`preserves ${kind} pagination and its mapped total/page shape`, async () => {
270
+ const provider = await fixture.create();
271
+ const descriptor = fixture.descriptors.pagination?.[kind];
272
+ if (descriptor === void 0 || provider.queries?.list === void 0) return;
273
+ const controller = new AbortController();
274
+ const result = await provider.queries.list(descriptor, context(controller.signal));
275
+ queryShape("list", result);
276
+ const expected = fixture.descriptors.expected?.pagination?.[kind];
277
+ if (expected !== void 0) expect(result).toEqual(expected);
278
+ observed(fixture, { lifecycle: "query", kind: "list", descriptor }, controller.signal);
279
+ });
280
+ }
281
+ it("opens live plans with the complete descriptor and subscribes with an AbortSignal", async () => {
282
+ const provider = await fixture.create();
283
+ const descriptor = fixture.descriptors.live;
284
+ liveDescriptorRequired(provider.live !== void 0, descriptor);
285
+ if (provider.live === void 0 || descriptor === void 0) return;
286
+ const openController = new AbortController();
287
+ const plan = await provider.live.open(descriptor, context(openController.signal));
288
+ observed(fixture, { lifecycle: "live-open", descriptor }, openController.signal);
289
+ livePlan(plan);
290
+ if (plan === null) return;
291
+ const subscribeController = new AbortController();
292
+ const observer = {
293
+ next: () => void 0,
294
+ status: () => void 0,
295
+ error: () => void 0,
296
+ complete: () => void 0
297
+ };
298
+ const unsubscribe = await plan.subscribe(observer, { signal: subscribeController.signal });
299
+ observed(fixture, { lifecycle: "live-subscribe" }, subscribeController.signal);
300
+ await unsubscribe();
301
+ });
302
+ });
303
+ }
304
+
305
+ // src/index.ts
306
+ var PACKAGE_NAME = "@burojs/test-helpers";
307
+ var PACKAGE_VERSION = "0.0.0";
308
+
309
+ export { PACKAGE_NAME, PACKAGE_VERSION, createBuroPage, createConfirmDialog, createFormView, createListView, createPickerObject, createResourceObject, createShowView, dataProviderContract };
310
+ //# sourceMappingURL=index.js.map
311
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/form-view.ts","../src/list-view.ts","../src/picker-object.ts","../src/show-view.ts","../src/resource-object.ts","../src/buro-page.ts","../src/confirm-dialog.ts","../src/data-provider-contract.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { PlaywrightLocator, PlaywrightPage } from './types.js';\n\nexport interface FormFieldObject {\n input: () => PlaywrightLocator;\n error: () => PlaywrightLocator;\n help: () => PlaywrightLocator;\n}\n\nexport interface FormView {\n field: (fieldName: string) => FormFieldObject;\n section: (sectionKey: string) => PlaywrightLocator;\n submit: () => PlaywrightLocator;\n cancel: () => PlaywrightLocator;\n action: (actionName: string) => PlaywrightLocator;\n}\n\nexport function createFormView(\n page: PlaywrightPage,\n resource: string,\n view: 'create' | 'edit' | 'show',\n): FormView {\n return {\n field: (fieldName) => {\n page.getByTestId(`form-field:${resource}:${view}:${fieldName}`);\n return {\n input: () => page.getByTestId(`form-field-input:${resource}:${view}:${fieldName}`),\n error: () => page.getByTestId(`form-field-error:${resource}:${view}:${fieldName}`),\n help: () => page.getByTestId(`form-field-help:${resource}:${view}:${fieldName}`),\n };\n },\n section: (sectionKey) => page.getByTestId(`form-section:${resource}:${view}:${sectionKey}`),\n submit: () => page.getByTestId(`form-submit:${resource}:${view}`),\n cancel: () => page.getByTestId(`form-cancel:${resource}:${view}`),\n action: (actionName) => page.getByTestId(`form-action:${resource}:${view}:${actionName}`),\n };\n}\n","import type { PlaywrightLocator, PlaywrightPage } from './types.js';\n\n/**\n * A row's address inside the filter-builder draft: `[]` is the root group,\n * `[0]` is its first row, `[1, 0]` is the first row of the group nested at\n * top-level index 1. Mirrors `DraftPath` in\n * `packages/ui-shadcn/src/list-kit/filter-builder/draft.ts` — the testid\n * suffix is `path.join('-')` (an empty path renders as the empty string, so\n * the root group's own testid carries a trailing `:`, e.g.\n * `list-filter:<r>:group::add-row`).\n */\nexport type FilterRowPath = readonly number[];\n\nexport interface ListView {\n goto: () => Promise<void>;\n row: (recordId: string) => RowObject;\n search: () => PlaywrightLocator;\n // ── Filter builder (SP2) ─────────────────────────────────────────────\n // The chip toolbar this file used to model (`list-dyn-filter:*`,\n // `list-add-filter:*`) is gone — SP2 replaced it with a popover panel:\n // open the panel, add a row, pick a field/operator/value, Apply. Applied\n // rows then render as removable badges OUTSIDE the panel. See\n // `packages/ui-shadcn/src/list-kit/filter-builder/*` for the components\n // these testids come from.\n /** The filter region container — `list-filters:<resource>`. */\n filtersContainer: () => PlaywrightLocator;\n /** Opens/closes the filter-builder popover. */\n filterButton: () => PlaywrightLocator;\n /** The popover body once open. */\n filterPanel: () => PlaywrightLocator;\n /** \"+ Filter\" — appends a new predicate row to the group at `groupPath` (root: `[]`). */\n filterAddRow: (groupPath?: FilterRowPath) => PlaywrightLocator;\n /** Field-select trigger for the row at `path`. */\n filterRowField: (path: FilterRowPath) => PlaywrightLocator;\n /** One field option inside an open field-select. */\n filterRowFieldOption: (path: FilterRowPath, field: string) => PlaywrightLocator;\n /** Operator-select trigger for the row at `path`. */\n filterRowOp: (path: FilterRowPath) => PlaywrightLocator;\n /** One operator option inside an open operator-select. */\n filterRowOpOption: (path: FilterRowPath, op: string) => PlaywrightLocator;\n /** Value control trigger/input for the row at `path` — shape depends on the field kind (text box, enum/relation/boolean popover). */\n filterRowValue: (path: FilterRowPath) => PlaywrightLocator;\n /** Removes the row at `path` from the draft (inside the panel, before Apply). */\n filterRowRemove: (path: FilterRowPath) => PlaywrightLocator;\n /** Commits the draft — applies it and closes the panel. */\n filterApply: () => PlaywrightLocator;\n /** Clears the draft back to empty (still requires Apply to take effect). */\n filterReset: () => PlaywrightLocator;\n /** One applied-filter badge outside the panel, by its top-level index. */\n filterBadge: (index: number) => PlaywrightLocator;\n /** A badge's × — removes that row immediately, no Apply needed. */\n filterBadgeRemove: (index: number) => PlaywrightLocator;\n /** \"Clear all\" — empties both the pinned slot and the builder group. */\n filterClearAll: () => PlaywrightLocator;\n /** An always-visible pinned control (`views.list.filters.pinned`) for `field`, rendered outside the panel. */\n filterPinned: (field: string) => PlaywrightLocator;\n paginationPrev: () => PlaywrightLocator;\n paginationNext: () => PlaywrightLocator;\n empty: () => PlaywrightLocator;\n toolbar: () => PlaywrightLocator;\n table: () => PlaywrightLocator;\n}\n\nexport interface RowObject {\n cell: (fieldName: string) => PlaywrightLocator;\n action: (actionName: string) => PlaywrightLocator;\n checkbox: () => PlaywrightLocator;\n open: () => Promise<void>;\n}\n\nexport interface ListViewOptions {\n basePath?: string;\n}\n\n/** `path.join('-')` — see `FilterRowPath`'s doc for the empty-path edge case. */\nfunction pathStr(path: FilterRowPath): string {\n return path.join('-');\n}\n\nexport function createListView(\n page: PlaywrightPage,\n resource: string,\n options: ListViewOptions = {},\n): ListView {\n const path = options.basePath ?? `/${resource}`;\n const filterBase = `list-filter:${resource}`;\n const rowBase = (rowPath: FilterRowPath) => `${filterBase}:row:${pathStr(rowPath)}`;\n const groupBase = (groupPath: FilterRowPath) => `${filterBase}:group:${pathStr(groupPath)}`;\n return {\n goto: async () => {\n await page.goto(path);\n },\n row: (recordId) => createRowObject(page, resource, recordId),\n search: () => page.getByTestId(`list-search:${resource}`),\n filtersContainer: () => page.getByTestId(`list-filters:${resource}`),\n filterButton: () => page.getByTestId(`${filterBase}:button`),\n filterPanel: () => page.getByTestId(`${filterBase}:panel`),\n filterAddRow: (groupPath = []) => page.getByTestId(`${groupBase(groupPath)}:add-row`),\n filterRowField: (rowPath) => page.getByTestId(`${rowBase(rowPath)}:field`),\n filterRowFieldOption: (rowPath, field) =>\n page.getByTestId(`${rowBase(rowPath)}:field:option:${field}`),\n filterRowOp: (rowPath) => page.getByTestId(`${rowBase(rowPath)}:op`),\n filterRowOpOption: (rowPath, op) => page.getByTestId(`${rowBase(rowPath)}:op:option:${op}`),\n filterRowValue: (rowPath) => page.getByTestId(`${rowBase(rowPath)}:value`),\n filterRowRemove: (rowPath) => page.getByTestId(`${rowBase(rowPath)}:remove`),\n filterApply: () => page.getByTestId(`${filterBase}:apply`),\n filterReset: () => page.getByTestId(`${filterBase}:reset`),\n filterBadge: (index) => page.getByTestId(`${filterBase}:badge:${index}`),\n filterBadgeRemove: (index) => page.getByTestId(`${filterBase}:badge:${index}:remove`),\n filterClearAll: () => page.getByTestId(`${filterBase}:clear-all`),\n filterPinned: (field) => page.getByTestId(`${filterBase}:pinned:${field}`),\n paginationPrev: () => page.getByTestId(`list-pagination-prev:${resource}`),\n paginationNext: () => page.getByTestId(`list-pagination-next:${resource}`),\n empty: () => page.getByTestId(`list-empty:${resource}`),\n toolbar: () => page.getByTestId(`list-toolbar:${resource}`),\n table: () => page.getByTestId(`list-table:${resource}`),\n };\n}\n\nfunction createRowObject(page: PlaywrightPage, resource: string, recordId: string): RowObject {\n const rowId = `list-row:${resource}:${recordId}`;\n return {\n cell: (fieldName) => page.getByTestId(`list-cell:${resource}:${recordId}:${fieldName}`),\n action: (actionName) =>\n page.getByTestId(`list-row-action:${resource}:${recordId}:${actionName}`),\n checkbox: () => page.getByTestId(`list-row-checkbox:${resource}:${recordId}`),\n open: async () => {\n await page.getByTestId(rowId).click();\n },\n };\n}\n","import type { PlaywrightLocator, PlaywrightPage } from './types.js';\n\nexport interface PickerObject {\n dialog: () => PlaywrightLocator;\n popover: () => PlaywrightLocator;\n option: (value: string) => PlaywrightLocator;\n search: () => PlaywrightLocator;\n clear: () => PlaywrightLocator;\n quickCreate: () => PlaywrightLocator;\n}\n\nexport function createPickerObject(page: PlaywrightPage, targetResource: string): PickerObject {\n return {\n dialog: () => page.getByTestId(`picker-dialog:${targetResource}`),\n popover: () => page.getByTestId(`picker-popover:${targetResource}`),\n option: (value) => page.getByTestId(`picker-option:${targetResource}:${value}`),\n search: () => page.getByTestId(`picker-search:${targetResource}`),\n clear: () => page.getByTestId(`picker-clear:${targetResource}`),\n quickCreate: () => page.getByTestId(`picker-quickcreate:${targetResource}`),\n };\n}\n","import { type FormView, createFormView } from './form-view.js';\nimport type { PlaywrightPage } from './types.js';\n\nexport type ShowView = FormView;\n\nexport function createShowView(page: PlaywrightPage, resource: string): ShowView {\n return createFormView(page, resource, 'show');\n}\n","import { type FormView, createFormView } from './form-view.js';\nimport { type ListView, type ListViewOptions, createListView } from './list-view.js';\nimport { type PickerObject, createPickerObject } from './picker-object.js';\nimport { type ShowView, createShowView } from './show-view.js';\nimport type { PlaywrightPage } from './types.js';\n\nexport interface ResourceObject {\n name: string;\n list: (options?: ListViewOptions) => ListView;\n edit: (recordId: string) => FormView;\n show: (recordId: string) => ShowView;\n create: () => FormView;\n picker: () => PickerObject;\n}\n\nexport function createResourceObject(page: PlaywrightPage, name: string): ResourceObject {\n return {\n name,\n list: (options) => createListView(page, name, options),\n edit: (_recordId) => createFormView(page, name, 'edit'),\n show: (_recordId) => createShowView(page, name),\n create: () => createFormView(page, name, 'create'),\n picker: () => createPickerObject(page, name),\n };\n}\n","import { type ResourceObject, createResourceObject } from './resource-object.js';\nimport type { PlaywrightLocator, PlaywrightPage } from './types.js';\n\nexport interface BuroPage {\n resource: (name: string) => ResourceObject;\n contextPicker: (sliceName: string) => PlaywrightLocator;\n notification: (type: 'success' | 'error' | 'progress' | 'info') => PlaywrightLocator;\n actionConfirm: (resource: string, scope: string, actionName: string) => PlaywrightLocator;\n page: PlaywrightPage;\n}\n\nexport function createBuroPage(page: PlaywrightPage): BuroPage {\n return {\n resource: (name) => createResourceObject(page, name),\n contextPicker: (sliceName) => page.getByTestId(`context-picker:${sliceName}`),\n notification: (type) => page.getByTestId(`notification:${type}`),\n actionConfirm: (resource, scope, actionName) =>\n page.getByTestId(`action-confirm-dialog:${resource}:${scope}:${actionName}`),\n page,\n };\n}\n","import type { PlaywrightLocator, PlaywrightPage } from './types.js';\n\nexport interface ConfirmDialogObject {\n dialog: () => PlaywrightLocator;\n yes: () => PlaywrightLocator;\n no: () => PlaywrightLocator;\n}\n\nexport function createConfirmDialog(\n page: PlaywrightPage,\n resource: string,\n scope: string,\n actionName: string,\n): ConfirmDialogObject {\n return {\n dialog: () => page.getByTestId(`action-confirm-dialog:${resource}:${scope}:${actionName}`),\n yes: () => page.getByTestId(`action-confirm-yes:${resource}:${scope}:${actionName}`),\n no: () => page.getByTestId(`action-confirm-no:${resource}:${scope}:${actionName}`),\n };\n}\n","import {\n resolveDataCapability,\n isDataError,\n validateBatchResult,\n validateMutationResult,\n type BatchedQueryOperation,\n type DataError,\n type DataProvider,\n type ErasedMutationDescriptor,\n type ErasedQueryDescriptor,\n type LiveObserver,\n} from '@burojs/core';\nimport { describe, expect, it } from 'vitest';\n\ntype QueryKind = ErasedQueryDescriptor['kind'];\ntype MutationKind = ErasedMutationDescriptor['kind'];\n\nexport interface ProviderObservedCall {\n readonly lifecycle: 'query' | 'mutation' | 'batch' | 'live-open' | 'live-subscribe';\n readonly kind?: QueryKind | MutationKind;\n readonly descriptor?: ErasedQueryDescriptor | ErasedMutationDescriptor;\n readonly descriptors?: readonly ErasedQueryDescriptor[];\n readonly signal?: AbortSignal;\n}\n\n/** Specimens supplied by an adapter author; unsupported optional methods are omitted. */\nexport interface ProviderContractDescriptors {\n readonly queries?: Partial<Record<QueryKind, ErasedQueryDescriptor>>;\n readonly mutations?: Partial<Record<MutationKind, ErasedMutationDescriptor>>;\n readonly batch?: readonly BatchedQueryOperation[];\n /** Independent batch groups prove that grouping is explicit, never merged by the helper. */\n readonly batchGroups?: readonly (readonly BatchedQueryOperation[])[];\n /** List specimens for both pagination protocols, including expected totals/pageInfo. */\n readonly pagination?: {\n readonly offset?: ErasedQueryDescriptor;\n readonly cursor?: ErasedQueryDescriptor;\n };\n readonly live?: ErasedQueryDescriptor;\n /** Error specimens must make the selected handler reject with a DataError. */\n readonly errors?: readonly ProviderContractErrorSpecimen[];\n /** Hand-authored mapped results; assertions never derive expectations from an adapter. */\n readonly expected?: {\n readonly queries?: Partial<Record<QueryKind, unknown>>;\n readonly mutations?: Partial<Record<MutationKind, unknown>>;\n readonly batch?: unknown;\n readonly pagination?: { readonly offset?: unknown; readonly cursor?: unknown };\n };\n}\n\nexport type ProviderContractErrorSpecimen =\n | {\n readonly lifecycle: 'query';\n readonly kind: QueryKind;\n readonly descriptor: ErasedQueryDescriptor;\n }\n | {\n readonly lifecycle: 'mutation';\n readonly kind: MutationKind;\n readonly descriptor: ErasedMutationDescriptor;\n };\n\nexport interface ProviderContractFixture {\n create(): DataProvider | Promise<DataProvider>;\n descriptors: ProviderContractDescriptors;\n observeCalls(): readonly ProviderObservedCall[];\n}\n\nconst SESSION = Object.freeze({\n sessionKey: 'contract-session',\n scopeKey: 'contract-scope',\n generation: 1,\n});\nconst REQUEST_CONTEXT = Object.freeze({ contract: 'provider' });\n\nfunction context(signal: AbortSignal) {\n return Object.freeze({\n signal,\n requestContext: REQUEST_CONTEXT,\n session: SESSION,\n providerServiceKey: 'contract-provider',\n });\n}\n\nfunction queryShape(kind: QueryKind, value: unknown): void {\n if (kind === 'custom') return;\n expect(value).toBeTypeOf('object');\n if (kind === 'list' || kind === 'many')\n expect((value as { data?: unknown }).data).toBeInstanceOf(Array);\n if (kind === 'one') expect(value).toHaveProperty('data');\n if (kind === 'count')\n expect([null, 'number']).toContain(typeof (value as { total?: unknown }).total);\n}\n\nfunction observed(\n fixture: ProviderContractFixture,\n expected: Omit<ProviderObservedCall, 'signal'>,\n signal: AbortSignal,\n): void {\n expect(fixture.observeCalls()).toContainEqual(expect.objectContaining({ ...expected, signal }));\n}\n\nasync function assertBatch(\n provider: DataProvider,\n fixture: ProviderContractFixture,\n operations: readonly BatchedQueryOperation[],\n expected: unknown,\n): Promise<void> {\n expect(provider.batch?.queries).toBeTypeOf('function');\n const controller = new AbortController();\n const result = await provider.batch!.queries(operations, context(controller.signal));\n validateBatchResult(operations, result);\n if (expected !== undefined) expect(result).toEqual(expected);\n for (const entry of result.results) if (entry.status === 'error') dataError(entry.error);\n observed(\n fixture,\n { lifecycle: 'batch', descriptors: operations.map((operation) => operation.descriptor) },\n controller.signal,\n );\n}\n\nfunction dataError(value: unknown): asserts value is DataError {\n expect(isDataError(value)).toBe(true);\n}\n\n/**\n * A supplied `descriptors.live` IS the adapter author's declaration that this\n * descriptor should produce a plan — an adapter with no live support omits the\n * descriptor (or the `live` slot) and is skipped instead. So a null plan here\n * is a failure, not a skip.\n *\n * Split out as its own assertion, rather than left inline, so the suite's own\n * tests can prove it rejects — the same reason `queryShape`/`observed`/`batch`\n * already are.\n */\nfunction livePlan(value: unknown): void {\n expect(value, 'descriptors.live was supplied, so live.open must return a plan').not.toBeNull();\n expect(value).toBeTypeOf('object');\n expect((value as { subscribe?: unknown } | null)?.subscribe).toBeTypeOf('function');\n}\n\n/**\n * The OTHER door into the same free green. `livePlan` closes \"supplied a\n * descriptor, returned a null plan\"; this closes \"provider has a `live` slot,\n * fixture supplied no descriptor\" — which used to `return` silently, leaving\n * the live cell green with nothing executed. Deleting the single line\n * `live: listDescriptor` from a fixture left `examples/marketplace-ops`'s\n * hasura contract at 35/35 green with the live `it` still passing.\n *\n * The rule is asymmetric on purpose: a provider with NO `live` slot is a\n * provider without live support, and skipping is right. A provider that HAS\n * one and a fixture that says nothing about it is an untested capability\n * reported as a pass.\n *\n * Split out for the same reason `livePlan` is: the suite's own tests drive it\n * directly to prove it rejects.\n */\nfunction liveDescriptorRequired(hasLiveSlot: boolean, descriptor: unknown): void {\n if (!hasLiveSlot) return;\n expect(\n descriptor,\n 'the provider exposes a `live` slot, so the fixture must supply `descriptors.live` — omitting it silently skips the whole live cell',\n ).toBeDefined();\n}\n\n/** @internal Direct assertion seam for the contract suite's intentionally broken-provider tests. */\nexport const __providerContractAssertions = Object.freeze({\n queryShape,\n observed,\n batch: validateBatchResult,\n mutation: validateMutationResult,\n dataError,\n livePlan,\n liveDescriptorRequired,\n});\n\n/**\n * Registers transport-agnostic provider conformance tests. It intentionally\n * imports only core contracts: adapters supply their own deterministic client\n * and expose provider-boundary observations through the fixture.\n */\nexport function dataProviderContract(name: string, fixture: ProviderContractFixture): void {\n describe(`${name} data provider contract`, () => {\n for (const kind of ['list', 'one', 'many', 'count', 'custom'] as const) {\n it(`preserves the ${kind} query descriptor and AbortSignal`, async () => {\n const provider = await fixture.create();\n const descriptor = fixture.descriptors.queries?.[kind];\n const handler = provider.queries?.[kind];\n if (descriptor === undefined || handler === undefined) return;\n const controller = new AbortController();\n const result = await handler(descriptor as never, context(controller.signal));\n queryShape(kind, result);\n const expected = fixture.descriptors.expected?.queries?.[kind];\n if (expected !== undefined) expect(result).toEqual(expected);\n observed(fixture, { lifecycle: 'query', kind, descriptor }, controller.signal);\n });\n }\n\n for (const kind of [\n 'createOne',\n 'createMany',\n 'updateOne',\n 'updateMany',\n 'deleteOne',\n 'deleteMany',\n 'custom',\n ] as const) {\n it(`validates ${kind} mutation output and preserves its request`, async () => {\n const provider = await fixture.create();\n const descriptor = fixture.descriptors.mutations?.[kind];\n const handler = provider.mutations?.[kind];\n if (descriptor === undefined || handler === undefined) return;\n const controller = new AbortController();\n const result = await handler(descriptor as never, context(controller.signal));\n validateMutationResult(descriptor, result);\n const expected = fixture.descriptors.expected?.mutations?.[kind];\n if (expected !== undefined) expect(result).toEqual(expected);\n observed(fixture, { lifecycle: 'mutation', kind, descriptor }, controller.signal);\n });\n }\n\n for (const specimen of fixture.descriptors.errors ?? []) {\n it(`returns a structured error for the ${specimen.lifecycle}.${specimen.kind} error specimen`, async () => {\n const provider = await fixture.create();\n const controller = new AbortController();\n const handler =\n specimen.lifecycle === 'query'\n ? provider.queries?.[specimen.kind as QueryKind]\n : provider.mutations?.[specimen.kind as MutationKind];\n expect(handler).toBeTypeOf('function');\n try {\n await (\n handler as (\n descriptor: never,\n operation: ReturnType<typeof context>,\n ) => Promise<unknown>\n )(specimen.descriptor as never, context(controller.signal));\n throw new Error('Provider error specimen resolved instead of rejecting.');\n } catch (error) {\n dataError(error);\n }\n observed(\n fixture,\n { lifecycle: specimen.lifecycle, kind: specimen.kind, descriptor: specimen.descriptor },\n controller.signal,\n );\n });\n }\n\n it('reports absent optional capabilities without invoking fallback handlers', async () => {\n const provider = await fixture.create();\n const before = fixture.observeCalls().length;\n for (const kind of ['list', 'one', 'many', 'count', 'custom'] as const) {\n if (provider.queries?.[kind] === undefined) {\n const capability = resolveDataCapability(provider, { lifecycle: 'query', kind });\n expect(capability).toMatchObject({ kind: 'capability', code: 'missing-data-capability' });\n }\n }\n for (const kind of [\n 'createOne',\n 'createMany',\n 'updateOne',\n 'updateMany',\n 'deleteOne',\n 'deleteMany',\n 'custom',\n ] as const) {\n if (provider.mutations?.[kind] === undefined) {\n const capability = resolveDataCapability(provider, { lifecycle: 'mutation', kind });\n expect(capability).toMatchObject({ kind: 'capability', code: 'missing-data-capability' });\n }\n }\n expect(fixture.observeCalls()).toHaveLength(before);\n });\n\n it('validates batch IDs, kinds, partial errors, and the shared AbortSignal', async () => {\n const provider = await fixture.create();\n const operations = fixture.descriptors.batch;\n if (provider.batch?.queries === undefined || operations === undefined) return;\n await assertBatch(provider, fixture, operations, fixture.descriptors.expected?.batch);\n });\n\n for (const [index, operations] of (fixture.descriptors.batchGroups ?? []).entries()) {\n it(`keeps batch group ${index + 1} isolated and validates its partial results`, async () => {\n const provider = await fixture.create();\n await assertBatch(provider, fixture, operations, undefined);\n });\n }\n\n for (const kind of ['offset', 'cursor'] as const) {\n it(`preserves ${kind} pagination and its mapped total/page shape`, async () => {\n const provider = await fixture.create();\n const descriptor = fixture.descriptors.pagination?.[kind];\n if (descriptor === undefined || provider.queries?.list === undefined) return;\n const controller = new AbortController();\n const result = await provider.queries.list(descriptor as never, context(controller.signal));\n queryShape('list', result);\n const expected = fixture.descriptors.expected?.pagination?.[kind];\n if (expected !== undefined) expect(result).toEqual(expected);\n observed(fixture, { lifecycle: 'query', kind: 'list', descriptor }, controller.signal);\n });\n }\n\n it('opens live plans with the complete descriptor and subscribes with an AbortSignal', async () => {\n const provider = await fixture.create();\n const descriptor = fixture.descriptors.live;\n liveDescriptorRequired(provider.live !== undefined, descriptor);\n if (provider.live === undefined || descriptor === undefined) return;\n const openController = new AbortController();\n const plan = await provider.live.open(descriptor, context(openController.signal));\n observed(fixture, { lifecycle: 'live-open', descriptor }, openController.signal);\n // A null plan used to `return` here, which handed every adapter a free\n // green on the whole live cell: `subscribe` was never reached, so\n // nothing about the plan was ever checked, and the `it` passed for an\n // adapter that had done no live work at all.\n livePlan(plan);\n if (plan === null) return; // unreachable past `livePlan`; narrows the type\n const subscribeController = new AbortController();\n const observer: LiveObserver = {\n next: () => undefined,\n status: () => undefined,\n error: () => undefined,\n complete: () => undefined,\n };\n const unsubscribe = await plan.subscribe(observer, { signal: subscribeController.signal });\n observed(fixture, { lifecycle: 'live-subscribe' }, subscribeController.signal);\n await unsubscribe();\n });\n });\n}\n","// Constants\nexport const PACKAGE_NAME = '@burojs/test-helpers' as const;\nexport const PACKAGE_VERSION = '0.0.0' as const;\n\n// Types\nexport type { PlaywrightPage, PlaywrightLocator } from './types.js';\n\n// Page object factories\nexport { createBuroPage, type BuroPage } from './buro-page.js';\nexport { createResourceObject, type ResourceObject } from './resource-object.js';\nexport {\n createListView,\n type ListView,\n type RowObject,\n type ListViewOptions,\n} from './list-view.js';\nexport { createFormView, type FormView, type FormFieldObject } from './form-view.js';\nexport { createShowView, type ShowView } from './show-view.js';\nexport { createPickerObject, type PickerObject } from './picker-object.js';\nexport { createConfirmDialog, type ConfirmDialogObject } from './confirm-dialog.js';\nexport {\n dataProviderContract,\n type ProviderContractDescriptors,\n type ProviderContractFixture,\n type ProviderObservedCall,\n} from './data-provider-contract.js';\n"]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@burojs/test-helpers",
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "description": "Buro: typed Playwright Page Objects over the testid convention",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "require": {
17
+ "types": "./dist/index.d.cts",
18
+ "default": "./dist/index.cjs"
19
+ }
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md"
26
+ ],
27
+ "dependencies": {
28
+ "@burojs/core": "0.2.0"
29
+ },
30
+ "peerDependencies": {
31
+ "@playwright/test": "^1.49.0",
32
+ "vitest": "^2.1.0"
33
+ },
34
+ "devDependencies": {
35
+ "tsup": "^8.3.0",
36
+ "typescript": "^5.5.0",
37
+ "vitest": "^2.1.0",
38
+ "expect-type": "^1.1.0",
39
+ "@burojs/tooling-tsconfig": "0.0.0",
40
+ "@burojs/tooling-eslint": "0.0.0"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "dev": "tsup --watch",
48
+ "typecheck": "tsc --noEmit",
49
+ "typecheck:tests": "tsc -p tsconfig.test.json"
50
+ }
51
+ }