artes 1.1.7 → 1.1.9
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 +52 -52
- package/cucumber.config.js +19 -17
- package/index.js +1 -1
- package/package.json +4 -4
- package/src/helper/executers/versionChecker.js +4 -2
- package/src/helper/pomController/elementController.js +102 -99
- package/src/helper/stepFunctions/APIActions.js +57 -67
- package/src/hooks/hooks.js +4 -1
- package/src/stepDefinitions/random.steps.js +28 -16
package/README.md
CHANGED
|
@@ -42,30 +42,30 @@ npx artes [options]
|
|
|
42
42
|
|
|
43
43
|
### Options
|
|
44
44
|
|
|
45
|
-
| Option
|
|
46
|
-
|
|
|
47
|
-
| 🆘 `-h, --help`
|
|
48
|
-
| 🏷️ `-v, --version`
|
|
49
|
-
| 🏗️ `-c, --create`
|
|
50
|
-
| ✅ `-y, --yes`
|
|
51
|
-
| 📊 `-r, --report`
|
|
52
|
-
| `--reportSuccess`
|
|
53
|
-
| 📁 `--features`
|
|
54
|
-
| 📜 `--stepDef` | Specify one or more step definition files' relative paths to use (comma-separated) | `artes --stepDef "tests/steps/login.js,tests/steps/home.js"`
|
|
55
|
-
| 🔖 `--tags`
|
|
56
|
-
| 🌐 `--env`
|
|
57
|
-
| 🕶️ `--headless`
|
|
58
|
-
| ⚡ `--parallel`
|
|
59
|
-
| 🔁 `--retry`
|
|
60
|
-
| 🎭 `--dryRun`
|
|
61
|
-
| 📈 `--percentage`
|
|
62
|
-
| 🌍 `--browser`
|
|
63
|
-
| 🔗 `--baseURL`
|
|
64
|
-
| 🖥️ `--maxScreen`
|
|
65
|
-
| 📏 `--width`
|
|
66
|
-
| 📐 `--height`
|
|
67
|
-
| ⏱️ `--timeout`
|
|
68
|
-
| 🐢 `--slowMo`
|
|
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
|
+
| `--reportSuccess` | Add screenshots and video records for also Success test cases | `artes --reportSuccess` |
|
|
53
|
+
| 📁 `--features` | Specify one or more feature files' relative paths to run (comma-separated) | `artes --features "tests/features/Alma,tests/features/Banan.feature"` |
|
|
54
|
+
| 📜 `--stepDef` | Specify one or more step definition files' relative paths to use (comma-separated) | `artes --stepDef "tests/steps/login.js,tests/steps/home.js"` |
|
|
55
|
+
| 🔖 `--tags` | Run tests with specified Cucumber tags | `artes --tags "@smoke or @wip"` |
|
|
56
|
+
| 🌐 `--env` | Set the environment for the test run | `artes --env "dev"` |
|
|
57
|
+
| 🕶️ `--headless` | Run browser in headless mode | `artes --headless` |
|
|
58
|
+
| ⚡ `--parallel` | Run tests in parallel mode | `artes --parallel 2` |
|
|
59
|
+
| 🔁 `--retry` | Retry failed tests | `artes --retry 3` |
|
|
60
|
+
| 🎭 `--dryRun` | Perform a dry run without executing tests | `artes --dryRun` |
|
|
61
|
+
| 📈 `--percentage` | Set minimum success percentage to pass test run (default is 0) | `artes --percentage 85` |
|
|
62
|
+
| 🌍 `--browser` | Specify browser to use (`chromium`, `firefox`, or `webkit`) | `artes --browser chromium` |
|
|
63
|
+
| 🔗 `--baseURL` | Set base URL for the tests | `artes --baseURL "https://example.com"` |
|
|
64
|
+
| 🖥️ `--maxScreen` | Maximize browser window on launch | `artes --maxScreen` |
|
|
65
|
+
| 📏 `--width` | Set browser width (default is 1280) | `artes --width 1920` |
|
|
66
|
+
| 📐 `--height` | Set browser height (default is 720) | `artes --height 1080` |
|
|
67
|
+
| ⏱️ `--timeout` | Set timeout for each test step in seconds (default is 30 seconds) | `artes --timeout 10` |
|
|
68
|
+
| 🐢 `--slowMo` | Slow down text execution for clear view (default: 0 seconds) | `artes --slowMo 1` |
|
|
69
69
|
|
|
70
70
|
\*\* To just run the tests: <br>
|
|
71
71
|
Globally: artes <br>
|
|
@@ -286,34 +286,34 @@ Then("User should see the login form", async () => {
|
|
|
286
286
|
|
|
287
287
|
You can configure Artes by editing the `artes.config.js` file. Below are the default configuration options with explanations:
|
|
288
288
|
|
|
289
|
-
| **Option** | **Default Value** | **Description**
|
|
290
|
-
| ----------------- | ---------------------------------------------------------------------------- |
|
|
291
|
-
| `timeout` | `30` | Default timeout in seconds.
|
|
292
|
-
| `slowMo` | `0` | Default slow motion in seconds
|
|
293
|
-
| `paths` | `[moduleConfig.featuresPath]` | Paths to feature files.
|
|
294
|
-
| `require` | `[moduleConfig.stepsPath, "src/stepDefinitions/*.js", "src/hooks/hooks.js"]` | Support code paths (CommonJS).
|
|
295
|
-
| `pomPath` | `moduleConfig.pomPath` | Path to Page Object Models.
|
|
296
|
-
| `import` | `[]` | Support code paths.
|
|
297
|
-
| `testPercentage` |
|
|
298
|
-
| `reportSuccess` |
|
|
299
|
-
| `format` | `["rerun:@rerun.txt", "allure-cucumberjs/reporter"]` | Formatter names/paths.
|
|
300
|
-
| `formatOptions` | `{ "resultsDir": "allure-result" }` | Formatter options.
|
|
301
|
-
| `parallel` | `1` | Number of parallel workers.
|
|
302
|
-
| `dryRun` | `false` | Prepare test run without execution.
|
|
303
|
-
| `failFast` | `false` | Stop on first test failure.
|
|
304
|
-
| `forceExit` | `false` | Force `process.exit()` after tests.
|
|
305
|
-
| `strict` | `true` | Fail on pending steps.
|
|
306
|
-
| `backtrace` | `false` | Show full backtrace for errors.
|
|
307
|
-
| `tags` | `""` | Tag expression to filter scenarios.
|
|
308
|
-
| `name` | `[]` | Run scenarios matching regex.
|
|
309
|
-
| `order` | `"defined"` | Run order (defined/random).
|
|
310
|
-
| `language` | `"en"` | Default feature file language.
|
|
311
|
-
| `loader` | `[]` | Module loader specifications.
|
|
312
|
-
| `requireModule` | `[]` | Transpilation module names.
|
|
313
|
-
| `retry` | `0` | Retry attempts for failing tests.
|
|
314
|
-
| `retryTagFilter` | `""` | Tag expression for retries.
|
|
315
|
-
| `publish` | `false` | Publish to cucumber.io.
|
|
316
|
-
| `worldParameters` | `{}` | Custom world parameters.
|
|
289
|
+
| **Option** | **Default Value** | **Description** |
|
|
290
|
+
| ----------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
291
|
+
| `timeout` | `30` | Default timeout in seconds. |
|
|
292
|
+
| `slowMo` | `0` | Default slow motion in seconds |
|
|
293
|
+
| `paths` | `[moduleConfig.featuresPath]` | Paths to feature files. |
|
|
294
|
+
| `require` | `[moduleConfig.stepsPath, "src/stepDefinitions/*.js", "src/hooks/hooks.js"]` | Support code paths (CommonJS). |
|
|
295
|
+
| `pomPath` | `moduleConfig.pomPath` | Path to Page Object Models. |
|
|
296
|
+
| `import` | `[]` | Support code paths. |
|
|
297
|
+
| `testPercentage` | `0` | Define test coverage percentage |
|
|
298
|
+
| `reportSuccess` | `true` | Add screenshots and video records for also success test cases |
|
|
299
|
+
| `format` | `["rerun:@rerun.txt", "allure-cucumberjs/reporter"]` | Formatter names/paths. |
|
|
300
|
+
| `formatOptions` | `{ "resultsDir": "allure-result" }` | Formatter options. |
|
|
301
|
+
| `parallel` | `1` | Number of parallel workers. |
|
|
302
|
+
| `dryRun` | `false` | Prepare test run without execution. |
|
|
303
|
+
| `failFast` | `false` | Stop on first test failure. |
|
|
304
|
+
| `forceExit` | `false` | Force `process.exit()` after tests. |
|
|
305
|
+
| `strict` | `true` | Fail on pending steps. |
|
|
306
|
+
| `backtrace` | `false` | Show full backtrace for errors. |
|
|
307
|
+
| `tags` | `""` | Tag expression to filter scenarios. |
|
|
308
|
+
| `name` | `[]` | Run scenarios matching regex. |
|
|
309
|
+
| `order` | `"defined"` | Run order (defined/random). |
|
|
310
|
+
| `language` | `"en"` | Default feature file language. |
|
|
311
|
+
| `loader` | `[]` | Module loader specifications. |
|
|
312
|
+
| `requireModule` | `[]` | Transpilation module names. |
|
|
313
|
+
| `retry` | `0` | Retry attempts for failing tests. |
|
|
314
|
+
| `retryTagFilter` | `""` | Tag expression for retries. |
|
|
315
|
+
| `publish` | `false` | Publish to cucumber.io. |
|
|
316
|
+
| `worldParameters` | `{}` | Custom world parameters. |
|
|
317
317
|
|
|
318
318
|
### Environment Configuration
|
|
319
319
|
|
package/cucumber.config.js
CHANGED
|
@@ -14,9 +14,7 @@ try {
|
|
|
14
14
|
console.log("Proceeding with default config.");
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const defaultFormats = [
|
|
18
|
-
"rerun:@rerun.txt",
|
|
19
|
-
"progress-bar"];
|
|
17
|
+
const defaultFormats = ["rerun:@rerun.txt", "progress-bar"];
|
|
20
18
|
|
|
21
19
|
const userFormatsFromEnv = process.env.REPORT_FORMAT
|
|
22
20
|
? JSON.parse(process.env.REPORT_FORMAT)
|
|
@@ -24,11 +22,13 @@ const userFormatsFromEnv = process.env.REPORT_FORMAT
|
|
|
24
22
|
|
|
25
23
|
const userFormatsFromConfig = artesConfig.format || [];
|
|
26
24
|
|
|
27
|
-
const finalFormats = [
|
|
28
|
-
...
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
const finalFormats = [
|
|
26
|
+
...new Set([
|
|
27
|
+
...defaultFormats,
|
|
28
|
+
...userFormatsFromEnv,
|
|
29
|
+
...userFormatsFromConfig,
|
|
30
|
+
]),
|
|
31
|
+
];
|
|
32
32
|
|
|
33
33
|
module.exports = {
|
|
34
34
|
default: {
|
|
@@ -45,11 +45,11 @@ module.exports = {
|
|
|
45
45
|
? path.join(moduleConfig.projectPath, artesConfig.features)
|
|
46
46
|
: [moduleConfig.featuresPath], // Paths to feature files
|
|
47
47
|
require: [
|
|
48
|
-
process.env.STEP_DEFINITIONS
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
process.env.STEP_DEFINITIONS
|
|
49
|
+
? [path.join(moduleConfig.projectPath, process.env.STEP_DEFINITIONS)]
|
|
50
|
+
: artesConfig.steps
|
|
51
|
+
? path.join(moduleConfig.projectPath, artesConfig.steps)
|
|
52
|
+
: moduleConfig.stepsPath,
|
|
53
53
|
"src/stepDefinitions/*.js",
|
|
54
54
|
"src/hooks/hooks.js",
|
|
55
55
|
], // Support code paths (CommonJS)
|
|
@@ -59,7 +59,9 @@ module.exports = {
|
|
|
59
59
|
import: artesConfig.import || [], // Support code paths
|
|
60
60
|
|
|
61
61
|
// Formatting and output
|
|
62
|
-
successReport: process.env.REPORT_SUCCESS
|
|
62
|
+
successReport: process.env.REPORT_SUCCESS
|
|
63
|
+
? true
|
|
64
|
+
: artesConfig.reportSuccess || false, // Include successful tests in report
|
|
63
65
|
format: finalFormats, // Formatter names/paths
|
|
64
66
|
formatOptions: artesConfig.formatOptions || {
|
|
65
67
|
resultsDir: `allure-result`,
|
|
@@ -130,8 +132,8 @@ module.exports = {
|
|
|
130
132
|
: artesConfig?.headless !== undefined
|
|
131
133
|
? artesConfig.headless
|
|
132
134
|
: true,
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
slowMo: process.env.SLOWMO
|
|
136
|
+
? Number(process.env.SLOWMO) * 1000
|
|
137
|
+
: artesConfig?.slowMo * 1000 || 0,
|
|
136
138
|
},
|
|
137
139
|
};
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "artes",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.9",
|
|
4
4
|
"description": "The simplest way to automate UI and API tests using Cucumber-style steps.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"artes": "./executer.js"
|
|
15
15
|
},
|
|
16
16
|
"publishConfig": {
|
|
17
|
-
|
|
18
|
-
},
|
|
17
|
+
"registry": "https://registry.npmjs.org/"
|
|
18
|
+
},
|
|
19
19
|
"author": "VhdAghyv",
|
|
20
20
|
"license": "ISC",
|
|
21
21
|
"dependencies": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"url": "https://github.com/4gayev1/Artes/issues"
|
|
43
43
|
},
|
|
44
44
|
"homepage": "https://github.com/4gayev1/Artes/blob/main/README.md",
|
|
45
|
-
|
|
45
|
+
"keywords": [
|
|
46
46
|
"kdt",
|
|
47
47
|
"automation",
|
|
48
48
|
"playwright",
|
|
@@ -5,8 +5,10 @@ const fs = require("fs");
|
|
|
5
5
|
function showVersion() {
|
|
6
6
|
const artesVersion = execSync("npm root -g").toString().trim();
|
|
7
7
|
const artesGPackageJSONPath = path.join(artesVersion, "artes/package.json");
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const asrtesGPackageJSON = JSON.parse(
|
|
9
|
+
fs.readFileSync(artesGPackageJSONPath, "utf8"),
|
|
10
|
+
);
|
|
11
|
+
console.log(`ARTES Version: ${asrtesGPackageJSON.version}`);
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
module.exports = {
|
|
@@ -2,134 +2,137 @@ const { context } = require("../../hooks/context");
|
|
|
2
2
|
|
|
3
3
|
let elements = {};
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
function addElements(newElements) {
|
|
7
6
|
elements = { ...elements, ...newElements };
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
// async function locatorExistenceChecker(locator){
|
|
10
|
+
// const locatorCount = await locator.count();
|
|
11
|
+
// console.log(locator, locatorCount)
|
|
12
|
+
// return locatorCount ==0 ? false : true;
|
|
13
|
+
// }
|
|
14
|
+
|
|
15
|
+
function selectorSeperator(element) {
|
|
16
|
+
const selector = element?.split("=");
|
|
17
|
+
const validTypes = ["xpath", "name", "placeholder", "text", "label", "role", "alt", "title", "testid"];
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
const selector = element?.split("=");
|
|
19
|
+
if (selector && validTypes.includes(selector[0]?.trim())) {
|
|
18
20
|
return [
|
|
19
|
-
selector[0]
|
|
21
|
+
selector[0].trim(),
|
|
20
22
|
selector[1] !== undefined ? selector[1].trim() : "",
|
|
21
23
|
];
|
|
24
|
+
}else{
|
|
25
|
+
return selector.join("=")
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
const selector =
|
|
26
|
-
elements?.[element]?.selector || elements?.[element] || element;
|
|
27
|
-
return resolveVariable(selectorSeperator(selector));
|
|
28
|
-
}
|
|
28
|
+
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
function getSelector(element) {
|
|
31
|
+
const selector =
|
|
32
|
+
elements?.[element]?.selector || elements?.[element] || element;
|
|
33
|
+
return resolveVariable(selectorSeperator(selector));
|
|
34
|
+
}
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
switch (selector[0]) {
|
|
40
|
-
case "xpath":
|
|
41
|
-
locator = context.page.locator(`xpath=${selector[1]}`, { exact: true });
|
|
42
|
-
break;
|
|
43
|
-
case "name":
|
|
44
|
-
locator = context.page.locator(`[name=${selector[1]}]`, {
|
|
45
|
-
exact: true,
|
|
46
|
-
});
|
|
47
|
-
break;
|
|
48
|
-
case "placeholder":
|
|
49
|
-
locator = context.page.getByPlaceholder(selector[1], { exact: true });
|
|
50
|
-
break;
|
|
51
|
-
case "text":
|
|
52
|
-
locator = context.page.getByText(selector[1], { exact: true });
|
|
53
|
-
break;
|
|
54
|
-
case "label":
|
|
55
|
-
locator = context.page.getByLabel(selector[1], { exact: true });
|
|
56
|
-
break;
|
|
57
|
-
case "role":
|
|
58
|
-
locator = context.page.getByRole(selector[1], { exact: true });
|
|
59
|
-
break;
|
|
60
|
-
case "alt":
|
|
61
|
-
locator = context.page.getByAltText(selector[1], { exact: true });
|
|
62
|
-
break;
|
|
63
|
-
case "title":
|
|
64
|
-
locator = context.page.getByTitle(selector[1], { exact: true });
|
|
65
|
-
break;
|
|
66
|
-
case "testid":
|
|
67
|
-
locator = context.page.getByTestId(selector[1], { exact: true });
|
|
68
|
-
break;
|
|
69
|
-
default:
|
|
70
|
-
locator = context.page.locator(selector[0], { exact: true });
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
36
|
+
function getElement(element) {
|
|
37
|
+
if (!context.page) {
|
|
38
|
+
throw new Error("Page context is not initialized.");
|
|
39
|
+
}
|
|
73
40
|
|
|
74
|
-
|
|
41
|
+
const selector = getSelector(element);
|
|
42
|
+
const waitTime = elements[element]?.waitTime * 1000 || 0;
|
|
43
|
+
|
|
44
|
+
let locator;
|
|
45
|
+
switch (selector[0]) {
|
|
46
|
+
case "xpath":
|
|
47
|
+
locator = context.page.locator(`xpath=${selector[1]}`, { exact: true });
|
|
48
|
+
break;
|
|
49
|
+
case "name":
|
|
50
|
+
locator = context.page.locator(`[name=${selector[1]}]`, {
|
|
51
|
+
exact: true,
|
|
52
|
+
});
|
|
53
|
+
break;
|
|
54
|
+
case "placeholder":
|
|
55
|
+
locator = context.page.getByPlaceholder(selector[1], { exact: true });
|
|
56
|
+
break;
|
|
57
|
+
case "text":
|
|
58
|
+
locator = context.page.getByText(selector[1], { exact: true });
|
|
59
|
+
break;
|
|
60
|
+
case "label":
|
|
61
|
+
locator = context.page.getByLabel(selector[1], { exact: true });
|
|
62
|
+
break;
|
|
63
|
+
case "role":
|
|
64
|
+
locator = context.page.getByRole(selector[1], { exact: true });
|
|
65
|
+
break;
|
|
66
|
+
case "alt":
|
|
67
|
+
locator = context.page.getByAltText(selector[1], { exact: true });
|
|
68
|
+
break;
|
|
69
|
+
case "title":
|
|
70
|
+
locator = context.page.getByTitle(selector[1], { exact: true });
|
|
71
|
+
break;
|
|
72
|
+
case "testid":
|
|
73
|
+
locator = context.page.getByTestId(selector[1], { exact: true });
|
|
74
|
+
break;
|
|
75
|
+
default:
|
|
76
|
+
locator = context.page.locator(selector, { exact: true });
|
|
77
|
+
break;
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
|
|
80
|
+
return locator;
|
|
81
|
+
}
|
|
78
82
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
function extractVarsFromResponse(responseBody, vars, customVarName) {
|
|
84
|
+
function getValueByPath(obj, path) {
|
|
85
|
+
const keys = path.split(".");
|
|
86
|
+
let current = obj;
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
88
|
+
for (const key of keys) {
|
|
89
|
+
if (current && typeof current === "object" && key in current) {
|
|
90
|
+
current = current[key];
|
|
91
|
+
} else {
|
|
92
|
+
return undefined;
|
|
90
93
|
}
|
|
91
|
-
|
|
92
|
-
return current;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
|
|
96
|
-
const path = v.trim();
|
|
97
|
-
const value = getValueByPath(responseBody, path);
|
|
98
|
-
if (value !== undefined) {
|
|
99
|
-
saveVar(value, customVarName, path);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
96
|
+
return current;
|
|
102
97
|
}
|
|
103
98
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
i === 0 ? part : part[0].toUpperCase() + part.slice(1),
|
|
110
|
-
)
|
|
111
|
-
.join("");
|
|
112
|
-
|
|
113
|
-
context.vars[flatKey] = value;
|
|
114
|
-
} else {
|
|
115
|
-
context.vars[customName] = value;
|
|
99
|
+
vars.split(",").forEach((v) => {
|
|
100
|
+
const path = v.trim();
|
|
101
|
+
const value = getValueByPath(responseBody, path);
|
|
102
|
+
if (value !== undefined) {
|
|
103
|
+
saveVar(value, customVarName, path);
|
|
116
104
|
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function saveVar(value, customName, path) {
|
|
109
|
+
if (!customName) {
|
|
110
|
+
const flatKey = path
|
|
111
|
+
.split(".")
|
|
112
|
+
.map((part, i) =>
|
|
113
|
+
i === 0 ? part : part[0].toUpperCase() + part.slice(1),
|
|
114
|
+
)
|
|
115
|
+
.join("");
|
|
116
|
+
|
|
117
|
+
context.vars[flatKey] = value;
|
|
118
|
+
} else {
|
|
119
|
+
context.vars[customName] = value;
|
|
117
120
|
}
|
|
121
|
+
}
|
|
118
122
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
function resolveVariable(template) {
|
|
124
|
+
if (typeof template !== "string") return template;
|
|
125
|
+
return template.replace(/{{\s*(\w+)\s*}}/g, (_, varName) => {
|
|
126
|
+
let value = context.vars[varName];
|
|
127
|
+
if (typeof value === "string") {
|
|
124
128
|
value = value
|
|
125
129
|
.replace(/\n/g, "\\n")
|
|
126
130
|
.replace(/\r/g, "\\r")
|
|
127
131
|
.replace(/\t/g, "\\t");
|
|
128
132
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
return value !== undefined ? value : `{{${varName}}}`;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
133
136
|
|
|
134
137
|
module.exports = {
|
|
135
138
|
getElement,
|
|
@@ -137,5 +140,5 @@ module.exports = {
|
|
|
137
140
|
getSelector,
|
|
138
141
|
extractVarsFromResponse,
|
|
139
142
|
saveVar,
|
|
140
|
-
resolveVariable
|
|
143
|
+
resolveVariable,
|
|
141
144
|
};
|
|
@@ -4,10 +4,9 @@ const {
|
|
|
4
4
|
context,
|
|
5
5
|
selector,
|
|
6
6
|
resolveVariable,
|
|
7
|
-
moduleConfig
|
|
7
|
+
moduleConfig,
|
|
8
8
|
} = require("../imports/commons");
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
function getMimeType(filePath) {
|
|
12
11
|
const ext = path.extname(filePath).toLowerCase();
|
|
13
12
|
const mimeTypes = {
|
|
@@ -29,59 +28,56 @@ function getMimeType(filePath) {
|
|
|
29
28
|
return mimeTypes[ext] || "application/octet-stream";
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
function processForm(
|
|
31
|
+
function processForm(requestBody) {
|
|
32
|
+
let formData = {};
|
|
33
33
|
for (const [key, value] of Object.entries(requestBody)) {
|
|
34
|
+
if (typeof value === "object") {
|
|
35
|
+
if (value.contentType) {
|
|
36
|
+
const content =
|
|
37
|
+
typeof value.data === "object"
|
|
38
|
+
? JSON.stringify(value.data)
|
|
39
|
+
: String(value.data);
|
|
40
|
+
|
|
41
|
+
formData[key] = {
|
|
42
|
+
name: value.filename || key,
|
|
43
|
+
mimeType: value.contentType,
|
|
44
|
+
buffer: Buffer.from(content, "utf8"),
|
|
45
|
+
};
|
|
46
|
+
return;
|
|
47
|
+
} else {
|
|
48
|
+
formData[key] = JSON.stringify(value);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
34
52
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
+
if (
|
|
54
|
+
typeof value === "string" &&
|
|
55
|
+
(value.endsWith(".pdf") ||
|
|
56
|
+
value.endsWith(".jpg") ||
|
|
57
|
+
value.endsWith(".png") ||
|
|
58
|
+
value.endsWith(".txt") ||
|
|
59
|
+
value.endsWith(".doc") ||
|
|
60
|
+
value.endsWith(".docx") ||
|
|
61
|
+
value.includes("/"))
|
|
62
|
+
) {
|
|
63
|
+
try {
|
|
64
|
+
const filePath = path.join(moduleConfig.projectPath, value);
|
|
65
|
+
if (fs.existsSync(filePath)) {
|
|
66
|
+
formData[key] = {
|
|
67
|
+
name: path.basename(filePath),
|
|
68
|
+
mimeType: getMimeType(filePath),
|
|
69
|
+
buffer: fs.readFileSync(filePath),
|
|
70
|
+
};
|
|
71
|
+
return;
|
|
53
72
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
typeof value === "string" &&
|
|
57
|
-
(value.endsWith(".pdf") ||
|
|
58
|
-
value.endsWith(".jpg") ||
|
|
59
|
-
value.endsWith(".png") ||
|
|
60
|
-
value.endsWith(".txt") ||
|
|
61
|
-
value.endsWith(".doc") ||
|
|
62
|
-
value.endsWith(".docx") ||
|
|
63
|
-
value.includes("/"))
|
|
64
|
-
) {
|
|
65
|
-
try {
|
|
66
|
-
const filePath = path.join(moduleConfig.projectPath, value);
|
|
67
|
-
if (fs.existsSync(filePath)) {
|
|
68
|
-
formData[key] = {
|
|
69
|
-
name: path.basename(filePath),
|
|
70
|
-
mimeType: getMimeType(filePath),
|
|
71
|
-
buffer: fs.readFileSync(filePath),
|
|
72
|
-
};
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
} catch (error) {
|
|
76
|
-
console.log(error);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
formData[key] = value;
|
|
81
|
-
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.log(error);
|
|
82
75
|
}
|
|
83
76
|
}
|
|
84
|
-
|
|
77
|
+
|
|
78
|
+
formData[key] = value;
|
|
79
|
+
}
|
|
80
|
+
return formData;
|
|
85
81
|
}
|
|
86
82
|
|
|
87
83
|
async function requestMaker(headers, data, requestDataType) {
|
|
@@ -131,7 +127,6 @@ async function responseMaker(request, response, duration) {
|
|
|
131
127
|
}
|
|
132
128
|
}
|
|
133
129
|
|
|
134
|
-
|
|
135
130
|
return responseObject;
|
|
136
131
|
}
|
|
137
132
|
|
|
@@ -146,7 +141,7 @@ const api = {
|
|
|
146
141
|
|
|
147
142
|
const requestStarts = performance.now();
|
|
148
143
|
|
|
149
|
-
const res = await context.request.get(URL
|
|
144
|
+
const res = await context.request.get(URL, req);
|
|
150
145
|
|
|
151
146
|
const duration = performance.now() - requestStarts;
|
|
152
147
|
|
|
@@ -159,7 +154,7 @@ const api = {
|
|
|
159
154
|
|
|
160
155
|
const requestStarts = performance.now();
|
|
161
156
|
|
|
162
|
-
const res = await context.request.head(URL
|
|
157
|
+
const res = await context.request.head(URL);
|
|
163
158
|
|
|
164
159
|
const duration = performance.now() - requestStarts;
|
|
165
160
|
|
|
@@ -177,8 +172,7 @@ const api = {
|
|
|
177
172
|
|
|
178
173
|
switch (requestDataType) {
|
|
179
174
|
case "multipart":
|
|
180
|
-
|
|
181
|
-
const formRequest = processForm( payloadJSON?.body || {});
|
|
175
|
+
const formRequest = processForm(payloadJSON?.body || {});
|
|
182
176
|
|
|
183
177
|
req = await requestMaker(
|
|
184
178
|
payloadJSON?.headers || {},
|
|
@@ -195,7 +189,7 @@ const api = {
|
|
|
195
189
|
|
|
196
190
|
const requestStarts = performance.now();
|
|
197
191
|
|
|
198
|
-
const res = await context.request.post(URL
|
|
192
|
+
const res = await context.request.post(URL, req);
|
|
199
193
|
|
|
200
194
|
const duration = performance.now() - requestStarts;
|
|
201
195
|
|
|
@@ -213,10 +207,7 @@ const api = {
|
|
|
213
207
|
|
|
214
208
|
switch (requestDataType) {
|
|
215
209
|
case "multipart":
|
|
216
|
-
|
|
217
|
-
const formRequest = processForm( payloadJSON?.body || {});
|
|
218
|
-
|
|
219
|
-
|
|
210
|
+
const formRequest = processForm(payloadJSON?.body || {});
|
|
220
211
|
|
|
221
212
|
req = await requestMaker(
|
|
222
213
|
payloadJSON?.headers || {},
|
|
@@ -234,7 +225,7 @@ const api = {
|
|
|
234
225
|
|
|
235
226
|
const requestStarts = performance.now();
|
|
236
227
|
|
|
237
|
-
const res = await context.request.put(URL
|
|
228
|
+
const res = await context.request.put(URL, req);
|
|
238
229
|
|
|
239
230
|
const duration = performance.now() - requestStarts;
|
|
240
231
|
|
|
@@ -252,10 +243,8 @@ const api = {
|
|
|
252
243
|
|
|
253
244
|
switch (requestDataType) {
|
|
254
245
|
case "multipart":
|
|
255
|
-
let formData = {};
|
|
256
|
-
|
|
257
|
-
const formRequest = processForm( payloadJSON?.body || {});
|
|
258
246
|
|
|
247
|
+
const formRequest = processForm(payloadJSON?.body || {});
|
|
259
248
|
|
|
260
249
|
req = await requestMaker(
|
|
261
250
|
payloadJSON?.headers || {},
|
|
@@ -273,7 +262,7 @@ const api = {
|
|
|
273
262
|
|
|
274
263
|
const requestStarts = performance.now();
|
|
275
264
|
|
|
276
|
-
const res = await context.request.patch(URL
|
|
265
|
+
const res = await context.request.patch(URL, req);
|
|
277
266
|
|
|
278
267
|
const duration = performance.now() - requestStarts;
|
|
279
268
|
|
|
@@ -289,11 +278,12 @@ const api = {
|
|
|
289
278
|
|
|
290
279
|
const req = await requestMaker(
|
|
291
280
|
payloadJSON?.headers || {},
|
|
292
|
-
payloadJSON?.body || {}
|
|
281
|
+
payloadJSON?.body || {},
|
|
282
|
+
);
|
|
293
283
|
|
|
294
284
|
const requestStarts = performance.now();
|
|
295
285
|
|
|
296
|
-
const res = await context.request.delete(URL
|
|
286
|
+
const res = await context.request.delete(URL, req);
|
|
297
287
|
|
|
298
288
|
const duration = performance.now() - requestStarts;
|
|
299
289
|
|
package/src/hooks/hooks.js
CHANGED
|
@@ -145,7 +145,10 @@ AfterAll(async function () {
|
|
|
145
145
|
const successRate =
|
|
146
146
|
successPercentage.toFixed(2) >= cucumberConfig.default.testPercentage;
|
|
147
147
|
|
|
148
|
-
if (
|
|
148
|
+
if (
|
|
149
|
+
cucumberConfig.default.testPercentage != 0 &&
|
|
150
|
+
!isNaN(successPercentage)
|
|
151
|
+
) {
|
|
149
152
|
if (successRate) {
|
|
150
153
|
console.log(
|
|
151
154
|
`Tests passed required ${cucumberConfig.default.testPercentage}% success rate with ${successPercentage.toFixed(2)}% !`,
|
|
@@ -6,30 +6,42 @@ Given("User sets random word as {string} variable", async (key) => {
|
|
|
6
6
|
context.vars[key] = word;
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
Given(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
Given(
|
|
10
|
+
"User sets random word that has {int} character as {string} variable",
|
|
11
|
+
async (key, count) => {
|
|
12
|
+
const word = random.lorem.word(count);
|
|
13
|
+
context.vars[key] = word;
|
|
14
|
+
},
|
|
15
|
+
);
|
|
13
16
|
|
|
14
|
-
Given(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
17
|
+
Given(
|
|
18
|
+
"User sets random word that has character between {int} and {int} as {string} variable",
|
|
19
|
+
async (key, from, to) => {
|
|
20
|
+
const word = random.lorem.word({ length: { min: from, max: to } });
|
|
21
|
+
context.vars[key] = word;
|
|
22
|
+
},
|
|
23
|
+
);
|
|
18
24
|
|
|
19
25
|
Given("User sets random words as {string} variable", async (key) => {
|
|
20
26
|
const words = random.lorem.words();
|
|
21
27
|
context.vars[key] = words;
|
|
22
28
|
});
|
|
23
29
|
|
|
24
|
-
Given(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
30
|
+
Given(
|
|
31
|
+
"User sets random {int} words as {string} variable",
|
|
32
|
+
async (key, count) => {
|
|
33
|
+
const words = random.lorem.words({ wordCount: count });
|
|
34
|
+
context.vars[key] = words;
|
|
35
|
+
},
|
|
36
|
+
);
|
|
28
37
|
|
|
29
|
-
Given(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
});
|
|
38
|
+
Given(
|
|
39
|
+
"User sets random words that range between {int} and {int} as {string} variable",
|
|
40
|
+
async (key, from, to) => {
|
|
41
|
+
const words = random.lorem.words({ min: from, max: to });
|
|
42
|
+
context.vars[key] = words;
|
|
43
|
+
},
|
|
44
|
+
);
|
|
33
45
|
|
|
34
46
|
Given(
|
|
35
47
|
"User sets random number from {int} to {int} as {string} variable",
|