artes 1.1.7 → 1.1.8
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 +101 -105
- package/src/helper/stepFunctions/APIActions.js +51 -61
- 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.8",
|
|
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,130 @@ 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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
function getSelector(element) {
|
|
25
|
-
const selector =
|
|
26
|
-
elements?.[element]?.selector || elements?.[element] || element;
|
|
27
|
-
return resolveVariable(selectorSeperator(selector));
|
|
28
|
-
}
|
|
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
|
+
return [
|
|
18
|
+
selector[0]?.trim(),
|
|
19
|
+
selector[1] !== undefined ? selector[1].trim() : "",
|
|
20
|
+
];
|
|
21
|
+
}
|
|
29
22
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
function getSelector(element) {
|
|
24
|
+
const selector =
|
|
25
|
+
elements?.[element]?.selector || elements?.[element] || element;
|
|
26
|
+
return resolveVariable(selectorSeperator(selector));
|
|
27
|
+
}
|
|
34
28
|
|
|
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
|
-
}
|
|
29
|
+
function getElement(element) {
|
|
30
|
+
if (!context.page) {
|
|
31
|
+
throw new Error("Page context is not initialized.");
|
|
32
|
+
}
|
|
73
33
|
|
|
74
|
-
|
|
34
|
+
const selector = getSelector(element);
|
|
35
|
+
const waitTime = elements[element]?.waitTime * 1000 || 0;
|
|
36
|
+
|
|
37
|
+
let locator;
|
|
38
|
+
switch (selector[0]) {
|
|
39
|
+
case "xpath":
|
|
40
|
+
locator = context.page.locator(`xpath=${selector[1]}`, { exact: true });
|
|
41
|
+
break;
|
|
42
|
+
case "name":
|
|
43
|
+
locator = context.page.locator(`[name=${selector[1]}]`, {
|
|
44
|
+
exact: true,
|
|
45
|
+
});
|
|
46
|
+
break;
|
|
47
|
+
case "placeholder":
|
|
48
|
+
locator = context.page.getByPlaceholder(selector[1], { exact: true });
|
|
49
|
+
break;
|
|
50
|
+
case "text":
|
|
51
|
+
locator = context.page.getByText(selector[1], { exact: true });
|
|
52
|
+
break;
|
|
53
|
+
case "label":
|
|
54
|
+
locator = context.page.getByLabel(selector[1], { exact: true });
|
|
55
|
+
break;
|
|
56
|
+
case "role":
|
|
57
|
+
locator = context.page.getByRole(selector[1], { exact: true });
|
|
58
|
+
break;
|
|
59
|
+
case "alt":
|
|
60
|
+
locator = context.page.getByAltText(selector[1], { exact: true });
|
|
61
|
+
break;
|
|
62
|
+
case "title":
|
|
63
|
+
locator = context.page.getByTitle(selector[1], { exact: true });
|
|
64
|
+
break;
|
|
65
|
+
case "testid":
|
|
66
|
+
locator = context.page.getByTestId(selector[1], { exact: true });
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
locator = context.page.locator(selector[0], { exact: true });
|
|
70
|
+
break;
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
|
|
73
|
+
return locator;
|
|
74
|
+
}
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
function extractVarsFromResponse(responseBody, vars, customVarName) {
|
|
77
|
+
function getValueByPath(obj, path) {
|
|
78
|
+
const keys = path.split(".");
|
|
79
|
+
let current = obj;
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
81
|
+
for (const key of keys) {
|
|
82
|
+
if (current && typeof current === "object" && key in current) {
|
|
83
|
+
current = current[key];
|
|
84
|
+
} else {
|
|
85
|
+
return undefined;
|
|
90
86
|
}
|
|
91
|
-
|
|
92
|
-
return current;
|
|
93
87
|
}
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
const path = v.trim();
|
|
97
|
-
const value = getValueByPath(responseBody, path);
|
|
98
|
-
if (value !== undefined) {
|
|
99
|
-
saveVar(value, customVarName, path);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
89
|
+
return current;
|
|
102
90
|
}
|
|
103
91
|
|
|
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;
|
|
92
|
+
vars.split(",").forEach((v) => {
|
|
93
|
+
const path = v.trim();
|
|
94
|
+
const value = getValueByPath(responseBody, path);
|
|
95
|
+
if (value !== undefined) {
|
|
96
|
+
saveVar(value, customVarName, path);
|
|
116
97
|
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function saveVar(value, customName, path) {
|
|
102
|
+
if (!customName) {
|
|
103
|
+
const flatKey = path
|
|
104
|
+
.split(".")
|
|
105
|
+
.map((part, i) =>
|
|
106
|
+
i === 0 ? part : part[0].toUpperCase() + part.slice(1),
|
|
107
|
+
)
|
|
108
|
+
.join("");
|
|
109
|
+
|
|
110
|
+
context.vars[flatKey] = value;
|
|
111
|
+
} else {
|
|
112
|
+
context.vars[customName] = value;
|
|
117
113
|
}
|
|
114
|
+
}
|
|
118
115
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
function resolveVariable(template) {
|
|
117
|
+
if (typeof template !== "string") return template;
|
|
118
|
+
return template.replace(/{{\s*(\w+)\s*}}/g, (_, varName) => {
|
|
119
|
+
let value = context.vars[varName];
|
|
120
|
+
if (typeof value === "string") {
|
|
124
121
|
value = value
|
|
125
122
|
.replace(/\n/g, "\\n")
|
|
126
123
|
.replace(/\r/g, "\\r")
|
|
127
124
|
.replace(/\t/g, "\\t");
|
|
128
125
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
return value !== undefined ? value : `{{${varName}}}`;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
133
129
|
|
|
134
130
|
module.exports = {
|
|
135
131
|
getElement,
|
|
@@ -137,5 +133,5 @@ module.exports = {
|
|
|
137
133
|
getSelector,
|
|
138
134
|
extractVarsFromResponse,
|
|
139
135
|
saveVar,
|
|
140
|
-
resolveVariable
|
|
136
|
+
resolveVariable,
|
|
141
137
|
};
|
|
@@ -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
|
|
|
@@ -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 || {},
|
|
@@ -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 || {},
|
|
@@ -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 || {},
|
|
@@ -289,7 +278,8 @@ 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
|
|
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",
|