import { ApiInterceptor } from '../helpers/index.js'; /** * BasePOMCore - Base class with common utilities for all Page Object Models * * This is the CORE version that provides generic methods. * Themes should extend this class and implement the abstract `cySelector` method * to provide theme-specific selector resolution. * * @example Theme extension: * ```ts * // theme/tests/cypress/src/core/BasePOM.ts * import { BasePOMCore } from '@nextsparkjs/testing/pom' * import { cySelector } from '../selectors' * * export abstract class BasePOM extends BasePOMCore { * protected cySelector(path: string, replacements?: Record): string { * return cySelector(path, replacements) * } * } * ``` */ /** * Type for placeholder replacements in selectors * Mirrors @nextsparkjs/core/selectors Replacements type */ type Replacements = Record; declare abstract class BasePOMCore { /** * Abstract method - themes must implement to provide their cySelector * This allows themes to use their extended THEME_SELECTORS * * @param path - Dot-notation path to the selector (e.g., "auth.login.form") * @param replacements - Optional placeholder replacements * @returns Cypress selector string like [data-cy="selector-value"] */ protected abstract cySelector(path: string, replacements?: Replacements): string; /** * Get a Cypress selector using the centralized selectors * Wrapper for cySelector with a shorter name * * @example * this.cy('auth.login.form') * // Returns: '[data-cy="login-form"]' * * this.cy('entities.table.row', { slug: 'tasks', id: '123' }) * // Returns: '[data-cy="tasks-row-123"]' */ protected cy(path: string, replacements?: Replacements): string; /** * Replaces placeholders in a selector pattern and wraps with data-cy attribute * * @param pattern - Selector pattern with {placeholder} syntax * @param replacements - Object with placeholder values * @returns Formatted data-cy selector string * * @example * selector('{slug}-row-{id}', { slug: 'tasks', id: '123' }) * // Returns: '[data-cy="tasks-row-123"]' */ protected selector(pattern: string, replacements?: Replacements): string; /** * Wrapper for cy.get with selector pattern support * @param pattern - Selector pattern or direct selector * @param replacements - Optional placeholder replacements */ protected get(pattern: string, replacements?: Replacements): Cypress.Chainable>; /** * Generic wait with configurable timeout * @param selector - CSS selector to wait for * @param timeout - Max wait time in ms (default: 15000) */ protected waitFor(selector: string, timeout?: number): this; /** * Wait for URL to contain a specific path * @param path - Path segment to check for */ protected waitForUrl(path: string): this; /** * Wait for URL to match a regex pattern * @param pattern - RegExp to match against URL */ protected waitForUrlMatch(pattern: RegExp): this; /** * Visit a URL and return self for chaining * @param url - URL to visit */ visit(url: string): this; /** * Wait for page to load (checks for body visible) */ waitForPageLoad(): this; /** * Get an element by selector * @param selector - CSS selector */ getElement(selector: string): Cypress.Chainable>; /** * Click on an element * @param selector - CSS selector */ click(selector: string): this; /** * Type text into an input * @param selector - CSS selector * @param text - Text to type */ type(selector: string, text: string): this; /** * Check if element exists * @param selector - CSS selector */ exists(selector: string): Cypress.Chainable>; /** * Check if element is visible * @param selector - CSS selector */ isVisible(selector: string): Cypress.Chainable>; /** * Check if element does not exist * @param selector - CSS selector */ notExists(selector: string): Cypress.Chainable>; } /** * DashboardEntityPOMCore - Base class for all entity Page Object Models * * Provides standard CRUD operations for dashboard entities: * - Navigation (list, create, edit, detail pages) * - Table interactions (search, filters, pagination, row actions) * - Form operations (fill fields, submit, cancel) * - API interceptor integration for deterministic waits * - Bulk actions * - Delete confirmation dialogs * * This is the CORE version. Themes should extend this class and implement * the abstract `cySelector` method from BasePOMCore. * * @example Theme extension: * ```ts * // theme/tests/cypress/src/core/DashboardEntityPOM.ts * import { DashboardEntityPOMCore } from '@nextsparkjs/testing/pom' * import { cySelector } from '../selectors' * * export abstract class DashboardEntityPOM extends DashboardEntityPOMCore { * protected cySelector(path: string, replacements?: Record): string { * return cySelector(path, replacements) * } * } * ``` */ interface EntityConfig { slug: string; singular?: string; plural?: string; tableName?: string; fields?: string[]; filters?: string[]; } declare abstract class DashboardEntityPOMCore extends BasePOMCore { protected slug: string; protected entityConfig: EntityConfig; protected _api: ApiInterceptor | null; /** * Get the entity slug (public accessor) * Useful for building dynamic selectors and URLs in tests */ get entitySlug(): string; constructor(entitySlugOrConfig: string | EntityConfig); /** * Get or create ApiInterceptor instance for this entity */ get api(): ApiInterceptor; /** * Setup API intercepts for all CRUD operations * Call this BEFORE navigation */ setupApiIntercepts(): this; /** * Get all selectors for this entity, with placeholders replaced * Uses the abstract cySelector method which themes implement */ get selectors(): { page: string; tableContainer: string; table: string; addButton: string; selectAll: string; selectionCount: string; row: (id: string) => string; rowSelect: (id: string) => string; rowMenu: (id: string) => string; rowAction: (action: string, id: string) => string; cell: (name: string, id: string) => string; rowGeneric: string; search: string; searchContainer: string; searchClear: string; pagination: string; pageSize: string; pageSizeOption: (size: string) => string; pageInfo: string; pageFirst: string; pagePrev: string; pageNext: string; pageLast: string; filter: (field: string) => string; filterTrigger: (field: string) => string; filterContent: (field: string) => string; filterOption: (field: string, value: string) => string; filterBadge: (field: string, value: string) => string; filterRemoveBadge: (field: string, value: string) => string; filterClearAll: (field: string) => string; bulkBar: string; bulkCount: string; bulkSelectAll: string; bulkStatus: string; bulkDelete: string; bulkClear: string; bulkStatusDialog: string; bulkStatusSelect: string; bulkStatusOption: (value: string) => string; bulkStatusCancel: string; bulkStatusConfirm: string; bulkDeleteDialog: string; bulkDeleteCancel: string; bulkDeleteConfirm: string; confirmDialog: string; confirmCancel: string; confirmAction: string; viewHeader: string; editHeader: string; createHeader: string; backButton: string; editButton: string; deleteButton: string; title: string; deleteDialog: string; deleteCancel: string; deleteConfirm: string; detail: string; form: string; field: (name: string) => string; submitButton: string; parentDeleteConfirm: string; parentDeleteCancel: string; rowActionEditGeneric: string; rowActionDeleteGeneric: string; }; /** * Navigate to entity list page */ visitList(): this; /** * Navigate to create page */ visitCreate(): this; /** * Navigate to edit page for specific entity */ visitEdit(id: string): this; /** * Navigate to detail/view page for specific entity */ visitDetail(id: string): this; /** * Navigate to list and wait for API response */ visitListWithApiWait(): this; /** * Navigate to edit page and wait for form to be visible */ visitEditWithApiWait(id: string): this; /** * Navigate to detail page and wait for content */ visitDetailWithApiWait(id: string): this; /** * Wait for list page to be fully loaded */ waitForList(): this; /** * Wait for form to be visible */ waitForForm(): this; /** * Wait for detail page to be loaded */ waitForDetail(): this; /** * Click the Add/Create button */ clickAdd(): this; /** * Type in the search input */ search(term: string): this; /** * Clear the search input */ clearSearch(): this; /** * Click a specific row by ID */ clickRow(id: string): this; /** * Find and click a row containing specific text */ clickRowByText(text: string): this; /** * Select a row checkbox */ selectRow(id: string): this; /** * Open the row menu (three dots) */ openRowMenu(id: string): this; /** * Click an action in the row menu */ clickRowAction(action: string, id: string): this; /** * Open a filter dropdown */ openFilter(field: string): this; /** * Select a filter option */ selectFilterOption(field: string, value: string): this; /** * Open filter and select option (convenience method) */ selectFilter(field: string, value: string): this; /** * Clear all selected options for a specific filter * NOTE: Clear button only appears when >1 option is selected */ clearFilter(field: string): this; /** * Go to next page */ nextPage(): this; /** * Go to previous page */ prevPage(): this; /** * Go to first page */ firstPage(): this; /** * Go to last page */ lastPage(): this; /** * Change page size */ setPageSize(size: string): this; /** * Fill a text input field */ fillTextField(name: string, value: string): this; /** * Fill a textarea field */ fillTextarea(name: string, value: string): this; /** * Select an option in a combobox/select field */ selectOption(name: string, value: string): this; /** * Submit the form */ submitForm(): this; /** * Click back button */ clickBack(): this; /** * Click edit button */ clickEdit(): this; /** * Click delete button */ clickDelete(): this; /** * Confirm delete in dialog */ confirmDelete(): this; /** * Cancel delete in dialog */ cancelDelete(): this; /** * Select all items using table header checkbox */ selectAll(): this; /** * Click bulk delete button */ bulkDelete(): this; /** * Confirm bulk delete */ confirmBulkDelete(): this; /** * Cancel bulk delete */ cancelBulkDelete(): this; /** * Click bulk status button */ bulkChangeStatus(): this; /** * Select status in bulk status dialog */ selectBulkStatus(value: string): this; /** * Confirm bulk status change */ confirmBulkStatus(): this; /** * Clear selection */ clearSelection(): this; /** * Assert text is visible in the list */ assertInList(text: string): this; /** * Assert text is not in the list */ assertNotInList(text: string): this; /** * Assert table is visible */ assertTableVisible(): this; /** * Assert form is visible */ assertFormVisible(): this; /** * Assert page title contains text */ assertPageTitle(expected: string): this; /** * Assert row exists */ assertRowExists(id: string): this; /** * Assert row does not exist */ assertRowNotExists(id: string): this; /** * Assert selection count */ assertSelectionCount(count: number): this; /** * Assert bulk bar is visible */ assertBulkBarVisible(): this; /** * Assert bulk bar is hidden */ assertBulkBarHidden(): this; } export { BasePOMCore, DashboardEntityPOMCore, type EntityConfig };