artes 1.0.41 → 1.0.43

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.
@@ -71,13 +71,20 @@ module.exports = {
71
71
  worldParameters: artesConfig.worldParameters || {}, // Custom world parameters
72
72
  },
73
73
 
74
+ api: {
75
+ baseURL: artesConfig?.baseURL ? artesConfig?.baseURL : "",
76
+ },
77
+
74
78
  browser: {
75
79
  browserType: artesConfig?.browser || "chrome",
76
80
  viewport: {
77
81
  width: artesConfig?.width || 1280,
78
82
  height: artesConfig?.height || 720,
79
83
  },
80
- maximizeScreen: artesConfig?.maximizeScreen !== undefined ? artesConfig.maximizeScreen : true,
84
+ maximizeScreen:
85
+ artesConfig?.maximizeScreen !== undefined
86
+ ? artesConfig.maximizeScreen
87
+ : true,
81
88
  headless: artesConfig?.headless !== undefined ? artesConfig.headless : true,
82
89
  },
83
90
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "artes",
3
- "version": "1.0.41",
3
+ "version": "1.0.43",
4
4
  "description": "The package provide step definitions and user writes feature files, and the package handles automation, with optional POM files and custom step definitions.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -35,6 +35,7 @@
35
35
  "allure-cucumberjs": "^3.0.5",
36
36
  "allure-js-commons": "^3.0.5",
37
37
  "playwright": "^1.48.2",
38
+ "ajv": "^8.17.1",
38
39
  "rimraf": "^6.0.1"
39
40
  },
40
41
  "repository": {
@@ -20,7 +20,7 @@ const invokeBrowser = async () => {
20
20
  dir: "./test-results/visualReport/",
21
21
  size: cucumberConfig.browser.viewport,
22
22
  },
23
- ignoreHTTPSErrors: true
23
+ ignoreHTTPSErrors: true,
24
24
  };
25
25
 
26
26
  switch (browserType) {
@@ -1,10 +1,13 @@
1
1
  const { request } = require("playwright");
2
+ const cucumberConfig = require("../../../cucumber.config.js");
2
3
 
3
- const requestContextOptions = {};
4
+ const requestContextOptions = {
5
+ baseURL: cucumberConfig.api.baseURL,
6
+ };
4
7
 
5
8
  async function invokeRequest() {
6
9
  const context = await request.newContext(requestContextOptions);
7
- return await context;
10
+ return context;
8
11
  }
9
12
 
10
13
  module.exports = { invokeRequest };
@@ -39,6 +39,7 @@ function createProject(createYes) {
39
39
  headless: false, // Set to true for headless browser mode
40
40
 
41
41
  // Configuration options:
42
+ // baseURL: "", // string - Base URL for API tests
42
43
  // paths: [], // string[] - Paths to feature files
43
44
  // steps: "", // string - Step definitions files
44
45
  // pomPath: "", // string - Path to POM files
@@ -3,6 +3,8 @@ const { Given, When, Then } = require("@cucumber/cucumber");
3
3
  const {
4
4
  getElement,
5
5
  getSelector,
6
+ saveVar,
7
+ resolveVariable,
6
8
  } = require("../pomController/elementController");
7
9
  const { context } = require("../../hooks/context");
8
10
 
@@ -35,6 +37,8 @@ module.exports = {
35
37
  Then,
36
38
  element,
37
39
  selector,
40
+ saveVar,
41
+ resolveVariable,
38
42
  page,
39
43
  request,
40
44
  context,
@@ -84,10 +84,35 @@ class Elements {
84
84
  this.elements?.[element]?.selector || this.elements?.[element] || element;
85
85
  return selector;
86
86
  }
87
+
88
+ static saveVar(value, customName, path) {
89
+ if (!customName) {
90
+ const flatKey = path
91
+ .split(".")
92
+ .map((part, i) =>
93
+ i === 0 ? part : part[0].toUpperCase() + part.slice(1),
94
+ )
95
+ .join("");
96
+
97
+ context.vars[flatKey] = value;
98
+ } else {
99
+ context.vars[customName] = value;
100
+ }
101
+ }
102
+
103
+ static resolveVariable(template) {
104
+ if (typeof template !== "string") return template;
105
+ return template.replace(/{{\s*(\w+)\s*}}/g, (_, varName) => {
106
+ const value = context.vars[varName];
107
+ return value !== undefined ? value : `{{${varName}}}`;
108
+ });
109
+ }
87
110
  }
88
111
 
89
112
  module.exports = {
90
113
  getElement: Elements.getElement.bind(Elements),
91
114
  addElements: Elements.addElements.bind(Elements),
92
115
  getSelector: Elements.getSelector.bind(Elements),
116
+ saveVar: Elements.saveVar.bind(Elements),
117
+ resolveVariable: Elements.resolveVariable.bind(Elements),
93
118
  };
@@ -1,23 +1,147 @@
1
- const { context } = require("../imports/commons");
1
+ const { context, selector, resolveVariable } = require("../imports/commons");
2
2
 
3
3
  const api = {
4
- get: async (url) => {
5
- return await context.request.get(url);
4
+ get: async (url, payload) => {
5
+ const URL = await selector(url);
6
+ const resolvedURL = await resolveVariable(URL);
7
+
8
+ const resolvedPayload = resolveVariable(payload);
9
+ const payloadJSON = (await resolvedPayload) && JSON.parse(resolvedPayload);
10
+
11
+ const res = await context.request.get(resolvedURL, {
12
+ headers: payloadJSON ? payloadJSON.headers : {},
13
+ });
14
+ const header = await res.headers();
15
+ const body = await res.json();
16
+
17
+ const response = {
18
+ url: res.url(),
19
+ response: res,
20
+ responseHeaders: header,
21
+ responseBody: body,
22
+ };
23
+
24
+ context.response = response;
6
25
  },
7
26
  head: async (url) => {
8
- return await context.request.head(url);
27
+ const URL = await selector(url);
28
+ const resolvedURL = await resolveVariable(URL);
29
+
30
+ const res = await context.request.head(resolvedURL);
31
+ const header = await res.headers();
32
+
33
+ const response = {
34
+ url: res.url(),
35
+ response: res,
36
+ responseHeaders: header,
37
+ responseBody: body,
38
+ };
39
+
40
+ context.response = response;
9
41
  },
10
- post: async (url) => {
11
- return await context.request.post(url);
42
+ post: async (url, payload) => {
43
+ const URL = await selector(url);
44
+ const resolvedURL = await resolveVariable(URL);
45
+
46
+ const resolvedPayload = await resolveVariable(payload);
47
+ const payloadJSON = (await resolvedPayload) && JSON.parse(resolvedPayload);
48
+
49
+ const res = await context.request.post(resolvedURL, {
50
+ headers: payloadJSON ? payloadJSON.headers : {},
51
+ data: payloadJSON ? payloadJSON.body : {},
52
+ });
53
+
54
+ const header = await res.headers();
55
+ const body = await res.json();
56
+
57
+ const response = {
58
+ url: res.url(),
59
+ requestHeaders: payloadJSON.headers,
60
+ requestBody: payloadJSON.body,
61
+ response: res,
62
+ responseHeaders: header,
63
+ responseBody: body,
64
+ };
65
+
66
+ context.response = response;
67
+ },
68
+ put: async (url, payload) => {
69
+ const URL = await selector(url);
70
+ const resolvedURL = await resolveVariable(URL);
71
+
72
+ const resolvedPayload = await resolveVariable(payload);
73
+ const payloadJSON = (await resolvedPayload) && JSON.parse(resolvedPayload);
74
+
75
+ const res = await context.request.put(resolvedURL, {
76
+ headers: payloadJSON ? payloadJSON.headers : {},
77
+ data: payloadJSON ? payloadJSON.body : {},
78
+ });
79
+
80
+ const header = await res.headers();
81
+ const body = await res.json();
82
+
83
+ const response = {
84
+ url: res.url(),
85
+ requestHeaders: payloadJSON.headers,
86
+ requestBody: payloadJSON.body,
87
+ response: res,
88
+ responseHeaders: header,
89
+ responseBody: body,
90
+ };
91
+
92
+ context.response = response;
12
93
  },
13
- put: async (url) => {
14
- return await context.request.put(url);
94
+ patch: async (url, payload) => {
95
+ const URL = await selector(url);
96
+ const resolvedURL = await resolveVariable(URL);
97
+
98
+ const resolvedPayload = await resolveVariable(payload);
99
+ const payloadJSON = (await resolvedPayload) && JSON.parse(resolvedPayload);
100
+
101
+ const res = await context.request.patch(resolvedURL, {
102
+ headers: payloadJSON ? payloadJSON.headers : {},
103
+ data: payloadJSON ? payloadJSON.body : {},
104
+ });
105
+
106
+ const header = await res.headers();
107
+ const body = await res.json();
108
+
109
+ const response = {
110
+ url: res.url(),
111
+ requestHeaders: payloadJSON.headers,
112
+ requestBody: payloadJSON.body,
113
+ response: res,
114
+ responseHeaders: header,
115
+ responseBody: body,
116
+ };
117
+
118
+ context.response = response;
15
119
  },
16
- patch: async (url) => {
17
- return await context.request.patch(url);
120
+ delete: async (url, payload) => {
121
+ const URL = await selector(url);
122
+ const resolvedURL = await resolveVariable(URL);
123
+
124
+ const resolvedPayload = await resolveVariable(payload);
125
+ const payloadJSON = (await resolvedPayload) && JSON.parse(resolvedPayload);
126
+
127
+ const res = await context.request.delete(resolvedURL, {
128
+ headers: payloadJSON.headers,
129
+ });
130
+
131
+ const header = await res.headers();
132
+ const body = await res.json();
133
+
134
+ const response = {
135
+ url: res.url(),
136
+ response: res,
137
+ responseHeaders: header,
138
+ responseBody: body,
139
+ };
140
+
141
+ context.response = response;
18
142
  },
19
- delete: async (url) => {
20
- return await context.request.head(url);
143
+ vars: () => {
144
+ context.vars
21
145
  },
22
146
  };
23
147
 
@@ -1,5 +1,10 @@
1
- const path = require('path');
2
- const { element, context, selector, moduleConfig } = require("../imports/commons");
1
+ const path = require("path");
2
+ const {
3
+ element,
4
+ context,
5
+ selector,
6
+ moduleConfig,
7
+ } = require("../imports/commons");
3
8
  const { frame } = require("../stepFunctions/frameActions");
4
9
 
5
10
  const mouse = {
@@ -127,7 +132,7 @@ const mouse = {
127
132
  await element(selector).scrollIntoViewIfNeeded();
128
133
  },
129
134
  upload: async (filePath, fileInput) => {
130
- const file = selector(filePath)
135
+ const file = selector(filePath);
131
136
  const fileChooserPromise = context.page.waitForEvent("filechooser");
132
137
  await element(fileInput).click();
133
138
  const fileChooser = await fileChooserPromise;
@@ -3,6 +3,8 @@ class Context {
3
3
  this.browser = undefined;
4
4
  this.page = undefined;
5
5
  this.request = undefined;
6
+ this.response = undefined;
7
+ this.vars = {};
6
8
  }
7
9
  }
8
10
 
@@ -30,6 +30,7 @@ Before(async function () {
30
30
  context.browser = await browser;
31
31
  context.page = await browser.newPage();
32
32
  await context.page.setDefaultTimeout(cucumberConfig.default.timeout * 1000);
33
+
33
34
  context.request = await request;
34
35
 
35
36
  await browser.tracing.start({
@@ -53,8 +54,17 @@ After(async function ({ pickle, result }) {
53
54
  await browser.tracing.stop({
54
55
  path: path.join(moduleConfig.projectPath, "./trace.zip"),
55
56
  });
57
+
58
+ if (context.response) {
59
+ await this.attach(
60
+ `Request Payload:\n${JSON.stringify(context.response, null, 2)}`,
61
+ "text/plain",
62
+ );
63
+ }
64
+
56
65
  await context.page.close();
57
66
  await browser.close();
67
+ await context.request.dispose();
58
68
 
59
69
  if (result?.status != Status.PASSED) {
60
70
  const videoPath = await context.page.video().path();
@@ -1,26 +1,230 @@
1
- const { When, context } = require("../helper/imports/commons");
2
- const { api, assert } = require("../helper/stepFunctions/exporter");
1
+ const {
2
+ When,
3
+ context,
4
+ expect,
5
+ selector,
6
+ saveVar
7
+ } = require("../helper/imports/commons");
8
+ const { api } = require("../helper/stepFunctions/exporter");
9
+ const Ajv = require("ajv");
10
+
11
+ function extractVarsFromResponse(vars, customVarName) {
12
+ const responseBody = context.response.responseBody;
13
+
14
+ function getValueByPath(obj, path) {
15
+ const keys = path.split(".");
16
+ let current = obj;
17
+
18
+ for (const key of keys) {
19
+ if (current && typeof current === "object" && key in current) {
20
+ current = current[key];
21
+ } else {
22
+ return undefined;
23
+ }
24
+ }
25
+
26
+ return current;
27
+ }
28
+
29
+ vars.split(",").forEach((v) => {
30
+ const path = v.trim();
31
+ const value = getValueByPath(responseBody, path);
32
+ if (value !== undefined) {
33
+ saveVar(value, customVarName, path);
34
+ }
35
+ });
36
+ }
3
37
 
4
38
  When("User sends GET request to {string}", async function (url) {
5
39
  await api.get(url);
6
40
  });
7
41
 
42
+ When(
43
+ "User sends GET request to {string} with payload:",
44
+ async function (url, reqParams) {
45
+ await api.get(url, payload);
46
+ },
47
+ );
48
+
49
+ When(
50
+ "User sends GET request to {string} and saves {string} variables",
51
+ async function (url, vars) {
52
+ await api.get(url, payload);
53
+ extractVarsFromResponse(vars);
54
+ },
55
+ );
56
+
57
+ When(
58
+ "User sends GET request to {string} with payload and saves {string} variables",
59
+ async function (url, vars, payload) {
60
+ await api.get(url, payload);
61
+ extractVarsFromResponse(vars);
62
+ },
63
+ );
64
+
8
65
  When("User sends HEAD request to {string}", async function (url) {
9
- await api.get(url);
66
+ await api.head(url);
10
67
  });
11
68
 
12
- When("User sends POST request to {string}", async function (url) {
13
- await api.get(url);
69
+ When(
70
+ "User sends POST request to {string} with payload:",
71
+ async function (url, payload) {
72
+ await api.post(url, payload);
73
+ },
74
+ );
75
+
76
+ When(
77
+ "User sends POST request to {string} with payload and saves {string} variables",
78
+ async function (url, payload) {
79
+ await api.post(url, payload);
80
+ extractVarsFromResponse(vars);
81
+ },
82
+ );
83
+
84
+ When(
85
+ "User sends PUT request to {string} with payload:",
86
+ async function (url, payload) {
87
+ await api.put(url, payload);
88
+ },
89
+ );
90
+
91
+ When(
92
+ "User sends PUT request to {string} with payload and saves {string} variables",
93
+ async function (url, payload) {
94
+ await api.put(url, payload);
95
+ extractVarsFromResponse(vars);
96
+ },
97
+ );
98
+
99
+ When(
100
+ "User sends PATCH request to {string} with payload:",
101
+ async function (url, payload) {
102
+ await api.patch(url, payload);
103
+ },
104
+ );
105
+
106
+ When(
107
+ "User sends PATCH request to {string} with payload and saves {string} variables",
108
+ async function (url, payload) {
109
+ await api.patch(url, payload);
110
+ extractVarsFromResponse(vars);
111
+ },
112
+ );
113
+
114
+ When("User sends DELETE request to {string}", async function (url) {
115
+ await api.delete(url);
14
116
  });
15
117
 
16
- When("User sends PUT request to {string}", async function (url) {
17
- await api.get(url);
118
+ When(
119
+ "User sends DELETE request to {string} and saves {string} variables",
120
+ async function (url, vars) {
121
+ await api.delete(url);
122
+ extractVarsFromResponse(vars);
123
+ },
124
+ );
125
+
126
+ When(
127
+ "User sends DELETE request to {string} with payload:",
128
+ async function (url, payload) {
129
+ await api.delete(url, payload);
130
+ },
131
+ );
132
+
133
+ When(
134
+ "User sends DELETE request to {string} with payload and saves {string} variables",
135
+ async function (url, vars, payload) {
136
+ await api.delete(url, payload);
137
+ extractVarsFromResponse(vars);
138
+ },
139
+ );
140
+
141
+ When(
142
+ "User saves {string} variable from response as {string}",
143
+ async function (vars, customVarName) {
144
+ extractVarsFromResponse(vars, customVarName);
145
+ },
146
+ );
147
+
148
+ When(
149
+ "User saves {string} variable as {string}",
150
+ async function (value, customVarName) {
151
+ saveVar(value, customVarName);
152
+ },
153
+ );
154
+
155
+ When("User wants to see saved variables", async function () {
156
+ console.log("\nVariables:", api.vars(), "\n");
157
+
18
158
  });
19
159
 
20
- When("User sends PATCH request to {string}", async function (url) {
21
- await api.get(url);
160
+ When("User wants to see request body", async function () {
161
+ console.log("Request Body: ", context.response.requestBody);
22
162
  });
23
163
 
24
- When("User sends DELETE request to {string}", async function (url) {
25
- await api.get(url);
164
+ When("User wants to see response body", async function () {
165
+ console.log("Request Body: ", context.response.responseBody);
26
166
  });
167
+
168
+ When(
169
+ "User sends {string} request to {string}",
170
+ async function (method, url, reqParams) {
171
+ const httpMethod = method.toUpperCase();
172
+
173
+ let headers = {};
174
+ let body;
175
+
176
+ if (["POST", "PUT", "PATCH"].includes(httpMethod)) {
177
+ const payload = JSON.parse(reqParams);
178
+ headers = payload.headers || {};
179
+ body = payload.body || {};
180
+ }
181
+
182
+ switch (httpMethod) {
183
+ case "GET":
184
+ await api.get(url);
185
+ break;
186
+ case "HEAD":
187
+ await api.head(url);
188
+ break;
189
+ case "POST":
190
+ await api.post(url, headers, body);
191
+ break;
192
+ case "PUT":
193
+ await api.put(url, headers, body);
194
+ break;
195
+ case "PATCH":
196
+ await api.patch(url, headers, body);
197
+ break;
198
+ case "DELETE":
199
+ await api.delete(url);
200
+ break;
201
+ default:
202
+ throw new Error(`Unsupported HTTP method: ${httpMethod}`);
203
+ }
204
+ },
205
+ );
206
+
207
+ When(
208
+ "User expects that request should have {int} status code",
209
+ async function (expectedStatusCode) {
210
+ const actualStatusCode = await context.response.response.status();
211
+ expect(actualStatusCode).toBe(expectedStatusCode);
212
+ },
213
+ );
214
+
215
+ When(
216
+ "User expects that response body should match {string} schema",
217
+ async function (expectedSchema) {
218
+ const schema = selector(expectedSchema);
219
+
220
+ const ajv = new Ajv();
221
+
222
+ const validate = ajv.compile(schema);
223
+
224
+ const responseBody = context.response?.responseBody;
225
+
226
+ const valid = validate(responseBody);
227
+
228
+ expect(valid).toBe(true);
229
+ },
230
+ );