@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/LICENSE +21 -0
- package/README.md +99 -0
- package/dist/helpers/index.d.ts +97 -0
- package/dist/helpers/index.js +126 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1994 -0
- package/dist/index.js.map +1 -0
- package/dist/pom/index.d.ts +473 -0
- package/dist/pom/index.js +817 -0
- package/dist/pom/index.js.map +1 -0
- package/dist/selector-factory-BivSWXbw.d.ts +123 -0
- package/dist/selectors/index.d.ts +1774 -0
- package/dist/selectors/index.js +1000 -0
- package/dist/selectors/index.js.map +1 -0
- package/dist/utils/index.d.ts +224 -0
- package/dist/utils/index.js +183 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 NextSpark
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# @nextsparkjs/testing
|
|
2
|
+
|
|
3
|
+
Testing utilities for NextSpark applications. Provides selectors, Page Object Models, and Cypress helpers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @nextsparkjs/testing
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @nextsparkjs/testing
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Basic Selectors
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { sel, cySelector, CORE_SELECTORS } from '@nextsparkjs/testing'
|
|
19
|
+
|
|
20
|
+
// Get selector value
|
|
21
|
+
sel('dashboard.navigation.main') // 'nav-main'
|
|
22
|
+
|
|
23
|
+
// Get Cypress selector
|
|
24
|
+
cySelector('dashboard.navigation.main') // '[data-cy="nav-main"]'
|
|
25
|
+
|
|
26
|
+
// Access raw selectors
|
|
27
|
+
CORE_SELECTORS.dashboard.navigation.main // 'nav-main'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Subpath Imports (Recommended)
|
|
31
|
+
|
|
32
|
+
For better tree-shaking:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { sel, cySelector } from '@nextsparkjs/testing/selectors'
|
|
36
|
+
import { createEntityTestingHelper } from '@nextsparkjs/testing/utils'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Theme Extension
|
|
40
|
+
|
|
41
|
+
Themes can extend core selectors:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { createSelectorHelpers, CORE_SELECTORS } from '@nextsparkjs/testing/selectors'
|
|
45
|
+
|
|
46
|
+
const THEME_SELECTORS = {
|
|
47
|
+
...CORE_SELECTORS,
|
|
48
|
+
myFeature: {
|
|
49
|
+
button: 'my-feature-btn',
|
|
50
|
+
form: 'my-feature-form',
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const { sel, cySelector } = createSelectorHelpers(THEME_SELECTORS)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Entity Testing Helper
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { createEntityTestingHelper } from '@nextsparkjs/testing/utils'
|
|
61
|
+
|
|
62
|
+
const testId = createEntityTestingHelper('products')
|
|
63
|
+
|
|
64
|
+
// Usage in components
|
|
65
|
+
<div data-cy={testId.page()}> // products-page
|
|
66
|
+
<form data-cy={testId.form()}> // products-form
|
|
67
|
+
<input data-cy={testId.field('name')} /> // products-field-name
|
|
68
|
+
<button data-cy={testId.formSubmit()}> // products-form-submit
|
|
69
|
+
Submit
|
|
70
|
+
</button>
|
|
71
|
+
</form>
|
|
72
|
+
</div>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Exports
|
|
76
|
+
|
|
77
|
+
### Main Entry (`@nextsparkjs/testing`)
|
|
78
|
+
|
|
79
|
+
- `sel()` - Get selector value by path
|
|
80
|
+
- `cySelector()` - Get Cypress selector string
|
|
81
|
+
- `CORE_SELECTORS` - All core selectors
|
|
82
|
+
- `createSelectorHelpers()` - Factory for custom selectors
|
|
83
|
+
- `createEntityTestingHelper()` - Entity-specific helper factory
|
|
84
|
+
|
|
85
|
+
### Selectors (`@nextsparkjs/testing/selectors`)
|
|
86
|
+
|
|
87
|
+
- All selector-related exports
|
|
88
|
+
|
|
89
|
+
### Utils (`@nextsparkjs/testing/utils`)
|
|
90
|
+
|
|
91
|
+
- `createTestId()` - Create test IDs
|
|
92
|
+
- `createCyId()` - Create Cypress IDs
|
|
93
|
+
- `createEntityTestingHelper()` - Entity helper factory
|
|
94
|
+
- `testingPatterns` - Common testing patterns
|
|
95
|
+
- `keyboardHelpers` - Keyboard navigation helpers
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ApiInterceptor - Helper for deterministic waits in Cypress
|
|
3
|
+
*
|
|
4
|
+
* Replaces unreliable cy.wait(ms) with cy.intercept() based waits
|
|
5
|
+
* that wait for actual API responses.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic usage:
|
|
8
|
+
* ```ts
|
|
9
|
+
* const api = new ApiInterceptor('customers')
|
|
10
|
+
* api.setupCrudIntercepts()
|
|
11
|
+
* cy.visit('/dashboard/customers')
|
|
12
|
+
* api.waitForList()
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @example Custom path:
|
|
16
|
+
* ```ts
|
|
17
|
+
* const api = new ApiInterceptor({
|
|
18
|
+
* slug: 'categories',
|
|
19
|
+
* customPath: '/api/v1/post-categories'
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
interface ApiInterceptorConfig {
|
|
24
|
+
/** Entity slug - used to generate aliases */
|
|
25
|
+
slug: string;
|
|
26
|
+
/** Custom API path (e.g., '/api/v1/post-categories') */
|
|
27
|
+
customPath?: string;
|
|
28
|
+
}
|
|
29
|
+
declare class ApiInterceptor {
|
|
30
|
+
private slug;
|
|
31
|
+
private endpoint;
|
|
32
|
+
constructor(slugOrConfig: string | ApiInterceptorConfig);
|
|
33
|
+
/** Get the API endpoint path */
|
|
34
|
+
get path(): string;
|
|
35
|
+
/** Get the entity slug */
|
|
36
|
+
get entitySlug(): string;
|
|
37
|
+
/** Get alias names for all operations */
|
|
38
|
+
get aliases(): {
|
|
39
|
+
list: string;
|
|
40
|
+
create: string;
|
|
41
|
+
update: string;
|
|
42
|
+
delete: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Setup intercepts for all CRUD operations
|
|
46
|
+
* Call this BEFORE navigation in beforeEach or at test start
|
|
47
|
+
*
|
|
48
|
+
* Note: We intercept both PUT and PATCH for updates since different
|
|
49
|
+
* APIs may use different HTTP methods for updates.
|
|
50
|
+
*/
|
|
51
|
+
setupCrudIntercepts(): this;
|
|
52
|
+
/**
|
|
53
|
+
* Setup only list + create intercepts
|
|
54
|
+
* Useful for list pages with inline create
|
|
55
|
+
*/
|
|
56
|
+
setupListIntercepts(): this;
|
|
57
|
+
/**
|
|
58
|
+
* Wait for list response (GET)
|
|
59
|
+
* Use after navigation or after mutations to wait for refresh
|
|
60
|
+
*/
|
|
61
|
+
waitForList(timeout?: number): Cypress.Chainable;
|
|
62
|
+
/**
|
|
63
|
+
* Wait for create response (POST) and validate success status
|
|
64
|
+
*/
|
|
65
|
+
waitForCreate(timeout?: number): Cypress.Chainable;
|
|
66
|
+
/**
|
|
67
|
+
* Wait for update response (PATCH or PUT) and validate success status
|
|
68
|
+
* Waits for PATCH first (more common), falls back to PUT
|
|
69
|
+
*/
|
|
70
|
+
waitForUpdate(timeout?: number): Cypress.Chainable;
|
|
71
|
+
/**
|
|
72
|
+
* Wait for delete response (DELETE) and validate success status
|
|
73
|
+
*/
|
|
74
|
+
waitForDelete(timeout?: number): Cypress.Chainable;
|
|
75
|
+
/**
|
|
76
|
+
* Wait for list refresh (alias for waitForList)
|
|
77
|
+
* Semantic name for use after create/update/delete
|
|
78
|
+
*/
|
|
79
|
+
waitForRefresh(timeout?: number): Cypress.Chainable;
|
|
80
|
+
/**
|
|
81
|
+
* Wait for create + list refresh
|
|
82
|
+
* Common pattern: create entity, wait for success, wait for list to refresh
|
|
83
|
+
*/
|
|
84
|
+
waitForCreateAndRefresh(timeout?: number): Cypress.Chainable;
|
|
85
|
+
/**
|
|
86
|
+
* Wait for update + list refresh
|
|
87
|
+
* Common pattern: update entity, wait for success, wait for list to refresh
|
|
88
|
+
*/
|
|
89
|
+
waitForUpdateAndRefresh(timeout?: number): Cypress.Chainable;
|
|
90
|
+
/**
|
|
91
|
+
* Wait for delete + list refresh
|
|
92
|
+
* Common pattern: delete entity, wait for success, wait for list to refresh
|
|
93
|
+
*/
|
|
94
|
+
waitForDeleteAndRefresh(timeout?: number): Cypress.Chainable;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { ApiInterceptor, type ApiInterceptorConfig };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// src/helpers/ApiInterceptor.ts
|
|
2
|
+
var ApiInterceptor = class {
|
|
3
|
+
constructor(slugOrConfig) {
|
|
4
|
+
if (typeof slugOrConfig === "string") {
|
|
5
|
+
this.slug = slugOrConfig;
|
|
6
|
+
this.endpoint = `/api/v1/${slugOrConfig}`;
|
|
7
|
+
} else {
|
|
8
|
+
this.slug = slugOrConfig.slug;
|
|
9
|
+
this.endpoint = slugOrConfig.customPath || `/api/v1/${slugOrConfig.slug}`;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// ============================================
|
|
13
|
+
// ACCESSORS
|
|
14
|
+
// ============================================
|
|
15
|
+
/** Get the API endpoint path */
|
|
16
|
+
get path() {
|
|
17
|
+
return this.endpoint;
|
|
18
|
+
}
|
|
19
|
+
/** Get the entity slug */
|
|
20
|
+
get entitySlug() {
|
|
21
|
+
return this.slug;
|
|
22
|
+
}
|
|
23
|
+
/** Get alias names for all operations */
|
|
24
|
+
get aliases() {
|
|
25
|
+
return {
|
|
26
|
+
list: `${this.slug}List`,
|
|
27
|
+
create: `${this.slug}Create`,
|
|
28
|
+
update: `${this.slug}Update`,
|
|
29
|
+
delete: `${this.slug}Delete`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// ============================================
|
|
33
|
+
// INTERCEPT SETUP
|
|
34
|
+
// ============================================
|
|
35
|
+
/**
|
|
36
|
+
* Setup intercepts for all CRUD operations
|
|
37
|
+
* Call this BEFORE navigation in beforeEach or at test start
|
|
38
|
+
*
|
|
39
|
+
* Note: We intercept both PUT and PATCH for updates since different
|
|
40
|
+
* APIs may use different HTTP methods for updates.
|
|
41
|
+
*/
|
|
42
|
+
setupCrudIntercepts() {
|
|
43
|
+
cy.intercept("GET", `${this.endpoint}*`).as(this.aliases.list);
|
|
44
|
+
cy.intercept("POST", this.endpoint).as(this.aliases.create);
|
|
45
|
+
cy.intercept("PUT", `${this.endpoint}/*`).as(this.aliases.update);
|
|
46
|
+
cy.intercept("PATCH", `${this.endpoint}/*`).as(`${this.aliases.update}Patch`);
|
|
47
|
+
cy.intercept("DELETE", `${this.endpoint}/*`).as(this.aliases.delete);
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Setup only list + create intercepts
|
|
52
|
+
* Useful for list pages with inline create
|
|
53
|
+
*/
|
|
54
|
+
setupListIntercepts() {
|
|
55
|
+
cy.intercept("GET", `${this.endpoint}*`).as(this.aliases.list);
|
|
56
|
+
cy.intercept("POST", this.endpoint).as(this.aliases.create);
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
// ============================================
|
|
60
|
+
// WAIT METHODS
|
|
61
|
+
// ============================================
|
|
62
|
+
/**
|
|
63
|
+
* Wait for list response (GET)
|
|
64
|
+
* Use after navigation or after mutations to wait for refresh
|
|
65
|
+
*/
|
|
66
|
+
waitForList(timeout = 1e4) {
|
|
67
|
+
return cy.wait(`@${this.aliases.list}`, { timeout });
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Wait for create response (POST) and validate success status
|
|
71
|
+
*/
|
|
72
|
+
waitForCreate(timeout = 1e4) {
|
|
73
|
+
return cy.wait(`@${this.aliases.create}`, { timeout }).its("response.statusCode").should("be.oneOf", [200, 201]);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Wait for update response (PATCH or PUT) and validate success status
|
|
77
|
+
* Waits for PATCH first (more common), falls back to PUT
|
|
78
|
+
*/
|
|
79
|
+
waitForUpdate(timeout = 1e4) {
|
|
80
|
+
return cy.wait(`@${this.aliases.update}Patch`, { timeout }).its("response.statusCode").should("be.oneOf", [200, 201]);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Wait for delete response (DELETE) and validate success status
|
|
84
|
+
*/
|
|
85
|
+
waitForDelete(timeout = 1e4) {
|
|
86
|
+
return cy.wait(`@${this.aliases.delete}`, { timeout }).its("response.statusCode").should("be.oneOf", [200, 204]);
|
|
87
|
+
}
|
|
88
|
+
// ============================================
|
|
89
|
+
// CONVENIENCE METHODS
|
|
90
|
+
// ============================================
|
|
91
|
+
/**
|
|
92
|
+
* Wait for list refresh (alias for waitForList)
|
|
93
|
+
* Semantic name for use after create/update/delete
|
|
94
|
+
*/
|
|
95
|
+
waitForRefresh(timeout = 1e4) {
|
|
96
|
+
return this.waitForList(timeout);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Wait for create + list refresh
|
|
100
|
+
* Common pattern: create entity, wait for success, wait for list to refresh
|
|
101
|
+
*/
|
|
102
|
+
waitForCreateAndRefresh(timeout = 1e4) {
|
|
103
|
+
this.waitForCreate(timeout);
|
|
104
|
+
return this.waitForList(timeout);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Wait for update + list refresh
|
|
108
|
+
* Common pattern: update entity, wait for success, wait for list to refresh
|
|
109
|
+
*/
|
|
110
|
+
waitForUpdateAndRefresh(timeout = 1e4) {
|
|
111
|
+
this.waitForUpdate(timeout);
|
|
112
|
+
return this.waitForList(timeout);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Wait for delete + list refresh
|
|
116
|
+
* Common pattern: delete entity, wait for success, wait for list to refresh
|
|
117
|
+
*/
|
|
118
|
+
waitForDeleteAndRefresh(timeout = 1e4) {
|
|
119
|
+
this.waitForDelete(timeout);
|
|
120
|
+
return this.waitForList(timeout);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export { ApiInterceptor };
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/ApiInterceptor.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * ApiInterceptor - Helper for deterministic waits in Cypress\n *\n * Replaces unreliable cy.wait(ms) with cy.intercept() based waits\n * that wait for actual API responses.\n *\n * @example Basic usage:\n * ```ts\n * const api = new ApiInterceptor('customers')\n * api.setupCrudIntercepts()\n * cy.visit('/dashboard/customers')\n * api.waitForList()\n * ```\n *\n * @example Custom path:\n * ```ts\n * const api = new ApiInterceptor({\n * slug: 'categories',\n * customPath: '/api/v1/post-categories'\n * })\n * ```\n */\n\nexport interface ApiInterceptorConfig {\n /** Entity slug - used to generate aliases */\n slug: string\n /** Custom API path (e.g., '/api/v1/post-categories') */\n customPath?: string\n}\n\nexport class ApiInterceptor {\n private slug: string\n private endpoint: string\n\n constructor(slugOrConfig: string | ApiInterceptorConfig) {\n if (typeof slugOrConfig === 'string') {\n this.slug = slugOrConfig\n this.endpoint = `/api/v1/${slugOrConfig}`\n } else {\n this.slug = slugOrConfig.slug\n this.endpoint = slugOrConfig.customPath || `/api/v1/${slugOrConfig.slug}`\n }\n }\n\n // ============================================\n // ACCESSORS\n // ============================================\n\n /** Get the API endpoint path */\n get path(): string {\n return this.endpoint\n }\n\n /** Get the entity slug */\n get entitySlug(): string {\n return this.slug\n }\n\n /** Get alias names for all operations */\n get aliases() {\n return {\n list: `${this.slug}List`,\n create: `${this.slug}Create`,\n update: `${this.slug}Update`,\n delete: `${this.slug}Delete`,\n }\n }\n\n // ============================================\n // INTERCEPT SETUP\n // ============================================\n\n /**\n * Setup intercepts for all CRUD operations\n * Call this BEFORE navigation in beforeEach or at test start\n *\n * Note: We intercept both PUT and PATCH for updates since different\n * APIs may use different HTTP methods for updates.\n */\n setupCrudIntercepts(): this {\n cy.intercept('GET', `${this.endpoint}*`).as(this.aliases.list)\n cy.intercept('POST', this.endpoint).as(this.aliases.create)\n // Intercept both PUT and PATCH for updates (APIs may use either)\n cy.intercept('PUT', `${this.endpoint}/*`).as(this.aliases.update)\n cy.intercept('PATCH', `${this.endpoint}/*`).as(`${this.aliases.update}Patch`)\n cy.intercept('DELETE', `${this.endpoint}/*`).as(this.aliases.delete)\n return this\n }\n\n /**\n * Setup only list + create intercepts\n * Useful for list pages with inline create\n */\n setupListIntercepts(): this {\n cy.intercept('GET', `${this.endpoint}*`).as(this.aliases.list)\n cy.intercept('POST', this.endpoint).as(this.aliases.create)\n return this\n }\n\n // ============================================\n // WAIT METHODS\n // ============================================\n\n /**\n * Wait for list response (GET)\n * Use after navigation or after mutations to wait for refresh\n */\n waitForList(timeout = 10000): Cypress.Chainable {\n return cy.wait(`@${this.aliases.list}`, { timeout })\n }\n\n /**\n * Wait for create response (POST) and validate success status\n */\n waitForCreate(timeout = 10000): Cypress.Chainable {\n return cy.wait(`@${this.aliases.create}`, { timeout })\n .its('response.statusCode')\n .should('be.oneOf', [200, 201])\n }\n\n /**\n * Wait for update response (PATCH or PUT) and validate success status\n * Waits for PATCH first (more common), falls back to PUT\n */\n waitForUpdate(timeout = 10000): Cypress.Chainable {\n // Try PATCH first (more common in modern APIs), fall back to PUT\n return cy.wait(`@${this.aliases.update}Patch`, { timeout })\n .its('response.statusCode')\n .should('be.oneOf', [200, 201])\n }\n\n /**\n * Wait for delete response (DELETE) and validate success status\n */\n waitForDelete(timeout = 10000): Cypress.Chainable {\n return cy.wait(`@${this.aliases.delete}`, { timeout })\n .its('response.statusCode')\n .should('be.oneOf', [200, 204])\n }\n\n // ============================================\n // CONVENIENCE METHODS\n // ============================================\n\n /**\n * Wait for list refresh (alias for waitForList)\n * Semantic name for use after create/update/delete\n */\n waitForRefresh(timeout = 10000): Cypress.Chainable {\n return this.waitForList(timeout)\n }\n\n /**\n * Wait for create + list refresh\n * Common pattern: create entity, wait for success, wait for list to refresh\n */\n waitForCreateAndRefresh(timeout = 10000): Cypress.Chainable {\n this.waitForCreate(timeout)\n return this.waitForList(timeout)\n }\n\n /**\n * Wait for update + list refresh\n * Common pattern: update entity, wait for success, wait for list to refresh\n */\n waitForUpdateAndRefresh(timeout = 10000): Cypress.Chainable {\n this.waitForUpdate(timeout)\n return this.waitForList(timeout)\n }\n\n /**\n * Wait for delete + list refresh\n * Common pattern: delete entity, wait for success, wait for list to refresh\n */\n waitForDeleteAndRefresh(timeout = 10000): Cypress.Chainable {\n this.waitForDelete(timeout)\n return this.waitForList(timeout)\n }\n}\n\nexport default ApiInterceptor\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { E as EntitySelectorHelpers, R as Replacements, a as SelectorHelpers, S as SelectorObject, c as createSelectorHelpers, g as getNestedValue, r as replacePlaceholders } from './selector-factory-BivSWXbw.js';
|
|
2
|
+
export { CORE_SELECTORS, CoreSelectorsType, SELECTORS, SelectorPath, SelectorsType, cySelector, entitySelectors, s, sel, selDev } from './selectors/index.js';
|
|
3
|
+
export { CypressIdPattern, EntityTestingHelper, TestIdPattern, createAriaLabel, createCyId, createEntityCyId, createEntityTestingHelper, createPriorityAttr, createStateAttr, createTestId, createTestingProps, keyboardHelpers, testingPatterns } from './utils/index.js';
|
|
4
|
+
export { BasePOMCore, DashboardEntityPOMCore, EntityConfig } from './pom/index.js';
|
|
5
|
+
export { ApiInterceptor, ApiInterceptorConfig } from './helpers/index.js';
|