@shopware-ag/acceptance-test-suite 11.21.0 → 11.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -852
- package/dist/index.d.mts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.mjs +38 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,857 +3,7 @@
|
|
|
3
3
|
[](https://github.com/shopware/acceptance-test-suite/blob/trunk/LICENSE)
|
|
4
4
|
|
|
5
5
|
# Shopware Acceptance Test Suite
|
|
6
|
-
This test suite is an extension to [Playwright](https://playwright.dev/) to easily create end-to-end and API acceptance tests for [Shopware](https://github.com/shopware/shopware). It provides several useful Playwright [fixtures](https://playwright.dev/docs/test-fixtures) to start testing with Shopware right away, including page contexts and [page objects](https://playwright.dev/docs/pom) for Storefront and Administration, API clients, test data creation and reusable test logic.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* [Installation](#installation)
|
|
11
|
-
* [Configuration](#configuration)
|
|
12
|
-
* [Usage](#usage)
|
|
13
|
-
* [Deployment Process](#deployment-process)
|
|
14
|
-
* [General fixtures](#general-fixtures)
|
|
15
|
-
* [Page Objects](#page-objects)
|
|
16
|
-
* [Actor Pattern](#actor-pattern)
|
|
17
|
-
* [Types](#types-in-the-test-suite)
|
|
18
|
-
* [Testing](#testing-within-the-test-suite)
|
|
19
|
-
* [Running tests](#running-tests-in-the-test-suite)
|
|
20
|
-
* [Local development with ATS](#local-development-with-ats)
|
|
21
|
-
* [Best practices](#best-practices)
|
|
22
|
-
* [Code contribution](#code-contribution)
|
|
23
|
-
* [Services](#services)
|
|
24
|
-
* [Test Data Service](#test-data-service)
|
|
25
|
-
* [When to use](#when-to-use-the-testdataservice-in-tests)
|
|
26
|
-
* [When and why to extend](#when-and-why-to-extend-the-testdataservice)
|
|
27
|
-
* [Available methods](#available-create-methods-in-testdataservice)
|
|
28
|
-
* [Writing new methods](#writing-new-methods-in-testdataservice)
|
|
29
|
-
* [Automatic cleanup](#automatic-cleanup-of-test-data-and-system-configurations)
|
|
30
|
-
* [Extending the TestDataService](#extending-the-testdataservice-in-external-projects)
|
|
31
|
-
|
|
32
|
-
## Installation
|
|
33
|
-
Start by creating your own [Playwright](https://playwright.dev/docs/intro) project.
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
npm init playwright@latest
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Add the package for the Shopware Acceptance Test Suite to your project.
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
npm install @shopware-ag/acceptance-test-suite
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Make sure to install Playwright and it's dependencies.
|
|
46
|
-
```
|
|
47
|
-
npm install
|
|
48
|
-
npx playwright install
|
|
49
|
-
npx playwright install-deps
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Configuration
|
|
53
|
-
The test suite is designed to test against any Shopware instance with pure API usage. To grant access to the instance under test you can use the following environment variables. You can decide between two authentication options - admin user or shopware integration (recommended).
|
|
54
|
-
|
|
55
|
-
```apacheconf
|
|
56
|
-
# .env
|
|
57
|
-
|
|
58
|
-
APP_URL="<url-to-the-shopware-instance>"
|
|
59
|
-
|
|
60
|
-
# Authentication via integration
|
|
61
|
-
SHOPWARE_ACCESS_KEY_ID="<your-shopware-integration-id>"
|
|
62
|
-
SHOPWARE_SECRET_ACCESS_KEY="<your-shopware-integration-secret>"
|
|
63
|
-
|
|
64
|
-
# Autentication via admin user
|
|
65
|
-
SHOPWARE_ADMIN_USERNAME="<administrator-user-name>"
|
|
66
|
-
SHOPWARE_ADMIN_PASSWORD="<administrator-user-password>"
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
To ensure Playwright is referencing the right instance, you can use the same environment variable in your Playwright configuration.
|
|
70
|
-
|
|
71
|
-
```TypeScript
|
|
72
|
-
// playwright.config.ts
|
|
73
|
-
|
|
74
|
-
import { defineConfig } from '@playwright/test';
|
|
75
|
-
|
|
76
|
-
export default defineConfig({
|
|
77
|
-
use: {
|
|
78
|
-
baseURL: process.env['APP_URL'],
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
For more information about how to configure your Playwright project, have a look into the [official documentation](https://playwright.dev/docs/test-configuration).
|
|
84
|
-
|
|
85
|
-
### Mailpit configuration
|
|
86
|
-
Set up your local Mailpit instance by following the instructions at [Mailpit GitHub repository](https://github.com/axllent/mailpit).
|
|
87
|
-
By default, Mailpit starts a web interface at `http://localhost:8025` and listens for SMTP on port `1025`.
|
|
88
|
-
Set the `MAILPIT_BASE_URL` environment variable in `playwright.config.ts` to `http://localhost:8025`. You can now run email tests, such as `tests/Mailpit.spec.ts`.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
## Usage
|
|
93
|
-
The test suite uses the [extension system](https://playwright.dev/docs/extensibility) of Playwright and can be used as a full drop-in for Playwright. But, as you might also want to add your own extensions, the best way to use it is to create your own base test file and use it as the central reference for your test files. Add it to your project root or a specific fixture directory and name it whatever you like.
|
|
94
|
-
|
|
95
|
-
Make sure to set `"type": "module",` in your `package.json`.
|
|
96
|
-
|
|
97
|
-
```TypeScript
|
|
98
|
-
// BaseTestFile.ts
|
|
99
|
-
|
|
100
|
-
import { test as base } from '@shopware-ag/acceptance-test-suite';
|
|
101
|
-
import type { FixtureTypes } from '@shopware-ag/acceptance-test-suite';
|
|
102
|
-
|
|
103
|
-
export * from '@shopware-ag/acceptance-test-suite';
|
|
104
|
-
|
|
105
|
-
export const test = base.extend<FixtureTypes>({
|
|
106
|
-
|
|
107
|
-
// Your own fixtures
|
|
108
|
-
|
|
109
|
-
});
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
Within your tests you can import the necessary dependencies from your base file.
|
|
113
|
-
|
|
114
|
-
```TypeScript
|
|
115
|
-
// tests/MyFirstTest.spec.ts
|
|
116
|
-
|
|
117
|
-
import { test, expect } from './../BaseTestFile';
|
|
118
|
-
|
|
119
|
-
test('My first test scenario.', async ({ AdminApiContext, DefaultSalesChannel }) => {
|
|
120
|
-
|
|
121
|
-
// Your test logic
|
|
122
|
-
|
|
123
|
-
});
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
In the example above you can see two Shopware specific fixtures that are used in the test, `AdminApiContext` and `DefaultSalesChannel`. Every fixture can be used as an argument within the test method. Read more about available fixtures in the next section.
|
|
127
|
-
|
|
128
|
-
## Deployment Process
|
|
129
|
-
|
|
130
|
-
To deploy a new version of the Acceptance Test Suite, follow the steps below:
|
|
131
|
-
|
|
132
|
-
1. **Create a Pull Request**
|
|
133
|
-
Open a new pull request with your changes. Ensure that all commits follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification to support automated versioning and changelog generation.
|
|
134
|
-
|
|
135
|
-
2. **Approval and Merge**
|
|
136
|
-
Once the pull request has been reviewed and approved, merge it into the main branch.
|
|
137
|
-
|
|
138
|
-
3. **Automated Deployment PR Creation**
|
|
139
|
-
After the merge, the [`release-please`](https://github.com/googleapis/release-please) tool will automatically open a new pull request. This deployment PR will include version bumps and a generated changelog.
|
|
140
|
-
|
|
141
|
-
4. **Review and Approve the Deployment PR**
|
|
142
|
-
The deployment pull request requires an additional approval before it can be merged.
|
|
143
|
-
|
|
144
|
-
5. **Merge the Deployment PR**
|
|
145
|
-
Once the deployment PR is approved and merged, a new release of the Acceptance Test Suite will be created in the GitHub repository. This action will also publish a new package version to NPM under
|
|
146
|
-
[@shopware-ag/acceptance-test-suite](https://www.npmjs.com/package/@shopware-ag/acceptance-test-suite).
|
|
147
|
-
|
|
148
|
-
6. **Use the New Version**
|
|
149
|
-
After a short delay, the newly published version will be available on NPM. You can then reference it in your individual project folders as needed.
|
|
150
|
-
|
|
151
|
-
### Troubleshooting
|
|
152
|
-
If you encounter any issues with the automated deployment process, please check the following [troubleshooting page of release-please](https://github.com/googleapis/release-please?tab=readme-ov-file#release-please-bot-does-not-create-a-release-pr-why).
|
|
153
|
-
|
|
154
|
-
In the most cases, the problem is related to the commit messages not following the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. Make sure to check your commit messages and rebase your branch if necessary. If your PR is merged with a commit message that does not follow the specification you can do the following:
|
|
155
|
-
|
|
156
|
-
1. **Create an empty commit to the main branch**
|
|
157
|
-
```bash
|
|
158
|
-
git commit --allow-empty -m "chore: release 2.0.0" -m "Release-As: 2.0.0"
|
|
159
|
-
```
|
|
160
|
-
When a commit to the main branch has Release-As: x.x.x (case insensitive) in the commit body, Release Please will open a new pull request for the specified version.
|
|
161
|
-
|
|
162
|
-
2. **Push the changes**
|
|
163
|
-
```bash
|
|
164
|
-
git push origin <your-branch>
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
3. **Adjust the release notes**
|
|
168
|
-
|
|
169
|
-
Don't forget to adjust the release notes in the deployment PR.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
## General fixtures
|
|
173
|
-
|
|
174
|
-
### DefaultSalesChannel
|
|
175
|
-
We try to encapsulate test execution within the system under test and make tests as deterministic as possible. The idea is, to have a separate sales channel created which is used to do tests within the standard Storefront. The `DefaultSalesChannel` fixture is a worker scoped fixture and is there to achieve exactly that. Using it will provide you with a new sales channel with default settings, including a default Storefront customer.
|
|
176
|
-
|
|
177
|
-
**Properties**
|
|
178
|
-
* `salesChannel`: The Shopware sales channel reference.
|
|
179
|
-
* `customer`: A default Storefront customer reference.
|
|
180
|
-
* `url`: The url to the sales channel Storefront.
|
|
181
|
-
|
|
182
|
-
### AdminApiContext
|
|
183
|
-
This context provides a ready to use client for the Admin-API of Shopware. It is based on the standard Playwright [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext), but will handle authentication for you, so you can start doing API request to the Shopware instance under test right away. You can use it, for example, for test data creation or API testing. Learn more about the usage of the Shopware Admin-API in the [API documentation](https://shopware.stoplight.io/docs/admin-api).
|
|
184
|
-
|
|
185
|
-
**Methods**
|
|
186
|
-
* `get`
|
|
187
|
-
* `post`
|
|
188
|
-
* `patch`
|
|
189
|
-
* `delete`
|
|
190
|
-
* `fetch`
|
|
191
|
-
* `head`
|
|
192
|
-
|
|
193
|
-
**Usage**
|
|
194
|
-
```TypeScript
|
|
195
|
-
import { test, expect } from './../BaseTestFile';
|
|
196
|
-
|
|
197
|
-
test('Property group test scenario', async ({ AdminApiContext }) => {
|
|
198
|
-
|
|
199
|
-
const response = await AdminApiContext.post('property-group?_response=1', {
|
|
200
|
-
data: {
|
|
201
|
-
name: 'Size',
|
|
202
|
-
description: 'Size',
|
|
203
|
-
displayType: 'text',
|
|
204
|
-
sortingType: 'name',
|
|
205
|
-
options: [{
|
|
206
|
-
name: 'Small',
|
|
207
|
-
}, {
|
|
208
|
-
name: 'Medium',
|
|
209
|
-
}, {
|
|
210
|
-
name: 'Large',
|
|
211
|
-
}],
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
expect(response.ok()).toBeTruthy();
|
|
216
|
-
});
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### StoreApiContext
|
|
220
|
-
This context provides a ready to use client for the Store-API of Shopware and is based on the standard Playwright [APIRequestContext](https://playwright.dev/docs/api/class-apirequestcontext). You can do API calls on behalf of a Storefront user. Learn more about the usage of the Shopware Store-API in the [documentation](https://shopware.stoplight.io/docs/store-api/).
|
|
221
|
-
|
|
222
|
-
Note that, other than the AdminApiContext, the StoreApiContext won't do an automated login of the shop customer. This is, because a Storefront user isn't always a registered user by default, and you might want to test this behaviour explicitly. You can use the `login` method to simply login as a registered shop customer.
|
|
223
|
-
|
|
224
|
-
**Methods**
|
|
225
|
-
* `login(user)`: Does a login of a customer and will store the login state for future requests.
|
|
226
|
-
* `get`
|
|
227
|
-
* `post`
|
|
228
|
-
* `patch`
|
|
229
|
-
* `delete`
|
|
230
|
-
* `fetch`
|
|
231
|
-
* `head`
|
|
232
|
-
|
|
233
|
-
**Usage**
|
|
234
|
-
```TypeScript
|
|
235
|
-
import { test, expect } from './../BaseTestFile';
|
|
236
|
-
|
|
237
|
-
test('Store customer test scenario', async ({ StoreApiContext, DefaultSalesChannel }) => {
|
|
238
|
-
|
|
239
|
-
// Login as the default customer.
|
|
240
|
-
await StoreApiContext.login(DefaultSalesChannel.customer);
|
|
241
|
-
|
|
242
|
-
// Create a new cart for the customer.
|
|
243
|
-
const response = await StoreApiContext.post('checkout/cart', {
|
|
244
|
-
data: { name: 'default-customer-cart' },
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
expect(response.ok()).toBeTruthy();
|
|
248
|
-
});
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### AdminPage
|
|
252
|
-
This fixture provides a Playwright [page](https://playwright.dev/docs/api/class-page) context for the Shopware Administration. It creates a new admin user with an authenticated session. You can start testing within the Administration using this page right away.
|
|
253
|
-
|
|
254
|
-
**Usage**
|
|
255
|
-
```TypeScript
|
|
256
|
-
import { test, expect } from './../BaseTestFile';
|
|
257
|
-
|
|
258
|
-
test('Shopware admin test scenario', async ({ AdminPage }) => {
|
|
259
|
-
|
|
260
|
-
await AdminPage.goto('#/sw/product/index');
|
|
261
|
-
await expect(AdminPage.locator('.sw-product-list__add-physical-button')).toBeVisible();
|
|
262
|
-
});
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
Note that this is just a very rough example. In most cases you won't use this page context directly, but maybe a [page-object](#page-objects) using this page.
|
|
266
|
-
|
|
267
|
-
### StorefrontPage
|
|
268
|
-
This fixture provides a Playwright [page](https://playwright.dev/docs/api/class-page) context for the Shopware Storefront of the default sales channel.
|
|
269
|
-
|
|
270
|
-
### Add new fixtures
|
|
271
|
-
To add new general fixtures create them inside the `src/fixtures` folder. Keep in mind, that you need to merge your new fixture inside the `/src/index.ts` file.
|
|
272
|
-
|
|
273
|
-
## Page Objects
|
|
274
|
-
Page objects can be helpful to simplify the usage of element selectors and make them available in a reusable way. They help you to organize page specific locators and provide helpers for interacting with a given page. Within our test suite we try to keep the page objects very simple and not to add too much logic to them. So most of the page objects resemble just a collection of element locators and maybe some little helper methods.
|
|
275
|
-
|
|
276
|
-
There are several page objects to navigate the different pages of the Administration and Storefront. You can use them as any other fixture within your test. There is also a guide on page objects in the official Playwright [documentation](https://playwright.dev/docs/pom).
|
|
277
|
-
|
|
278
|
-
**Usage**
|
|
279
|
-
```TypeScript
|
|
280
|
-
import { test, expect } from './../BaseTestFile';
|
|
281
|
-
|
|
282
|
-
test('Storefront cart test scenario', async ({ StorefrontPage, StorefrontCheckoutCart }) => {
|
|
283
|
-
|
|
284
|
-
await StorefrontPage.goto(StorefrontCheckoutCart.url());
|
|
285
|
-
await expect(StorefrontCheckoutCart.grandTotalPrice).toHaveText('€100.00*');
|
|
286
|
-
});
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
You can get an overview of all available page objects in the [repository](https://github.com/shopware/acceptance-test-suite/tree/trunk/src/page-objects) of this test suite.
|
|
290
|
-
|
|
291
|
-
### Page Object module
|
|
292
|
-
The `modules` folder is designed to house reusable utility functions that operate on a `Page` object (from Playwright). These functions dynamically interact with different browser pages or contexts using the `page` parameter.
|
|
293
|
-
For example, utility functions like `getCustomFieldCardLocators` or `getSelectFieldListitem` are used across multiple page objects to handle specific functionality (e.g., managing custom fields or select field list items). Centralizing these utilities in the `modules` folder improves code organization, readability, and reduces duplication.
|
|
294
|
-
Create a new class inside module when it helps to streamline the codebase and avoid repetitive logic across page objects.
|
|
295
|
-
|
|
296
|
-
You can find how `getCustomFieldCardLocators` is defined in the [modules folder ](./src/page-objects/administration/modules/CustomFieldCard.ts) and used in other page object class [here](./src/page-objects/administration/ProductDetail.ts).
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
### Add new Page Objects
|
|
300
|
-
Page objects are organized mainly by their usage in the administration or storefront. To add a new page object just add it in the respective subfolder and reference it in the `AdministrationPages.ts` or `StorefrontPages.ts`.
|
|
301
|
-
|
|
302
|
-
**Usage**
|
|
303
|
-
```TypeScript
|
|
304
|
-
import { test as base } from '@playwright/test';
|
|
305
|
-
import type { FixtureTypes } from '../types/FixtureTypes';
|
|
306
|
-
|
|
307
|
-
import { ProductDetail } from './administration/ProductDetail';
|
|
308
|
-
import { OrderDetail } from './administration/OrderDetail';
|
|
309
|
-
import { CustomerListing } from './administration/CustomerListing';
|
|
310
|
-
// [...]
|
|
311
|
-
import { MyNewPage } from './administration/MyNewPage';
|
|
312
|
-
|
|
313
|
-
export interface AdministrationPageTypes {
|
|
314
|
-
AdminProductDetail: ProductDetail;
|
|
315
|
-
AdminOrderDetail: OrderDetail;
|
|
316
|
-
AdminCustomerListing: CustomerListing;
|
|
317
|
-
// [...]
|
|
318
|
-
AdminMyNewPage: MyNewPage;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
export const AdminPageObjects = {
|
|
322
|
-
ProductDetail,
|
|
323
|
-
OrderDetail,
|
|
324
|
-
CustomerListing,
|
|
325
|
-
// [...]
|
|
326
|
-
MyNewPage,
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
## Actor pattern
|
|
331
|
-
The actor pattern is a very simple concept that we added to our test suite. It is something that is not related to Playwright, but similar concepts exist in other testing frameworks. We implemented it, because we want to have reusable test logic that can be used in a human-readable form, without abstracting away Playwright as a framework. So you are totally free to use it or not. Any normal Playwright functionality will still be usable in your tests.
|
|
332
|
-
|
|
333
|
-
The concept adds two new entities besides the already mentioned page objects.
|
|
334
|
-
|
|
335
|
-
* **Actor**: A specific user with a given context performing actions (tasks) inside the application.
|
|
336
|
-
* **Task**: A certain action performed by an actor.
|
|
337
|
-
* **Pages**: A page of the application on which an actor performs a task.
|
|
338
|
-
|
|
339
|
-
### Actors
|
|
340
|
-
The actor class is just a lightweight solution to simplify the execution of reusable test logic or the navigation to a certain page.
|
|
341
|
-
|
|
342
|
-
**Properties**
|
|
343
|
-
* `name`: The human-readable name of the actor.
|
|
344
|
-
* `page`: A Playwright page context the actor is navigating.
|
|
345
|
-
|
|
346
|
-
**Methods**
|
|
347
|
-
* `goesTo`: Accepts an url of a page the actor should navigate to.
|
|
348
|
-
* `attemptsTo`: Accepts a "task" function with reusable test logic the actor should perform.
|
|
349
|
-
* `expects`: A one-to-one export of the Playwright `expect` method to use it in the actor pattern.
|
|
350
|
-
|
|
351
|
-
These methods lead to the following pattern:
|
|
352
|
-
|
|
353
|
-
* The **actor** *goes to* a **page**.
|
|
354
|
-
* The **actor** *attempts to* perform a certain **task**.
|
|
355
|
-
* The **actor** *expects* a certain result.
|
|
356
|
-
|
|
357
|
-
Translated into test code this pattern can look like this:
|
|
358
|
-
|
|
359
|
-
```TypeScript
|
|
360
|
-
import { test } from './../BaseTestFile';
|
|
361
|
-
|
|
362
|
-
test('Product detail test scenario', async ({
|
|
363
|
-
ShopCustomer,
|
|
364
|
-
StorefrontProductDetail,
|
|
365
|
-
ProductData
|
|
366
|
-
}) => {
|
|
367
|
-
|
|
368
|
-
await ShopCustomer.goesTo(StorefrontProductDetail.url(ProductData));
|
|
369
|
-
await ShopCustomer.attemptsTo(AddProductToCart(ProductData));
|
|
370
|
-
await ShopCustomer.expects(StorefrontProductDetail.offCanvasSummaryTotalPrice).toHaveText('€99.99*');
|
|
371
|
-
});
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
In this example you can see that this pattern creates tests that are very comprehensible, even for non-tech people. They also make it easier to abstract simple test logic that might be used in different scenarios into executable tasks, like adding a product to the cart.
|
|
375
|
-
|
|
376
|
-
The test suite offers two different actors by default:
|
|
377
|
-
|
|
378
|
-
* `ShopCustomer`: A user that is navigating the Storefront.
|
|
379
|
-
* `ShopAdmin`: A user that is managing Shopware via the Administration.
|
|
380
|
-
|
|
381
|
-
### Tasks
|
|
382
|
-
Tasks are small chunks of reusable test logic that can be passed to the `attemptsTo` method of an actor. They are created via Playwright fixtures and have access to the same dependencies. Every executed task will automatically be wrapped in a test step of Playwright, so you get nicely structured reports of your tests.
|
|
383
|
-
|
|
384
|
-
**Example**
|
|
385
|
-
```TypeScript
|
|
386
|
-
import { test as base } from '@playwright/test';
|
|
387
|
-
import type { FixtureTypes, Task } from '@shopware-ag/acceptance-test-suite';
|
|
388
|
-
|
|
389
|
-
export const Login = base.extend<{ Login: Task }, FixtureTypes>({
|
|
390
|
-
Login: async ({
|
|
391
|
-
ShopCustomer,
|
|
392
|
-
DefaultSalesChannel,
|
|
393
|
-
StorefrontAccountLogin,
|
|
394
|
-
StorefrontAccount,
|
|
395
|
-
}, use)=> {
|
|
396
|
-
const task = () => {
|
|
397
|
-
return async function Login() {
|
|
398
|
-
const { customer } = DefaultSalesChannel;
|
|
399
|
-
|
|
400
|
-
await ShopCustomer.goesTo(StorefrontAccountLogin.url());
|
|
401
|
-
|
|
402
|
-
await StorefrontAccountLogin.emailInput.fill(customer.email);
|
|
403
|
-
await StorefrontAccountLogin.passwordInput.fill(customer.password);
|
|
404
|
-
await StorefrontAccountLogin.loginButton.click();
|
|
405
|
-
|
|
406
|
-
await ShopCustomer.expects(StorefrontAccount.personalDataCardTitle).toBeVisible();
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
await use(task);
|
|
411
|
-
},
|
|
412
|
-
});
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
This fixture is the "login" task and performs a simple Storefront login of the default customer. Everytime we need a logged in shop customer, we can simply reuse this logic in our test.
|
|
416
|
-
|
|
417
|
-
```TypeScript
|
|
418
|
-
import { test } from './../BaseTestFile';
|
|
419
|
-
|
|
420
|
-
test('Customer login test scenario', async ({ ShopCustomer, Login }) => {
|
|
421
|
-
|
|
422
|
-
await ShopCustomer.attemptsTo(Login());
|
|
423
|
-
});
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
You can create your own tasks in the same way to make them available for the actor pattern. Every task is just a simple Playwright fixture containing a function call with the corresponding test logic. Make sure to merge your task fixtures with other fixtures you created in your base test file. You can use the `mergeTests` method of Playwright to combine several fixtures into one test extension. Use `/src/tasks/shop-customer-tasks.ts` or `/src/tasks/shop-admin-tasks.ts` for that.
|
|
427
|
-
|
|
428
|
-
To keep tests easily readable, use names for your tasks so that in the test itself the code line resembles the `Actor.attemptsTo(doSomething)` pattern as good as possible.
|
|
429
|
-
|
|
430
|
-
**Example**
|
|
431
|
-
```TypeScript
|
|
432
|
-
// Bad example
|
|
433
|
-
await ShopCustomer.attemptsTo(ProductCart);
|
|
434
|
-
|
|
435
|
-
// Better example
|
|
436
|
-
await ShopCustomer.attemptsTo(PutProductIntoCart);
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
## Types in the Test Suite
|
|
440
|
-
|
|
441
|
-
The Shopware Acceptance Test Suite leverages TypeScript’s static typing to ensure that test data structures, API interactions, and test logic are consistent and error-resistant.
|
|
442
|
-
|
|
443
|
-
### Shopware Types
|
|
444
|
-
|
|
445
|
-
The centralized type definition file, [ShopwareTypes.ts](https://github.com/shopware/acceptance-test-suite/blob/trunk/src/types/ShopwareTypes.ts) is tightly coupled with the TestDataService, which defines the shape and default data of all supported Shopware entities. Each supported entity—such as Product, Customer, Media, etc.—is defined with its properties and default values. These types are then referenced throughout the TestDataService to provide IntelliSense, validation, and consistent data structures.
|
|
446
|
-
|
|
447
|
-
```
|
|
448
|
-
export type ProductReview = components['schemas']['ProductReview'] & {
|
|
449
|
-
id: string,
|
|
450
|
-
productId: string,
|
|
451
|
-
salesChannelId: string,
|
|
452
|
-
title: string,
|
|
453
|
-
content: string,
|
|
454
|
-
points: number,
|
|
455
|
-
}
|
|
456
|
-
```
|
|
457
|
-
Within that example above you are importing the auto-generated type for `ProductReview` from the Shopware Admin API OpenAPI schema and extending it with additional or overridden fields using & { ... }.
|
|
458
|
-
|
|
459
|
-
Sometimes, you might want to remove fields from a type. TypeScript provides the Omit<T, K> utility to exclude fields from a type:
|
|
460
|
-
|
|
461
|
-
```
|
|
462
|
-
export type Country = Omit<components['schemas']['Country'], 'states'> & {
|
|
463
|
-
id: string,
|
|
464
|
-
states: [{
|
|
465
|
-
name: string,
|
|
466
|
-
shortCode: string,
|
|
467
|
-
}],
|
|
468
|
-
}
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
For custom use cases, simply define a custom type:
|
|
472
|
-
|
|
473
|
-
```
|
|
474
|
-
export type CustomShippingMethod = {
|
|
475
|
-
name: string;
|
|
476
|
-
active: boolean;
|
|
477
|
-
deliveryTimeId: string;
|
|
478
|
-
}
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
## Testing within the Test Suite
|
|
482
|
-
The `tests` folder ensures the reliability of the testing framework by validating the functionality of tools and data used in tests. Add tests to verify any new features or changes you introduce:
|
|
483
|
-
|
|
484
|
-
- **Page Objects**: Ensure they are correctly implemented and interact with the application as expected, including navigation, element visibility, and user interactions.
|
|
485
|
-
- **TestDataService Methods**: Verify that methods for creating, getting, and cleaning up test data (e.g., products, customers, orders) work correctly and produce consistent results.
|
|
486
|
-
|
|
487
|
-
```TypeScript
|
|
488
|
-
//Example for page objects
|
|
489
|
-
|
|
490
|
-
await ShopAdmin.goesTo(AdminManufacturerCreate.url());
|
|
491
|
-
await ShopAdmin.expects(AdminManufacturerCreate.nameInput).toBeVisible();
|
|
492
|
-
await ShopAdmin.expects(AdminManufacturerCreate.saveButton).toBeVisible();
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
```TypeScript
|
|
496
|
-
//Example for TestDataService
|
|
497
|
-
|
|
498
|
-
const product = await TestDataService.createProductWithImage({ description: 'Test Description' });
|
|
499
|
-
expect(product.description).toEqual('Test Description');
|
|
500
|
-
expect(product.coverId).toBeDefined();
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
## Running tests in the Test Suite
|
|
504
|
-
If you want to work on the test suite and try to execute tests from within this repository, you have to run a corresponding docker image for a specific Shopware version.
|
|
505
|
-
|
|
506
|
-
We publish pre-built images at the [GitHub container registry](https://github.com/orgs/shopware/packages/container/package/acceptance-test-suite%2Ftest-image). The images are built on a daily basis, check to see which versions are available.
|
|
507
|
-
|
|
508
|
-
In order to select an image, export the corresponding tag as `SHOPWARE_VERSION` and start the containers:
|
|
509
|
-
|
|
510
|
-
```bash
|
|
511
|
-
SHOPWARE_VERSION=trunk docker compose up --wait shopware
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
<details>
|
|
515
|
-
<summary>ℹ️ What if the version I'd like to test is not available as a pre-built image?</summary>
|
|
516
|
-
|
|
517
|
-
If you want to test with an image that's not available already, you can build it yourself by exporting a few more variables:
|
|
518
|
-
|
|
519
|
-
```bash
|
|
520
|
-
export PHP_VERSION="8.3" # PHP version of the base image
|
|
521
|
-
export SHOPWARE_VERSION="v6.5.8.0" # Shopware version to check out. This may bei either a branch or a tag, depending on the value of SHOPWARE_BUILD_SOURCE
|
|
522
|
-
export SHOPWARE_BUILD_SOURCE="tag" # Either "branch" or "tag"
|
|
523
|
-
|
|
524
|
-
docker compose up --attach-dependencies shopware # This will build the image if it's not available
|
|
525
|
-
```
|
|
526
|
-
</details>
|
|
527
|
-
|
|
528
|
-
Afterwards you can execute the normal playwright commands:
|
|
529
|
-
|
|
530
|
-
```bash
|
|
531
|
-
npx playwright test --ui
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
## Local development with ATS
|
|
535
|
-
To work locally with the Acceptance Test Suite (ATS) and your development setup, follow these steps:
|
|
536
|
-
|
|
537
|
-
### Create your Page Objects and TestDataService methods
|
|
538
|
-
|
|
539
|
-
In the ATS repository ([shopware/acceptance-test-suite](https://github.com/shopware/acceptance-test-suite)), create or modify your custom page objects, TestDataService methods, or any related files.
|
|
540
|
-
|
|
541
|
-
After making your changes, build the project by running the following command in the ATS repository:
|
|
542
|
-
```bash
|
|
543
|
-
npm run build
|
|
544
|
-
```
|
|
545
|
-
This will generate the necessary artifacts in the `dist` folder.
|
|
546
|
-
|
|
547
|
-
Copy the generated artifacts (e.g., all files in the `dist` folder) from the ATS repository to your local Shopware instance's `node_modules` folder, specifically under the ATS package path:
|
|
548
|
-
```bash
|
|
549
|
-
cp -R dist/* <path-to-your-shopware-instance>/tests/acceptance/node_modules/@shopware-ag/acceptance-test-suite/dist
|
|
550
|
-
````
|
|
551
|
-
### Adjust tests, Page Objects, and methods
|
|
552
|
-
|
|
553
|
-
In your Shopware instance, adjust any tests, page objects, TestDataService methods, or other related files to align them with the changes made in the ATS repository.
|
|
554
|
-
|
|
555
|
-
### Run the tests
|
|
556
|
-
|
|
557
|
-
Execute the tests to verify your changes. Use the following command from your Shopware project's acceptance test directory:
|
|
558
|
-
```bash
|
|
559
|
-
cd tests/acceptance
|
|
560
|
-
npx playwright test --ui
|
|
561
|
-
```
|
|
562
|
-
This will launch the Playwright Test Runner UI where you can select and run specific tests.
|
|
563
|
-
By following these steps, you can work locally with the ATS and test your changes in your Shopware instance.
|
|
564
|
-
|
|
565
|
-
## Best practices
|
|
566
|
-
|
|
567
|
-
A good first read about this is the official [playwright best practices page](https://playwright.dev/docs/best-practices). It describes the most important practices which should also be followed when writing acceptance tests for Shopware.
|
|
568
|
-
|
|
569
|
-
The most important part is [test isolation](https://playwright.dev/docs/best-practices#make-tests-as-isolated-as-possible) which helps to prevent flaky behavior and enables the test to be run in parallel and on systems with an unknown state.
|
|
570
|
-
|
|
571
|
-
### Dos
|
|
572
|
-
|
|
573
|
-
- use the [`TestDataService`](./src/services/TestDataService.ts) for creating test data
|
|
574
|
-
- create all the data that is required for your test case. That includes sales channels, customers and users (the page fixtures handle most of the common use cases)...
|
|
575
|
-
- ...and clean it up if you don't need it anymore. The TestDataService will take care of it if you used it to create the test data
|
|
576
|
-
- if you need specific settings for your test, set it explicitly for the user/customer/sales channel
|
|
577
|
-
- directly jump to detail pages with the id of the entities you've created
|
|
578
|
-
- if that's no possible, use the search with a unique name to filter lists to just that single entity
|
|
579
|
-
- if you need to skip tests, comment any relevant github issues as part of the skip method: `test.skip('Blocked by https://[...])`
|
|
580
|
-
|
|
581
|
-
### Don'ts
|
|
582
|
-
|
|
583
|
-
- do not expect lists/tables to only contain one item, leverage unique ids/names to open or find your entity instead
|
|
584
|
-
- same with helper functions, do not expect to only get one item back from the API. Always use unique criteria to the API call
|
|
585
|
-
- avoid unused fixtures: if you request a fixture but don't use any data from the fixture, the test or fixture should be refactored
|
|
586
|
-
- do not depend on implicit configuration and existing data. Examples:
|
|
587
|
-
- rules
|
|
588
|
-
- flows
|
|
589
|
-
- categories
|
|
590
|
-
- do not expect the shop to have the defaults en_GB and EUR
|
|
591
|
-
- do not change global settings (sales channel is ok, because it's created by us)
|
|
592
|
-
- basically everything in Settings that is not specific to a sales channel (tax, search, etc.)
|
|
593
|
-
|
|
594
|
-
### Sensitive Data / Credentials
|
|
595
|
-
Sometimes you have to provide sensitie data or credentials for your tests to run, for example credentials for a sandbox environment for a payment provider. Apart from avoiding to have those credentials in the acutal code, you should also prevent them from appearing in logs or traces. To achieve that you should outsource steps using sensitive data to another project, running before the actual test project, and disable traces for it.
|
|
596
|
-
|
|
597
|
-
**Example**
|
|
598
|
-
```Typescript
|
|
599
|
-
projects: [
|
|
600
|
-
// Init project using sensitive data
|
|
601
|
-
{
|
|
602
|
-
name: 'init',
|
|
603
|
-
testMatch: /.*\.init\.ts/,
|
|
604
|
-
use : {trace : 'off'}
|
|
605
|
-
},
|
|
606
|
-
|
|
607
|
-
{
|
|
608
|
-
// actual test project
|
|
609
|
-
// [...]
|
|
610
|
-
dependencies: ['init'],
|
|
611
|
-
}]
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
### Debugging API calls
|
|
615
|
-
Debugging API calls may not be an easy task at first glance, because if the call you made returns an error, it is not directly visible to you. But you can use the `errors[]`-array of the response and log that on the console.
|
|
616
|
-
|
|
617
|
-
**Example**
|
|
618
|
-
```Typescript
|
|
619
|
-
const response = await this.AdminApiClient.post('some/route', {
|
|
620
|
-
data: {
|
|
621
|
-
limit: 1,
|
|
622
|
-
filter: [
|
|
623
|
-
{
|
|
624
|
-
type: 'equals',
|
|
625
|
-
field: 'someField',
|
|
626
|
-
value: 'someValue',
|
|
627
|
-
},
|
|
628
|
-
],
|
|
629
|
-
},
|
|
630
|
-
});
|
|
631
|
-
const responseData = await response.json();
|
|
632
|
-
console.log(responseData.errors[0]);
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
## Code contribution
|
|
636
|
-
You can contribute to this project via its [official repository](https://github.com/shopware/acceptance-test-suite/) on GitHub.
|
|
637
|
-
|
|
638
|
-
This project uses [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). Please make sure to form your commits accordingly to the spec.
|
|
639
|
-
|
|
640
|
-
## Services
|
|
641
|
-
|
|
642
|
-
The test suite provides several services that can be used to simplify your test code. These services are designed to be reusable and can be easily extended to fit your specific needs.
|
|
643
|
-
|
|
644
|
-
### Test Data Service
|
|
645
|
-
The `TestDataService` is a powerful utility designed to simplify test data creation, management, and cleanup when writing acceptance and API tests for Shopware. It provides ready-to-use functions for common data needs and ensures reliable, isolated test environments.
|
|
646
|
-
For detailed documentation of the methods you can have a look at the [service class](https://github.com/shopware/acceptance-test-suite/blob/trunk/src/services/TestDataService.ts) or simply use the auto-completion of your IDE.
|
|
647
|
-
|
|
648
|
-
### When to use the TestDataService in tests
|
|
649
|
-
|
|
650
|
-
You should use the `TestDataService` whenever you need **test data** that matches common Shopware structures, such as:
|
|
651
|
-
|
|
652
|
-
- Creating a **basic product**, **customer**, **order**, **category**, etc.
|
|
653
|
-
- Setting up **media** resources like product images or digital downloads.
|
|
654
|
-
- Creating **promotions**, **rules**, or **payment/shipping methods**.
|
|
655
|
-
- Fetching existing entities via helper methods (`getCurrency()`, `getShippingMethod()`, etc.).
|
|
656
|
-
- **Assigning relations** between entities (e.g., linking a product to a category).
|
|
657
|
-
|
|
658
|
-
**Typical examples include:**
|
|
659
|
-
|
|
660
|
-
```typescript
|
|
661
|
-
const product = await TestDataService.createBasicProduct();
|
|
662
|
-
const customer = await TestDataService.createCustomer();
|
|
663
|
-
const shipping = await TestDataService.createBasicShippingMethod();
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
### When and why to extend the TestDataService
|
|
667
|
-
|
|
668
|
-
You should add new functions to the TestDataService (or extend it) when:
|
|
669
|
-
|
|
670
|
-
- Your project or plugin introduces **new entity types** (e.g., `CommercialCustomerGroup`, `CustomProductType`).
|
|
671
|
-
- You need a **specialized creation logic** (e.g., a shipping method with multiple rules, a pre-configured product bundle).
|
|
672
|
-
- Existing methods require **modifications** that should not affect the core service.
|
|
673
|
-
- You want to **reuse the same setup across multiple tests** without duplicating logic.
|
|
674
|
-
- You require **special cleanup handling** for newly created entities.
|
|
675
|
-
|
|
676
|
-
Using and extending the `TestDataService` properly ensures your acceptance tests stay **readable**, **maintainable**, and **scalable** even as your Shopware project grows.
|
|
677
|
-
|
|
678
|
-
### Available `create*` methods in TestDataService
|
|
679
|
-
|
|
680
|
-
These methods are designed to streamline the setup of test data, ensuring consistency and efficiency in your testing processes. They are much more available than listed below, but these are the most common ones. Please use your IDE auto-completion to find all available methods.
|
|
681
|
-
|
|
682
|
-
- `createBasicProduct(): Promise<Product>`
|
|
683
|
-
- `createVariantProducts(parentProduct: Product, propertyGroups: PropertyGroup[]): Promise<Product[]>`
|
|
684
|
-
- `createCustomer(): Promise<Customer>`
|
|
685
|
-
- `createCustomerGroup(): Promise<CustomerGroup>`
|
|
686
|
-
- `createOrder(lineItems: SimpleLineItem[], customer: Customer): Promise<Order>`
|
|
687
|
-
- `createCategory(): Promise<Category>`
|
|
688
|
-
- `createColorPropertyGroup(): Promise<PropertyGroup>`
|
|
689
|
-
- `createBasicPaymentMethod(): Promise<PaymentMethod>`
|
|
690
|
-
- `createBasicShippingMethod(): Promise<ShippingMethod>`
|
|
691
|
-
|
|
692
|
-
- [...]
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
### Available `assign*` methods in TestDataService
|
|
696
|
-
|
|
697
|
-
These methods are designed to establish associations between entities, such as linking products to categories or assigning media to manufacturers, ensuring that your test data reflects realistic scenarios. They are much more available than listed below, but these are the most common ones. Please use your IDE auto-completion to find all available methods.
|
|
698
|
-
|
|
699
|
-
- `assignProductCategory(productId: string, categoryIds: string[]): Promise<void>`
|
|
700
|
-
- `assignProductManufacturer(productId: string, manufacturerId: string): Promise<void>`
|
|
701
|
-
- `assignProductMedia(productId: string, mediaId: string): Promise<void>`
|
|
702
|
-
|
|
703
|
-
- [...]
|
|
704
|
-
|
|
705
|
-
### Available `get*` methods in TestDataService
|
|
706
|
-
They are much more available than listed below, but these are the most common ones. Please use your IDE auto-completion to find all available methods.
|
|
707
|
-
|
|
708
|
-
- `getCountry(iso2: string): Promise<Country>`
|
|
709
|
-
- `getCurrency(isoCode: string): Promise<Currency>`
|
|
710
|
-
- `getCustomerGroups(): Promise<CustomerGroup[]>`
|
|
711
|
-
- `getPaymentMethod(name = 'Invoice'): Promise<PaymentMethod>`
|
|
712
|
-
|
|
713
|
-
- [...]
|
|
714
|
-
|
|
715
|
-
### Writing new methods in `TestDataService`
|
|
716
|
-
|
|
717
|
-
If you want to add new functionality to this service — such as a new type of entity creation — you can follow this approach:
|
|
718
|
-
|
|
719
|
-
#### 1. Define the purpose
|
|
720
|
-
|
|
721
|
-
Decide whether you're creating, assigning, or retrieving data. Most methods fall into one of the following patterns:
|
|
722
|
-
- `create*`: Creates a new entity (e.g. product, customer, category)
|
|
723
|
-
- `assign*`: Links existing entities (e.g. assign media to product)
|
|
724
|
-
- `get*`: Retrieves specific or filtered data from the system
|
|
725
|
-
|
|
726
|
-
#### 2. Implement the method
|
|
727
|
-
|
|
728
|
-
Use the `AdminApiContext` to interact with the Shopware Admin API. Here's a simplified example of adding a method to [create a new shipping method](https://github.com/shopware/acceptance-test-suite/blob/e8d2a5e8cee2194b914aa35aa87fe7cf04060834/src/services/TestDataService.ts#L679)
|
|
729
|
-
|
|
730
|
-
#### 3. Follow naming conventions
|
|
731
|
-
|
|
732
|
-
Be consistent in naming:
|
|
733
|
-
- Use `createBasic*` for standardized, default setups with predefined values (e.g. `createBasicProduct`)
|
|
734
|
-
- Use `create*With*` for variations (e.g. `createProductWithImage`)
|
|
735
|
-
- Use `assign*` for methods that associate two entities (e.g. `assignProductMedia`)
|
|
736
|
-
- Use `get*` to retrieve specific entities or lists (e.g. `getCurrency`)
|
|
737
|
-
|
|
738
|
-
#### 4. Add a return type
|
|
739
|
-
|
|
740
|
-
Always define a return type (typically a `Promise<...>`) to improve autocompletion and documentation support.
|
|
741
|
-
|
|
742
|
-
#### 5. Add cleanup logic
|
|
743
|
-
|
|
744
|
-
Make sure to clean up the entity via code after test run by putting the entity to a record. See example below:
|
|
745
|
-
|
|
746
|
-
```typescript
|
|
747
|
-
async createBasicRule(): Promise<Rule> {
|
|
748
|
-
[...]
|
|
749
|
-
|
|
750
|
-
this.addCreatedRecord('rule', rule.id);
|
|
751
|
-
|
|
752
|
-
[...]
|
|
753
|
-
}
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
Further information you can explore in the chapter: [Automatic Cleanup](#automatic-cleanup-of-test-data-and-system-configurations)
|
|
757
|
-
|
|
758
|
-
#### 6. Test the method
|
|
759
|
-
|
|
760
|
-
Once added, use your new method inside a test to verify it works as expected (`/tests/TestDataService.spec.ts`):
|
|
761
|
-
|
|
762
|
-
```typescript
|
|
763
|
-
test('Verify new shipping method creation', async ({ TestDataService }) => {
|
|
764
|
-
const shippingMethod = await TestDataService.createShippingMethod({
|
|
765
|
-
name: 'Express Delivery'
|
|
766
|
-
});
|
|
767
|
-
|
|
768
|
-
expect(shippingMethod.name).toEqual('Express Delivery');
|
|
769
|
-
});
|
|
770
|
-
```
|
|
771
|
-
|
|
772
|
-
### Automatic cleanup of test data and system configurations
|
|
773
|
-
|
|
774
|
-
The `TestDataService` includes a built-in mechanism to ensure that any test data & system configuration entries created during a test run is automatically deleted afterward. This ensures that the Shopware instance remains clean and consistent between tests, helping to maintain **test isolation** and prevent **state leakage**.
|
|
775
|
-
|
|
776
|
-
#### How cleanup works
|
|
777
|
-
|
|
778
|
-
When you create an entity using a `create*` method (e.g., `createBasicProduct`, `createCustomer`), the service automatically registers that entity for deletion by calling the `addCreatedRecord()` method:
|
|
779
|
-
```typescript
|
|
780
|
-
this.addCreatedRecord('product', product.id);
|
|
781
|
-
```
|
|
782
|
-
|
|
783
|
-
These records are stored in a cleanup queue that is processed at the end of each test using the Playwright lifecycle.
|
|
784
|
-
|
|
785
|
-
#### Cleanup execution
|
|
786
|
-
|
|
787
|
-
The `cleanup()` method handles the deletion of all registered entities and system config changes. All created records are grouped into two categories:
|
|
788
|
-
* Priority Deletions (`priorityDeleteOperations`) – for entities with dependencies that must be deleted first (e.g. orders, customers)
|
|
789
|
-
* Standard Deletions (`deleteOperations`) – for all other entities
|
|
790
|
-
|
|
791
|
-
This prioritization prevents errors when deleting interdependent data. Any modified system configurations are reset to their previous state after deleting priority records.
|
|
792
|
-
The priority entities can be found in the `TestDataService` class. If you want to add a new entity to the priority deletion list, you can do so by adding it to the `priorityDeleteOperations` array.
|
|
793
|
-
|
|
794
|
-
#### Skipping cleanup
|
|
795
|
-
|
|
796
|
-
In rare scenarios, such as performance testing or debugging, you may want to prevent cleanup for specific entities. You can simply skip the cleanUp by calling `TestDataService.setCleanUp(false)` within your test.
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
### Extending the TestDataService in external projects
|
|
800
|
-
|
|
801
|
-
The `TestDataService` is designed to be **easily extendable**. This allows you to add project-specific data generation methods while still benefiting from the existing, standardized base functionality.
|
|
802
|
-
|
|
803
|
-
#### 1. Create a new subclass
|
|
804
|
-
|
|
805
|
-
You can create a new TypeScript class that **extends** the base `TestDataService`.
|
|
806
|
-
|
|
807
|
-
```typescript
|
|
808
|
-
import { TestDataService } from '@shopware-ag/acceptance-test-suite';
|
|
809
|
-
|
|
810
|
-
export class CustomTestDataService extends TestDataService {
|
|
811
|
-
|
|
812
|
-
constructor(AdminApiContext, DefaultSalesChannel) {
|
|
813
|
-
super(...);
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
async createCustomCustomerGroup(data: Partial<CustomerGroup>) {
|
|
817
|
-
const response = await this.adminApi.post('customer-group?_response=true', {
|
|
818
|
-
data: {
|
|
819
|
-
...
|
|
820
|
-
},
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
const { data: createdGroup } = await response.json();
|
|
824
|
-
this.addCreatedRecord('customer-group', createdGroup.id);
|
|
825
|
-
|
|
826
|
-
return createdGroup;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
```
|
|
830
|
-
|
|
831
|
-
#### 2. Provide the extended service as a fixture
|
|
832
|
-
|
|
833
|
-
Following the Playwright [fixture system](https://playwright.dev/docs/test-fixtures) described in the README, you create a new fixture that initializes your extended service.
|
|
834
|
-
|
|
835
|
-
Example from `AcceptanceTest.ts`:
|
|
836
|
-
|
|
837
|
-
```typescript
|
|
838
|
-
import { test as base } from '@shopware-ag/acceptance-test-suite';
|
|
839
|
-
import type { FixtureTypes } from './BaseTestFile';
|
|
840
|
-
import { CustomTestDataService } from './CustomTestDataService';
|
|
841
|
-
|
|
842
|
-
export interface CustomTestDataServiceType {
|
|
843
|
-
TestDataService: CustomTestDataService;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
export const test = base.extend<FixtureTypes & CustomTestDataServiceType>({
|
|
847
|
-
TestDataService: async ({ AdminApiContext, DefaultSalesChannel }, use) => {
|
|
848
|
-
const service = new CustomTestDataService(AdminApiContext, DefaultSalesChannel.salesChannel);
|
|
849
|
-
await use(service);
|
|
850
|
-
await service.cleanUp();
|
|
851
|
-
},
|
|
852
|
-
});
|
|
853
|
-
```
|
|
854
|
-
|
|
855
|
-
In this setup:
|
|
856
|
-
- The `TestDataService` fixture is **overridden** with your custom `CustomTestDataService`.
|
|
857
|
-
- Now all tests that use `TestDataService` will have access to both the original and your extended methods.
|
|
858
|
-
- The automated cleanup is still in place, ensuring that any test data created during the test run is removed afterward.
|
|
7
|
+
This repository remains the source code for the test suite but all guides, examples, and setup instructions is in the [documentation](https://developer.shopware.com/docs/guides/plugins/plugins/testing/playwright/)
|
|
859
8
|
|
|
9
|
+
Feel free to open [issues](https://github.com/shopware/acceptance-test-suite/issues) or [pull requests](https://github.com/shopware/acceptance-test-suite/pulls) for bugs or improvements related to the test suite code itself.
|
package/dist/index.d.mts
CHANGED
|
@@ -2098,6 +2098,7 @@ declare class FirstRunWizard implements PageObject {
|
|
|
2098
2098
|
}
|
|
2099
2099
|
|
|
2100
2100
|
declare class FlowBuilderCreate implements PageObject {
|
|
2101
|
+
readonly contentView: Locator;
|
|
2101
2102
|
readonly saveButton: Locator;
|
|
2102
2103
|
readonly header: Locator;
|
|
2103
2104
|
readonly smartBarHeader: Locator;
|
|
@@ -2143,6 +2144,8 @@ declare class FlowBuilderCreate implements PageObject {
|
|
|
2143
2144
|
}
|
|
2144
2145
|
|
|
2145
2146
|
declare class FlowBuilderListing implements PageObject {
|
|
2147
|
+
readonly page: Page;
|
|
2148
|
+
readonly contentView: Locator;
|
|
2146
2149
|
readonly createFlowButton: Locator;
|
|
2147
2150
|
readonly firstFlowName: Locator;
|
|
2148
2151
|
readonly firstFlowContextButton: Locator;
|
|
@@ -2157,9 +2160,11 @@ declare class FlowBuilderListing implements PageObject {
|
|
|
2157
2160
|
readonly successAlert: Locator;
|
|
2158
2161
|
readonly successAlertMessage: Locator;
|
|
2159
2162
|
readonly searchBar: Locator;
|
|
2160
|
-
readonly
|
|
2163
|
+
readonly pagination: Locator;
|
|
2164
|
+
readonly testFlowNameCells: Locator;
|
|
2165
|
+
readonly flowTemplatesTab: Locator;
|
|
2161
2166
|
constructor(page: Page);
|
|
2162
|
-
url(): string;
|
|
2167
|
+
url(tabName?: string): string;
|
|
2163
2168
|
getLineItemByFlowName(flowName: string): Promise<Record<string, Locator>>;
|
|
2164
2169
|
}
|
|
2165
2170
|
|
|
@@ -2183,6 +2188,8 @@ declare class FlowBuilderDetail extends FlowBuilderCreate implements PageObject
|
|
|
2183
2188
|
readonly alertMessage: Locator;
|
|
2184
2189
|
readonly successMessage: Locator;
|
|
2185
2190
|
readonly actionContentTag: Locator;
|
|
2191
|
+
readonly skeletonLoader: Locator;
|
|
2192
|
+
readonly messageClose: Locator;
|
|
2186
2193
|
constructor(page: Page, instanceMeta: HelperFixtureTypes['InstanceMeta']);
|
|
2187
2194
|
url(flowId: string, tabName?: string): string;
|
|
2188
2195
|
getTooltipText(toolTipArea: Locator): Promise<string>;
|
|
@@ -2449,6 +2456,7 @@ declare class CategoryDetail implements PageObject {
|
|
|
2449
2456
|
}
|
|
2450
2457
|
|
|
2451
2458
|
declare class RuleCreate implements PageObject {
|
|
2459
|
+
readonly header: Locator;
|
|
2452
2460
|
readonly nameInput: Locator;
|
|
2453
2461
|
readonly priorityInput: Locator;
|
|
2454
2462
|
readonly conditionTypeSelectionInput: Locator;
|
|
@@ -2465,6 +2473,7 @@ declare class RuleCreate implements PageObject {
|
|
|
2465
2473
|
}
|
|
2466
2474
|
|
|
2467
2475
|
declare class RuleDetail extends RuleCreate implements PageObject {
|
|
2476
|
+
readonly contentView: Locator;
|
|
2468
2477
|
readonly shippingMethodAvailabilityRulesCard: Locator;
|
|
2469
2478
|
readonly shippingMethodAvailabilityRulesCardLink: Locator;
|
|
2470
2479
|
readonly shippingMethodAvailabilityRulesCardTable: Locator;
|
|
@@ -2481,8 +2490,10 @@ declare class RuleDetail extends RuleCreate implements PageObject {
|
|
|
2481
2490
|
readonly promotionCustomerRulesCardEmptyState: Locator;
|
|
2482
2491
|
readonly promotionCartRulesCard: Locator;
|
|
2483
2492
|
readonly promotionCartRulesCardEmptyState: Locator;
|
|
2493
|
+
readonly assignmentModal: Locator;
|
|
2484
2494
|
readonly assignmentModalAddButton: Locator;
|
|
2485
2495
|
readonly assignmentModalSearchField: Locator;
|
|
2496
|
+
readonly conditionORContainer: Locator;
|
|
2486
2497
|
constructor(page: Page, instanceMeta: HelperFixtureTypes['InstanceMeta']);
|
|
2487
2498
|
getEntityCard(cardLocator: Locator): Promise<Record<string, Locator>>;
|
|
2488
2499
|
url(ruleId?: string, tabName?: string): string;
|
|
@@ -2490,6 +2501,8 @@ declare class RuleDetail extends RuleCreate implements PageObject {
|
|
|
2490
2501
|
|
|
2491
2502
|
declare class RuleListing implements PageObject {
|
|
2492
2503
|
readonly createRuleButton: Locator;
|
|
2504
|
+
readonly header: Locator;
|
|
2505
|
+
readonly grid: Locator;
|
|
2493
2506
|
readonly page: Page;
|
|
2494
2507
|
constructor(page: Page);
|
|
2495
2508
|
url(): string;
|
package/dist/index.d.ts
CHANGED
|
@@ -2098,6 +2098,7 @@ declare class FirstRunWizard implements PageObject {
|
|
|
2098
2098
|
}
|
|
2099
2099
|
|
|
2100
2100
|
declare class FlowBuilderCreate implements PageObject {
|
|
2101
|
+
readonly contentView: Locator;
|
|
2101
2102
|
readonly saveButton: Locator;
|
|
2102
2103
|
readonly header: Locator;
|
|
2103
2104
|
readonly smartBarHeader: Locator;
|
|
@@ -2143,6 +2144,8 @@ declare class FlowBuilderCreate implements PageObject {
|
|
|
2143
2144
|
}
|
|
2144
2145
|
|
|
2145
2146
|
declare class FlowBuilderListing implements PageObject {
|
|
2147
|
+
readonly page: Page;
|
|
2148
|
+
readonly contentView: Locator;
|
|
2146
2149
|
readonly createFlowButton: Locator;
|
|
2147
2150
|
readonly firstFlowName: Locator;
|
|
2148
2151
|
readonly firstFlowContextButton: Locator;
|
|
@@ -2157,9 +2160,11 @@ declare class FlowBuilderListing implements PageObject {
|
|
|
2157
2160
|
readonly successAlert: Locator;
|
|
2158
2161
|
readonly successAlertMessage: Locator;
|
|
2159
2162
|
readonly searchBar: Locator;
|
|
2160
|
-
readonly
|
|
2163
|
+
readonly pagination: Locator;
|
|
2164
|
+
readonly testFlowNameCells: Locator;
|
|
2165
|
+
readonly flowTemplatesTab: Locator;
|
|
2161
2166
|
constructor(page: Page);
|
|
2162
|
-
url(): string;
|
|
2167
|
+
url(tabName?: string): string;
|
|
2163
2168
|
getLineItemByFlowName(flowName: string): Promise<Record<string, Locator>>;
|
|
2164
2169
|
}
|
|
2165
2170
|
|
|
@@ -2183,6 +2188,8 @@ declare class FlowBuilderDetail extends FlowBuilderCreate implements PageObject
|
|
|
2183
2188
|
readonly alertMessage: Locator;
|
|
2184
2189
|
readonly successMessage: Locator;
|
|
2185
2190
|
readonly actionContentTag: Locator;
|
|
2191
|
+
readonly skeletonLoader: Locator;
|
|
2192
|
+
readonly messageClose: Locator;
|
|
2186
2193
|
constructor(page: Page, instanceMeta: HelperFixtureTypes['InstanceMeta']);
|
|
2187
2194
|
url(flowId: string, tabName?: string): string;
|
|
2188
2195
|
getTooltipText(toolTipArea: Locator): Promise<string>;
|
|
@@ -2449,6 +2456,7 @@ declare class CategoryDetail implements PageObject {
|
|
|
2449
2456
|
}
|
|
2450
2457
|
|
|
2451
2458
|
declare class RuleCreate implements PageObject {
|
|
2459
|
+
readonly header: Locator;
|
|
2452
2460
|
readonly nameInput: Locator;
|
|
2453
2461
|
readonly priorityInput: Locator;
|
|
2454
2462
|
readonly conditionTypeSelectionInput: Locator;
|
|
@@ -2465,6 +2473,7 @@ declare class RuleCreate implements PageObject {
|
|
|
2465
2473
|
}
|
|
2466
2474
|
|
|
2467
2475
|
declare class RuleDetail extends RuleCreate implements PageObject {
|
|
2476
|
+
readonly contentView: Locator;
|
|
2468
2477
|
readonly shippingMethodAvailabilityRulesCard: Locator;
|
|
2469
2478
|
readonly shippingMethodAvailabilityRulesCardLink: Locator;
|
|
2470
2479
|
readonly shippingMethodAvailabilityRulesCardTable: Locator;
|
|
@@ -2481,8 +2490,10 @@ declare class RuleDetail extends RuleCreate implements PageObject {
|
|
|
2481
2490
|
readonly promotionCustomerRulesCardEmptyState: Locator;
|
|
2482
2491
|
readonly promotionCartRulesCard: Locator;
|
|
2483
2492
|
readonly promotionCartRulesCardEmptyState: Locator;
|
|
2493
|
+
readonly assignmentModal: Locator;
|
|
2484
2494
|
readonly assignmentModalAddButton: Locator;
|
|
2485
2495
|
readonly assignmentModalSearchField: Locator;
|
|
2496
|
+
readonly conditionORContainer: Locator;
|
|
2486
2497
|
constructor(page: Page, instanceMeta: HelperFixtureTypes['InstanceMeta']);
|
|
2487
2498
|
getEntityCard(cardLocator: Locator): Promise<Record<string, Locator>>;
|
|
2488
2499
|
url(ruleId?: string, tabName?: string): string;
|
|
@@ -2490,6 +2501,8 @@ declare class RuleDetail extends RuleCreate implements PageObject {
|
|
|
2490
2501
|
|
|
2491
2502
|
declare class RuleListing implements PageObject {
|
|
2492
2503
|
readonly createRuleButton: Locator;
|
|
2504
|
+
readonly header: Locator;
|
|
2505
|
+
readonly grid: Locator;
|
|
2493
2506
|
readonly page: Page;
|
|
2494
2507
|
constructor(page: Page);
|
|
2495
2508
|
url(): string;
|
package/dist/index.mjs
CHANGED
|
@@ -5448,6 +5448,7 @@ var __publicField$u = (obj, key, value) => {
|
|
|
5448
5448
|
};
|
|
5449
5449
|
class FlowBuilderCreate {
|
|
5450
5450
|
constructor(page, instanceMeta) {
|
|
5451
|
+
__publicField$u(this, "contentView");
|
|
5451
5452
|
//header
|
|
5452
5453
|
__publicField$u(this, "saveButton");
|
|
5453
5454
|
__publicField$u(this, "header");
|
|
@@ -5493,6 +5494,7 @@ class FlowBuilderCreate {
|
|
|
5493
5494
|
__publicField$u(this, "instanceMeta");
|
|
5494
5495
|
this.page = page;
|
|
5495
5496
|
this.instanceMeta = instanceMeta;
|
|
5497
|
+
this.contentView = page.locator(".sw-desktop__content");
|
|
5496
5498
|
this.saveButton = page.locator(".sw-flow-detail__save");
|
|
5497
5499
|
this.header = page.locator("h2");
|
|
5498
5500
|
this.smartBarHeader = page.locator(".smart-bar__header");
|
|
@@ -5554,6 +5556,8 @@ var __publicField$t = (obj, key, value) => {
|
|
|
5554
5556
|
};
|
|
5555
5557
|
class FlowBuilderListing {
|
|
5556
5558
|
constructor(page) {
|
|
5559
|
+
__publicField$t(this, "page");
|
|
5560
|
+
__publicField$t(this, "contentView");
|
|
5557
5561
|
__publicField$t(this, "createFlowButton");
|
|
5558
5562
|
__publicField$t(this, "firstFlowName");
|
|
5559
5563
|
__publicField$t(this, "firstFlowContextButton");
|
|
@@ -5568,8 +5572,11 @@ class FlowBuilderListing {
|
|
|
5568
5572
|
__publicField$t(this, "successAlert");
|
|
5569
5573
|
__publicField$t(this, "successAlertMessage");
|
|
5570
5574
|
__publicField$t(this, "searchBar");
|
|
5571
|
-
__publicField$t(this, "
|
|
5575
|
+
__publicField$t(this, "pagination");
|
|
5576
|
+
__publicField$t(this, "testFlowNameCells");
|
|
5577
|
+
__publicField$t(this, "flowTemplatesTab");
|
|
5572
5578
|
this.page = page;
|
|
5579
|
+
this.contentView = page.locator(".sw-desktop__content");
|
|
5573
5580
|
this.createFlowButton = page.locator(".sw-flow-list__create");
|
|
5574
5581
|
this.firstFlowName = page.locator(".sw-data-grid__cell--name a").first();
|
|
5575
5582
|
this.firstFlowContextButton = page.locator(".sw-data-grid__actions-menu").first();
|
|
@@ -5584,9 +5591,12 @@ class FlowBuilderListing {
|
|
|
5584
5591
|
this.successAlert = page.locator(".sw-alert__body");
|
|
5585
5592
|
this.successAlertMessage = page.locator(".sw-alert__message");
|
|
5586
5593
|
this.searchBar = page.locator(".sw-search-bar__input");
|
|
5594
|
+
this.pagination = page.locator(".sw-pagination");
|
|
5595
|
+
this.testFlowNameCells = page.locator(".sw-data-grid__cell--name a").getByText("Test flow");
|
|
5596
|
+
this.flowTemplatesTab = page.locator(".sw-tabs-item").getByText("Flow templates");
|
|
5587
5597
|
}
|
|
5588
|
-
url() {
|
|
5589
|
-
return
|
|
5598
|
+
url(tabName = "flows") {
|
|
5599
|
+
return `#/sw/flow/index/${tabName}`;
|
|
5590
5600
|
}
|
|
5591
5601
|
async getLineItemByFlowName(flowName) {
|
|
5592
5602
|
const lineItem = this.page.locator(".sw-data-grid__row").filter({ hasText: flowName });
|
|
@@ -5650,6 +5660,8 @@ class FlowBuilderDetail extends FlowBuilderCreate {
|
|
|
5650
5660
|
__publicField$s(this, "alertMessage");
|
|
5651
5661
|
__publicField$s(this, "successMessage");
|
|
5652
5662
|
__publicField$s(this, "actionContentTag");
|
|
5663
|
+
__publicField$s(this, "skeletonLoader");
|
|
5664
|
+
__publicField$s(this, "messageClose");
|
|
5653
5665
|
this.generalTab = page.locator(".sw-flow-detail__tab-general");
|
|
5654
5666
|
if (satisfies(instanceMeta.version, "<6.7")) {
|
|
5655
5667
|
this.successMessage = page.locator(".sw-alert__title");
|
|
@@ -5666,6 +5678,8 @@ class FlowBuilderDetail extends FlowBuilderCreate {
|
|
|
5666
5678
|
this.flowTab = page.locator(".sw-flow-detail__tab-flow");
|
|
5667
5679
|
this.templateName = page.getByLabel("Name");
|
|
5668
5680
|
this.actionContentTag = page.locator(".sw-flow-sequence-action__content").locator(".tag");
|
|
5681
|
+
this.skeletonLoader = page.locator(".sw-skeleton");
|
|
5682
|
+
this.messageClose = page.locator(".mt-banner__close");
|
|
5669
5683
|
}
|
|
5670
5684
|
url(flowId, tabName = "general") {
|
|
5671
5685
|
if (!flowId || flowId === "") {
|
|
@@ -6292,6 +6306,7 @@ var __publicField$f = (obj, key, value) => {
|
|
|
6292
6306
|
};
|
|
6293
6307
|
class RuleCreate {
|
|
6294
6308
|
constructor(page, instanceMeta) {
|
|
6309
|
+
__publicField$f(this, "header");
|
|
6295
6310
|
__publicField$f(this, "nameInput");
|
|
6296
6311
|
__publicField$f(this, "priorityInput");
|
|
6297
6312
|
__publicField$f(this, "conditionTypeSelectionInput");
|
|
@@ -6304,6 +6319,7 @@ class RuleCreate {
|
|
|
6304
6319
|
__publicField$f(this, "instanceMeta");
|
|
6305
6320
|
this.page = page;
|
|
6306
6321
|
this.instanceMeta = instanceMeta;
|
|
6322
|
+
this.header = page.locator(".smart-bar__header");
|
|
6307
6323
|
this.nameInput = page.getByLabel("Name");
|
|
6308
6324
|
this.priorityInput = page.getByLabel("Priority");
|
|
6309
6325
|
this.conditionTypeSelectionInput = page.locator(".sw-condition-type-select").locator(".sw-single-select__selection");
|
|
@@ -6330,6 +6346,7 @@ var __publicField$e = (obj, key, value) => {
|
|
|
6330
6346
|
class RuleDetail extends RuleCreate {
|
|
6331
6347
|
constructor(page, instanceMeta) {
|
|
6332
6348
|
super(page, instanceMeta);
|
|
6349
|
+
__publicField$e(this, "contentView");
|
|
6333
6350
|
__publicField$e(this, "shippingMethodAvailabilityRulesCard");
|
|
6334
6351
|
__publicField$e(this, "shippingMethodAvailabilityRulesCardLink");
|
|
6335
6352
|
__publicField$e(this, "shippingMethodAvailabilityRulesCardTable");
|
|
@@ -6346,8 +6363,11 @@ class RuleDetail extends RuleCreate {
|
|
|
6346
6363
|
__publicField$e(this, "promotionCustomerRulesCardEmptyState");
|
|
6347
6364
|
__publicField$e(this, "promotionCartRulesCard");
|
|
6348
6365
|
__publicField$e(this, "promotionCartRulesCardEmptyState");
|
|
6366
|
+
__publicField$e(this, "assignmentModal");
|
|
6349
6367
|
__publicField$e(this, "assignmentModalAddButton");
|
|
6350
6368
|
__publicField$e(this, "assignmentModalSearchField");
|
|
6369
|
+
__publicField$e(this, "conditionORContainer");
|
|
6370
|
+
this.contentView = page.locator(".sw-desktop__content");
|
|
6351
6371
|
this.shippingMethodAvailabilityRulesCard = page.locator(".sw-settings-rule-detail-assignments__card-shipping_method_availability_rule");
|
|
6352
6372
|
this.shippingMethodAvailabilityRulesCardLink = this.shippingMethodAvailabilityRulesCard.getByRole("link");
|
|
6353
6373
|
this.shippingMethodAvailabilityRulesCardTable = page.locator(".sw-settings-rule-detail-assignments__entity-listing-shipping_method_availability_rule");
|
|
@@ -6364,12 +6384,14 @@ class RuleDetail extends RuleCreate {
|
|
|
6364
6384
|
this.promotionCustomerRulesCardEmptyState = this.promotionCustomerRulesCard.getByRole("alert");
|
|
6365
6385
|
this.promotionCartRulesCard = page.locator(".sw-settings-rule-detail-assignments__card-promotion_cart_rule");
|
|
6366
6386
|
this.promotionCartRulesCardEmptyState = this.promotionCartRulesCard.getByRole("alert");
|
|
6367
|
-
this.
|
|
6387
|
+
this.assignmentModal = page.locator(".sw-settings-rule-add-assignment-modal");
|
|
6388
|
+
this.assignmentModalSearchField = this.assignmentModal.getByRole("textbox");
|
|
6368
6389
|
if (satisfies(instanceMeta.version, "<6.7")) {
|
|
6369
|
-
this.assignmentModalAddButton =
|
|
6390
|
+
this.assignmentModalAddButton = this.assignmentModal.locator(".sw-button--primary").getByText("Add");
|
|
6370
6391
|
} else {
|
|
6371
|
-
this.assignmentModalAddButton =
|
|
6392
|
+
this.assignmentModalAddButton = this.assignmentModal.locator(".mt-button--primary").getByText("Add");
|
|
6372
6393
|
}
|
|
6394
|
+
this.conditionORContainer = page.locator(".sw-condition-or-container");
|
|
6373
6395
|
}
|
|
6374
6396
|
async getEntityCard(cardLocator) {
|
|
6375
6397
|
return {
|
|
@@ -6390,9 +6412,13 @@ var __publicField$d = (obj, key, value) => {
|
|
|
6390
6412
|
class RuleListing {
|
|
6391
6413
|
constructor(page) {
|
|
6392
6414
|
__publicField$d(this, "createRuleButton");
|
|
6415
|
+
__publicField$d(this, "header");
|
|
6416
|
+
__publicField$d(this, "grid");
|
|
6393
6417
|
__publicField$d(this, "page");
|
|
6394
6418
|
this.page = page;
|
|
6395
6419
|
this.createRuleButton = page.getByText("Create rule");
|
|
6420
|
+
this.header = page.locator(".smart-bar__header");
|
|
6421
|
+
this.grid = page.locator(".sw-data-grid__table)");
|
|
6396
6422
|
}
|
|
6397
6423
|
url() {
|
|
6398
6424
|
return `#/sw/settings/rule/index`;
|
|
@@ -8803,7 +8829,7 @@ async function setViewport(page, options = {}) {
|
|
|
8803
8829
|
try {
|
|
8804
8830
|
await page.waitForResponse((response) => response.url().includes(config.requestURL));
|
|
8805
8831
|
} catch {
|
|
8806
|
-
console.warn(`[
|
|
8832
|
+
console.warn(`[Error] Timed out waiting for request: "${config.requestURL}".`);
|
|
8807
8833
|
}
|
|
8808
8834
|
}
|
|
8809
8835
|
if (config.waitForSelector) {
|
|
@@ -8811,7 +8837,7 @@ async function setViewport(page, options = {}) {
|
|
|
8811
8837
|
const locator2 = typeof config.waitForSelector === "string" ? page.locator(config.waitForSelector) : config.waitForSelector;
|
|
8812
8838
|
await locator2.waitFor({ state: "visible", timeout: 1e4 });
|
|
8813
8839
|
} catch {
|
|
8814
|
-
console.warn(`[
|
|
8840
|
+
console.warn(`[Error] ${config.waitForSelector} not found or timed out.`);
|
|
8815
8841
|
}
|
|
8816
8842
|
}
|
|
8817
8843
|
const locator = typeof config.scrollableElementVertical === "string" ? page.locator(config.scrollableElementVertical) : config.scrollableElementVertical;
|
|
@@ -8824,7 +8850,7 @@ async function setViewport(page, options = {}) {
|
|
|
8824
8850
|
contentHeight = await scrollableElementVertical.evaluate((el) => el.scrollHeight);
|
|
8825
8851
|
}
|
|
8826
8852
|
} catch {
|
|
8827
|
-
console.warn(`[
|
|
8853
|
+
console.warn(`[Warning] Scrollable element not found. Applying default height: ${config.contentHeight}.`);
|
|
8828
8854
|
}
|
|
8829
8855
|
}
|
|
8830
8856
|
let headerHeight = config.headerHeight;
|
|
@@ -8845,7 +8871,7 @@ async function setViewport(page, options = {}) {
|
|
|
8845
8871
|
}
|
|
8846
8872
|
}
|
|
8847
8873
|
} catch {
|
|
8848
|
-
console.warn(`[Info]
|
|
8874
|
+
console.warn(`[Info] Header not found.`);
|
|
8849
8875
|
}
|
|
8850
8876
|
}
|
|
8851
8877
|
let contentWidth = config.width;
|
|
@@ -8859,12 +8885,12 @@ async function setViewport(page, options = {}) {
|
|
|
8859
8885
|
contentWidth = config.width;
|
|
8860
8886
|
}
|
|
8861
8887
|
} catch {
|
|
8862
|
-
console.warn(`[
|
|
8888
|
+
console.warn(`[Warning] Scrollable element not found. Applying default width: ${config.width}.`);
|
|
8863
8889
|
}
|
|
8864
8890
|
}
|
|
8865
8891
|
const totalHeight = contentHeight + headerHeight + config.additionalHeight;
|
|
8866
8892
|
await page.setViewportSize({ width: contentWidth, height: totalHeight });
|
|
8867
|
-
console.warn(`[
|
|
8893
|
+
console.warn(`[Success] Viewport size: width=${contentWidth}, height=${totalHeight}`);
|
|
8868
8894
|
return;
|
|
8869
8895
|
}
|
|
8870
8896
|
|