artes 1.0.78 → 1.0.80

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 CHANGED
@@ -42,21 +42,21 @@ npx artes [options]
42
42
 
43
43
  ### Options
44
44
 
45
- | Option | Description | Usage Example |
46
- | ------------------- | ------------------------------------------------------------- | ------------------------------------------------- |
47
- | 🆘 `-h, --help` | Show the usage options | `artes -h` or `artes --help` |
48
- | 🏷️ `-v, --version` | Show the current version of Artes | `artes -v` or `artes --version` |
49
- | 🏗️ `-c, --create` | Create an example project with Artes | `artes -c` or `artes --create` |
50
- | ✅ `-y, --yes` | Skip the confirmation prompt when creating an example project | `artes -c -y` or `artes --create --yes` |
51
- | 📊 `-r, --report` | Run tests and generate Allure report | `artes -r` or `artes --report` |
52
- | 📁 `--features` | Specify one or more feature files' relative paths to run (comma-separated) | `artes --features "tests/features/Alma, tests/features/Banan.feature"` |
53
- | 🔖 `--tags` | Run tests with specified Cucumber tags | `artes --tags "@smoke or @wip"` |
54
- | 🌐 `--env` | Set the environment for the test run | `artes --env "dev"` |
55
- | 🕶️ `--headless` | Run browser in headless mode | `artes --headless` |
56
- | ⚡ `--parallel` | Run tests in parallel mode | `artes --parallel 2` |
57
- | 🔁 `--retry` | Retry failed tests | `artes --retry 3` |
58
- | 🎭 `--dryrun` | Perform a dry run without executing tests | `artes --dryrun` |
59
-
45
+ | Option | Description | Usage Example |
46
+ | ------------------ | -------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
47
+ | 🆘 `-h, --help` | Show the usage options | `artes -h` or `artes --help` |
48
+ | 🏷️ `-v, --version` | Show the current version of Artes | `artes -v` or `artes --version` |
49
+ | 🏗️ `-c, --create` | Create an example project with Artes | `artes -c` or `artes --create` |
50
+ | ✅ `-y, --yes` | Skip the confirmation prompt when creating an example project | `artes -c -y` or `artes --create --yes` |
51
+ | 📊 `-r, --report` | Run tests and generate Allure report | `artes -r` or `artes --report` |
52
+ | 📁 `--features` | Specify one or more feature files' relative paths to run (comma-separated) | `artes --features "tests/features/Alma, tests/features/Banan.feature"` |
53
+ | 🔖 `--tags` | Run tests with specified Cucumber tags | `artes --tags "@smoke or @wip"` |
54
+ | 🌐 `--env` | Set the environment for the test run | `artes --env "dev"` |
55
+ | 🕶️ `--headless` | Run browser in headless mode | `artes --headless` |
56
+ | ⚡ `--parallel` | Run tests in parallel mode | `artes --parallel 2` |
57
+ | 🔁 `--retry` | Retry failed tests | `artes --retry 3` |
58
+ | 🎭 `--dryrun` | Perform a dry run without executing tests | `artes --dryrun` |
59
+ | 📈 `--percentage` | Set minimum success percentage to pass test run(default is 0) | `artes --percentage 85` |
60
60
 
61
61
  \*\* To just run the tests: <br>
62
62
  Globally: artes <br>
@@ -277,39 +277,38 @@ Then("User should see the login form", async () => {
277
277
 
278
278
  You can configure Artes by editing the `artes.config.js` file. Below are the default configuration options with explanations:
279
279
 
280
-
281
- | **Option** | **Default Value** | **Description** |
282
- | ----------------- | ---------------------------------------------------- | ----------------------------------------------------- |
283
- | `timeout` | `30` | Default timeout in milliseconds. |
284
- | `paths` | `[moduleConfig.featuresPath]` | Paths to feature files. |
280
+ | **Option** | **Default Value** | **Description** |
281
+ | ----------------- | ---------------------------------------------------------------------------- | ----------------------------------- |
282
+ | `timeout` | `30` | Default timeout in milliseconds. |
283
+ | `paths` | `[moduleConfig.featuresPath]` | Paths to feature files. |
285
284
  | `require` | `[moduleConfig.stepsPath, "src/stepDefinitions/*.js", "src/hooks/hooks.js"]` | Support code paths (CommonJS). |
286
- | `pomPath` | `moduleConfig.pomPath` | Path to Page Object Models. |
287
- | `import` | `[]` | Support code paths. |
288
- | `format` | `["rerun:@rerun.txt", "allure-cucumberjs/reporter"]` | Formatter names/paths. |
289
- | `formatOptions` | `{ "resultsDir": "allure-result" }` | Formatter options. |
290
- | `parallel` | `1` | Number of parallel workers. |
291
- | `dryRun` | `false` | Prepare test run without execution. |
292
- | `failFast` | `false` | Stop on first test failure. |
293
- | `forceExit` | `false` | Force `process.exit()` after tests. |
294
- | `strict` | `true` | Fail on pending steps. |
295
- | `backtrace` | `false` | Show full backtrace for errors. |
296
- | `tags` | `""` | Tag expression to filter scenarios. |
297
- | `name` | `[]` | Run scenarios matching regex. |
298
- | `order` | `"defined"` | Run order (defined/random). |
299
- | `language` | `"en"` | Default feature file language. |
300
- | `loader` | `[]` | Module loader specifications. |
301
- | `requireModule` | `[]` | Transpilation module names. |
302
- | `retry` | `0` | Retry attempts for failing tests. |
303
- | `retryTagFilter` | `""` | Tag expression for retries. |
304
- | `publish` | `false` | Publish to cucumber.io. |
305
- | `worldParameters` | `{}` | Custom world parameters. |
285
+ | `pomPath` | `moduleConfig.pomPath` | Path to Page Object Models. |
286
+ | `import` | `[]` | Support code paths. |
287
+ | `format` | `["rerun:@rerun.txt", "allure-cucumberjs/reporter"]` | Formatter names/paths. |
288
+ | `formatOptions` | `{ "resultsDir": "allure-result" }` | Formatter options. |
289
+ | `parallel` | `1` | Number of parallel workers. |
290
+ | `dryRun` | `false` | Prepare test run without execution. |
291
+ | `failFast` | `false` | Stop on first test failure. |
292
+ | `forceExit` | `false` | Force `process.exit()` after tests. |
293
+ | `strict` | `true` | Fail on pending steps. |
294
+ | `backtrace` | `false` | Show full backtrace for errors. |
295
+ | `tags` | `""` | Tag expression to filter scenarios. |
296
+ | `name` | `[]` | Run scenarios matching regex. |
297
+ | `order` | `"defined"` | Run order (defined/random). |
298
+ | `language` | `"en"` | Default feature file language. |
299
+ | `loader` | `[]` | Module loader specifications. |
300
+ | `requireModule` | `[]` | Transpilation module names. |
301
+ | `retry` | `0` | Retry attempts for failing tests. |
302
+ | `retryTagFilter` | `""` | Tag expression for retries. |
303
+ | `publish` | `false` | Publish to cucumber.io. |
304
+ | `worldParameters` | `{}` | Custom world parameters. |
306
305
 
307
306
  ### Environment Configuration
308
307
 
309
- | **Option** | **Default Value** | **Description** |
310
- | ----------- | ----------------- | ---------------------------------- |
311
- | `env` | `""` | Environment configuration. Should match the name with the baseURL object, like "dev" |
312
- | `baseURL` | `""` | Base URL for API requests. Can be object {"dev":"dev-api.com", "pre":"pre-api.com"}, or string "dev-api.com" |
308
+ | **Option** | **Default Value** | **Description** |
309
+ | ---------- | ----------------- | ------------------------------------------------------------------------------------------------------------ |
310
+ | `env` | `""` | Environment configuration. Should match the name with the baseURL object, like "dev" |
311
+ | `baseURL` | `""` | Base URL for API requests. Can be object {"dev":"dev-api.com", "pre":"pre-api.com"}, or string "dev-api.com" |
313
312
 
314
313
  ---
315
314
 
@@ -17,6 +17,9 @@ try {
17
17
  module.exports = {
18
18
  default: {
19
19
  // File paths and patterns
20
+ testPercentage: process.env.PERCENTAGE
21
+ ? process.env.PERCENTAGE
22
+ : artesConfig.testPercentage || 0, // number - Percentage of tests to run (0-100)
20
23
  timeout: artesConfig.timeout || 30, // Default timeout in milliseconds
21
24
  paths: process.env.FEATURES
22
25
  ? [path.join(moduleConfig.projectPath, process.env.FEATURES)]
@@ -47,8 +50,12 @@ module.exports = {
47
50
  }, // Formatter options
48
51
 
49
52
  // Execution options
50
- parallel: process.env.PARALLEL ? JSON.parse(process.env.PARALLEL) : artesConfig.parallel || 1, // Number of parallel workers
51
- dryRun: process.env.DRYRUN ? JSON.parse(process.env.DRYRUN) : artesConfig.dryRun || false, // Prepare test run without execution
53
+ parallel: process.env.PARALLEL
54
+ ? JSON.parse(process.env.PARALLEL)
55
+ : artesConfig.parallel || 1, // Number of parallel workers
56
+ dryRun: process.env.DRYRUN
57
+ ? JSON.parse(process.env.DRYRUN)
58
+ : artesConfig.dryRun || false, // Prepare test run without execution
52
59
  failFast: artesConfig.failFast || false, // Stop on first test failure
53
60
  forceExit: artesConfig.forceExit || false, // Force process.exit() after tests
54
61
  strict: artesConfig.strict || true, // Fail on pending steps
@@ -67,7 +74,9 @@ module.exports = {
67
74
  requireModule: artesConfig.requireModule || [], // Transpilation module names
68
75
 
69
76
  // Retry logic
70
- retry: process.env.RETRY ? JSON.parse(process.env.RETRY) : artesConfig.retry || 0, // Retry attempts for failing tests
77
+ retry: process.env.RETRY
78
+ ? JSON.parse(process.env.RETRY)
79
+ : artesConfig.retry || 0, // Retry attempts for failing tests
71
80
  retryTagFilter: artesConfig.retryTagFilter || "", // Tag expression for retries
72
81
 
73
82
  // Publishing
@@ -3,7 +3,14 @@
3
3
  If you don't want to deal with Playwright methods directly, you can simply use the following predefined actions methods by import them:
4
4
 
5
5
  ```javascript
6
- const { mouse, keyboard, frame, elementInteractions, page, api } = require("artes");
6
+ const {
7
+ mouse,
8
+ keyboard,
9
+ frame,
10
+ elementInteractions,
11
+ page,
12
+ api,
13
+ } = require("artes");
7
14
  ```
8
15
 
9
16
  - **Mouse Actions:**
@@ -2114,16 +2121,21 @@ The `api` object provides methods for making HTTP requests with automatic URL re
2114
2121
  Makes a GET request to the specified URL.
2115
2122
 
2116
2123
  **Parameters:**
2124
+
2117
2125
  - `url` (string): The target URL (supports variable resolution)
2118
2126
  - `payload` (string, optional): JSON string containing headers and other request options
2119
2127
 
2120
2128
  **Returns:** Promise that resolves when the request completes
2121
2129
 
2122
2130
  **Example:**
2131
+
2123
2132
  ```javascript
2124
- await api.get("https://api.example.com/users", JSON.stringify({
2125
- headers: { "Authorization": "Bearer {{token}}" }
2126
- }));
2133
+ await api.get(
2134
+ "https://api.example.com/users",
2135
+ JSON.stringify({
2136
+ headers: { Authorization: "Bearer {{token}}" },
2137
+ }),
2138
+ );
2127
2139
  ```
2128
2140
 
2129
2141
  ### `api.head(url)`
@@ -2131,11 +2143,13 @@ await api.get("https://api.example.com/users", JSON.stringify({
2131
2143
  Makes a HEAD request to the specified URL.
2132
2144
 
2133
2145
  **Parameters:**
2146
+
2134
2147
  - `url` (string): The target URL (supports variable resolution)
2135
2148
 
2136
2149
  **Returns:** Promise that resolves when the request completes
2137
2150
 
2138
2151
  **Example:**
2152
+
2139
2153
  ```javascript
2140
2154
  await api.head("https://api.example.com/status");
2141
2155
  ```
@@ -2145,6 +2159,7 @@ await api.head("https://api.example.com/status");
2145
2159
  Makes a POST request to the specified URL.
2146
2160
 
2147
2161
  **Parameters:**
2162
+
2148
2163
  - `url` (string): The target URL (supports variable resolution)
2149
2164
  - `payload` (string): JSON string containing headers, body, and other request options
2150
2165
  - `requestDataType` (string, optional): Request data type ("multipart" for form data, default for JSON)
@@ -2152,18 +2167,26 @@ Makes a POST request to the specified URL.
2152
2167
  **Returns:** Promise that resolves when the request completes
2153
2168
 
2154
2169
  **Example:**
2170
+
2155
2171
  ```javascript
2156
2172
  // Regular POST request
2157
- await api.post("https://api.example.com/users", JSON.stringify({
2158
- headers: { "Content-Type": "application/json" },
2159
- body: { name: "John", email: "john@example.com" }
2160
- }));
2173
+ await api.post(
2174
+ "https://api.example.com/users",
2175
+ JSON.stringify({
2176
+ headers: { "Content-Type": "application/json" },
2177
+ body: { name: "John", email: "john@example.com" },
2178
+ }),
2179
+ );
2161
2180
 
2162
2181
  // Multipart form data
2163
- await api.post("https://api.example.com/upload", JSON.stringify({
2164
- headers: {},
2165
- body: { file: "/path/to/file.pdf", description: "Document upload" }
2166
- }), "multipart");
2182
+ await api.post(
2183
+ "https://api.example.com/upload",
2184
+ JSON.stringify({
2185
+ headers: {},
2186
+ body: { file: "/path/to/file.pdf", description: "Document upload" },
2187
+ }),
2188
+ "multipart",
2189
+ );
2167
2190
  ```
2168
2191
 
2169
2192
  ### `api.put(url, payload, requestDataType)`
@@ -2171,6 +2194,7 @@ await api.post("https://api.example.com/upload", JSON.stringify({
2171
2194
  Makes a PUT request to the specified URL.
2172
2195
 
2173
2196
  **Parameters:**
2197
+
2174
2198
  - `url` (string): The target URL (supports variable resolution)
2175
2199
  - `payload` (string): JSON string containing headers, body, and other request options
2176
2200
  - `requestDataType` (string, optional): Request data type ("multipart" for form data, default for JSON)
@@ -2178,11 +2202,15 @@ Makes a PUT request to the specified URL.
2178
2202
  **Returns:** Promise that resolves when the request completes
2179
2203
 
2180
2204
  **Example:**
2205
+
2181
2206
  ```javascript
2182
- await api.put("https://api.example.com/users/{{userId}}", JSON.stringify({
2183
- headers: { "Content-Type": "application/json" },
2184
- body: { name: "Updated Name" }
2185
- }));
2207
+ await api.put(
2208
+ "https://api.example.com/users/{{userId}}",
2209
+ JSON.stringify({
2210
+ headers: { "Content-Type": "application/json" },
2211
+ body: { name: "Updated Name" },
2212
+ }),
2213
+ );
2186
2214
  ```
2187
2215
 
2188
2216
  ### `api.patch(url, payload, requestDataType)`
@@ -2190,6 +2218,7 @@ await api.put("https://api.example.com/users/{{userId}}", JSON.stringify({
2190
2218
  Makes a PATCH request to the specified URL.
2191
2219
 
2192
2220
  **Parameters:**
2221
+
2193
2222
  - `url` (string): The target URL (supports variable resolution)
2194
2223
  - `payload` (string): JSON string containing headers, body, and other request options
2195
2224
  - `requestDataType` (string, optional): Request data type ("multipart" for form data, default for JSON)
@@ -2197,11 +2226,15 @@ Makes a PATCH request to the specified URL.
2197
2226
  **Returns:** Promise that resolves when the request completes
2198
2227
 
2199
2228
  **Example:**
2229
+
2200
2230
  ```javascript
2201
- await api.patch("https://api.example.com/users/{{userId}}", JSON.stringify({
2202
- headers: { "Content-Type": "application/json" },
2203
- body: { email: "newemail@example.com" }
2204
- }));
2231
+ await api.patch(
2232
+ "https://api.example.com/users/{{userId}}",
2233
+ JSON.stringify({
2234
+ headers: { "Content-Type": "application/json" },
2235
+ body: { email: "newemail@example.com" },
2236
+ }),
2237
+ );
2205
2238
  ```
2206
2239
 
2207
2240
  ### `api.delete(url, payload)`
@@ -2209,16 +2242,21 @@ await api.patch("https://api.example.com/users/{{userId}}", JSON.stringify({
2209
2242
  Makes a DELETE request to the specified URL.
2210
2243
 
2211
2244
  **Parameters:**
2245
+
2212
2246
  - `url` (string): The target URL (supports variable resolution)
2213
2247
  - `payload` (string, optional): JSON string containing headers and other request options
2214
2248
 
2215
2249
  **Returns:** Promise that resolves when the request completes
2216
2250
 
2217
2251
  **Example:**
2252
+
2218
2253
  ```javascript
2219
- await api.delete("https://api.example.com/users/{{userId}}", JSON.stringify({
2220
- headers: { "Authorization": "Bearer {{token}}" }
2221
- }));
2254
+ await api.delete(
2255
+ "https://api.example.com/users/{{userId}}",
2256
+ JSON.stringify({
2257
+ headers: { Authorization: "Bearer {{token}}" },
2258
+ }),
2259
+ );
2222
2260
  ```
2223
2261
 
2224
2262
  ### `api.vars()`
@@ -2228,12 +2266,12 @@ Returns the current context variables object.
2228
2266
  **Returns:** Object containing all stored variables
2229
2267
 
2230
2268
  **Example:**
2269
+
2231
2270
  ```javascript
2232
2271
  const currentVars = api.vars();
2233
2272
  console.log(currentVars); // { token: "abc123", userId: "user456" }
2234
2273
  ```
2235
2274
 
2236
-
2237
2275
  ## Static Methods
2238
2276
 
2239
2277
  ### `extractVarsFromResponse(object, vars, customVarName)`
@@ -2241,15 +2279,18 @@ console.log(currentVars); // { token: "abc123", userId: "user456" }
2241
2279
  Extracts variables from the response body using dot notation paths.
2242
2280
 
2243
2281
  **Parameters:**
2282
+
2244
2283
  - `vars` (string): Comma-separated list of dot notation paths (e.g., "user.id,user.name")
2245
2284
  - `customVarName` (string, optional): Custom variable name to use instead of auto-generated names
2246
2285
 
2247
2286
  **Behavior:**
2287
+
2248
2288
  - Extracts values from `context.response.responseBody` using specified paths
2249
2289
  - Saves extracted values as variables using `saveVar()`
2250
2290
  - If `customVarName` is provided, uses it; otherwise generates camelCase names
2251
2291
 
2252
2292
  **Example:**
2293
+
2253
2294
  ```javascript
2254
2295
  // Response body: { user: { id: 123, profile: { name: "John" } } }
2255
2296
  extractVarsFromResponse(object, "user.id,user.profile.name");
@@ -2264,11 +2305,13 @@ extractVarsFromResponse(object, "user.id", "currentUserId");
2264
2305
  Saves a variable to the context with either a custom name or auto-generated camelCase name.
2265
2306
 
2266
2307
  **Parameters:**
2308
+
2267
2309
  - `value` (any): The value to store
2268
2310
  - `customName` (string, optional): Custom variable name
2269
2311
  - `path` (string): Dot notation path used for auto-generating variable name
2270
2312
 
2271
2313
  **Behavior:**
2314
+
2272
2315
  - If `customName` is provided, uses it as the variable name
2273
2316
  - Otherwise, converts dot notation path to camelCase (e.g., "user.profile.name" → "userProfileName")
2274
2317
 
@@ -2277,17 +2320,19 @@ Saves a variable to the context with either a custom name or auto-generated came
2277
2320
  Resolves variable placeholders in template strings using the format `{{variableName}}`.
2278
2321
 
2279
2322
  **Parameters:**
2323
+
2280
2324
  - `template` (string): Template string containing variable placeholders
2281
2325
 
2282
2326
  **Returns:** String with variables resolved, or original value if not a string
2283
2327
 
2284
2328
  **Example:**
2329
+
2285
2330
  ```javascript
2286
2331
  // Assuming context.vars = { userId: "123", token: "abc" }
2287
- resolveVariable("https://api.example.com/users/{{userId}}")
2332
+ resolveVariable("https://api.example.com/users/{{userId}}");
2288
2333
  // Returns: "https://api.example.com/users/123"
2289
2334
 
2290
- resolveVariable("Bearer {{token}}")
2335
+ resolveVariable("Bearer {{token}}");
2291
2336
  // Returns: "Bearer abc"
2292
2337
  ```
2293
2338
 
@@ -2301,9 +2346,12 @@ context.vars.baseUrl = "https://api.example.com";
2301
2346
  context.vars.token = "your-auth-token";
2302
2347
 
2303
2348
  // Make a GET request
2304
- await api.get("{{baseUrl}}/users", JSON.stringify({
2305
- headers: { "Authorization": "Bearer {{token}}" }
2306
- }));
2349
+ await api.get(
2350
+ "{{baseUrl}}/users",
2351
+ JSON.stringify({
2352
+ headers: { Authorization: "Bearer {{token}}" },
2353
+ }),
2354
+ );
2307
2355
 
2308
2356
  // Extract user ID from response
2309
2357
  extractVarsFromResponse("data.0.id", "firstUserId");
@@ -2315,29 +2363,39 @@ await api.get("{{baseUrl}}/users/{{firstUserId}}/profile");
2315
2363
  ### File Upload Example
2316
2364
 
2317
2365
  ```javascript
2318
- await api.post("{{baseUrl}}/upload", JSON.stringify({
2319
- headers: { "Authorization": "Bearer {{token}}" },
2320
- body: {
2321
- file: "/path/to/document.pdf",
2322
- description: "Important document",
2323
- metadata: { type: "legal", priority: "high" }
2324
- }
2325
- }), "multipart");
2366
+ await api.post(
2367
+ "{{baseUrl}}/upload",
2368
+ JSON.stringify({
2369
+ headers: { Authorization: "Bearer {{token}}" },
2370
+ body: {
2371
+ file: "/path/to/document.pdf",
2372
+ description: "Important document",
2373
+ metadata: { type: "legal", priority: "high" },
2374
+ },
2375
+ }),
2376
+ "multipart",
2377
+ );
2326
2378
  ```
2327
2379
 
2328
2380
  ### Variable Extraction and Chaining
2329
2381
 
2330
2382
  ```javascript
2331
2383
  // Login request
2332
- await api.post("{{baseUrl}}/auth/login", JSON.stringify({
2333
- body: { username: "user@example.com", password: "password" }
2334
- }));
2384
+ await api.post(
2385
+ "{{baseUrl}}/auth/login",
2386
+ JSON.stringify({
2387
+ body: { username: "user@example.com", password: "password" },
2388
+ }),
2389
+ );
2335
2390
 
2336
2391
  // Extract token from login response
2337
2392
  extractVarsFromResponse("access_token", "authToken");
2338
2393
 
2339
2394
  // Use token in protected endpoint
2340
- await api.get("{{baseUrl}}/protected-data", JSON.stringify({
2341
- headers: { "Authorization": "Bearer {{authToken}}" }
2342
- }));
2343
- ```
2395
+ await api.get(
2396
+ "{{baseUrl}}/protected-data",
2397
+ JSON.stringify({
2398
+ headers: { Authorization: "Bearer {{authToken}}" },
2399
+ }),
2400
+ );
2401
+ ```
package/executer.js CHANGED
@@ -24,7 +24,8 @@ const flags = {
24
24
  headless: args.includes("--headless"),
25
25
  parallel: args.includes("--parallel"),
26
26
  retry: args.includes("--retry"),
27
- dryrun: args.includes("--dryrun")
27
+ dryrun: args.includes("--dryrun"),
28
+ percentage: args.includes("--percentage"),
28
29
  };
29
30
 
30
31
  function main() {
package/index.js CHANGED
@@ -11,7 +11,7 @@ const {
11
11
  page,
12
12
  request,
13
13
  random,
14
- resolveVariable
14
+ resolveVariable,
15
15
  } = require("./src/helper/imports/commons");
16
16
  const {
17
17
  keyboard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artes",
3
- "version": "1.0.78",
3
+ "version": "1.0.80",
4
4
  "description": "The simplest way to automate UI and API tests using Cucumber-style steps.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -58,13 +58,13 @@ const invokeBrowser = async () => {
58
58
  }
59
59
 
60
60
  const context = await browser.newContext(browserContextOptions);
61
-
61
+
62
62
  return {
63
63
  browser: browser,
64
- context: context
64
+ context: context,
65
65
  };
66
66
  };
67
67
 
68
68
  module.exports = {
69
69
  invokeBrowser,
70
- };
70
+ };
@@ -1,5 +1,5 @@
1
1
  function showHelp() {
2
- console.log(`
2
+ console.log(`
3
3
  🚀 Artes - Playwright Test Runner
4
4
 
5
5
  Description:
@@ -45,8 +45,11 @@ function showHelp() {
45
45
 
46
46
  🎭 --dryrun Perform a dry run without executing tests
47
47
  Usage: artes --dryrun
48
+
49
+ 📈 --percentage Set minimum success percentage to pass test run
50
+ Usage: artes --percentage 85
48
51
  `);
49
- }
52
+ }
50
53
 
51
54
  module.exports = {
52
55
  showHelp,
@@ -11,14 +11,16 @@ function runTests(args, flags) {
11
11
  const tags = args[args.indexOf("--tags") + 1];
12
12
  const parallel = args[args.indexOf("--parallel") + 1];
13
13
  const retry = args[args.indexOf("--retry") + 1];
14
+ const percentage = args[args.indexOf("percentage") + 1];
14
15
 
15
16
  flags.env && console.log("Running env:", env);
16
17
  flags.env ? (process.env.ENV = JSON.stringify(env)) : "";
17
18
 
18
19
  flags.report
19
20
  ? (process.env.REPORT_FORMAT = JSON.stringify([
20
- "rerun:@rerun.txt","progress-bar",
21
- "allure-cucumberjs/reporter:./allure-results"
21
+ "rerun:@rerun.txt",
22
+ "progress-bar",
23
+ "allure-cucumberjs/reporter:./allure-results",
22
24
  ]))
23
25
  : "";
24
26
 
@@ -35,6 +37,7 @@ function runTests(args, flags) {
35
37
  flags.parallel ? (process.env.PARALLEL = JSON.stringify(parallel)) : "";
36
38
  flags.retry ? (process.env.RETRY = JSON.stringify(retry)) : "";
37
39
  flags.dryrun ? (process.env.DRYRUN = JSON.stringify(true)) : "";
40
+ flags.percentage ? (process.env.PERCENTAGE = percentage) : "";
38
41
 
39
42
  try {
40
43
  console.log("🧪 Running tests...");
@@ -86,7 +86,6 @@ class Elements {
86
86
  }
87
87
 
88
88
  static extractVarsFromResponse(responseBody, vars, customVarName) {
89
-
90
89
  function getValueByPath(obj, path) {
91
90
  const keys = path.split(".");
92
91
  let current = obj;
@@ -1,6 +1,11 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs");
3
- const { context, selector, resolveVariable, moduleConfig } = require("../imports/commons");
3
+ const {
4
+ context,
5
+ selector,
6
+ resolveVariable,
7
+ moduleConfig,
8
+ } = require("../imports/commons");
4
9
 
5
10
  function getMimeType(filePath) {
6
11
  const ext = path.extname(filePath).toLowerCase();
@@ -90,11 +95,12 @@ async function requestMaker(headers, data, requestDataType) {
90
95
  async function responseMaker(request, response, duration) {
91
96
  const responseObject = {};
92
97
 
93
- response && Object.assign(responseObject, {
94
- "Response Params": `URL: ${response.url()}
98
+ response &&
99
+ Object.assign(responseObject, {
100
+ "Response Params": `URL: ${response.url()}
95
101
  Response Status: ${await response.status()}
96
- Response Time: ${Math.round(duration)} ms`
97
- });
102
+ Response Time: ${Math.round(duration)} ms`,
103
+ });
98
104
 
99
105
  request?.headers &&
100
106
  Object.assign(responseObject, { "Request Headers": await request.headers });
@@ -117,7 +123,9 @@ async function responseMaker(request, response, duration) {
117
123
  }
118
124
  }
119
125
 
120
- Object.assign(responseObject, { "Response Time": `${Math.round(duration)} ms`});
126
+ Object.assign(responseObject, {
127
+ "Response Time": `${Math.round(duration)} ms`,
128
+ });
121
129
 
122
130
  return responseObject;
123
131
  }
@@ -168,7 +176,7 @@ const api = {
168
176
  switch (requestDataType) {
169
177
  case "multipart":
170
178
  let formData = {};
171
-
179
+
172
180
  for (const [key, value] of Object.entries(payloadJSON?.body)) {
173
181
  processForm(formData, key, value);
174
182
  }
@@ -208,7 +216,7 @@ const api = {
208
216
  switch (requestDataType) {
209
217
  case "multipart":
210
218
  let formData = {};
211
-
219
+
212
220
  for (const [key, value] of Object.entries(payloadJSON?.body)) {
213
221
  processForm(formData, key, value);
214
222
  }
@@ -249,7 +257,7 @@ const api = {
249
257
  switch (requestDataType) {
250
258
  case "multipart":
251
259
  let formData = {};
252
-
260
+
253
261
  for (const [key, value] of Object.entries(payloadJSON?.body)) {
254
262
  processForm(formData, key, value);
255
263
  }
@@ -18,4 +18,4 @@ const browser = {
18
18
 
19
19
  module.exports = {
20
20
  browser,
21
- };
21
+ };
@@ -3,8 +3,10 @@ const {
3
3
  Before,
4
4
  After,
5
5
  Status,
6
- setDefaultTimeout, AfterStep,
7
- BeforeStep
6
+ setDefaultTimeout,
7
+ AfterStep,
8
+ BeforeStep,
9
+ AfterAll,
8
10
  } = require("@cucumber/cucumber");
9
11
  const { invokeBrowser } = require("../helper/contextManager/browserManager");
10
12
  const { invokeRequest } = require("../helper/contextManager/requestManager");
@@ -15,6 +17,9 @@ const fs = require("fs");
15
17
  const { moduleConfig } = require("artes/src/helper/imports/commons");
16
18
  const path = require("path");
17
19
 
20
+ let totalTests = 0;
21
+ let testsFailed = 0;
22
+
18
23
  setDefaultTimeout(cucumberConfig.default.timeout * 1000);
19
24
 
20
25
  BeforeAll(async function () {
@@ -22,6 +27,7 @@ BeforeAll(async function () {
22
27
  });
23
28
 
24
29
  Before(async function () {
30
+ totalTests++;
25
31
  context.vars = {};
26
32
 
27
33
  const { browser, context: browserContext } = await invokeBrowser();
@@ -44,36 +50,38 @@ Before(async function () {
44
50
  BeforeStep(async function ({ pickleStep }) {
45
51
  const stepText = pickleStep.text;
46
52
 
47
- const methods = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];
53
+ const methods = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"];
48
54
 
49
- if( methods.some(method => stepText.includes(method))){
50
- context.response = {}
55
+ if (methods.some((method) => stepText.includes(method))) {
56
+ context.response = {};
51
57
  }
52
- })
58
+ });
53
59
 
54
- AfterStep(async function ({pickleStep}) {
60
+ AfterStep(async function ({ pickleStep }) {
55
61
  const stepText = pickleStep.text;
56
62
 
57
- const methods = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'];
63
+ const methods = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"];
58
64
 
59
- if( methods.some(method => stepText.includes(method))){
60
- if (await context.response) {
61
- for (const [key, value] of Object.entries(context.response)) {
62
- let text = `${key}:\n`;
63
-
64
- if (typeof value === 'object') {
65
- text += JSON.stringify(value, null, 2);
66
- } else {
67
- text += value;
65
+ if (methods.some((method) => stepText.includes(method))) {
66
+ if (await context.response) {
67
+ for (const [key, value] of Object.entries(context.response)) {
68
+ let text = `${key}:\n`;
69
+
70
+ if (typeof value === "object") {
71
+ text += JSON.stringify(value, null, 2);
72
+ } else {
73
+ text += value;
74
+ }
75
+
76
+ await this.attach(text, "text/plain");
68
77
  }
69
-
70
- await this.attach(text, "text/plain");
71
78
  }
72
- }}
73
- })
79
+ }
80
+ });
74
81
 
75
82
  After(async function ({ pickle, result }) {
76
83
  if (result?.status != Status.PASSED) {
84
+ testsFailed++;
77
85
  let img = await context.page.screenshot({
78
86
  path: `./test-results/visualReport/${pickle.name}/${pickle.name}.png`,
79
87
  type: "png",
@@ -88,33 +96,52 @@ After(async function ({ pickle, result }) {
88
96
  if (context.response) {
89
97
  for (const [key, value] of Object.entries(context.response)) {
90
98
  let text = `${key}:\n`;
91
-
92
- if (typeof value === 'object') {
99
+
100
+ if (typeof value === "object") {
93
101
  text += JSON.stringify(value, null, 2);
94
102
  } else {
95
103
  text += value;
96
104
  }
97
-
105
+
98
106
  await this.attach(text, "text/plain");
99
107
  }
100
108
  }
101
109
 
102
110
  await context.page?.close();
103
-
111
+
104
112
  await context.browserContext?.close();
105
-
113
+
106
114
  await context.browser?.close();
107
-
115
+
108
116
  await context.request?.dispose();
109
117
 
110
118
  if (result?.status != Status.PASSED && context.page.video()) {
111
119
  const videoPath = await context.page.video().path();
112
- await new Promise(resolve => setTimeout(resolve, 1000));
113
-
120
+ await new Promise((resolve) => setTimeout(resolve, 1000));
121
+
114
122
  if (fs.existsSync(videoPath)) {
115
123
  const webmBuffer = await fs.readFileSync(videoPath);
116
124
  await this.attach(webmBuffer, "video/webm");
117
125
  }
118
126
  }
127
+ });
119
128
 
120
- });
129
+ AfterAll(async function () {
130
+ const successPercentage = 100 - (testsFailed / totalTests) * 100;
131
+ const successRate =
132
+ successPercentage.toFixed(2) >= cucumberConfig.testPercentage;
133
+
134
+ if (!isNaN(successPercentage)) {
135
+ if (successRate) {
136
+ console.log(
137
+ `Tests passed with ${successPercentage.toFixed(2)}% results!`,
138
+ );
139
+ process.exit(0);
140
+ } else {
141
+ console.log(
142
+ `Tests failed with ${successPercentage.toFixed(2)}% results!`,
143
+ );
144
+ process.exit(1);
145
+ }
146
+ }
147
+ });
@@ -160,7 +160,11 @@ When(
160
160
  When(
161
161
  "User saves {string} variable from response as {string}",
162
162
  async function (vars, customVarName) {
163
- await extractVarsFromResponse(context.response["Response Body"], vars, customVarName);
163
+ await extractVarsFromResponse(
164
+ context.response["Response Body"],
165
+ vars,
166
+ customVarName,
167
+ );
164
168
  },
165
169
  );
166
170
 
@@ -18,14 +18,14 @@ Given(
18
18
  "User sends GET request to {string} and save {string} variable from {string} array as a {string} randomly",
19
19
  async (endPoint, varName, fromArray, variableKey) => {
20
20
  await api.get(endPoint);
21
- let responseBody
22
- if(fromArray=="[]"){
23
- responseBody = await context.response["Response Body"]
24
- }else{
25
- responseBody = await context.response["Response Body"][fromArray]
21
+ let responseBody;
22
+ if (fromArray == "[]") {
23
+ responseBody = await context.response["Response Body"];
24
+ } else {
25
+ responseBody = await context.response["Response Body"][fromArray];
26
26
  }
27
27
  const randomContent =
28
- responseBody[random.number.int({ min: 0, max: responseBody.length - 1 })];
28
+ responseBody[random.number.int({ min: 0, max: responseBody.length - 1 })];
29
29
  context.vars[variableKey] = randomContent[varName];
30
30
  },
31
- );
31
+ );