@nextsparkjs/testing 0.1.0-beta.39

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,1994 @@
1
+ // src/selectors/selector-factory.ts
2
+ function getNestedValue(obj, path) {
3
+ return path.split(".").reduce((current, key) => {
4
+ if (current && typeof current === "object" && key in current) {
5
+ return current[key];
6
+ }
7
+ return void 0;
8
+ }, obj);
9
+ }
10
+ function replacePlaceholders(selector, replacements) {
11
+ if (!replacements) return selector;
12
+ return Object.entries(replacements).reduce(
13
+ (result, [key, value]) => result.replace(new RegExp(`\\{${key}\\}`, "g"), String(value)),
14
+ selector
15
+ );
16
+ }
17
+ var isDevelopment = process.env.NODE_ENV === "development";
18
+ var isTest = process.env.NODE_ENV === "test";
19
+ var enableTestingAttributes = isDevelopment || isTest;
20
+ function createSelectorHelpers(selectors) {
21
+ function sel2(path, replacements) {
22
+ const value = getNestedValue(selectors, path);
23
+ if (value === void 0) {
24
+ if (process.env.NODE_ENV === "development") {
25
+ console.error(`[sel] Invalid selector path: "${path}"`);
26
+ }
27
+ return `INVALID_SELECTOR_${path.replace(/\./g, "_")}`;
28
+ }
29
+ if (typeof value !== "string") {
30
+ if (process.env.NODE_ENV === "development") {
31
+ console.error(
32
+ `[sel] Path "${path}" points to an object, not a string. Did you forget a property?`
33
+ );
34
+ }
35
+ return `INVALID_SELECTOR_${path.replace(/\./g, "_")}`;
36
+ }
37
+ return replacePlaceholders(value, replacements);
38
+ }
39
+ function cySelector2(path, replacements) {
40
+ return `[data-cy="${sel2(path, replacements)}"]`;
41
+ }
42
+ function selDev2(path, replacements) {
43
+ if (!enableTestingAttributes) return void 0;
44
+ return sel2(path, replacements);
45
+ }
46
+ function entitySelectors2(slug) {
47
+ return {
48
+ // Page
49
+ page: () => sel2("entities.page.container", { slug }),
50
+ title: () => sel2("entities.page.title", { slug }),
51
+ // Table
52
+ table: () => sel2("entities.table.element", { slug }),
53
+ tableContainer: () => sel2("entities.table.container", { slug }),
54
+ search: () => sel2("entities.table.search", { slug }),
55
+ addButton: () => sel2("entities.table.addButton", { slug }),
56
+ row: (id) => sel2("entities.table.row", { slug, id }),
57
+ rowMenu: (id) => sel2("entities.table.rowMenu", { slug, id }),
58
+ rowAction: (action, id) => sel2("entities.table.rowAction", { slug, action, id }),
59
+ cell: (field, id) => sel2("entities.table.cell", { slug, field, id }),
60
+ // Form
61
+ form: () => sel2("entities.form.container", { slug }),
62
+ field: (name) => sel2("entities.form.field", { slug, name }),
63
+ submitButton: () => sel2("entities.form.submitButton", { slug }),
64
+ // Header
65
+ header: (mode) => sel2("entities.header.container", { slug, mode }),
66
+ backButton: () => sel2("entities.header.backButton", { slug }),
67
+ editButton: () => sel2("entities.header.editButton", { slug }),
68
+ deleteButton: () => sel2("entities.header.deleteButton", { slug }),
69
+ // Detail
70
+ detail: () => sel2("entities.detail.container", { slug }),
71
+ // Filter
72
+ filter: (field) => sel2("entities.filter.container", { slug, field }),
73
+ filterTrigger: (field) => sel2("entities.filter.trigger", { slug, field }),
74
+ filterOption: (field, value) => sel2("entities.filter.option", { slug, field, value })
75
+ };
76
+ }
77
+ return {
78
+ SELECTORS: selectors,
79
+ sel: sel2,
80
+ s: sel2,
81
+ selDev: selDev2,
82
+ cySelector: cySelector2,
83
+ entitySelectors: entitySelectors2
84
+ };
85
+ }
86
+
87
+ // src/selectors/core-selectors.ts
88
+ var CORE_SELECTORS = {
89
+ // ===========================================================================
90
+ // AUTH
91
+ // ===========================================================================
92
+ auth: {
93
+ login: {
94
+ // Structure
95
+ card: "login-form-card",
96
+ header: "login-header",
97
+ footer: "login-footer",
98
+ form: "login-form",
99
+ options: "login-options",
100
+ // Inputs
101
+ emailInput: "login-email-input",
102
+ passwordInput: "login-password-input",
103
+ emailError: "login-email-error",
104
+ passwordError: "login-password-error",
105
+ // Buttons
106
+ submit: "login-submit",
107
+ googleSignin: "login-google-signin",
108
+ showEmail: "login-show-email",
109
+ hideEmail: "login-hide-email",
110
+ // Links
111
+ forgotPassword: "login-forgot-password",
112
+ signupLink: "login-signup-link",
113
+ // Misc
114
+ inviteBanner: "login-invite-banner",
115
+ errorAlert: "login-error-alert",
116
+ rememberCheckbox: "login-remember-checkbox"
117
+ },
118
+ signup: {
119
+ form: "signup-form",
120
+ firstName: "signup-first-name",
121
+ lastName: "signup-last-name",
122
+ email: "signup-email",
123
+ password: "signup-password",
124
+ confirmPassword: "signup-confirm-password",
125
+ submitButton: "signup-submit",
126
+ googleButton: "signup-google",
127
+ loginLink: "signup-login-link",
128
+ inviteBanner: "signup-invite-banner",
129
+ error: "signup-error"
130
+ },
131
+ forgotPassword: {
132
+ form: "forgot-password-form",
133
+ email: "forgot-password-email",
134
+ submitButton: "forgot-password-submit",
135
+ backToLogin: "forgot-password-back",
136
+ successMessage: "forgot-password-success",
137
+ successBack: "forgot-password-success-back",
138
+ retryButton: "forgot-password-retry",
139
+ error: "forgot-password-error"
140
+ },
141
+ resetPassword: {
142
+ form: "reset-password-form",
143
+ password: "reset-password-password",
144
+ confirmPassword: "reset-password-confirm",
145
+ submitButton: "reset-password-submit",
146
+ error: "reset-password-error",
147
+ success: "reset-password-success",
148
+ loginLink: "reset-password-login-link",
149
+ backToLogin: "reset-password-back"
150
+ },
151
+ verifyEmail: {
152
+ container: "verify-email-container",
153
+ resendButton: "verify-email-resend",
154
+ successMessage: "verify-email-success",
155
+ error: "verify-email-error"
156
+ },
157
+ devKeyring: {
158
+ container: "devkeyring-container",
159
+ trigger: "devkeyring-trigger",
160
+ content: "devkeyring-content",
161
+ user: "devkeyring-user-{index}"
162
+ }
163
+ },
164
+ // ===========================================================================
165
+ // DASHBOARD - Shell & TopNav
166
+ // ===========================================================================
167
+ dashboard: {
168
+ shell: {
169
+ container: "dashboard-container",
170
+ quickCreateButton: "topnav-quick-create-button",
171
+ quickCreateDropdown: "topnav-quick-create-dropdown",
172
+ quickCreateLink: "quick-create-{slug}-link"
173
+ },
174
+ topnav: {
175
+ sidebarToggle: "topnav-sidebar-toggle",
176
+ header: "topnav-header",
177
+ logo: "topnav-logo",
178
+ searchSection: "topnav-search-section",
179
+ actions: "topnav-actions",
180
+ notifications: "topnav-notifications",
181
+ help: "topnav-help",
182
+ themeToggle: "topnav-theme-toggle",
183
+ superadmin: "topnav-superadmin",
184
+ devtools: "topnav-devtools",
185
+ userMenuTrigger: "topnav-user-menu-trigger",
186
+ userMenu: "topnav-user-menu",
187
+ menuItem: "topnav-menu-{icon}",
188
+ menuAction: "topnav-menu-{action}",
189
+ userLoading: "topnav-user-loading",
190
+ signin: "topnav-signin",
191
+ signup: "topnav-signup",
192
+ // Mobile
193
+ mobileActions: "topnav-mobile-actions",
194
+ mobileMenuToggle: "topnav-mobile-menu-toggle",
195
+ mobileMenu: "topnav-mobile-menu",
196
+ mobileUserInfo: "topnav-mobile-user-info",
197
+ mobileLinkProfile: "topnav-mobile-link-profile",
198
+ mobileLinkSettings: "topnav-mobile-link-settings",
199
+ mobileLinkBilling: "topnav-mobile-link-billing",
200
+ mobileSignout: "topnav-mobile-signout",
201
+ mobileNavSuperadmin: "topnav-mobile-nav-superadmin",
202
+ mobileNavDevtools: "topnav-mobile-nav-devtools"
203
+ },
204
+ sidebar: {
205
+ main: "sidebar-main",
206
+ header: "sidebar-header",
207
+ content: "sidebar-content",
208
+ footer: "sidebar-footer"
209
+ },
210
+ navigation: {
211
+ main: "nav-main",
212
+ dashboardLink: "nav-link-dashboard",
213
+ entityLink: "nav-link-entity-{slug}",
214
+ section: "nav-section-{id}",
215
+ sectionLabel: "nav-section-label-{id}",
216
+ sectionItem: "nav-section-item-{sectionId}-{itemId}"
217
+ },
218
+ // Mobile components
219
+ mobile: {
220
+ topbar: {
221
+ header: "mobile-topbar-header",
222
+ userProfile: "mobile-topbar-user-profile",
223
+ notifications: "mobile-topbar-notifications",
224
+ themeToggle: "mobile-topbar-theme-toggle"
225
+ },
226
+ bottomNav: {
227
+ nav: "mobile-bottomnav-nav",
228
+ item: "mobile-bottomnav-item-{id}"
229
+ },
230
+ moreSheet: {
231
+ content: "mobile-more-sheet-content",
232
+ item: "mobile-more-sheet-item-{id}",
233
+ superadminLink: "mobile-more-sheet-superadmin-link",
234
+ teamSwitcher: "mobile-more-sheet-team-switcher",
235
+ signoutButton: "mobile-more-sheet-signout-button"
236
+ },
237
+ quickCreateSheet: {
238
+ content: "mobile-quick-create-sheet-content",
239
+ item: "mobile-quick-create-sheet-item-{slug}"
240
+ }
241
+ }
242
+ },
243
+ // ===========================================================================
244
+ // DASHBOARD - Entities (Dynamic with {slug})
245
+ // ===========================================================================
246
+ entities: {
247
+ page: {
248
+ container: "{slug}-page",
249
+ title: "{slug}-title"
250
+ },
251
+ list: {
252
+ container: "{slug}-list"
253
+ },
254
+ table: {
255
+ container: "{slug}-table-container",
256
+ element: "{slug}-table",
257
+ search: "{slug}-search",
258
+ addButton: "{slug}-add",
259
+ selectionCount: "{slug}-selection-count",
260
+ selectAll: "{slug}-select-all",
261
+ row: "{slug}-row-{id}",
262
+ rowSelect: "{slug}-select-{id}",
263
+ cell: "{slug}-cell-{field}-{id}",
264
+ rowMenu: "{slug}-menu-{id}",
265
+ rowActionsMenu: "{slug}-actions-{id}",
266
+ rowAction: "{slug}-menu-{action}-{id}",
267
+ quickAction: "{slug}-quick-{action}-{id}"
268
+ },
269
+ pagination: {
270
+ container: "{slug}-pagination",
271
+ pageSize: "{slug}-page-size",
272
+ pageSizeOption: "{slug}-page-size-{size}",
273
+ pageInfo: "{slug}-page-info",
274
+ first: "{slug}-page-first",
275
+ prev: "{slug}-page-prev",
276
+ next: "{slug}-page-next",
277
+ last: "{slug}-page-last"
278
+ },
279
+ bulk: {
280
+ bar: "{slug}-bulk-bar",
281
+ count: "{slug}-bulk-count",
282
+ selectAll: "{slug}-bulk-select-all",
283
+ statusButton: "{slug}-bulk-status",
284
+ deleteButton: "{slug}-bulk-delete",
285
+ clearButton: "{slug}-bulk-clear",
286
+ statusDialog: "{slug}-bulk-status-dialog",
287
+ statusSelect: "{slug}-bulk-status-select",
288
+ statusOption: "{slug}-bulk-status-option-{value}",
289
+ statusCancel: "{slug}-bulk-status-cancel",
290
+ statusConfirm: "{slug}-bulk-status-confirm",
291
+ deleteDialog: "{slug}-bulk-delete-dialog",
292
+ deleteCancel: "{slug}-bulk-delete-cancel",
293
+ deleteConfirm: "{slug}-bulk-delete-confirm"
294
+ },
295
+ header: {
296
+ container: "{slug}-{mode}-header",
297
+ backButton: "{slug}-back-btn",
298
+ title: "{slug}-title",
299
+ copyId: "{slug}-copy-id",
300
+ editButton: "{slug}-edit-btn",
301
+ deleteButton: "{slug}-delete-btn",
302
+ deleteDialog: "{slug}-delete-dialog",
303
+ deleteCancel: "{slug}-delete-cancel",
304
+ deleteConfirm: "{slug}-delete-confirm"
305
+ },
306
+ detail: {
307
+ container: "{slug}-detail"
308
+ },
309
+ form: {
310
+ container: "{slug}-form",
311
+ field: "{slug}-field-{name}",
312
+ submitButton: "{slug}-form-submit",
313
+ cancelButton: "{slug}-form-cancel"
314
+ },
315
+ filter: {
316
+ container: "{slug}-filter-{field}",
317
+ trigger: "{slug}-filter-{field}-trigger",
318
+ content: "{slug}-filter-{field}-content",
319
+ option: "{slug}-filter-{field}-option-{value}",
320
+ badge: "{slug}-filter-{field}-badge-{value}",
321
+ removeBadge: "{slug}-filter-{field}-remove-{value}",
322
+ clearAll: "{slug}-filter-{field}-clear-all"
323
+ },
324
+ search: {
325
+ container: "{slug}-search",
326
+ icon: "{slug}-search-icon",
327
+ input: "{slug}-search-input",
328
+ clear: "{slug}-search-clear"
329
+ },
330
+ confirm: {
331
+ dialog: "{slug}-confirm-dialog",
332
+ cancel: "{slug}-confirm-cancel",
333
+ action: "{slug}-confirm-action"
334
+ },
335
+ childEntity: {
336
+ container: "{parentSlug}-{childName}-container",
337
+ addButton: "{parentSlug}-{childName}-add-button"
338
+ }
339
+ },
340
+ // ===========================================================================
341
+ // DASHBOARD - Global Search
342
+ // ===========================================================================
343
+ globalSearch: {
344
+ modal: "search-modal",
345
+ trigger: "search-trigger",
346
+ input: "search-input",
347
+ results: "search-results",
348
+ result: "search-result"
349
+ },
350
+ // ===========================================================================
351
+ // DASHBOARD - Taxonomies (Categories, Tags, etc.)
352
+ // ===========================================================================
353
+ taxonomies: {
354
+ list: {
355
+ container: "taxonomies-list-table",
356
+ createButton: "taxonomies-create-button",
357
+ row: "taxonomy-row-{id}",
358
+ editButton: "taxonomies-edit-{id}",
359
+ deleteButton: "taxonomies-delete-{id}"
360
+ },
361
+ form: {
362
+ dialog: "taxonomy-form-dialog",
363
+ nameInput: "taxonomy-name-input",
364
+ slugInput: "taxonomy-slug-input",
365
+ descriptionInput: "taxonomy-description-input",
366
+ iconInput: "taxonomy-icon-input",
367
+ colorInput: "taxonomy-color-input",
368
+ parentSelect: "taxonomy-parent-select",
369
+ orderInput: "taxonomy-order-input",
370
+ saveButton: "taxonomy-save-button",
371
+ cancelButton: "taxonomy-cancel-button"
372
+ },
373
+ confirmDelete: {
374
+ dialog: "taxonomy-delete-dialog",
375
+ confirmButton: "taxonomy-delete-confirm",
376
+ cancelButton: "taxonomy-delete-cancel"
377
+ }
378
+ },
379
+ // ===========================================================================
380
+ // DASHBOARD - Teams
381
+ // ===========================================================================
382
+ teams: {
383
+ switcher: {
384
+ compact: "team-switcher-compact",
385
+ full: "team-switcher",
386
+ dropdown: "team-switcher-dropdown",
387
+ option: "team-option-{slug}",
388
+ manageLink: "manage-teams-link",
389
+ createButton: "create-team-button"
390
+ },
391
+ switchModal: {
392
+ container: "team-switch-modal"
393
+ },
394
+ create: {
395
+ dialog: "create-team-dialog",
396
+ button: "create-team-button",
397
+ nameInput: "team-name-input",
398
+ slugInput: "team-slug-input",
399
+ descriptionInput: "team-description-input",
400
+ cancel: "cancel-create-team",
401
+ submit: "submit-create-team"
402
+ },
403
+ members: {
404
+ section: "team-members-section",
405
+ row: "member-row-{id}",
406
+ actions: "member-actions-{id}",
407
+ makeRole: "make-{role}-action",
408
+ remove: "remove-member-action"
409
+ },
410
+ invite: {
411
+ button: "invite-member-button",
412
+ buttonDisabled: "invite-member-button-disabled",
413
+ dialog: "invite-member-dialog",
414
+ emailInput: "member-email-input",
415
+ roleSelect: "member-role-select",
416
+ roleOption: "role-option-{role}",
417
+ cancel: "cancel-invite-member",
418
+ submit: "submit-invite-member"
419
+ },
420
+ invitations: {
421
+ row: "invitation-row-{id}",
422
+ cancel: "cancel-invitation-{id}"
423
+ }
424
+ },
425
+ // ===========================================================================
426
+ // DASHBOARD - Block Editor (Pages & Posts)
427
+ // ===========================================================================
428
+ blockEditor: {
429
+ container: "builder-editor",
430
+ titleInput: "editor-title-input",
431
+ slugInput: "editor-slug-input",
432
+ saveButton: "save-btn",
433
+ statusBadge: "status-badge",
434
+ leftSidebarToggle: "left-sidebar-toggle",
435
+ viewModeToggle: "view-mode-toggle",
436
+ blockPicker: {
437
+ container: "block-picker",
438
+ searchInput: "block-search-input",
439
+ categoryAll: "category-all",
440
+ category: "category-{category}",
441
+ blockItem: "block-item-{slug}",
442
+ addBlock: "add-block-{slug}"
443
+ },
444
+ blockCanvas: {
445
+ container: "block-preview-canvas",
446
+ empty: "block-preview-canvas-empty"
447
+ },
448
+ previewCanvas: {
449
+ container: "block-preview-canvas",
450
+ empty: "block-preview-canvas-empty",
451
+ block: "preview-block-{id}",
452
+ moveUp: "preview-block-{id}-move-up",
453
+ moveDown: "preview-block-{id}-move-down"
454
+ },
455
+ sortableBlock: {
456
+ container: "sortable-block-{id}",
457
+ dragHandle: "drag-handle-{id}",
458
+ duplicate: "duplicate-block-{id}",
459
+ remove: "remove-block-{id}",
460
+ error: "block-error-{id}"
461
+ },
462
+ settingsPanel: {
463
+ container: "block-settings-panel",
464
+ empty: "settings-panel-empty",
465
+ error: "settings-panel-error",
466
+ resetProps: "reset-block-props",
467
+ removeBlock: "remove-block-settings",
468
+ tabContent: "tab-content",
469
+ tabDesign: "tab-design",
470
+ tabAdvanced: "tab-advanced"
471
+ },
472
+ pageSettings: {
473
+ container: "page-settings-panel",
474
+ seoTrigger: "seo-settings-trigger",
475
+ metaTitle: "seo-meta-title",
476
+ metaDescription: "seo-meta-description",
477
+ metaKeywords: "seo-meta-keywords",
478
+ ogImage: "seo-og-image",
479
+ customFieldsTrigger: "custom-fields-trigger",
480
+ customFieldKey: "custom-field-key-{index}",
481
+ customFieldValue: "custom-field-value-{index}",
482
+ customFieldRemove: "custom-field-remove-{index}",
483
+ addCustomField: "add-custom-field"
484
+ },
485
+ statusSelector: {
486
+ trigger: "status-selector",
487
+ option: "status-option-{value}"
488
+ },
489
+ dynamicForm: {
490
+ container: "dynamic-form",
491
+ field: "field-{name}",
492
+ fieldGroup: "field-group-{id}",
493
+ arrayGroup: "array-group-{name}"
494
+ },
495
+ arrayField: {
496
+ container: "array-field-{name}",
497
+ item: "array-field-{name}-{index}-{field}",
498
+ moveUp: "array-field-{name}-{index}-move-up",
499
+ moveDown: "array-field-{name}-{index}-move-down",
500
+ remove: "array-field-{name}-{index}-remove",
501
+ add: "array-field-{name}-add"
502
+ },
503
+ entityFieldsSidebar: {
504
+ container: "entity-fields-sidebar",
505
+ field: "field-{name}",
506
+ category: "category-{slug}"
507
+ },
508
+ // Post-specific fields
509
+ postFields: {
510
+ excerpt: "field-excerpt",
511
+ featuredImage: "field-featuredImage",
512
+ featuredImageUpload: "field-featuredImage-upload",
513
+ categories: "field-categories",
514
+ categoryOption: "category-option-{id}",
515
+ categoryBadge: "category-badge-{id}",
516
+ categoryRemove: "category-remove-{id}"
517
+ },
518
+ // Page/Post locale field
519
+ localeField: {
520
+ select: "field-locale",
521
+ option: "locale-option-{locale}"
522
+ }
523
+ },
524
+ // ===========================================================================
525
+ // SETTINGS
526
+ // ===========================================================================
527
+ settings: {
528
+ layout: {
529
+ main: "settings-layout-main",
530
+ nav: "settings-layout-nav",
531
+ backToDashboard: "settings-layout-back-to-dashboard",
532
+ header: "settings-layout-header",
533
+ contentArea: "settings-layout-content-area",
534
+ sidebar: "settings-layout-sidebar",
535
+ pageContent: "settings-layout-page-content"
536
+ },
537
+ sidebar: {
538
+ main: "settings-sidebar-main",
539
+ header: "settings-sidebar-header",
540
+ navItems: "settings-sidebar-nav-items",
541
+ navItem: "settings-sidebar-nav-{section}"
542
+ },
543
+ overview: {
544
+ container: "settings-overview",
545
+ item: "settings-overview-{key}"
546
+ },
547
+ profile: {
548
+ container: "settings-profile",
549
+ form: "profile-form",
550
+ avatar: "profile-avatar",
551
+ avatarUpload: "profile-avatar-upload",
552
+ firstName: "profile-first-name",
553
+ lastName: "profile-last-name",
554
+ email: "profile-email",
555
+ submitButton: "profile-submit",
556
+ successMessage: "profile-success"
557
+ },
558
+ password: {
559
+ container: "settings-password",
560
+ form: "password-form",
561
+ currentPassword: "password-current",
562
+ newPassword: "password-new",
563
+ confirmPassword: "password-confirm",
564
+ submitButton: "password-submit",
565
+ successMessage: "password-success"
566
+ },
567
+ team: {
568
+ container: "settings-team",
569
+ name: "team-name",
570
+ slug: "team-slug",
571
+ description: "team-description",
572
+ avatar: "team-avatar",
573
+ avatarUpload: "team-avatar-upload",
574
+ submitButton: "team-submit",
575
+ deleteButton: "team-delete",
576
+ deleteDialog: "team-delete-dialog",
577
+ deleteConfirm: "team-delete-confirm"
578
+ },
579
+ members: {
580
+ container: "settings-members",
581
+ inviteButton: "members-invite",
582
+ inviteDialog: "members-invite-dialog",
583
+ inviteEmail: "members-invite-email",
584
+ inviteRole: "members-invite-role",
585
+ inviteSubmit: "members-invite-submit",
586
+ memberRow: "member-row-{id}",
587
+ memberRole: "member-role-{id}",
588
+ memberRemove: "member-remove-{id}",
589
+ pendingInvites: "members-pending-invites",
590
+ pendingInvite: "pending-invite-{id}",
591
+ cancelInvite: "cancel-invite-{id}"
592
+ },
593
+ billing: {
594
+ container: "settings-billing",
595
+ main: "billing-main",
596
+ header: "billing-header",
597
+ currentPlan: "billing-current-plan",
598
+ upgradeButton: "billing-upgrade",
599
+ upgradePlan: "billing-upgrade-plan",
600
+ cancelButton: "billing-cancel",
601
+ addPayment: "billing-add-payment",
602
+ invoicesTable: "billing-invoices",
603
+ invoicesTableAlt: "invoices-table",
604
+ invoiceRow: "invoice-row-{id}",
605
+ invoicesRow: "invoices-row",
606
+ invoiceDownload: "invoice-download-{id}",
607
+ invoicesLoadMore: "invoices-load-more",
608
+ invoiceStatusBadge: "invoice-status-badge",
609
+ paymentMethod: "billing-payment-method",
610
+ paymentMethodAlt: "payment-method",
611
+ updatePayment: "billing-update-payment",
612
+ usage: "billing-usage",
613
+ usageDashboard: "usage-dashboard"
614
+ },
615
+ pricing: {
616
+ table: "pricing-table",
617
+ settingsTable: "pricing-settings-table"
618
+ },
619
+ features: {
620
+ placeholder: "feature-placeholder-{feature}",
621
+ content: "{feature}-content",
622
+ placeholderUpgradeBtn: "placeholder-upgrade-btn"
623
+ },
624
+ apiKeys: {
625
+ page: "api-keys-page",
626
+ title: "api-keys-title",
627
+ container: "settings-api-keys",
628
+ createButton: "api-keys-create-button",
629
+ createDialog: "api-keys-create-dialog",
630
+ list: "api-keys-list",
631
+ skeleton: "api-keys-skeleton",
632
+ empty: "api-keys-empty",
633
+ emptyCreateButton: "api-keys-empty-create-button",
634
+ keyName: "api-key-name",
635
+ keyScopes: "api-key-scopes",
636
+ scopeOption: "api-key-scope-{scope}",
637
+ createSubmit: "api-key-create-submit",
638
+ keyRow: "api-key-row-{id}",
639
+ keyName_: "api-keys-name-{id}",
640
+ keyPrefix: "api-keys-prefix-{id}",
641
+ copyPrefix: "api-keys-copy-prefix-{id}",
642
+ keyStatus: "api-keys-status-{id}",
643
+ statusBadge: "api-keys-status-badge-{id}",
644
+ menuTrigger: "api-keys-menu-trigger-{id}",
645
+ menu: "api-keys-menu-{id}",
646
+ viewDetails: "api-keys-view-details-{id}",
647
+ toggle: "api-keys-toggle-{id}",
648
+ revoke: "api-keys-revoke-{id}",
649
+ scopes: "api-keys-scopes-{id}",
650
+ scope: "api-keys-scope-{id}-{scope}",
651
+ stats: "api-keys-stats-{id}",
652
+ totalRequests: "api-keys-total-requests-{id}",
653
+ last24h: "api-keys-last-24h-{id}",
654
+ avgTime: "api-keys-avg-time-{id}",
655
+ metadata: "api-keys-metadata-{id}",
656
+ createdAt: "api-keys-created-at-{id}",
657
+ lastUsed: "api-keys-last-used-{id}",
658
+ expiresAt: "api-keys-expires-at-{id}",
659
+ detailsDialog: "api-keys-details-dialog",
660
+ detailsTitle: "api-keys-details-title",
661
+ detailsLoading: "api-keys-details-loading",
662
+ detailsContent: "api-keys-details-content",
663
+ detailsBasicInfo: "api-keys-details-basic-info",
664
+ detailsName: "api-keys-details-name",
665
+ detailsStatus: "api-keys-details-status",
666
+ detailsStats: "api-keys-details-stats",
667
+ detailsTotalRequests: "api-keys-details-total-requests",
668
+ detailsLast24h: "api-keys-details-last-24h",
669
+ detailsLast7d: "api-keys-details-last-7d",
670
+ detailsLast30d: "api-keys-details-last-30d",
671
+ detailsAvgTime: "api-keys-details-avg-time",
672
+ detailsSuccessRate: "api-keys-details-success-rate",
673
+ keyReveal: "api-key-reveal-{id}",
674
+ keyRevoke: "api-key-revoke-{id}",
675
+ revokeDialog: "api-key-revoke-dialog",
676
+ revokeConfirm: "api-key-revoke-confirm",
677
+ newKeyDisplay: "api-key-new-display",
678
+ copyKey: "api-key-copy",
679
+ dialogFooter: "api-keys-dialog-footer"
680
+ },
681
+ notifications: {
682
+ container: "settings-notifications",
683
+ emailToggle: "notifications-email",
684
+ pushToggle: "notifications-push",
685
+ category: "notifications-{category}",
686
+ submitButton: "notifications-submit"
687
+ },
688
+ teams: {
689
+ main: "teams-settings-main",
690
+ header: "teams-settings-header",
691
+ loading: "teams-settings-loading",
692
+ singleUser: "teams-settings-single-user",
693
+ teamsList: "teams-settings-teams-list",
694
+ teamDetails: "teams-settings-team-details"
695
+ },
696
+ plans: {
697
+ main: "plans-settings-main",
698
+ header: "plans-settings-header",
699
+ table: "plans-settings-table"
700
+ }
701
+ },
702
+ // ===========================================================================
703
+ // SUPERADMIN (Super Admin Panel)
704
+ // ===========================================================================
705
+ superadmin: {
706
+ container: "superadmin-container",
707
+ navigation: {
708
+ dashboard: "superadmin-nav-dashboard",
709
+ users: "superadmin-nav-users",
710
+ teams: "superadmin-nav-teams",
711
+ teamRoles: "superadmin-nav-team-roles",
712
+ docs: "superadmin-nav-docs",
713
+ subscriptions: "superadmin-nav-subscriptions",
714
+ analytics: "superadmin-nav-analytics",
715
+ config: "superadmin-nav-config",
716
+ exitToDashboard: "superadmin-sidebar-exit-to-dashboard"
717
+ },
718
+ dashboard: {
719
+ container: "superadmin-dashboard"
720
+ },
721
+ users: {
722
+ container: "superadmin-users-container",
723
+ table: "superadmin-users-table",
724
+ search: "superadmin-users-search",
725
+ row: "superadmin-user-row-{id}",
726
+ viewButton: "superadmin-user-view-{id}",
727
+ editButton: "superadmin-user-edit-{id}",
728
+ banButton: "superadmin-user-ban-{id}",
729
+ deleteButton: "superadmin-user-delete-{id}",
730
+ impersonateButton: "superadmin-user-impersonate-{id}"
731
+ },
732
+ userDetail: {
733
+ container: "superadmin-user-detail",
734
+ email: "superadmin-user-email",
735
+ role: "superadmin-user-role",
736
+ status: "superadmin-user-status",
737
+ teams: "superadmin-user-teams",
738
+ activity: "superadmin-user-activity",
739
+ actions: "superadmin-user-actions",
740
+ // User Metadata
741
+ metas: "superadmin-user-metas",
742
+ metasTitle: "superadmin-user-metas-title",
743
+ metasTable: "superadmin-user-metas-table",
744
+ metasEmpty: "superadmin-user-metas-empty",
745
+ metaRow: "superadmin-user-meta-row-{key}",
746
+ metaKey: "superadmin-user-meta-key-{key}",
747
+ metaValue: "superadmin-user-meta-value-{key}",
748
+ metaType: "superadmin-user-meta-type-{key}",
749
+ metaPublic: "superadmin-user-meta-public-{key}",
750
+ metaSearchable: "superadmin-user-meta-searchable-{key}"
751
+ },
752
+ teams: {
753
+ container: "superadmin-teams-container",
754
+ table: "superadmin-teams-table",
755
+ search: "superadmin-teams-search",
756
+ row: "superadmin-team-row-{id}",
757
+ actionsButton: "superadmin-team-actions-{id}",
758
+ viewButton: "superadmin-team-view-{id}",
759
+ editButton: "superadmin-team-edit-{id}",
760
+ deleteButton: "superadmin-team-delete-{id}"
761
+ },
762
+ teamDetail: {
763
+ container: "superadmin-team-detail",
764
+ name: "superadmin-team-name",
765
+ owner: "superadmin-team-owner",
766
+ members: "superadmin-team-members",
767
+ plan: "superadmin-team-plan",
768
+ usage: "superadmin-team-usage"
769
+ },
770
+ subscriptions: {
771
+ container: "superadmin-subscriptions-container",
772
+ mrr: "superadmin-subscriptions-mrr",
773
+ planDistribution: "superadmin-subscriptions-plan-distribution",
774
+ planCount: "superadmin-subscriptions-plan-count-{plan}",
775
+ activeCount: "superadmin-subscriptions-active-count"
776
+ },
777
+ pagination: {
778
+ pageSize: "superadmin-page-size-select",
779
+ first: "superadmin-pagination-first",
780
+ prev: "superadmin-pagination-prev",
781
+ next: "superadmin-pagination-next",
782
+ last: "superadmin-pagination-last"
783
+ },
784
+ filters: {
785
+ search: "superadmin-search-{context}",
786
+ dropdown: "superadmin-filter-{context}"
787
+ },
788
+ permissions: {
789
+ row: "superadmin-permission-row-{permission}"
790
+ },
791
+ teamRoles: {
792
+ backButton: "back-to-superadmin",
793
+ roleCard: "role-card-{role}",
794
+ permissionRow: "permission-row-{permission}"
795
+ },
796
+ planFeatures: {
797
+ featureRow: "superadmin-feature-row-{slug}",
798
+ limitRow: "superadmin-limit-row-{slug}"
799
+ }
800
+ },
801
+ // ===========================================================================
802
+ // DEVTOOLS
803
+ // ===========================================================================
804
+ devtools: {
805
+ navigation: {
806
+ sidebar: "devtools-sidebar",
807
+ collapseToggle: "devtools-sidebar-collapse-toggle",
808
+ navItem: "devtools-nav-{section}",
809
+ exitToDashboard: "devtools-sidebar-exit-to-dashboard",
810
+ goToSuperadmin: "devtools-sidebar-go-to-superadmin",
811
+ mobileHeader: "devtools-mobile-header"
812
+ },
813
+ home: {
814
+ page: "devtools-home-page",
815
+ styleLink: "devtools-home-style-link",
816
+ testsLink: "devtools-home-tests-link",
817
+ configLink: "devtools-home-config-link"
818
+ },
819
+ style: {
820
+ page: "devtools-style-page",
821
+ tabComponents: "devtools-style-tab-components",
822
+ tabFieldTypes: "devtools-style-tab-field-types",
823
+ tabTheme: "devtools-style-tab-theme",
824
+ tabGuidelines: "devtools-style-tab-guidelines",
825
+ componentGallery: "devtools-style-component-gallery",
826
+ fieldTypes: "devtools-style-field-types",
827
+ themePreview: "devtools-style-theme-preview"
828
+ },
829
+ config: {
830
+ page: "devtools-config-page",
831
+ viewer: "devtools-config-viewer",
832
+ tabTheme: "devtools-config-tab-theme",
833
+ tabEntities: "devtools-config-tab-entities",
834
+ themeContent: "devtools-config-theme-content",
835
+ entitiesContent: "devtools-config-entities-content",
836
+ copyTheme: "devtools-config-copy-theme",
837
+ copyEntities: "devtools-config-copy-entities"
838
+ },
839
+ tests: {
840
+ page: "devtools-tests-page",
841
+ viewer: "devtools-tests-viewer",
842
+ loading: "devtools-tests-loading",
843
+ tree: "devtools-tests-tree",
844
+ folder: "devtools-tests-folder-{name}",
845
+ file: "devtools-tests-file-{name}",
846
+ content: "devtools-tests-content",
847
+ markdownContent: "devtools-tests-markdown-content",
848
+ notFound: "devtools-tests-not-found",
849
+ backToList: "devtools-tests-back-to-list",
850
+ emptyState: "devtools-tests-empty-state",
851
+ fileLoading: "devtools-tests-file-loading",
852
+ error: "devtools-tests-error",
853
+ // Dashboard
854
+ dashboard: "devtools-tests-dashboard",
855
+ dashboardButton: "devtools-tests-dashboard-button",
856
+ dashboardStats: "devtools-tests-dashboard-stats",
857
+ dashboardStatFeatures: "devtools-tests-dashboard-stat-features",
858
+ dashboardStatFlows: "devtools-tests-dashboard-stat-flows",
859
+ dashboardStatFiles: "devtools-tests-dashboard-stat-files",
860
+ dashboardStatTags: "devtools-tests-dashboard-stat-tags",
861
+ dashboardGaps: "devtools-tests-dashboard-gaps",
862
+ dashboardGapItem: "devtools-tests-dashboard-gap-{slug}"
863
+ },
864
+ features: {
865
+ page: "devtools-features-page",
866
+ viewer: "devtools-features-viewer",
867
+ search: "devtools-features-search",
868
+ filterAll: "devtools-features-filter-all",
869
+ filterCategory: "devtools-features-filter-{category}",
870
+ coverageAll: "devtools-features-coverage-all",
871
+ coverageCovered: "devtools-features-coverage-covered",
872
+ coverageUncovered: "devtools-features-coverage-uncovered",
873
+ card: "devtools-features-card-{slug}",
874
+ copyTag: "devtools-features-copy-{slug}"
875
+ },
876
+ flows: {
877
+ page: "devtools-flows-page",
878
+ viewer: "devtools-flows-viewer",
879
+ search: "devtools-flows-search",
880
+ filterAll: "devtools-flows-filter-all",
881
+ filterCategory: "devtools-flows-filter-{category}",
882
+ coverageAll: "devtools-flows-coverage-all",
883
+ coverageCovered: "devtools-flows-coverage-covered",
884
+ coverageUncovered: "devtools-flows-coverage-uncovered",
885
+ card: "devtools-flows-card-{slug}",
886
+ copyTag: "devtools-flows-copy-{slug}"
887
+ },
888
+ blocks: {
889
+ page: "devtools-blocks-page",
890
+ viewer: "devtools-blocks-viewer",
891
+ search: "devtools-blocks-search",
892
+ filterAll: "devtools-blocks-filter-all",
893
+ filterCategory: "devtools-blocks-filter-{category}",
894
+ coverageAll: "devtools-blocks-coverage-all",
895
+ coverageCovered: "devtools-blocks-coverage-covered",
896
+ coverageUncovered: "devtools-blocks-coverage-uncovered",
897
+ card: "devtools-blocks-card-{slug}",
898
+ copyTag: "devtools-blocks-copy-{slug}",
899
+ viewDetails: "devtools-blocks-view-{slug}",
900
+ detail: {
901
+ page: "devtools-block-detail-{slug}",
902
+ back: "devtools-block-detail-back",
903
+ tabPreview: "devtools-block-detail-tab-preview",
904
+ tabFields: "devtools-block-detail-tab-fields",
905
+ tabOverview: "devtools-block-detail-tab-overview",
906
+ preview: "devtools-block-detail-preview-{slug}",
907
+ exampleSelector: "devtools-block-example-selector",
908
+ exampleBtn: "devtools-block-example-btn-{index}",
909
+ exampleName: "devtools-block-example-name",
910
+ exampleDescription: "devtools-block-example-description"
911
+ }
912
+ },
913
+ tags: {
914
+ page: "devtools-tags-page",
915
+ viewer: "devtools-tags-viewer",
916
+ search: "devtools-tags-search",
917
+ category: "devtools-tags-category-{category}",
918
+ tag: "devtools-tags-tag-{tag}",
919
+ tagLink: "devtools-tags-link-{tag}",
920
+ filesPanel: "devtools-tags-files-panel-{tag}"
921
+ },
922
+ scheduledActions: {
923
+ page: "devtools-scheduled-actions-page",
924
+ filterStatus: "scheduled-actions-filter-status",
925
+ filterType: "scheduled-actions-filter-type",
926
+ filterApply: "scheduled-actions-filter-apply",
927
+ filterReset: "scheduled-actions-filter-reset",
928
+ table: "scheduled-actions-table",
929
+ row: "scheduled-actions-row-{id}",
930
+ cellType: "scheduled-actions-cell-type",
931
+ cellStatus: "scheduled-actions-cell-status",
932
+ cellScheduledAt: "scheduled-actions-cell-scheduled-at",
933
+ cellTeam: "scheduled-actions-cell-team",
934
+ cellPayload: "scheduled-actions-cell-payload",
935
+ cellError: "scheduled-actions-cell-error",
936
+ statusPending: "scheduled-actions-status-pending",
937
+ statusRunning: "scheduled-actions-status-running",
938
+ statusCompleted: "scheduled-actions-status-completed",
939
+ statusFailed: "scheduled-actions-status-failed",
940
+ pagination: "scheduled-actions-pagination",
941
+ paginationPrev: "scheduled-actions-pagination-prev",
942
+ paginationNext: "scheduled-actions-pagination-next",
943
+ emptyState: "scheduled-actions-empty-state"
944
+ }
945
+ },
946
+ // ===========================================================================
947
+ // PUBLIC PAGES
948
+ // ===========================================================================
949
+ public: {
950
+ navbar: {
951
+ container: "public-navbar",
952
+ logo: "navbar-logo",
953
+ loginButton: "navbar-login",
954
+ signupButton: "navbar-signup"
955
+ },
956
+ footer: {
957
+ container: "public-footer",
958
+ logo: "footer-logo"
959
+ },
960
+ page: {
961
+ container: "public-page-{slug}",
962
+ title: "page-title",
963
+ content: "page-content"
964
+ },
965
+ blog: {
966
+ listContainer: "blog-list",
967
+ postCard: "blog-post-{slug}"
968
+ }
969
+ },
970
+ // ===========================================================================
971
+ // COMMON / SHARED
972
+ // ===========================================================================
973
+ common: {
974
+ permissionDenied: "permission-denied",
975
+ loading: "loading-spinner",
976
+ error: "error-message",
977
+ toast: "toast-{type}",
978
+ modal: {
979
+ overlay: "modal-overlay",
980
+ container: "modal-container",
981
+ title: "modal-title",
982
+ close: "modal-close",
983
+ content: "modal-content",
984
+ footer: "modal-footer"
985
+ }
986
+ }
987
+ };
988
+
989
+ // src/selectors/selectors.ts
990
+ var helpers = createSelectorHelpers(CORE_SELECTORS);
991
+ var SELECTORS = helpers.SELECTORS;
992
+ var sel = helpers.sel;
993
+ var s = helpers.s;
994
+ var selDev = helpers.selDev;
995
+ var cySelector = helpers.cySelector;
996
+ var entitySelectors = helpers.entitySelectors;
997
+
998
+ // src/utils/utils.ts
999
+ var isDevelopment2 = process.env.NODE_ENV === "development";
1000
+ var isTest2 = process.env.NODE_ENV === "test";
1001
+ var enableTestingAttributes2 = isDevelopment2 || isTest2;
1002
+ function createTestId(component, element, action) {
1003
+ if (!enableTestingAttributes2) return void 0;
1004
+ const parts = [component, element, action].filter(Boolean);
1005
+ return parts.join("-");
1006
+ }
1007
+ function createCyId(domain, element) {
1008
+ if (!enableTestingAttributes2) return void 0;
1009
+ return `${domain}-${element}`;
1010
+ }
1011
+ function createStateAttr(state) {
1012
+ return state;
1013
+ }
1014
+ function createPriorityAttr(priority) {
1015
+ return priority;
1016
+ }
1017
+ function createTestingProps(config) {
1018
+ const props = {};
1019
+ if (config.testId) {
1020
+ props["data-testid"] = enableTestingAttributes2 ? config.testId : void 0;
1021
+ }
1022
+ if (config.cyId) {
1023
+ props["data-cy"] = enableTestingAttributes2 ? config.cyId : void 0;
1024
+ }
1025
+ if (config.state) {
1026
+ props["data-state"] = config.state;
1027
+ }
1028
+ if (config.priority) {
1029
+ props["data-priority"] = config.priority;
1030
+ }
1031
+ if (config.taskId) {
1032
+ props["data-task-id"] = config.taskId;
1033
+ }
1034
+ if (config.userId) {
1035
+ props["data-user-id"] = config.userId;
1036
+ }
1037
+ return Object.fromEntries(
1038
+ Object.entries(props).filter((entry) => entry[1] !== void 0)
1039
+ );
1040
+ }
1041
+ function createAriaLabel(template, values) {
1042
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
1043
+ return String(values[key] ?? match);
1044
+ });
1045
+ }
1046
+ var testingPatterns = {
1047
+ // Task/Todo components
1048
+ task: {
1049
+ card: (taskId) => createTestingProps({
1050
+ testId: createTestId("task", "card"),
1051
+ cyId: createCyId("task", "item"),
1052
+ taskId
1053
+ }),
1054
+ checkbox: () => createTestingProps({
1055
+ testId: createTestId("task", "checkbox"),
1056
+ cyId: createCyId("task", "toggle")
1057
+ }),
1058
+ title: () => createTestingProps({
1059
+ testId: createTestId("task", "title"),
1060
+ cyId: createCyId("task", "title")
1061
+ }),
1062
+ deleteButton: () => createTestingProps({
1063
+ testId: createTestId("task", "delete", "button"),
1064
+ cyId: createCyId("task", "delete")
1065
+ })
1066
+ },
1067
+ // Navigation components
1068
+ nav: {
1069
+ searchDropdown: () => createTestingProps({
1070
+ testId: createTestId("nav", "search", "dropdown"),
1071
+ cyId: createCyId("nav", "search")
1072
+ }),
1073
+ notifications: () => createTestingProps({
1074
+ testId: createTestId("nav", "notifications", "button"),
1075
+ cyId: createCyId("nav", "notifications")
1076
+ }),
1077
+ userMenu: () => createTestingProps({
1078
+ testId: createTestId("nav", "user", "menu"),
1079
+ cyId: createCyId("nav", "user-menu")
1080
+ })
1081
+ },
1082
+ // Form components
1083
+ form: {
1084
+ input: (fieldName) => createTestingProps({
1085
+ testId: createTestId("form", fieldName, "input"),
1086
+ cyId: createCyId("form", fieldName)
1087
+ }),
1088
+ submitButton: () => createTestingProps({
1089
+ testId: createTestId("form", "submit", "button"),
1090
+ cyId: createCyId("form", "submit")
1091
+ })
1092
+ }
1093
+ };
1094
+ var keyboardHelpers = {
1095
+ /**
1096
+ * Handle Enter and Space key activation
1097
+ */
1098
+ createActivationHandler: (onActivate) => {
1099
+ return (e) => {
1100
+ if (e.key === "Enter" || e.key === " ") {
1101
+ e.preventDefault();
1102
+ onActivate();
1103
+ }
1104
+ };
1105
+ },
1106
+ /**
1107
+ * Handle Escape key for closing
1108
+ */
1109
+ createEscapeHandler: (onClose) => {
1110
+ return (e) => {
1111
+ if (e.key === "Escape") {
1112
+ e.preventDefault();
1113
+ onClose();
1114
+ }
1115
+ };
1116
+ },
1117
+ /**
1118
+ * Handle arrow navigation in lists
1119
+ */
1120
+ createArrowNavigationHandler: (currentIndex, maxIndex, onIndexChange) => {
1121
+ return (e) => {
1122
+ switch (e.key) {
1123
+ case "ArrowDown":
1124
+ e.preventDefault();
1125
+ onIndexChange(currentIndex < maxIndex ? currentIndex + 1 : 0);
1126
+ break;
1127
+ case "ArrowUp":
1128
+ e.preventDefault();
1129
+ onIndexChange(currentIndex > 0 ? currentIndex - 1 : maxIndex);
1130
+ break;
1131
+ }
1132
+ };
1133
+ }
1134
+ };
1135
+ function createEntityCyId(entitySlug, component, detail) {
1136
+ if (!enableTestingAttributes2) return void 0;
1137
+ const parts = [entitySlug, component, detail].filter(Boolean);
1138
+ return parts.join("-");
1139
+ }
1140
+ function createEntityTestingHelper(entitySlug) {
1141
+ return {
1142
+ // Generic (framework-agnostic)
1143
+ get: (component, detail) => createEntityCyId(entitySlug, component, detail),
1144
+ // Page
1145
+ page: () => createEntityCyId(entitySlug, "page"),
1146
+ formPage: () => createEntityCyId(entitySlug, "form-page"),
1147
+ // Form
1148
+ form: () => createEntityCyId(entitySlug, "form"),
1149
+ formSubmit: () => createEntityCyId(entitySlug, "form-submit"),
1150
+ formCancel: () => createEntityCyId(entitySlug, "form-cancel"),
1151
+ // Fields
1152
+ field: (name) => createEntityCyId(entitySlug, "field", name),
1153
+ fieldOption: (name, value) => createEntityCyId(entitySlug, `field-${name}-option`, value),
1154
+ fieldError: (name) => createEntityCyId(entitySlug, `field-${name}`, "error"),
1155
+ // Sections
1156
+ section: (name) => createEntityCyId(entitySlug, "section", name),
1157
+ // Card
1158
+ card: (id) => createEntityCyId(entitySlug, "card", id),
1159
+ // List/Table
1160
+ table: () => createEntityCyId(entitySlug, "table"),
1161
+ row: (id) => createEntityCyId(entitySlug, "row", id),
1162
+ createBtn: () => createEntityCyId(entitySlug, "create-btn"),
1163
+ searchInput: () => createEntityCyId(entitySlug, "search-input"),
1164
+ // Filters
1165
+ filter: (field) => createEntityCyId(entitySlug, "filter", field),
1166
+ filterTrigger: (field) => createEntityCyId(entitySlug, `filter-${field}`, "trigger"),
1167
+ filterOption: (field, value) => createEntityCyId(entitySlug, `filter-${field}-option`, value),
1168
+ // Actions
1169
+ action: (action, id) => createEntityCyId(entitySlug, `action-${action}`, id),
1170
+ actionsTrigger: (id) => createEntityCyId(entitySlug, "actions-trigger", id),
1171
+ // Dialogs
1172
+ confirmDelete: () => createEntityCyId(entitySlug, "confirm-delete"),
1173
+ confirmDeleteBtn: () => createEntityCyId(entitySlug, "confirm-delete-btn"),
1174
+ cancelDeleteBtn: () => createEntityCyId(entitySlug, "cancel-delete-btn")
1175
+ };
1176
+ }
1177
+
1178
+ // src/pom/BasePOMCore.ts
1179
+ var BasePOMCore = class {
1180
+ /**
1181
+ * Get a Cypress selector using the centralized selectors
1182
+ * Wrapper for cySelector with a shorter name
1183
+ *
1184
+ * @example
1185
+ * this.cy('auth.login.form')
1186
+ * // Returns: '[data-cy="login-form"]'
1187
+ *
1188
+ * this.cy('entities.table.row', { slug: 'tasks', id: '123' })
1189
+ * // Returns: '[data-cy="tasks-row-123"]'
1190
+ */
1191
+ cy(path, replacements) {
1192
+ return this.cySelector(path, replacements);
1193
+ }
1194
+ /**
1195
+ * Replaces placeholders in a selector pattern and wraps with data-cy attribute
1196
+ *
1197
+ * @param pattern - Selector pattern with {placeholder} syntax
1198
+ * @param replacements - Object with placeholder values
1199
+ * @returns Formatted data-cy selector string
1200
+ *
1201
+ * @example
1202
+ * selector('{slug}-row-{id}', { slug: 'tasks', id: '123' })
1203
+ * // Returns: '[data-cy="tasks-row-123"]'
1204
+ */
1205
+ selector(pattern, replacements = {}) {
1206
+ let result = pattern;
1207
+ for (const [key, value] of Object.entries(replacements)) {
1208
+ result = result.replaceAll(`{${key}}`, String(value));
1209
+ }
1210
+ return `[data-cy="${result}"]`;
1211
+ }
1212
+ /**
1213
+ * Wrapper for cy.get with selector pattern support
1214
+ * @param pattern - Selector pattern or direct selector
1215
+ * @param replacements - Optional placeholder replacements
1216
+ */
1217
+ get(pattern, replacements = {}) {
1218
+ return cy.get(this.selector(pattern, replacements));
1219
+ }
1220
+ /**
1221
+ * Generic wait with configurable timeout
1222
+ * @param selector - CSS selector to wait for
1223
+ * @param timeout - Max wait time in ms (default: 15000)
1224
+ */
1225
+ waitFor(selector, timeout = 15e3) {
1226
+ cy.get(selector, { timeout }).should("be.visible");
1227
+ return this;
1228
+ }
1229
+ /**
1230
+ * Wait for URL to contain a specific path
1231
+ * @param path - Path segment to check for
1232
+ */
1233
+ waitForUrl(path) {
1234
+ cy.url().should("include", path);
1235
+ return this;
1236
+ }
1237
+ /**
1238
+ * Wait for URL to match a regex pattern
1239
+ * @param pattern - RegExp to match against URL
1240
+ */
1241
+ waitForUrlMatch(pattern) {
1242
+ cy.url().should("match", pattern);
1243
+ return this;
1244
+ }
1245
+ /**
1246
+ * Visit a URL and return self for chaining
1247
+ * @param url - URL to visit
1248
+ */
1249
+ visit(url) {
1250
+ cy.visit(url);
1251
+ return this;
1252
+ }
1253
+ /**
1254
+ * Wait for page to load (checks for body visible)
1255
+ */
1256
+ waitForPageLoad() {
1257
+ cy.get("body").should("be.visible");
1258
+ return this;
1259
+ }
1260
+ /**
1261
+ * Get an element by selector
1262
+ * @param selector - CSS selector
1263
+ */
1264
+ getElement(selector) {
1265
+ return cy.get(selector);
1266
+ }
1267
+ /**
1268
+ * Click on an element
1269
+ * @param selector - CSS selector
1270
+ */
1271
+ click(selector) {
1272
+ cy.get(selector).click();
1273
+ return this;
1274
+ }
1275
+ /**
1276
+ * Type text into an input
1277
+ * @param selector - CSS selector
1278
+ * @param text - Text to type
1279
+ */
1280
+ type(selector, text) {
1281
+ cy.get(selector).clear().type(text);
1282
+ return this;
1283
+ }
1284
+ /**
1285
+ * Check if element exists
1286
+ * @param selector - CSS selector
1287
+ */
1288
+ exists(selector) {
1289
+ return cy.get(selector).should("exist");
1290
+ }
1291
+ /**
1292
+ * Check if element is visible
1293
+ * @param selector - CSS selector
1294
+ */
1295
+ isVisible(selector) {
1296
+ return cy.get(selector).should("be.visible");
1297
+ }
1298
+ /**
1299
+ * Check if element does not exist
1300
+ * @param selector - CSS selector
1301
+ */
1302
+ notExists(selector) {
1303
+ return cy.get(selector).should("not.exist");
1304
+ }
1305
+ };
1306
+
1307
+ // src/helpers/ApiInterceptor.ts
1308
+ var ApiInterceptor = class {
1309
+ constructor(slugOrConfig) {
1310
+ if (typeof slugOrConfig === "string") {
1311
+ this.slug = slugOrConfig;
1312
+ this.endpoint = `/api/v1/${slugOrConfig}`;
1313
+ } else {
1314
+ this.slug = slugOrConfig.slug;
1315
+ this.endpoint = slugOrConfig.customPath || `/api/v1/${slugOrConfig.slug}`;
1316
+ }
1317
+ }
1318
+ // ============================================
1319
+ // ACCESSORS
1320
+ // ============================================
1321
+ /** Get the API endpoint path */
1322
+ get path() {
1323
+ return this.endpoint;
1324
+ }
1325
+ /** Get the entity slug */
1326
+ get entitySlug() {
1327
+ return this.slug;
1328
+ }
1329
+ /** Get alias names for all operations */
1330
+ get aliases() {
1331
+ return {
1332
+ list: `${this.slug}List`,
1333
+ create: `${this.slug}Create`,
1334
+ update: `${this.slug}Update`,
1335
+ delete: `${this.slug}Delete`
1336
+ };
1337
+ }
1338
+ // ============================================
1339
+ // INTERCEPT SETUP
1340
+ // ============================================
1341
+ /**
1342
+ * Setup intercepts for all CRUD operations
1343
+ * Call this BEFORE navigation in beforeEach or at test start
1344
+ *
1345
+ * Note: We intercept both PUT and PATCH for updates since different
1346
+ * APIs may use different HTTP methods for updates.
1347
+ */
1348
+ setupCrudIntercepts() {
1349
+ cy.intercept("GET", `${this.endpoint}*`).as(this.aliases.list);
1350
+ cy.intercept("POST", this.endpoint).as(this.aliases.create);
1351
+ cy.intercept("PUT", `${this.endpoint}/*`).as(this.aliases.update);
1352
+ cy.intercept("PATCH", `${this.endpoint}/*`).as(`${this.aliases.update}Patch`);
1353
+ cy.intercept("DELETE", `${this.endpoint}/*`).as(this.aliases.delete);
1354
+ return this;
1355
+ }
1356
+ /**
1357
+ * Setup only list + create intercepts
1358
+ * Useful for list pages with inline create
1359
+ */
1360
+ setupListIntercepts() {
1361
+ cy.intercept("GET", `${this.endpoint}*`).as(this.aliases.list);
1362
+ cy.intercept("POST", this.endpoint).as(this.aliases.create);
1363
+ return this;
1364
+ }
1365
+ // ============================================
1366
+ // WAIT METHODS
1367
+ // ============================================
1368
+ /**
1369
+ * Wait for list response (GET)
1370
+ * Use after navigation or after mutations to wait for refresh
1371
+ */
1372
+ waitForList(timeout = 1e4) {
1373
+ return cy.wait(`@${this.aliases.list}`, { timeout });
1374
+ }
1375
+ /**
1376
+ * Wait for create response (POST) and validate success status
1377
+ */
1378
+ waitForCreate(timeout = 1e4) {
1379
+ return cy.wait(`@${this.aliases.create}`, { timeout }).its("response.statusCode").should("be.oneOf", [200, 201]);
1380
+ }
1381
+ /**
1382
+ * Wait for update response (PATCH or PUT) and validate success status
1383
+ * Waits for PATCH first (more common), falls back to PUT
1384
+ */
1385
+ waitForUpdate(timeout = 1e4) {
1386
+ return cy.wait(`@${this.aliases.update}Patch`, { timeout }).its("response.statusCode").should("be.oneOf", [200, 201]);
1387
+ }
1388
+ /**
1389
+ * Wait for delete response (DELETE) and validate success status
1390
+ */
1391
+ waitForDelete(timeout = 1e4) {
1392
+ return cy.wait(`@${this.aliases.delete}`, { timeout }).its("response.statusCode").should("be.oneOf", [200, 204]);
1393
+ }
1394
+ // ============================================
1395
+ // CONVENIENCE METHODS
1396
+ // ============================================
1397
+ /**
1398
+ * Wait for list refresh (alias for waitForList)
1399
+ * Semantic name for use after create/update/delete
1400
+ */
1401
+ waitForRefresh(timeout = 1e4) {
1402
+ return this.waitForList(timeout);
1403
+ }
1404
+ /**
1405
+ * Wait for create + list refresh
1406
+ * Common pattern: create entity, wait for success, wait for list to refresh
1407
+ */
1408
+ waitForCreateAndRefresh(timeout = 1e4) {
1409
+ this.waitForCreate(timeout);
1410
+ return this.waitForList(timeout);
1411
+ }
1412
+ /**
1413
+ * Wait for update + list refresh
1414
+ * Common pattern: update entity, wait for success, wait for list to refresh
1415
+ */
1416
+ waitForUpdateAndRefresh(timeout = 1e4) {
1417
+ this.waitForUpdate(timeout);
1418
+ return this.waitForList(timeout);
1419
+ }
1420
+ /**
1421
+ * Wait for delete + list refresh
1422
+ * Common pattern: delete entity, wait for success, wait for list to refresh
1423
+ */
1424
+ waitForDeleteAndRefresh(timeout = 1e4) {
1425
+ this.waitForDelete(timeout);
1426
+ return this.waitForList(timeout);
1427
+ }
1428
+ };
1429
+
1430
+ // src/pom/DashboardEntityPOMCore.ts
1431
+ var DashboardEntityPOMCore = class extends BasePOMCore {
1432
+ constructor(entitySlugOrConfig) {
1433
+ super();
1434
+ this._api = null;
1435
+ if (typeof entitySlugOrConfig === "string") {
1436
+ this.slug = entitySlugOrConfig;
1437
+ this.entityConfig = { slug: entitySlugOrConfig };
1438
+ } else {
1439
+ this.slug = entitySlugOrConfig.slug;
1440
+ this.entityConfig = entitySlugOrConfig;
1441
+ }
1442
+ }
1443
+ /**
1444
+ * Get the entity slug (public accessor)
1445
+ * Useful for building dynamic selectors and URLs in tests
1446
+ */
1447
+ get entitySlug() {
1448
+ return this.slug;
1449
+ }
1450
+ // ============================================
1451
+ // API INTERCEPTOR
1452
+ // ============================================
1453
+ /**
1454
+ * Get or create ApiInterceptor instance for this entity
1455
+ */
1456
+ get api() {
1457
+ if (!this._api) {
1458
+ this._api = new ApiInterceptor(this.slug);
1459
+ }
1460
+ return this._api;
1461
+ }
1462
+ /**
1463
+ * Setup API intercepts for all CRUD operations
1464
+ * Call this BEFORE navigation
1465
+ */
1466
+ setupApiIntercepts() {
1467
+ this.api.setupCrudIntercepts();
1468
+ return this;
1469
+ }
1470
+ // ============================================
1471
+ // SELECTORS (uses cySelector from theme)
1472
+ // ============================================
1473
+ /**
1474
+ * Get all selectors for this entity, with placeholders replaced
1475
+ * Uses the abstract cySelector method which themes implement
1476
+ */
1477
+ get selectors() {
1478
+ const slug = this.slug;
1479
+ return {
1480
+ // Page
1481
+ page: this.cySelector("entities.page.container", { slug }),
1482
+ pageTitle: this.cySelector("entities.page.title", { slug }),
1483
+ // Table
1484
+ tableContainer: this.cySelector("entities.table.container", { slug }),
1485
+ table: this.cySelector("entities.table.element", { slug }),
1486
+ addButton: this.cySelector("entities.table.addButton", { slug }),
1487
+ search: this.cySelector("entities.table.search", { slug }),
1488
+ searchContainer: this.cySelector("entities.table.search", { slug }),
1489
+ searchClear: this.cySelector("entities.search.clear", { slug }),
1490
+ selectAll: this.cySelector("entities.table.selectAll", { slug }),
1491
+ selectionCount: this.cySelector("entities.table.selectionCount", { slug }),
1492
+ row: (id) => this.cySelector("entities.table.row", { slug, id }),
1493
+ rowSelect: (id) => this.cySelector("entities.table.rowSelect", { slug, id }),
1494
+ rowMenu: (id) => this.cySelector("entities.table.rowMenu", { slug, id }),
1495
+ rowAction: (action, id) => this.cySelector("entities.table.rowAction", { slug, action, id }),
1496
+ cell: (field, id) => this.cySelector("entities.table.cell", { slug, field, id }),
1497
+ rowGeneric: `[data-cy^="${slug}-row-"]`,
1498
+ // Pagination
1499
+ pagination: this.cySelector("entities.pagination.container", { slug }),
1500
+ pageSize: this.cySelector("entities.pagination.pageSize", { slug }),
1501
+ pageSizeOption: (size) => this.cySelector("entities.pagination.pageSizeOption", { slug, size }),
1502
+ pageInfo: this.cySelector("entities.pagination.pageInfo", { slug }),
1503
+ pageFirst: this.cySelector("entities.pagination.first", { slug }),
1504
+ pagePrev: this.cySelector("entities.pagination.prev", { slug }),
1505
+ pageNext: this.cySelector("entities.pagination.next", { slug }),
1506
+ pageLast: this.cySelector("entities.pagination.last", { slug }),
1507
+ // Header (detail pages) - modes: view, edit, create
1508
+ viewHeader: this.cySelector("entities.header.container", { slug, mode: "view" }),
1509
+ editHeader: this.cySelector("entities.header.container", { slug, mode: "edit" }),
1510
+ createHeader: this.cySelector("entities.header.container", { slug, mode: "create" }),
1511
+ backButton: this.cySelector("entities.header.backButton", { slug }),
1512
+ editButton: this.cySelector("entities.header.editButton", { slug }),
1513
+ deleteButton: this.cySelector("entities.header.deleteButton", { slug }),
1514
+ copyId: this.cySelector("entities.header.copyId", { slug }),
1515
+ title: this.cySelector("entities.header.title", { slug }),
1516
+ // Delete confirmation
1517
+ deleteDialog: this.cySelector("entities.header.deleteDialog", { slug }),
1518
+ deleteCancel: this.cySelector("entities.header.deleteCancel", { slug }),
1519
+ deleteConfirm: this.cySelector("entities.header.deleteConfirm", { slug }),
1520
+ // Form
1521
+ form: this.cySelector("entities.form.container", { slug }),
1522
+ field: (name) => this.cySelector("entities.form.field", { slug, name }),
1523
+ submitButton: this.cySelector("entities.form.submitButton", { slug }),
1524
+ // Filters
1525
+ filter: (field) => this.cySelector("entities.filter.container", { slug, field }),
1526
+ filterTrigger: (field) => this.cySelector("entities.filter.trigger", { slug, field }),
1527
+ filterContent: (field) => this.cySelector("entities.filter.content", { slug, field }),
1528
+ filterOption: (field, value) => this.cySelector("entities.filter.option", { slug, field, value }),
1529
+ filterBadge: (field, value) => this.cySelector("entities.filter.badge", { slug, field, value }),
1530
+ filterRemoveBadge: (field, value) => this.cySelector("entities.filter.removeBadge", { slug, field, value }),
1531
+ filterClearAll: (field) => this.cySelector("entities.filter.clearAll", { slug, field }),
1532
+ // Bulk actions
1533
+ bulkBar: this.cySelector("entities.bulk.bar", { slug }),
1534
+ bulkCount: this.cySelector("entities.bulk.count", { slug }),
1535
+ bulkSelectAll: this.cySelector("entities.bulk.selectAll", { slug }),
1536
+ bulkStatus: this.cySelector("entities.bulk.statusButton", { slug }),
1537
+ bulkDelete: this.cySelector("entities.bulk.deleteButton", { slug }),
1538
+ bulkClear: this.cySelector("entities.bulk.clearButton", { slug }),
1539
+ // Bulk status dialog
1540
+ bulkStatusDialog: this.cySelector("entities.bulk.statusDialog", { slug }),
1541
+ bulkStatusSelect: this.cySelector("entities.bulk.statusSelect", { slug }),
1542
+ bulkStatusOption: (value) => this.cySelector("entities.bulk.statusOption", { slug, value }),
1543
+ bulkStatusCancel: this.cySelector("entities.bulk.statusCancel", { slug }),
1544
+ bulkStatusConfirm: this.cySelector("entities.bulk.statusConfirm", { slug }),
1545
+ // Bulk delete dialog
1546
+ bulkDeleteDialog: this.cySelector("entities.bulk.deleteDialog", { slug }),
1547
+ bulkDeleteCancel: this.cySelector("entities.bulk.deleteCancel", { slug }),
1548
+ bulkDeleteConfirm: this.cySelector("entities.bulk.deleteConfirm", { slug }),
1549
+ // Generic confirm dialog
1550
+ confirmDialog: this.cySelector("entities.confirm.dialog", { slug }),
1551
+ confirmCancel: this.cySelector("entities.confirm.cancel", { slug }),
1552
+ confirmAction: this.cySelector("entities.confirm.action", { slug }),
1553
+ // Parent delete confirmation (EntityDetailWrapper - generic, no slug)
1554
+ parentDeleteConfirm: '[data-cy="confirm-delete"]',
1555
+ parentDeleteCancel: '[data-cy="cancel-delete"]',
1556
+ // Row action selectors (generic patterns for checking existence)
1557
+ rowActionEditGeneric: `[data-cy^="${slug}-action-edit-"]`,
1558
+ rowActionDeleteGeneric: `[data-cy^="${slug}-action-delete-"]`,
1559
+ // Detail view
1560
+ detail: this.cySelector("entities.detail.container", { slug })
1561
+ };
1562
+ }
1563
+ // ============================================
1564
+ // NAVIGATION
1565
+ // ============================================
1566
+ /**
1567
+ * Navigate to entity list page
1568
+ */
1569
+ visitList() {
1570
+ cy.visit(`/dashboard/${this.slug}`);
1571
+ return this;
1572
+ }
1573
+ /**
1574
+ * Navigate to create page
1575
+ */
1576
+ visitCreate() {
1577
+ cy.visit(`/dashboard/${this.slug}/create`);
1578
+ return this;
1579
+ }
1580
+ /**
1581
+ * Navigate to edit page for specific entity
1582
+ */
1583
+ visitEdit(id) {
1584
+ cy.visit(`/dashboard/${this.slug}/${id}/edit`);
1585
+ return this;
1586
+ }
1587
+ /**
1588
+ * Navigate to detail/view page for specific entity
1589
+ */
1590
+ visitDetail(id) {
1591
+ cy.visit(`/dashboard/${this.slug}/${id}`);
1592
+ return this;
1593
+ }
1594
+ // ============================================
1595
+ // API-AWARE NAVIGATION
1596
+ // ============================================
1597
+ /**
1598
+ * Navigate to list and wait for API response
1599
+ */
1600
+ visitListWithApiWait() {
1601
+ this.setupApiIntercepts();
1602
+ this.visitList();
1603
+ this.api.waitForList();
1604
+ return this;
1605
+ }
1606
+ /**
1607
+ * Navigate to edit page and wait for form to be visible
1608
+ */
1609
+ visitEditWithApiWait(id) {
1610
+ this.setupApiIntercepts();
1611
+ this.visitEdit(id);
1612
+ this.waitForForm();
1613
+ return this;
1614
+ }
1615
+ /**
1616
+ * Navigate to detail page and wait for content
1617
+ */
1618
+ visitDetailWithApiWait(id) {
1619
+ this.setupApiIntercepts();
1620
+ this.visitDetail(id);
1621
+ this.waitForDetail();
1622
+ return this;
1623
+ }
1624
+ // ============================================
1625
+ // WAITS
1626
+ // ============================================
1627
+ /**
1628
+ * Wait for list page to be fully loaded
1629
+ */
1630
+ waitForList() {
1631
+ cy.url().should("include", `/dashboard/${this.slug}`);
1632
+ cy.get(this.selectors.tableContainer, { timeout: 15e3 }).should("be.visible");
1633
+ return this;
1634
+ }
1635
+ /**
1636
+ * Wait for form to be visible
1637
+ */
1638
+ waitForForm() {
1639
+ cy.get(this.selectors.form, { timeout: 15e3 }).should("be.visible");
1640
+ return this;
1641
+ }
1642
+ /**
1643
+ * Wait for detail page to be loaded
1644
+ */
1645
+ waitForDetail() {
1646
+ cy.url().should("match", new RegExp(`/dashboard/${this.slug}/[a-z0-9-]+$`));
1647
+ cy.get(this.selectors.editButton, { timeout: 15e3 }).should("be.visible");
1648
+ return this;
1649
+ }
1650
+ // ============================================
1651
+ // TABLE ACTIONS
1652
+ // ============================================
1653
+ /**
1654
+ * Click the Add/Create button
1655
+ */
1656
+ clickAdd() {
1657
+ cy.get(this.selectors.addButton).click();
1658
+ return this;
1659
+ }
1660
+ /**
1661
+ * Type in the search input
1662
+ */
1663
+ search(term) {
1664
+ cy.get(this.selectors.search).clear().type(term);
1665
+ return this;
1666
+ }
1667
+ /**
1668
+ * Clear the search input
1669
+ */
1670
+ clearSearch() {
1671
+ cy.get(this.selectors.searchClear).click();
1672
+ return this;
1673
+ }
1674
+ /**
1675
+ * Click a specific row by ID
1676
+ */
1677
+ clickRow(id) {
1678
+ cy.get(this.selectors.row(id)).click();
1679
+ return this;
1680
+ }
1681
+ /**
1682
+ * Find and click a row containing specific text
1683
+ */
1684
+ clickRowByText(text) {
1685
+ cy.contains(this.selectors.rowGeneric, text).click();
1686
+ return this;
1687
+ }
1688
+ /**
1689
+ * Select a row checkbox
1690
+ */
1691
+ selectRow(id) {
1692
+ cy.get(this.selectors.rowSelect(id)).click();
1693
+ return this;
1694
+ }
1695
+ /**
1696
+ * Open the row menu (three dots)
1697
+ */
1698
+ openRowMenu(id) {
1699
+ cy.get(this.selectors.rowMenu(id)).click();
1700
+ return this;
1701
+ }
1702
+ /**
1703
+ * Click an action in the row menu
1704
+ */
1705
+ clickRowAction(action, id) {
1706
+ cy.get(this.selectors.rowAction(action, id)).click();
1707
+ return this;
1708
+ }
1709
+ // ============================================
1710
+ // FILTERS
1711
+ // ============================================
1712
+ /**
1713
+ * Open a filter dropdown
1714
+ */
1715
+ openFilter(field) {
1716
+ cy.get(this.selectors.filterTrigger(field)).click();
1717
+ return this;
1718
+ }
1719
+ /**
1720
+ * Select a filter option
1721
+ */
1722
+ selectFilterOption(field, value) {
1723
+ cy.get(this.selectors.filterOption(field, value)).click();
1724
+ return this;
1725
+ }
1726
+ /**
1727
+ * Open filter and select option (convenience method)
1728
+ */
1729
+ selectFilter(field, value) {
1730
+ this.openFilter(field);
1731
+ this.selectFilterOption(field, value);
1732
+ return this;
1733
+ }
1734
+ /**
1735
+ * Remove a filter badge
1736
+ */
1737
+ removeFilterBadge(field, value) {
1738
+ cy.get(this.selectors.filterRemoveBadge(field, value)).click();
1739
+ return this;
1740
+ }
1741
+ /**
1742
+ * Clear all values for a filter
1743
+ */
1744
+ clearFilter(field) {
1745
+ cy.get(this.selectors.filterClearAll(field)).click();
1746
+ return this;
1747
+ }
1748
+ // ============================================
1749
+ // PAGINATION
1750
+ // ============================================
1751
+ /**
1752
+ * Go to next page
1753
+ */
1754
+ nextPage() {
1755
+ cy.get(this.selectors.pageNext).click();
1756
+ return this;
1757
+ }
1758
+ /**
1759
+ * Go to previous page
1760
+ */
1761
+ prevPage() {
1762
+ cy.get(this.selectors.pagePrev).click();
1763
+ return this;
1764
+ }
1765
+ /**
1766
+ * Go to first page
1767
+ */
1768
+ firstPage() {
1769
+ cy.get(this.selectors.pageFirst).click();
1770
+ return this;
1771
+ }
1772
+ /**
1773
+ * Go to last page
1774
+ */
1775
+ lastPage() {
1776
+ cy.get(this.selectors.pageLast).click();
1777
+ return this;
1778
+ }
1779
+ /**
1780
+ * Change page size
1781
+ */
1782
+ setPageSize(size) {
1783
+ cy.get(this.selectors.pageSize).click();
1784
+ cy.get(this.selectors.pageSizeOption(size)).click();
1785
+ return this;
1786
+ }
1787
+ // ============================================
1788
+ // FORM ACTIONS
1789
+ // ============================================
1790
+ /**
1791
+ * Fill a text input field
1792
+ */
1793
+ fillTextField(name, value) {
1794
+ cy.get(this.selectors.field(name)).find("input").clear().type(value);
1795
+ return this;
1796
+ }
1797
+ /**
1798
+ * Fill a textarea field
1799
+ */
1800
+ fillTextarea(name, value) {
1801
+ cy.get(this.selectors.field(name)).find("textarea").clear().type(value);
1802
+ return this;
1803
+ }
1804
+ /**
1805
+ * Select an option in a combobox/select field
1806
+ */
1807
+ selectOption(name, value) {
1808
+ cy.get(this.selectors.field(name)).find('[role="combobox"]').click();
1809
+ cy.get(`[data-cy="${this.slug}-field-${name}-option-${value}"]`).click();
1810
+ return this;
1811
+ }
1812
+ /**
1813
+ * Submit the form
1814
+ */
1815
+ submitForm() {
1816
+ cy.get(this.selectors.submitButton).click();
1817
+ return this;
1818
+ }
1819
+ // ============================================
1820
+ // HEADER/DETAIL ACTIONS
1821
+ // ============================================
1822
+ /**
1823
+ * Click back button
1824
+ */
1825
+ clickBack() {
1826
+ cy.get(this.selectors.backButton).click();
1827
+ return this;
1828
+ }
1829
+ /**
1830
+ * Click edit button
1831
+ */
1832
+ clickEdit() {
1833
+ cy.get(this.selectors.editButton).click();
1834
+ return this;
1835
+ }
1836
+ /**
1837
+ * Click delete button
1838
+ */
1839
+ clickDelete() {
1840
+ cy.get(this.selectors.deleteButton).click();
1841
+ return this;
1842
+ }
1843
+ /**
1844
+ * Confirm delete in dialog
1845
+ */
1846
+ confirmDelete() {
1847
+ cy.get(this.selectors.deleteConfirm).click();
1848
+ return this;
1849
+ }
1850
+ /**
1851
+ * Cancel delete in dialog
1852
+ */
1853
+ cancelDelete() {
1854
+ cy.get(this.selectors.deleteCancel).click();
1855
+ return this;
1856
+ }
1857
+ // ============================================
1858
+ // BULK ACTIONS
1859
+ // ============================================
1860
+ /**
1861
+ * Select all items using table header checkbox
1862
+ */
1863
+ selectAll() {
1864
+ cy.get(this.selectors.selectAll).click();
1865
+ return this;
1866
+ }
1867
+ /**
1868
+ * Click bulk delete button
1869
+ */
1870
+ bulkDelete() {
1871
+ cy.get(this.selectors.bulkDelete).click();
1872
+ return this;
1873
+ }
1874
+ /**
1875
+ * Confirm bulk delete
1876
+ */
1877
+ confirmBulkDelete() {
1878
+ cy.get(this.selectors.bulkDeleteConfirm).click();
1879
+ return this;
1880
+ }
1881
+ /**
1882
+ * Cancel bulk delete
1883
+ */
1884
+ cancelBulkDelete() {
1885
+ cy.get(this.selectors.bulkDeleteCancel).click();
1886
+ return this;
1887
+ }
1888
+ /**
1889
+ * Click bulk status button
1890
+ */
1891
+ bulkChangeStatus() {
1892
+ cy.get(this.selectors.bulkStatus).click();
1893
+ return this;
1894
+ }
1895
+ /**
1896
+ * Select status in bulk status dialog
1897
+ */
1898
+ selectBulkStatus(value) {
1899
+ cy.get(this.selectors.bulkStatusSelect).click();
1900
+ cy.get(this.selectors.bulkStatusOption(value)).click();
1901
+ return this;
1902
+ }
1903
+ /**
1904
+ * Confirm bulk status change
1905
+ */
1906
+ confirmBulkStatus() {
1907
+ cy.get(this.selectors.bulkStatusConfirm).click();
1908
+ return this;
1909
+ }
1910
+ /**
1911
+ * Clear selection
1912
+ */
1913
+ clearSelection() {
1914
+ cy.get(this.selectors.bulkClear).click();
1915
+ return this;
1916
+ }
1917
+ // ============================================
1918
+ // ASSERTIONS
1919
+ // ============================================
1920
+ /**
1921
+ * Assert text is visible in the list
1922
+ */
1923
+ assertInList(text) {
1924
+ cy.contains(text).should("be.visible");
1925
+ return this;
1926
+ }
1927
+ /**
1928
+ * Assert text is not in the list
1929
+ */
1930
+ assertNotInList(text) {
1931
+ cy.contains(text).should("not.exist");
1932
+ return this;
1933
+ }
1934
+ /**
1935
+ * Assert table is visible
1936
+ */
1937
+ assertTableVisible() {
1938
+ cy.get(this.selectors.table).should("be.visible");
1939
+ return this;
1940
+ }
1941
+ /**
1942
+ * Assert form is visible
1943
+ */
1944
+ assertFormVisible() {
1945
+ cy.get(this.selectors.form).should("be.visible");
1946
+ return this;
1947
+ }
1948
+ /**
1949
+ * Assert page title contains text
1950
+ */
1951
+ assertPageTitle(expected) {
1952
+ cy.get(this.selectors.title).should("contain.text", expected);
1953
+ return this;
1954
+ }
1955
+ /**
1956
+ * Assert row exists
1957
+ */
1958
+ assertRowExists(id) {
1959
+ cy.get(this.selectors.row(id)).should("exist");
1960
+ return this;
1961
+ }
1962
+ /**
1963
+ * Assert row does not exist
1964
+ */
1965
+ assertRowNotExists(id) {
1966
+ cy.get(this.selectors.row(id)).should("not.exist");
1967
+ return this;
1968
+ }
1969
+ /**
1970
+ * Assert selection count
1971
+ */
1972
+ assertSelectionCount(count) {
1973
+ cy.get(this.selectors.selectionCount).should("contain.text", count.toString());
1974
+ return this;
1975
+ }
1976
+ /**
1977
+ * Assert bulk bar is visible
1978
+ */
1979
+ assertBulkBarVisible() {
1980
+ cy.get(this.selectors.bulkBar).should("be.visible");
1981
+ return this;
1982
+ }
1983
+ /**
1984
+ * Assert bulk bar is hidden
1985
+ */
1986
+ assertBulkBarHidden() {
1987
+ cy.get(this.selectors.bulkBar).should("not.be.visible");
1988
+ return this;
1989
+ }
1990
+ };
1991
+
1992
+ export { ApiInterceptor, BasePOMCore, CORE_SELECTORS, DashboardEntityPOMCore, SELECTORS, createAriaLabel, createCyId, createEntityCyId, createEntityTestingHelper, createPriorityAttr, createSelectorHelpers, createStateAttr, createTestId, createTestingProps, cySelector, entitySelectors, getNestedValue, keyboardHelpers, replacePlaceholders, s, sel, selDev, testingPatterns };
1993
+ //# sourceMappingURL=index.js.map
1994
+ //# sourceMappingURL=index.js.map