@cuppet/core 1.0.1 → 1.0.2
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 +33 -244
- package/features/app/multilingualStrings/multilingualStrings.js +3 -271
- package/features/app/stepDefinitions/generalSteps.js +4 -2
- package/features/app/stepDefinitions/ifVisibleSteps.js +2 -1
- package/features/app/stepDefinitions/iframeSteps.js +10 -6
- package/features/app/stepDefinitions/pageElements.js +31 -16
- package/features/app/world.js +10 -2
- package/package.json +1 -1
- package/src/elementInteraction.js +29 -44
- package/src/helperFunctions.js +0 -17
package/README.md
CHANGED
|
@@ -12,43 +12,16 @@ npm install @cuppet/core
|
|
|
12
12
|
|
|
13
13
|
### Basic Setup
|
|
14
14
|
|
|
15
|
-
```
|
|
16
|
-
|
|
15
|
+
```bash
|
|
16
|
+
yarn install
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### There is a predefined structure in place made available for testing changes. To execute the example test run:
|
|
20
20
|
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
const { BrowserManager, AppiumManager } = require('@cuppet/core');
|
|
24
|
-
const config = require('config');
|
|
25
|
-
|
|
26
|
-
Before(async function (testCase) {
|
|
27
|
-
const browserArgs = config.get('browserOptions.args');
|
|
28
|
-
const browserViewport = config.get('browserOptions.viewport.default');
|
|
29
|
-
|
|
30
|
-
const browserManager = new BrowserManager(browserViewport, browserArgs);
|
|
31
|
-
await browserManager.initialize();
|
|
32
|
-
this.browserManager = browserManager;
|
|
33
|
-
this.browser = browserManager.browser;
|
|
34
|
-
this.page = browserManager.page;
|
|
35
|
-
});
|
|
21
|
+
```bash
|
|
22
|
+
yarn test
|
|
36
23
|
```
|
|
37
24
|
|
|
38
|
-
### In your step definitions
|
|
39
|
-
|
|
40
|
-
```javascript
|
|
41
|
-
const { Given, When, Then } = require('@cucumber/cucumber');
|
|
42
|
-
const { elementInteraction, mainFunctions } = require('@cuppet/core');
|
|
43
|
-
|
|
44
|
-
Given('I am on the homepage', async function () {
|
|
45
|
-
await mainFunctions.visitPath(this.page, 'homepage');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
When('I click the {string} button', async function (cssSelector) {
|
|
49
|
-
await elementInteraction.click(this.page, cssSelector);
|
|
50
|
-
});
|
|
51
|
-
```
|
|
52
25
|
|
|
53
26
|
## Available Components
|
|
54
27
|
|
|
@@ -77,23 +50,24 @@ When('I click the {string} button', async function (cssSelector) {
|
|
|
77
50
|
|
|
78
51
|
The cuppet-core package includes pre-built step definitions that you can use in your main project. Here are several ways to integrate them:
|
|
79
52
|
|
|
80
|
-
### Option 1: Import and use directly in your
|
|
81
|
-
|
|
82
|
-
```javascript
|
|
83
|
-
// In your project's step definition file (e.g., mySteps.js)
|
|
84
|
-
const { Given, When, Then } = require('@cucumber/cucumber');
|
|
85
|
-
const { stepDefinitions } = require('@cuppet/core');
|
|
53
|
+
### Option 1: Import and use directly in your cucumber based project
|
|
86
54
|
|
|
87
|
-
|
|
88
|
-
const { generalSteps, helperSteps, apiSteps } = stepDefinitions;
|
|
55
|
+
Make sure your Cucumber configuration includes the step definition paths:
|
|
89
56
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
57
|
+
```javascript
|
|
58
|
+
// cucumber.js
|
|
59
|
+
module.exports = {
|
|
60
|
+
default: {
|
|
61
|
+
requireModule: ['@cuppet/core'],
|
|
62
|
+
require: [
|
|
63
|
+
'node_modules/@cuppet/core/features/app/stepDefinitions/*.js',
|
|
64
|
+
'features/step-definitions/**/*.js', // Your project's step definitions
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
94
68
|
```
|
|
95
69
|
|
|
96
|
-
### Option 2: Import step definitions separately
|
|
70
|
+
### Option 2: Import step definitions on bulk or separately and override them
|
|
97
71
|
|
|
98
72
|
```javascript
|
|
99
73
|
// Import step definitions directly
|
|
@@ -102,25 +76,30 @@ const stepDefinitions = require('@cuppet/core/stepDefinitions');
|
|
|
102
76
|
// Or import specific step definition modules
|
|
103
77
|
const generalSteps = require('@cuppet/core/features/app/stepDefinitions/generalSteps');
|
|
104
78
|
const apiSteps = require('@cuppet/core/features/app/stepDefinitions/apiSteps');
|
|
79
|
+
const { Then } = require('@cucumber/cucumber');
|
|
80
|
+
|
|
81
|
+
Then('the response should be an {string}', async function (type) {
|
|
82
|
+
console.log("Add your new custom logic", type)
|
|
83
|
+
});
|
|
105
84
|
```
|
|
106
85
|
|
|
107
|
-
### Option 3: Extend
|
|
86
|
+
### Option 3: Extend or customize step definitions
|
|
108
87
|
|
|
109
88
|
```javascript
|
|
110
89
|
// In your project's step definition file
|
|
111
90
|
const { Given, When, Then } = require('@cucumber/cucumber');
|
|
112
|
-
const
|
|
91
|
+
const generalSteps = require('@cuppet/core/features/app/stepDefinitions/generalSteps');
|
|
113
92
|
|
|
114
93
|
// Extend the base step definitions with your custom logic
|
|
115
|
-
Given('I am logged in as {string}', async function (
|
|
94
|
+
Given('I am logged in as {string}', async function (userName) {
|
|
116
95
|
// Your custom login logic
|
|
117
96
|
await this.page.goto('https://your-app.com/login');
|
|
118
|
-
await this.page.fill('[data-testid="username"]',
|
|
97
|
+
await this.page.fill('[data-testid="username"]', userName);
|
|
119
98
|
await this.page.fill('[data-testid="password"]', 'password');
|
|
120
99
|
await this.page.click('[data-testid="login-button"]');
|
|
121
100
|
|
|
122
|
-
// Then use
|
|
123
|
-
await
|
|
101
|
+
// Then use a base step definition
|
|
102
|
+
await generalSteps.['I follow {string}'].call(this,userName);
|
|
124
103
|
});
|
|
125
104
|
```
|
|
126
105
|
|
|
@@ -134,201 +113,11 @@ Given('I am logged in as {string}', async function (userType) {
|
|
|
134
113
|
- `iframeSteps` - Iframe handling steps
|
|
135
114
|
- `ifVisibleSteps` - Conditional visibility steps
|
|
136
115
|
- `lighthouseSteps` - Performance testing steps
|
|
137
|
-
- `pageElements` - Page element
|
|
138
|
-
- `pageElementsConfig` - Page element
|
|
139
|
-
- `pageElementsJson` - JSON
|
|
116
|
+
- `pageElements` - Page element testing
|
|
117
|
+
- `pageElementsConfig` - Page element testing with values from config
|
|
118
|
+
- `pageElementsJson` - Page element testing with values from locally stored JSON file
|
|
140
119
|
- `visualRegressionSteps` - Visual regression testing steps
|
|
141
120
|
|
|
142
|
-
### Cucumber Configuration
|
|
143
|
-
|
|
144
|
-
Make sure your Cucumber configuration includes the step definition paths:
|
|
145
|
-
|
|
146
|
-
```javascript
|
|
147
|
-
// cucumber.js
|
|
148
|
-
module.exports = {
|
|
149
|
-
default: {
|
|
150
|
-
requireModule: ['@cuppet/core'],
|
|
151
|
-
require: [
|
|
152
|
-
'node_modules/@cuppet/core/features/app/stepDefinitions/*.js',
|
|
153
|
-
'features/step-definitions/**/*.js', // Your project's step definitions
|
|
154
|
-
],
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## Practical Examples
|
|
160
|
-
|
|
161
|
-
Here are real-world examples of how to use, override, and extend step definitions from cuppet-core:
|
|
162
|
-
|
|
163
|
-
### 1. Direct Usage - Use existing steps as-is
|
|
164
|
-
|
|
165
|
-
```javascript
|
|
166
|
-
// In your step definition file
|
|
167
|
-
const { Given, When, Then } = require('@cucumber/cucumber');
|
|
168
|
-
const { stepDefinitions } = require('@cuppet/core');
|
|
169
|
-
|
|
170
|
-
const { generalSteps, helperSteps } = stepDefinitions;
|
|
171
|
-
|
|
172
|
-
// Use them directly
|
|
173
|
-
Given('I go to homepage', async function () {
|
|
174
|
-
await generalSteps['I go to {string}'].call(this, 'homepage');
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
When('I wait for page to load', async function () {
|
|
178
|
-
await helperSteps['I wait for {string} seconds'].call(this, '3');
|
|
179
|
-
});
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### 2. Override - Replace with your custom logic
|
|
183
|
-
|
|
184
|
-
```javascript
|
|
185
|
-
// Override the default login step for your specific application
|
|
186
|
-
Given('I log in', async function () {
|
|
187
|
-
// Your custom login logic
|
|
188
|
-
await this.page.goto('https://myapp.com/login');
|
|
189
|
-
await this.page.fill('#email', 'test@example.com');
|
|
190
|
-
await this.page.fill('#password', 'password123');
|
|
191
|
-
await this.page.click('#login-button');
|
|
192
|
-
await this.page.waitForNavigation();
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// Override navigation with your base URL
|
|
196
|
-
Given('I go to {string}', async function (path) {
|
|
197
|
-
const baseUrl = 'https://myapp.com';
|
|
198
|
-
await this.page.goto(`${baseUrl}/${path}`);
|
|
199
|
-
});
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 3. Extend - Add custom logic before/after existing steps
|
|
203
|
-
|
|
204
|
-
```javascript
|
|
205
|
-
// Add logging to existing steps
|
|
206
|
-
Given('I go to {string} with logging', async function (path) {
|
|
207
|
-
console.log(`Navigating to: ${path}`);
|
|
208
|
-
|
|
209
|
-
// Use the original step
|
|
210
|
-
await generalSteps['I go to {string}'].call(this, path);
|
|
211
|
-
|
|
212
|
-
console.log(`Successfully navigated to: ${path}`);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// Add validation after navigation
|
|
216
|
-
Given('I go to {string} and verify page loaded', async function (path) {
|
|
217
|
-
// Use the original step
|
|
218
|
-
await generalSteps['I go to {string}'].call(this, path);
|
|
219
|
-
|
|
220
|
-
// Add custom validation
|
|
221
|
-
await this.page.waitForSelector('[data-testid="page-content"]');
|
|
222
|
-
});
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### 4. Composite Steps - Combine multiple existing steps
|
|
226
|
-
|
|
227
|
-
```javascript
|
|
228
|
-
// Create a setup step that combines multiple existing steps
|
|
229
|
-
Given('I set up test environment', async function () {
|
|
230
|
-
// Clear data
|
|
231
|
-
await helperSteps['I clear the json file'].call(this);
|
|
232
|
-
|
|
233
|
-
// Set viewport
|
|
234
|
-
await generalSteps['I set viewport size to {string}'].call(this, '1920x1080');
|
|
235
|
-
|
|
236
|
-
// Navigate to setup page
|
|
237
|
-
await generalSteps['I go to {string}'].call(this, 'setup');
|
|
238
|
-
|
|
239
|
-
// Wait for setup to complete
|
|
240
|
-
await helperSteps['I wait for {string} seconds'].call(this, '2');
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// Create role-specific login steps
|
|
244
|
-
Given('I am logged in as {string}', async function (userType) {
|
|
245
|
-
const credentials = {
|
|
246
|
-
admin: { username: 'admin', password: 'admin123' },
|
|
247
|
-
user: { username: 'user', password: 'user123' },
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
const user = credentials[userType];
|
|
251
|
-
await generalSteps['I log in as {string} {string}'].call(this, user.username, user.password);
|
|
252
|
-
});
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### 5. Error Handling - Wrap existing steps with retry logic
|
|
256
|
-
|
|
257
|
-
```javascript
|
|
258
|
-
// Create a retry wrapper for navigation
|
|
259
|
-
Given('I go to {string} with retry', async function (path) {
|
|
260
|
-
const maxRetries = 3;
|
|
261
|
-
|
|
262
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
263
|
-
try {
|
|
264
|
-
await generalSteps['I go to {string}'].call(this, path);
|
|
265
|
-
return; // Success, exit the loop
|
|
266
|
-
} catch (error) {
|
|
267
|
-
console.log(`Navigation attempt ${i + 1} failed: ${error.message}`);
|
|
268
|
-
|
|
269
|
-
if (i === maxRetries - 1) {
|
|
270
|
-
throw error; // Re-throw on last attempt
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Wait before retry
|
|
274
|
-
await helperSteps['I wait for {string} seconds'].call(this, '2');
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### 6. Utility Functions - Create reusable helpers
|
|
281
|
-
|
|
282
|
-
```javascript
|
|
283
|
-
// Create utility functions that use existing steps
|
|
284
|
-
const stepUtils = {
|
|
285
|
-
async loginWithRetry(page, username, password, maxRetries = 3) {
|
|
286
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
287
|
-
try {
|
|
288
|
-
await stepDefinitions.generalSteps['I log in as {string} {string}'].call({ page }, username, password);
|
|
289
|
-
return true;
|
|
290
|
-
} catch (error) {
|
|
291
|
-
console.log(`Login attempt ${i + 1} failed`);
|
|
292
|
-
if (i === maxRetries - 1) throw error;
|
|
293
|
-
await stepDefinitions.helperSteps['I wait for {string} seconds'].call({ page }, '2');
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
},
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
// Use the utility functions
|
|
300
|
-
Given('I log in with retry mechanism', async function () {
|
|
301
|
-
await stepUtils.loginWithRetry(this.page, 'testuser', 'testpass');
|
|
302
|
-
});
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### 7. Feature-Specific Steps - Create steps for your application
|
|
306
|
-
|
|
307
|
-
```javascript
|
|
308
|
-
// E-commerce specific steps
|
|
309
|
-
Given('I add {string} to cart', async function (productName) {
|
|
310
|
-
// Navigate to product page
|
|
311
|
-
await generalSteps['I go to {string}'].call(this, `products/${productName}`);
|
|
312
|
-
|
|
313
|
-
// Add to cart logic
|
|
314
|
-
await this.page.click('[data-testid="add-to-cart"]');
|
|
315
|
-
await helperSteps['I wait for {string} seconds'].call(this, '1');
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// User management specific steps
|
|
319
|
-
Given('I create a new user account', async function () {
|
|
320
|
-
await generalSteps['I go to {string}'].call(this, 'register');
|
|
321
|
-
|
|
322
|
-
// Fill registration form
|
|
323
|
-
await this.page.fill('[data-testid="username"]', 'newuser');
|
|
324
|
-
await this.page.fill('[data-testid="email"]', 'newuser@example.com');
|
|
325
|
-
await this.page.fill('[data-testid="password"]', 'password123');
|
|
326
|
-
await this.page.click('[data-testid="register-button"]');
|
|
327
|
-
|
|
328
|
-
await helperSteps['I wait for {string} seconds'].call(this, '2');
|
|
329
|
-
});
|
|
330
|
-
```
|
|
331
|
-
|
|
332
121
|
## Project-Specific Components
|
|
333
122
|
|
|
334
123
|
The following components should be created in your project as they are specific to your application:
|
|
@@ -1,288 +1,20 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
-
multilingualStrings: function (lang
|
|
2
|
+
multilingualStrings: function (lang) {
|
|
3
3
|
const translations = {
|
|
4
4
|
de: {
|
|
5
5
|
Submit: 'Einreichen',
|
|
6
6
|
Delete: 'Löschen',
|
|
7
7
|
Run: 'Laufen',
|
|
8
8
|
everywhere: 'nachgesehen',
|
|
9
|
-
'use current location': 'Standort nutzen',
|
|
10
|
-
'advanced search': 'Erweiterte Suche',
|
|
11
|
-
'we looked': 'Wir haben überall',
|
|
12
|
-
'but could not find your page.': 'aber die gewünsche Seite nicht gefunden',
|
|
13
|
-
'03 March 2021': '03 März 2021',
|
|
14
|
-
apply: 'jetzt bewerben',
|
|
15
|
-
"You haven't applied recently.": 'Sie haben sich in letzter Zeit nicht beworben.',
|
|
16
|
-
'my applications': 'Meine Bewerbungen',
|
|
17
|
-
'you have not saved any jobs yet': 'Sie haben sich bisher keine Stellenanzeigen gemerkt',
|
|
18
|
-
'you are applying for this job:': 'Bewerbung',
|
|
19
|
-
},
|
|
20
|
-
nl: {
|
|
21
|
-
'we looked': 'we zochten',
|
|
22
|
-
everywhere: 'overal',
|
|
23
|
-
'but could not find your page.': 'maar konden de pagina niet vinden.',
|
|
24
|
-
'Page not found': 'Pagina niet gevonden',
|
|
25
|
-
'03 March 2021': '03 maart 2021',
|
|
26
|
-
},
|
|
27
|
-
pt: {
|
|
28
|
-
'03 March 2021': '03 março 2021',
|
|
29
|
-
},
|
|
30
|
-
ar: {
|
|
31
|
-
'advanced search': 'búsqueda avanzada',
|
|
32
|
-
'use current location': 'usar ubicación actual',
|
|
33
|
-
'we looked': 'buscamos por',
|
|
34
|
-
everywhere: 'todas partes',
|
|
35
|
-
'but could not find your page.': 'pero no pudimos encontrar tu página',
|
|
36
|
-
'03 March 2021': '03 marzo 2021',
|
|
37
|
-
},
|
|
38
|
-
pl: {
|
|
39
|
-
'advanced search': 'wyszukiwanie zaawansowane',
|
|
40
|
-
'use current location': 'użyj aktualnej lokalizacji',
|
|
41
|
-
'we looked': 'szukaliśmy',
|
|
42
|
-
everywhere: 'wszędzie',
|
|
43
|
-
'but could not find your page.': 'ale nie mogliśmy znaleźć tej strony.',
|
|
44
|
-
'03 March 2021': '03 Marzec 2021',
|
|
45
|
-
'work from home jobs': 'tylko praca zdalna',
|
|
46
|
-
'remote jobs found for you': 'zdalne oferty pracy znalezione dla Ciebie',
|
|
47
|
-
},
|
|
48
|
-
cs: {
|
|
49
|
-
'advanced search': 'podrobné vyhledávání',
|
|
50
|
-
'use current location': 'použít současnou polohu',
|
|
51
|
-
'we looked': 'Hledali jsme',
|
|
52
|
-
everywhere: 'všude,',
|
|
53
|
-
'but could not find your page.': 'ale danou stránku jsme bohužel nenašli.',
|
|
54
|
-
'03 March 2021': '03 Březen 2021',
|
|
55
|
-
},
|
|
56
|
-
cl: {
|
|
57
|
-
'advanced search': 'búsqueda avanzada',
|
|
58
|
-
'use current location': 'usar ubicación actual',
|
|
59
|
-
'we looked': 'buscamos por',
|
|
60
|
-
everywhere: 'todas partes',
|
|
61
|
-
'but could not find your page.': 'pero no pudimos encontrar tu página',
|
|
62
|
-
'03 March 2021': '03 marzo 2021',
|
|
63
|
-
'work from home jobs': 'solo empleos por teletrabajo',
|
|
64
|
-
'remote jobs found for you': 'teletrabajo trabajos para ti',
|
|
65
|
-
},
|
|
66
|
-
ro: {
|
|
67
|
-
'advanced search': 'căutare avansată',
|
|
68
|
-
'use current location': 'utilizați locația curentă',
|
|
69
|
-
'we looked': 'ne-am uitat',
|
|
70
|
-
everywhere: 'pretutindeni',
|
|
71
|
-
'but could not find your page.': 'dar nu v-am putut găsi pagina.',
|
|
72
|
-
'03 March 2021': '03 Martie 2021',
|
|
73
|
-
apply: 'aplică',
|
|
74
|
-
},
|
|
75
|
-
hu: {
|
|
76
|
-
'advanced search': 'haladó keresés',
|
|
77
|
-
'use current location': 'használja a jelenlegi helyszínt',
|
|
78
|
-
'we looked': 'mindenhol',
|
|
79
|
-
everywhere: 'kerestük',
|
|
80
|
-
'but could not find your page.': 'de nem sikerült betölteni a keresett oldalt',
|
|
81
|
-
'03 March 2021': '03 március 2021',
|
|
82
|
-
apply: 'jelentkezem',
|
|
83
|
-
},
|
|
84
|
-
es: {
|
|
85
|
-
'advanced search': 'búsqueda avanzada',
|
|
86
|
-
'use current location': 'usar ubicación actual',
|
|
87
|
-
'we looked': 'buscamos por',
|
|
88
|
-
everywhere: 'todas partes',
|
|
89
|
-
'but could not find your page.': 'pero no pudimos encontrar tu página',
|
|
90
|
-
'03 March 2021': '03 Marcha 2021',
|
|
91
|
-
},
|
|
92
|
-
it: {
|
|
93
|
-
'advanced search': 'ricerca avanzata',
|
|
94
|
-
'use current location': 'usa la posizione attuale',
|
|
95
|
-
'we looked': 'abbiamo guardato',
|
|
96
|
-
everywhere: 'da tutte le parti',
|
|
97
|
-
'but could not find your page.': 'ma non sono riuscito a trovare la tua pagina.',
|
|
98
|
-
'03 March 2021': '03 Marzo 2021',
|
|
99
|
-
"You haven't applied recently.": 'Non hai candidature recenti.',
|
|
100
|
-
'my applications': 'le mie candidature',
|
|
101
|
-
'you have not saved any jobs yet': 'non hai ancora salvato le tue offerte preferite',
|
|
102
|
-
'you are applying for this job:': 'ti stai candidando a questa offerta',
|
|
103
|
-
},
|
|
104
|
-
'de-ch': {
|
|
105
|
-
'advanced search': 'fortgeschrittene Suche',
|
|
106
|
-
'use current location': 'Standort verwenden',
|
|
107
|
-
'we looked': 'wir haben nachgeschaut',
|
|
108
|
-
everywhere: 'überall',
|
|
109
|
-
'but could not find your page.': 'aber konnten deine Seite nicht finden',
|
|
110
|
-
'03 March 2021': '03 März 2021',
|
|
111
|
-
apply: 'bewerben',
|
|
112
|
-
},
|
|
113
|
-
'pt-br': {
|
|
114
|
-
'advanced search': 'pesquisa avançada',
|
|
115
|
-
'use current location': 'usar localização atual',
|
|
116
|
-
'we looked': 'nós procuramos',
|
|
117
|
-
everywhere: 'em todo os lugares',
|
|
118
|
-
'but could not find your page.': 'mas não conseguimos encontrar o que está sendo buscado',
|
|
119
|
-
'03 March 2021': '03 Marchar 2021',
|
|
120
|
-
},
|
|
121
|
-
nb: {
|
|
122
|
-
'advanced search': 'utvidet søk',
|
|
123
|
-
'use current location': 'bruk stedet du befinner deg på nå',
|
|
124
|
-
'we looked': 'vi har lett',
|
|
125
|
-
everywhere: 'over alt',
|
|
126
|
-
'but could not find your page.': 'men vi kunne dessverre ikke finne siden du leter etter',
|
|
127
|
-
'03 March 2021': '03 mars 2021',
|
|
128
|
-
},
|
|
129
|
-
el: {
|
|
130
|
-
'advanced search': 'σύνθετη αναζήτηση',
|
|
131
|
-
'use current location': 'χρησιμοποίησε την τρέχουσα τοποθεσία μου',
|
|
132
|
-
'we looked': 'ψάξαμε',
|
|
133
|
-
everywhere: 'παντού',
|
|
134
|
-
'but could not find your page.': 'αλλά δεν μπορέσαμε να βρούμε τη σελίδα που αναζητάς.',
|
|
135
|
-
'03 March 2021': '03 Μαρτίου 2021',
|
|
136
|
-
apply: 'κάνε την αίτησή σου',
|
|
137
|
-
'work from home jobs': 'αποκλειστικά εξ αποστάσεως εργασία',
|
|
138
|
-
'remote jobs found for you': 'remote θέσεις εργασίας βρέθηκαν για σένα',
|
|
139
|
-
},
|
|
140
|
-
fr: {
|
|
141
|
-
'advanced search': 'recherche avancée',
|
|
142
|
-
'use current location': 'à proximité',
|
|
143
|
-
'we looked': 'nous avons regardé',
|
|
144
|
-
everywhere: 'partout',
|
|
145
|
-
'but could not find your page.': 'mais nous ne trouvons pas cette page.',
|
|
146
|
-
'03 March 2021': '3 mars 2021',
|
|
147
|
-
apply: 'postuler',
|
|
148
|
-
"You haven't applied recently.": 'aucune candidature à ce jour',
|
|
149
|
-
'my applications': 'candidatures',
|
|
150
|
-
'you have not saved any jobs yet': "pour l'instant, aucune offre sélectionnée",
|
|
151
|
-
'you are applying for this job:': 'vous postulez à cette offre:',
|
|
152
|
-
},
|
|
153
|
-
cn: {
|
|
154
|
-
'advanced search': '高级搜索',
|
|
155
|
-
'use current location': '使用当前位置',
|
|
156
|
-
'we looked': '我们搜索了',
|
|
157
|
-
everywhere: '所有类目',
|
|
158
|
-
'but could not find your page.': '但未查到您要找的页面',
|
|
159
|
-
'03 March 2021': '03 三月 2021',
|
|
160
|
-
},
|
|
161
|
-
tr: {
|
|
162
|
-
'advanced search': 'gelişmiş arama',
|
|
163
|
-
'use current location': 'mevcut konumu kullan',
|
|
164
|
-
'we looked': 'her yere',
|
|
165
|
-
everywhere: 'baktık',
|
|
166
|
-
'but could not find your page.': 'ama bulamadık',
|
|
167
|
-
'03 March 2021': '03 mart 2021',
|
|
168
|
-
apply: 'başvur',
|
|
169
9
|
},
|
|
170
10
|
da: {
|
|
171
11
|
'advanced search': 'avanceret søgning',
|
|
172
|
-
'use current location': 'brug nuværende lokalitet',
|
|
173
|
-
'we looked': 'vi har ledt',
|
|
174
|
-
everywhere: 'overalt',
|
|
175
|
-
'but could not find your page.': 'men kunne ikke finde din side.',
|
|
176
|
-
'03 March 2021': '03 marts 2021',
|
|
177
|
-
apply: 'ansøg',
|
|
178
|
-
'my applications': 'mine ansøgninger',
|
|
179
|
-
'you have not saved any jobs yet': 'du har ingen gemte job endnu',
|
|
180
|
-
'you are applying for this job:': 'du ansøger nu dette job',
|
|
181
12
|
},
|
|
182
13
|
sv: {
|
|
183
14
|
'advanced search': 'avancerat sök',
|
|
184
|
-
'use current location': 'använd nuvarande plats',
|
|
185
|
-
'we looked': 'vi har sökt ',
|
|
186
|
-
everywhere: 'överallt',
|
|
187
|
-
'but could not find your page.': 'men kan inte hitta sidan.',
|
|
188
|
-
'03 March 2021': '03 Mars 2021',
|
|
189
|
-
"You haven't applied recently.": 'Du har inte ansökt till något jobb ännu',
|
|
190
|
-
'my applications': 'mina ansökningar',
|
|
191
|
-
'you have not saved any jobs yet': 'du har inte sparat några jobb ännu.',
|
|
192
|
-
'view 3 more': 'se 3 till',
|
|
193
|
-
'view 6 more': 'se 6 till',
|
|
194
|
-
'thank you for your application': 'tack för din ansökan',
|
|
195
|
-
'the application process.': 'ansökningsprocessen.',
|
|
196
|
-
'See what comes ahead in the application process': 'Se vad som händer härnäst i ansökningsprocessen',
|
|
197
|
-
'applying is easy.': 'att ansöka är enkelt.',
|
|
198
|
-
'you get a confirmation email.': 'du får ett bekräftelsemejl.',
|
|
199
|
-
"We'll now evaluate your application": 'Nu kommer vi snart att utvärdera hur',
|
|
200
|
-
'your profile matches the job': 'din profil passar för jobbet.',
|
|
201
|
-
"We'll contact you for some additional information.":
|
|
202
|
-
'Vi kommer att kontakta dig för kompletterande information.',
|
|
203
|
-
'unsupported file type': 'filtypen stöds ej',
|
|
204
|
-
'we only allow files up to 3 mb, please try again':
|
|
205
|
-
'vi tillåter endast filer upp till 3 mb, vänligen prova igen',
|
|
206
|
-
'You have successfully updated your resume details': 'Ditt CV är uppdaterat',
|
|
207
|
-
'add files': 'lägg till filer',
|
|
208
|
-
'Log out': 'Logga ut',
|
|
209
|
-
'login to your account': 'logga in',
|
|
210
|
-
'You removed the job from your saved jobs': 'Du tog bort jobbet från dina sparade jobb',
|
|
211
|
-
'delete account': 'radera konto',
|
|
212
|
-
'You have successfully deleted your account. A confirmation email has been sent.':
|
|
213
|
-
'Du har raderat ditt konto. Ett bekräftelsemail har skickats.',
|
|
214
|
-
'We do not recognize the combination of this e-mail address':
|
|
215
|
-
'Vi känner inte igen den här kombinationen av e-postadress och lösenord',
|
|
216
|
-
'We are taking action to delete your data from our system.':
|
|
217
|
-
'We are taking action to delete your data from our system.',
|
|
218
|
-
'show job alerts': 'visa jobbvarningar',
|
|
219
|
-
daily: 'dagligen',
|
|
220
|
-
'You have successfully updated your job alert.': 'Jobbevakningen har uppdaterats.',
|
|
221
|
-
'yes, delete': 'ja, radera',
|
|
222
|
-
'You have successfully deleted your job alert.': 'Jobbevakningen är raderad.',
|
|
223
|
-
'continue as guest': 'fortsätt som gäst',
|
|
224
|
-
"You'll start receiving emails": 'u kommer att börja få e-postmeddelanden',
|
|
225
|
-
'manage your job alerts': 'hantera dina jobbevakningar',
|
|
226
|
-
weekly: 'varje vecka',
|
|
227
|
-
'notification by email': 'notifieras via email',
|
|
228
|
-
'clear all': 'rensa allt',
|
|
229
|
-
'be the first to know about interesting jobs': 'be the first to know about interesting jobs',
|
|
230
|
-
'phone numbers': 'telefonnummer',
|
|
231
|
-
'You can add up to two phone numbers to your profile': 'Du kan lägga till upp till två telefonnummer',
|
|
232
|
-
'add phone number': 'lägg till telefonnummer',
|
|
233
|
-
'You have successfully updated your phone number': 'du har uppdaterat dina telefonuppgifter',
|
|
234
|
-
primary: 'primär',
|
|
235
|
-
Mobile: 'Mobile',
|
|
236
|
-
Landline: 'Landline',
|
|
237
|
-
'You have successfully deleted your phone number': 'du har raderat dina telefonuppgifter',
|
|
238
|
-
'phone number is invalid': 'number är ogiltigt.',
|
|
239
|
-
address: 'adress',
|
|
240
|
-
'successfully updated your address details': 'successfully updated your address details',
|
|
241
|
-
'You can add up to two addresses to your profile': 'Du kan lägga till upp till två adresser',
|
|
242
|
-
'add address': 'lägg till adress',
|
|
243
|
-
Primary: 'primär',
|
|
244
|
-
Bulgaria: 'Bulgarien',
|
|
245
|
-
secondary: 'sekundär',
|
|
246
|
-
'no results': 'inget resultat',
|
|
247
|
-
'You have successfully updated your name details': 'Dina uppgifter är uppdaterade',
|
|
248
|
-
Albania: 'Albanien',
|
|
249
|
-
'add education': 'lägg till utbildning',
|
|
250
|
-
'You have successfully updated your education details': 'Din utbildningsinformation är uppdaterad',
|
|
251
|
-
'please fill in the degree name': 'ange utbildningsnamn',
|
|
252
|
-
'please fill in the degree type': 'ange utbildningstyp',
|
|
253
|
-
'please fill in the institution / training facility / school': 'ange utbildningsinstans',
|
|
254
|
-
'please fill in the city': 'ange ort',
|
|
255
|
-
'please fill in the country': 'ange land',
|
|
256
|
-
'please fill in the start date': 'ange startdatum',
|
|
257
|
-
'please fill in the end date': 'ange slutdatum',
|
|
258
|
-
'You have successfully deleted your education details': 'Din utbildningsinformation är borttagen',
|
|
259
|
-
'add new work experience': 'lägg till ny arbetslivserfarenhet',
|
|
260
|
-
'You have successfully updated your work experience details': 'Din arbetslivserfarenhet är uppdaterad',
|
|
261
|
-
'You have successfully deleted your work experience details': 'Din arbetslivserfarenhet är borttagen',
|
|
262
|
-
'add certifications': 'lägg till certifiering',
|
|
263
|
-
'You have successfully updated your certification details':
|
|
264
|
-
'Dina certifieringar och intyg är uppdaterade',
|
|
265
|
-
'You have successfully deleted your certification details':
|
|
266
|
-
'Dina certifieringsinställningar har raderats',
|
|
267
|
-
'You have successfully updated your skills details': 'Din kompetens är uppdaterad',
|
|
268
|
-
'You have successfully deleted your skills details': 'Din kompetens är borttagen',
|
|
269
|
-
'You have successfully updated your links details.': 'Länkar är uppdaterade',
|
|
270
|
-
other: 'annat',
|
|
271
|
-
'You have successfully deleted your link record': 'Länken är borttagen',
|
|
272
|
-
'email address': 'e-postadress',
|
|
273
|
-
'United Kingdom of Great Britain and Northern Ireland': 'Bulgarien',
|
|
274
|
-
march: 'mars',
|
|
275
|
-
French: 'Swedish',
|
|
276
|
-
work: 'jobb',
|
|
277
|
-
Monthly: 'Månadsvis',
|
|
278
|
-
'I prefer remote work': 'Jag föredrar att arbeta på distans',
|
|
279
|
-
'Work preferences have successfully been updated': 'Jobbpreferenser är uppdaterade',
|
|
280
|
-
'$ 2500 per month': 'SEK 2500 per month',
|
|
281
|
-
'Value unspecified': 'välj',
|
|
282
|
-
select: 'välj',
|
|
283
15
|
},
|
|
284
16
|
};
|
|
285
|
-
//
|
|
286
|
-
return translations[lang]
|
|
17
|
+
// Return all translations for the given language, or undefined if not found
|
|
18
|
+
return translations[lang];
|
|
287
19
|
},
|
|
288
20
|
};
|
|
@@ -45,7 +45,8 @@ Given('I log in as {string} {string}', async function (username, password) {
|
|
|
45
45
|
await navigationPromise;
|
|
46
46
|
});
|
|
47
47
|
Given('I follow {string}', async function (text) {
|
|
48
|
-
|
|
48
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
49
|
+
await utils.followLink(this.page, resolvedText);
|
|
49
50
|
});
|
|
50
51
|
Given('I reload the page', async function () {
|
|
51
52
|
await main.reloadPage(this.page);
|
|
@@ -89,7 +90,8 @@ Given('I set viewport size to {string}', async function (resolution) {
|
|
|
89
90
|
});
|
|
90
91
|
Then('I {string} the alert dialog with text {string}', async function (action, expectedText) {
|
|
91
92
|
const accept = action.toLowerCase() === 'accept' ? true : false;
|
|
92
|
-
|
|
93
|
+
const resolvedText = this.mlStrings ? this.mlStrings[expectedText] : expectedText;
|
|
94
|
+
await main.handleAlert(this.page, accept, resolvedText);
|
|
93
95
|
});
|
|
94
96
|
|
|
95
97
|
Then('I {string} the alert dialog', async function (action) {
|
|
@@ -6,7 +6,8 @@ Then('I should see {string} if visible', async function (text) {
|
|
|
6
6
|
if (config.has('skipSteps') && config.get('skipSteps') === text) {
|
|
7
7
|
return true;
|
|
8
8
|
}
|
|
9
|
-
|
|
9
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
10
|
+
await utils.seeTextByXpath(this.page, resolvedText);
|
|
10
11
|
});
|
|
11
12
|
When('I type {string} in {string} if visible', async function (text, cssSelector) {
|
|
12
13
|
const selector = this.commonFields[cssSelector] ?? cssSelector;
|
|
@@ -3,15 +3,16 @@ const utils = require('../../../src/elementInteraction');
|
|
|
3
3
|
const dataStorage = require('../../../src/dataStorage');
|
|
4
4
|
|
|
5
5
|
Then('I should see {string} in iframe {string}', async function (text, frameSelector) {
|
|
6
|
-
const
|
|
6
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
7
7
|
let frame = await utils.getFrameBySelector(this.page, frameSelector);
|
|
8
|
-
await utils.seeTextByXpath(frame,
|
|
8
|
+
await utils.seeTextByXpath(frame, resolvedText);
|
|
9
9
|
});
|
|
10
10
|
Then(
|
|
11
11
|
'I wait for the text {string} to appear within {string} seconds in iframe {string}',
|
|
12
12
|
async function (text, time, frameSelector) {
|
|
13
13
|
let frame = await utils.getFrameBySelector(this.page, frameSelector);
|
|
14
|
-
|
|
14
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
15
|
+
await utils.seeTextByXpath(frame, resolvedText, time * 1000);
|
|
15
16
|
}
|
|
16
17
|
);
|
|
17
18
|
When('I click on element {string} in iframe with selector {string}', async function (elementSelector, frameSelector) {
|
|
@@ -20,7 +21,8 @@ When('I click on element {string} in iframe with selector {string}', async funct
|
|
|
20
21
|
});
|
|
21
22
|
When('I click on the text {string} in iframe with selector {string}', async function (text, frameSelector) {
|
|
22
23
|
let frame = await utils.getFrameBySelector(this.page, frameSelector);
|
|
23
|
-
|
|
24
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
25
|
+
await utils.clickByText(frame, resolvedText);
|
|
24
26
|
});
|
|
25
27
|
When(
|
|
26
28
|
'I type {string} in {string} field in iframe with selector {string}',
|
|
@@ -33,7 +35,8 @@ When(
|
|
|
33
35
|
'I click on the text {string} in iframe with selector {string} and follow the new tab',
|
|
34
36
|
async function (text, frameSelector) {
|
|
35
37
|
let frame = await utils.getFrameBySelector(this.page, frameSelector);
|
|
36
|
-
|
|
38
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
39
|
+
this.page = await utils.clickLinkOpenNewTab(this.browser, frame, resolvedText, true);
|
|
37
40
|
}
|
|
38
41
|
);
|
|
39
42
|
When(
|
|
@@ -48,7 +51,8 @@ When(
|
|
|
48
51
|
'I store the string matching the {string} pattern from the {string} text in iframe {string}',
|
|
49
52
|
async function (pattern, text, frameSelector) {
|
|
50
53
|
let frame = await utils.getFrameBySelector(this.page, frameSelector);
|
|
51
|
-
|
|
54
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
55
|
+
await dataStorage.storeTextFromPattern(frame, pattern, resolvedText);
|
|
52
56
|
}
|
|
53
57
|
);
|
|
54
58
|
Then(
|
|
@@ -23,10 +23,12 @@ When('I click on all the elements with selector {string}', async function (cssSe
|
|
|
23
23
|
await utils.clickAllElements(this.page, selector);
|
|
24
24
|
});
|
|
25
25
|
When('I click on the text {string}', async function (text) {
|
|
26
|
-
|
|
26
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
27
|
+
await utils.clickByText(this.page, resolvedText);
|
|
27
28
|
});
|
|
28
29
|
When('I click on the text {string} in the {string} region', async function (text, region) {
|
|
29
|
-
|
|
30
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
31
|
+
await utils.clickTextInRegion(this.page, resolvedText, region);
|
|
30
32
|
});
|
|
31
33
|
|
|
32
34
|
Then('I should see {string} in {string}', async function (value, cssSelector) {
|
|
@@ -35,7 +37,8 @@ Then('I should see {string} in {string}', async function (value, cssSelector) {
|
|
|
35
37
|
});
|
|
36
38
|
|
|
37
39
|
Then('I should see {string} in {string} region', async function (text, region) {
|
|
38
|
-
|
|
40
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
41
|
+
await utils.seeTextInRegion(this.page, resolvedText, region);
|
|
39
42
|
});
|
|
40
43
|
|
|
41
44
|
Then('I should see the element with selector {string}', async function (cssSelector) {
|
|
@@ -59,13 +62,16 @@ Then('I wait for element with {string} selector to appear within {string} second
|
|
|
59
62
|
await utils.seeElement(this.page, selector, true, time * 1000);
|
|
60
63
|
});
|
|
61
64
|
Then('I should not see {string}', async function (text) {
|
|
62
|
-
|
|
65
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
66
|
+
await utils.notSeeText(this.page, resolvedText);
|
|
63
67
|
});
|
|
64
68
|
Then('I wait for the text {string} to appear within {string} seconds', async function (text, time) {
|
|
65
|
-
|
|
69
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
70
|
+
await utils.seeTextByXpath(this.page, resolvedText, time * 1000);
|
|
66
71
|
});
|
|
67
72
|
Then('I wait for the text {string} to disappear within {string} seconds', async function (text, time) {
|
|
68
|
-
|
|
73
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
74
|
+
await utils.disappearText(this.page, resolvedText, time * 1000);
|
|
69
75
|
});
|
|
70
76
|
Then('I upload the {string} in {string} field', async function (fileName, cssSelector) {
|
|
71
77
|
const selector = this.commonFields[cssSelector] ?? cssSelector;
|
|
@@ -95,9 +101,10 @@ Then('I select {string} from {string}', async function (value, cssSelector) {
|
|
|
95
101
|
const selector = this.commonFields[cssSelector] ?? cssSelector;
|
|
96
102
|
await utils.selectOptionByValue(this.page, selector, value);
|
|
97
103
|
});
|
|
98
|
-
Then('I select text {string} from {string}', async function (
|
|
104
|
+
Then('I select text {string} from {string}', async function (text, cssSelector) {
|
|
99
105
|
const selector = this.commonFields[cssSelector] ?? cssSelector;
|
|
100
|
-
|
|
106
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
107
|
+
await utils.selectOptionByText(this.page, selector, resolvedText);
|
|
101
108
|
});
|
|
102
109
|
|
|
103
110
|
Then('I check if link {string} has href {string}', async function (text, href) {
|
|
@@ -118,7 +125,8 @@ Then(
|
|
|
118
125
|
Then(
|
|
119
126
|
'I check if element with text {string} has attribute {string} with {string} value',
|
|
120
127
|
async function (text, attribute, value) {
|
|
121
|
-
|
|
128
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
129
|
+
await utils.validateValueOfElementAttributeByText(this.page, resolvedText, attribute, value);
|
|
122
130
|
}
|
|
123
131
|
);
|
|
124
132
|
Then('I upload {string} file to dropzone {string} field', async function (file, cssSelector) {
|
|
@@ -126,28 +134,34 @@ Then('I upload {string} file to dropzone {string} field', async function (file,
|
|
|
126
134
|
await utils.uploadToDropzone(this.page, file, selector);
|
|
127
135
|
});
|
|
128
136
|
Then('I should see {string} in the schema markup of the page', async function (text) {
|
|
129
|
-
|
|
137
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
138
|
+
await utils.validateTextInSchemaOrg(this.page, resolvedText);
|
|
130
139
|
});
|
|
131
140
|
Then('I should not see {string} in the schema markup of the page', async function (text) {
|
|
132
|
-
|
|
141
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
142
|
+
await utils.validateTextNotInSchemaOrg(this.page, resolvedText);
|
|
133
143
|
});
|
|
134
144
|
Then('I should see {string} in page scripts', async function (text) {
|
|
135
|
-
|
|
145
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
146
|
+
await utils.validateTextInScript(this.page, resolvedText);
|
|
136
147
|
});
|
|
137
148
|
Then('I should {string} see {string} in the {string} accordion', async function (isVisible, text, cssSelector) {
|
|
138
149
|
const selector = this.commonFields[cssSelector] ?? cssSelector;
|
|
139
150
|
if (isVisible === 'not') {
|
|
140
151
|
isVisible = false;
|
|
141
152
|
}
|
|
142
|
-
|
|
153
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
154
|
+
await utils.textVisibilityInAccordion(this.page, selector, resolvedText, Boolean(isVisible));
|
|
143
155
|
});
|
|
144
156
|
Then('I select the first autocomplete option for {string} on the {string} field', async function (text, cssSelector) {
|
|
145
157
|
const selector = this.commonFields[cssSelector] ?? cssSelector;
|
|
146
|
-
|
|
158
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
159
|
+
await utils.selectOptionFirstAutocomplete(this.page, resolvedText, selector);
|
|
147
160
|
});
|
|
148
161
|
Then('I select {string} from chosen {string}', async function (text, cssSelector) {
|
|
149
162
|
const selector = this.commonFields[cssSelector] ?? cssSelector;
|
|
150
|
-
|
|
163
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
164
|
+
await utils.selectOptionFromChosen(this.page, resolvedText, selector);
|
|
151
165
|
});
|
|
152
166
|
|
|
153
167
|
Then('I set date {string} in flatpickr with selector {string}', async function (date, cssSelector) {
|
|
@@ -186,7 +200,8 @@ Given('I check if checkbox options with locator {string} are not in alphabetical
|
|
|
186
200
|
await utils.iCheckIfCheckboxOptionsAreInAlphabeticalOrder(this.page, selector, false);
|
|
187
201
|
});
|
|
188
202
|
When('I click on the text {string} and follow the new tab', async function (text) {
|
|
189
|
-
|
|
203
|
+
const resolvedText = this.mlStrings[text] ?? text;
|
|
204
|
+
this.page = await utils.clickLinkOpenNewTab(this.browser, this.page, resolvedText, true);
|
|
190
205
|
});
|
|
191
206
|
When('I click on the element {string} and follow the new tab', async function (cssSelector) {
|
|
192
207
|
const selector = this.commonFields[cssSelector] ?? cssSelector;
|
package/features/app/world.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const { setWorldConstructor, setDefaultTimeout } = require('@cucumber/cucumber');
|
|
2
|
+
const config = require('config');
|
|
2
3
|
const commonFields = require('./commonComponents/commonFields');
|
|
4
|
+
const strings = require('./multilingualStrings/multilingualStrings');
|
|
3
5
|
|
|
4
6
|
setDefaultTimeout(120 * 1000);
|
|
5
7
|
|
|
@@ -8,10 +10,16 @@ class World {
|
|
|
8
10
|
constructor({ attach }) {
|
|
9
11
|
this.attach = attach;
|
|
10
12
|
this.commonFields = commonFields;
|
|
13
|
+
this.enableMlSupport();
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
enableMlSupport() {
|
|
17
|
+
const lang = config.has('language') ? config.get('language') : null;
|
|
18
|
+
if (lang) {
|
|
19
|
+
this.mlStrings = strings.multilingualStrings(lang) ?? {};
|
|
20
|
+
} else {
|
|
21
|
+
this.mlStrings = {};
|
|
22
|
+
}
|
|
15
23
|
}
|
|
16
24
|
}
|
|
17
25
|
setWorldConstructor(World);
|
package/package.json
CHANGED
|
@@ -169,15 +169,14 @@ module.exports = {
|
|
|
169
169
|
* Click on the text of a link and expect it to open in a new tab. target="_blank"
|
|
170
170
|
* @param {Browser} browser
|
|
171
171
|
* @param {Page} page
|
|
172
|
-
* @param value -
|
|
172
|
+
* @param value - text of link
|
|
173
173
|
* @param xpath - flag, whether to use xpath or not
|
|
174
174
|
* @returns {Promise<Object>}
|
|
175
175
|
*/
|
|
176
176
|
clickLinkOpenNewTab: async function (browser, page, value, xpath = true) {
|
|
177
177
|
let objectToClick;
|
|
178
178
|
if (xpath) {
|
|
179
|
-
|
|
180
|
-
objectToClick = await page.waitForSelector('xpath/' + `//body//*[text()[contains(.,'${result}')]]`);
|
|
179
|
+
objectToClick = await page.waitForSelector('xpath/' + `//body//*[text()[contains(.,'${value}')]]`);
|
|
181
180
|
} else {
|
|
182
181
|
objectToClick = await page.waitForSelector(value);
|
|
183
182
|
}
|
|
@@ -303,12 +302,11 @@ module.exports = {
|
|
|
303
302
|
* @returns {Promise<void>}
|
|
304
303
|
*/
|
|
305
304
|
validateValueOfElementAttributeByText: async function (page, text, attribute, value) {
|
|
306
|
-
const
|
|
307
|
-
const selector = 'xpath/' + `//body//*[text()[contains(.,'${result}')]]`;
|
|
305
|
+
const selector = 'xpath/' + `//body//*[text()[contains(.,'${text}')]]`;
|
|
308
306
|
await page.waitForSelector(selector);
|
|
309
307
|
const attrValue = await page.$eval(selector, (el, attribute) => el.getAttribute(attribute), attribute);
|
|
310
308
|
if (value !== attrValue) {
|
|
311
|
-
throw new Error(`The provided text "${
|
|
309
|
+
throw new Error(`The provided text "${text}" doesn't match element which attribute has value: ${value}.`);
|
|
312
310
|
}
|
|
313
311
|
},
|
|
314
312
|
|
|
@@ -359,7 +357,6 @@ module.exports = {
|
|
|
359
357
|
* @returns {Promise<void>}
|
|
360
358
|
*/
|
|
361
359
|
seeTextByXpath: async function (page, text, time = 6000) {
|
|
362
|
-
let result = await helper.getMultilingualString(text);
|
|
363
360
|
const options = {
|
|
364
361
|
visible: true, // Wait for the element to be visible (default: false)
|
|
365
362
|
timeout: time, // Maximum time to wait in milliseconds (default: 30000)
|
|
@@ -370,9 +367,9 @@ module.exports = {
|
|
|
370
367
|
});
|
|
371
368
|
}
|
|
372
369
|
try {
|
|
373
|
-
await page.waitForSelector('xpath/' + `//body//*[text()[contains(.,"${
|
|
370
|
+
await page.waitForSelector('xpath/' + `//body//*[text()[contains(.,"${text}")]]`, options);
|
|
374
371
|
} catch (error) {
|
|
375
|
-
throw new Error(`Could not find text : ${
|
|
372
|
+
throw new Error(`Could not find text : ${text}. The error thrown is: ${error}`);
|
|
376
373
|
}
|
|
377
374
|
},
|
|
378
375
|
|
|
@@ -385,14 +382,13 @@ module.exports = {
|
|
|
385
382
|
* @returns {Promise<void>}
|
|
386
383
|
*/
|
|
387
384
|
seeTextByElementHandle: async function (page, selector, text) {
|
|
388
|
-
const result = await helper.getMultilingualString(text);
|
|
389
385
|
await page.waitForSelector(selector);
|
|
390
386
|
let textContent = await page.$eval(selector, (element) => element.textContent.trim());
|
|
391
387
|
if (!textContent) {
|
|
392
388
|
textContent = await page.$eval(selector, (element) => element.value.trim());
|
|
393
389
|
}
|
|
394
|
-
if (textContent !==
|
|
395
|
-
throw new Error(`Expected ${
|
|
390
|
+
if (textContent !== text) {
|
|
391
|
+
throw new Error(`Expected ${text} text, but found ${textContent} instead.`);
|
|
396
392
|
}
|
|
397
393
|
},
|
|
398
394
|
|
|
@@ -406,13 +402,12 @@ module.exports = {
|
|
|
406
402
|
*/
|
|
407
403
|
seeTextInRegion: async function (page, text, region) {
|
|
408
404
|
const regionClass = await helper.getRegion(page, region);
|
|
409
|
-
const result = await helper.getMultilingualString(text);
|
|
410
405
|
try {
|
|
411
406
|
await page.waitForSelector(
|
|
412
|
-
'xpath/' + `//*[contains(@class,'${regionClass}') and .//text()[contains(.,"${
|
|
407
|
+
'xpath/' + `//*[contains(@class,'${regionClass}') and .//text()[contains(.,"${text}")]]`
|
|
413
408
|
);
|
|
414
409
|
} catch {
|
|
415
|
-
throw new Error(`Cannot find ${
|
|
410
|
+
throw new Error(`Cannot find ${text} in ${regionClass}!`);
|
|
416
411
|
}
|
|
417
412
|
},
|
|
418
413
|
|
|
@@ -425,8 +420,7 @@ module.exports = {
|
|
|
425
420
|
*/
|
|
426
421
|
hoverTextInRegion: async function (page, text, region) {
|
|
427
422
|
const regionClass = await helper.getRegion(page, region);
|
|
428
|
-
const
|
|
429
|
-
const selector = 'xpath/' + `//*[@class='${regionClass}']//*[text()='${result}']`;
|
|
423
|
+
const selector = 'xpath/' + `//*[@class='${regionClass}']//*[text()='${text}']`;
|
|
430
424
|
try {
|
|
431
425
|
const element = await page.waitForSelector(selector);
|
|
432
426
|
const parentElementHandle = await page.evaluateHandle((el) => el.parentElement, element);
|
|
@@ -443,8 +437,7 @@ module.exports = {
|
|
|
443
437
|
* @returns {Promise<void>}
|
|
444
438
|
*/
|
|
445
439
|
notSeeText: async function (page, text) {
|
|
446
|
-
|
|
447
|
-
const isTextInDom = await page.$('xpath/' + `//*[text()[contains(.,'${result}')]]`);
|
|
440
|
+
const isTextInDom = await page.$('xpath/' + `//*[text()[contains(.,'${text}')]]`);
|
|
448
441
|
// isVisible() is used for the cases where the text is in the DOM, but not visible
|
|
449
442
|
// If you need to NOT have it in the DOM - use notSeeElement() or extend this step with flag
|
|
450
443
|
const visibility = await isTextInDom?.isVisible();
|
|
@@ -461,7 +454,6 @@ module.exports = {
|
|
|
461
454
|
* @returns {Promise<void>}
|
|
462
455
|
*/
|
|
463
456
|
validateTextInField: async function (page, text, selector) {
|
|
464
|
-
let result = await helper.getMultilingualString(text);
|
|
465
457
|
let value = '';
|
|
466
458
|
await page.waitForSelector(selector);
|
|
467
459
|
try {
|
|
@@ -475,8 +467,8 @@ module.exports = {
|
|
|
475
467
|
} catch (error) {
|
|
476
468
|
throw new Error(error);
|
|
477
469
|
}
|
|
478
|
-
if (value !==
|
|
479
|
-
throw new Error(`Value of element ${value} does not match the text ${
|
|
470
|
+
if (value !== text) {
|
|
471
|
+
throw new Error(`Value of element ${value} does not match the text ${text}`);
|
|
480
472
|
}
|
|
481
473
|
},
|
|
482
474
|
|
|
@@ -489,7 +481,6 @@ module.exports = {
|
|
|
489
481
|
* @returns {Promise<void>}
|
|
490
482
|
*/
|
|
491
483
|
textVisibilityInAccordion: async function (page, cssSelector, text, isVisible) {
|
|
492
|
-
let result = await helper.getMultilingualString(text);
|
|
493
484
|
const el = await page.$(cssSelector);
|
|
494
485
|
if (el) {
|
|
495
486
|
const isShown = await (await page.evaluateHandle((el) => el.clientHeight, el)).jsonValue();
|
|
@@ -498,9 +489,9 @@ module.exports = {
|
|
|
498
489
|
}
|
|
499
490
|
if (isShown) {
|
|
500
491
|
const textValue = await (await page.evaluateHandle((el) => el.textContent.trim(), el)).jsonValue();
|
|
501
|
-
if (isVisible && textValue !==
|
|
502
|
-
throw new Error(`Element text: ${textValue} does not match the expected: ${
|
|
503
|
-
} else if (!isVisible && textValue ===
|
|
492
|
+
if (isVisible && textValue !== text) {
|
|
493
|
+
throw new Error(`Element text: ${textValue} does not match the expected: ${text}!`);
|
|
494
|
+
} else if (!isVisible && textValue === text) {
|
|
504
495
|
throw new Error(`Element text: ${textValue} is visible but it should not be!`);
|
|
505
496
|
}
|
|
506
497
|
}
|
|
@@ -518,14 +509,13 @@ module.exports = {
|
|
|
518
509
|
* @returns {Promise<void>}
|
|
519
510
|
*/
|
|
520
511
|
disappearText: async function (page, text, time) {
|
|
521
|
-
let result = await helper.getMultilingualString(text);
|
|
522
512
|
const options = {
|
|
523
513
|
visible: true, // Wait for the element to be visible (default: false)
|
|
524
514
|
timeout: 250, // 250ms and for that reason time is multiplied by 4 to add up to a full second.
|
|
525
515
|
};
|
|
526
516
|
for (let i = 0; i < time * 4; i++) {
|
|
527
517
|
try {
|
|
528
|
-
await page.waitForSelector('xpath/' + `//*[text()[contains(.,'${
|
|
518
|
+
await page.waitForSelector('xpath/' + `//*[text()[contains(.,'${text}')]]`, options);
|
|
529
519
|
} catch {
|
|
530
520
|
console.log(`Element disappeared in ${time * 4}.`);
|
|
531
521
|
break;
|
|
@@ -543,11 +533,10 @@ module.exports = {
|
|
|
543
533
|
*/
|
|
544
534
|
clickTextInRegion: async function (page, text, region) {
|
|
545
535
|
const regionClass = await helper.getRegion(page, region);
|
|
546
|
-
const result = await helper.getMultilingualString(text);
|
|
547
536
|
await page.waitForSelector('xpath/' + `//*[@class='${regionClass}']`);
|
|
548
537
|
const elements =
|
|
549
|
-
(await page.$$('xpath/' + `//*[@class='${regionClass}']//*[text()='${
|
|
550
|
-
(await page.$$('xpath/' + `//*[@class='${regionClass}']//*[contains(text(),'${
|
|
538
|
+
(await page.$$('xpath/' + `//*[@class='${regionClass}']//*[text()='${text}']`)) ||
|
|
539
|
+
(await page.$$('xpath/' + `//*[@class='${regionClass}']//*[contains(text(),'${text}')]`));
|
|
551
540
|
|
|
552
541
|
if (!elements?.[0]) {
|
|
553
542
|
throw new Error('Element not found!');
|
|
@@ -608,18 +597,17 @@ module.exports = {
|
|
|
608
597
|
* Put value in a field. It directly places the text like Ctrl+V(Paste) will do it.
|
|
609
598
|
* @param {Page} page
|
|
610
599
|
* @param selector
|
|
611
|
-
* @param
|
|
600
|
+
* @param text
|
|
612
601
|
* @param skip
|
|
613
602
|
* @returns {Promise<boolean>}
|
|
614
603
|
*/
|
|
615
|
-
fillField: async function (page, selector,
|
|
616
|
-
let result = await helper.getMultilingualString(data);
|
|
604
|
+
fillField: async function (page, selector, text, skip = false) {
|
|
617
605
|
const skipped = await this.customWaitForSkippableElement(page, selector, skip);
|
|
618
606
|
if (skipped) {
|
|
619
607
|
return true;
|
|
620
608
|
}
|
|
621
609
|
try {
|
|
622
|
-
await page.$eval(selector, (el, name) => (el.value = name),
|
|
610
|
+
await page.$eval(selector, (el, name) => (el.value = name), text);
|
|
623
611
|
await new Promise(function (resolve) {
|
|
624
612
|
setTimeout(resolve, 500);
|
|
625
613
|
});
|
|
@@ -637,7 +625,6 @@ module.exports = {
|
|
|
637
625
|
* @returns {Promise<boolean>}
|
|
638
626
|
*/
|
|
639
627
|
typeInField: async function (page, selector, text, skip = false) {
|
|
640
|
-
let result = await helper.getMultilingualString(text);
|
|
641
628
|
const skipped = await this.customWaitForSkippableElement(page, selector, skip);
|
|
642
629
|
if (skipped) {
|
|
643
630
|
return true;
|
|
@@ -650,7 +637,7 @@ module.exports = {
|
|
|
650
637
|
setTimeout(resolve, 150);
|
|
651
638
|
});
|
|
652
639
|
try {
|
|
653
|
-
await page.type(selector,
|
|
640
|
+
await page.type(selector, text, { delay: 250 });
|
|
654
641
|
} catch (error) {
|
|
655
642
|
throw new Error(`Cannot type into field due to ${error}`);
|
|
656
643
|
}
|
|
@@ -745,14 +732,13 @@ module.exports = {
|
|
|
745
732
|
* @returns {Promise<void>}
|
|
746
733
|
*/
|
|
747
734
|
selectOptionByText: async function (page, selector, text) {
|
|
748
|
-
let result = await helper.getMultilingualString(text);
|
|
749
735
|
await page.waitForSelector(selector);
|
|
750
|
-
const objectToSelect = await page.$('xpath/' + `//body//*[contains(text(), '${
|
|
736
|
+
const objectToSelect = await page.$('xpath/' + `//body//*[contains(text(), '${text}')]`);
|
|
751
737
|
if (objectToSelect) {
|
|
752
738
|
const value = await (await objectToSelect.getProperty('value')).jsonValue();
|
|
753
739
|
await page.select(selector, value);
|
|
754
740
|
} else {
|
|
755
|
-
throw new Error(`Could not find option with text: ${
|
|
741
|
+
throw new Error(`Could not find option with text: ${text}`);
|
|
756
742
|
}
|
|
757
743
|
},
|
|
758
744
|
|
|
@@ -894,18 +880,17 @@ module.exports = {
|
|
|
894
880
|
* Sets value into codemirror field
|
|
895
881
|
* @param {Page} page
|
|
896
882
|
* @param cssSelector
|
|
897
|
-
* @param
|
|
883
|
+
* @param text
|
|
898
884
|
* @returns {Promise<void>}
|
|
899
885
|
*/
|
|
900
|
-
setValueInCodeMirrorField: async function (page, cssSelector,
|
|
901
|
-
let result = await helper.getMultilingualString(value);
|
|
886
|
+
setValueInCodeMirrorField: async function (page, cssSelector, text) {
|
|
902
887
|
await page.waitForSelector(cssSelector);
|
|
903
888
|
try {
|
|
904
889
|
const jsCode = `
|
|
905
890
|
(function () {
|
|
906
891
|
const textArea = document.querySelector('${cssSelector}');
|
|
907
892
|
let editor = CodeMirror.fromTextArea(textArea);
|
|
908
|
-
editor.getDoc().setValue("${
|
|
893
|
+
editor.getDoc().setValue("${text}");
|
|
909
894
|
})();
|
|
910
895
|
`;
|
|
911
896
|
await page.evaluate(jsCode);
|
package/src/helperFunctions.js
CHANGED
|
@@ -43,23 +43,6 @@ module.exports = {
|
|
|
43
43
|
await page.waitForFunction(jsCode);
|
|
44
44
|
},
|
|
45
45
|
|
|
46
|
-
/**
|
|
47
|
-
* Returns the translated variant of the inputted text or the text itself if there isn't a translation.
|
|
48
|
-
* @param text
|
|
49
|
-
* @returns {Promise<string>}
|
|
50
|
-
*/
|
|
51
|
-
getMultilingualString: async function (text) {
|
|
52
|
-
const lang = config.has('language') ? config.get('language') : null;
|
|
53
|
-
let result;
|
|
54
|
-
if (lang) {
|
|
55
|
-
let string = strings.multilingualStrings(lang, text);
|
|
56
|
-
result = string ?? text;
|
|
57
|
-
} else {
|
|
58
|
-
result = text;
|
|
59
|
-
}
|
|
60
|
-
return result;
|
|
61
|
-
},
|
|
62
|
-
|
|
63
46
|
/**
|
|
64
47
|
* Retrieves the class name of an element based on a property in the config json.
|
|
65
48
|
* You can set directly the full class, partial or ID, but mind that it always resolves to
|