@playq/core 0.2.77
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 +41 -0
- package/bin/playq.js +175 -0
- package/cucumber.js +10 -0
- package/dist/exec/featureFileCache.d.ts +21 -0
- package/dist/exec/featureFileCache.js +124 -0
- package/dist/exec/featureFilePreProcess.d.ts +12 -0
- package/dist/exec/featureFilePreProcess.js +208 -0
- package/dist/exec/preLoader.d.ts +1 -0
- package/dist/exec/preLoader.js +72 -0
- package/dist/exec/preProcessEntry.d.ts +1 -0
- package/dist/exec/preProcessEntry.js +83 -0
- package/dist/exec/preProcess_old_todelete.d.ts +1 -0
- package/dist/exec/preProcess_old_todelete.js +258 -0
- package/dist/exec/runner.d.ts +1 -0
- package/dist/exec/runner.js +221 -0
- package/dist/exec/runner_orchestrator.d.ts +1 -0
- package/dist/exec/runner_orchestrator.js +85 -0
- package/dist/exec/sgGenerator.d.ts +11 -0
- package/dist/exec/sgGenerator.js +310 -0
- package/dist/global.d.ts +15 -0
- package/dist/global.js +185 -0
- package/dist/helper/actions/api/apiRequestActions.d.ts +117 -0
- package/dist/helper/actions/api/apiRequestActions.js +374 -0
- package/dist/helper/actions/api/apiValidationActions.d.ts +119 -0
- package/dist/helper/actions/api/apiValidationActions.js +615 -0
- package/dist/helper/actions/apiActions.d.ts +18 -0
- package/dist/helper/actions/apiActions.js +34 -0
- package/dist/helper/actions/apiStepDefs.d.ts +1 -0
- package/dist/helper/actions/apiStepDefs.js +64 -0
- package/dist/helper/actions/comm/commonActions.d.ts +58 -0
- package/dist/helper/actions/comm/commonActions.js +198 -0
- package/dist/helper/actions/comm/utilityActions.d.ts +131 -0
- package/dist/helper/actions/comm/utilityActions.js +351 -0
- package/dist/helper/actions/commActions.d.ts +18 -0
- package/dist/helper/actions/commActions.js +34 -0
- package/dist/helper/actions/commStepDefs.d.ts +1 -0
- package/dist/helper/actions/commStepDefs.js +57 -0
- package/dist/helper/actions/stepGroupStepDefs.d.ts +1 -0
- package/dist/helper/actions/stepGroupStepDefs.js +15 -0
- package/dist/helper/actions/web/alertActions.d.ts +61 -0
- package/dist/helper/actions/web/alertActions.js +224 -0
- package/dist/helper/actions/web/cookieActions.d.ts +45 -0
- package/dist/helper/actions/web/cookieActions.js +186 -0
- package/dist/helper/actions/web/downloadActions.d.ts +40 -0
- package/dist/helper/actions/web/downloadActions.js +153 -0
- package/dist/helper/actions/web/elementReaderActions.d.ts +95 -0
- package/dist/helper/actions/web/elementReaderActions.js +326 -0
- package/dist/helper/actions/web/formActions.d.ts +122 -0
- package/dist/helper/actions/web/formActions.js +423 -0
- package/dist/helper/actions/web/iframeActions.d.ts +23 -0
- package/dist/helper/actions/web/iframeActions.js +108 -0
- package/dist/helper/actions/web/javascriptActions.d.ts +14 -0
- package/dist/helper/actions/web/javascriptActions.js +77 -0
- package/dist/helper/actions/web/keyboardActions.d.ts +35 -0
- package/dist/helper/actions/web/keyboardActions.js +118 -0
- package/dist/helper/actions/web/localStorageActions.d.ts +51 -0
- package/dist/helper/actions/web/localStorageActions.js +163 -0
- package/dist/helper/actions/web/mouseActions.d.ts +240 -0
- package/dist/helper/actions/web/mouseActions.js +609 -0
- package/dist/helper/actions/web/reportingActions.d.ts +34 -0
- package/dist/helper/actions/web/reportingActions.js +58 -0
- package/dist/helper/actions/web/screenshotActions.d.ts +34 -0
- package/dist/helper/actions/web/screenshotActions.js +151 -0
- package/dist/helper/actions/web/testDataActions.d.ts +21 -0
- package/dist/helper/actions/web/testDataActions.js +211 -0
- package/dist/helper/actions/web/validationActions.d.ts +547 -0
- package/dist/helper/actions/web/validationActions.js +1754 -0
- package/dist/helper/actions/web/waitActions.d.ts +191 -0
- package/dist/helper/actions/web/waitActions.js +589 -0
- package/dist/helper/actions/web/webNavigation.d.ts +104 -0
- package/dist/helper/actions/web/webNavigation.js +288 -0
- package/dist/helper/actions/webActions.d.ts +32 -0
- package/dist/helper/actions/webActions.js +48 -0
- package/dist/helper/actions/webStepDefs.d.ts +1 -0
- package/dist/helper/actions/webStepDefs.js +455 -0
- package/dist/helper/browsers/browserManager.d.ts +1 -0
- package/dist/helper/browsers/browserManager.js +56 -0
- package/dist/helper/bundle/defaultEntries.d.ts +6 -0
- package/dist/helper/bundle/defaultEntries.js +200 -0
- package/dist/helper/bundle/env.d.ts +1 -0
- package/dist/helper/bundle/env.js +157 -0
- package/dist/helper/bundle/vars.d.ts +9 -0
- package/dist/helper/bundle/vars.js +375 -0
- package/dist/helper/faker/customFaker.d.ts +55 -0
- package/dist/helper/faker/customFaker.js +45 -0
- package/dist/helper/faker/modules/data/postcodes_valid_sg.json +17 -0
- package/dist/helper/faker/modules/dateTime.d.ts +18 -0
- package/dist/helper/faker/modules/dateTime.js +106 -0
- package/dist/helper/faker/modules/mobile.d.ts +4 -0
- package/dist/helper/faker/modules/mobile.js +59 -0
- package/dist/helper/faker/modules/nric.d.ts +32 -0
- package/dist/helper/faker/modules/nric.js +84 -0
- package/dist/helper/faker/modules/passport.d.ts +3 -0
- package/dist/helper/faker/modules/passport.js +36 -0
- package/dist/helper/faker/modules/person.d.ts +14 -0
- package/dist/helper/faker/modules/person.js +73 -0
- package/dist/helper/faker/modules/postcode.d.ts +6 -0
- package/dist/helper/faker/modules/postcode.js +47 -0
- package/dist/helper/fixtures/locAggregate.d.ts +7 -0
- package/dist/helper/fixtures/locAggregate.js +94 -0
- package/dist/helper/fixtures/logFixture.d.ts +8 -0
- package/dist/helper/fixtures/logFixture.js +56 -0
- package/dist/helper/fixtures/webFixture.d.ts +19 -0
- package/dist/helper/fixtures/webFixture.js +186 -0
- package/dist/helper/fixtures/webLocFixture.d.ts +2 -0
- package/dist/helper/fixtures/webLocFixture.js +144 -0
- package/dist/helper/report/allureStepLogger.d.ts +0 -0
- package/dist/helper/report/allureStepLogger.js +25 -0
- package/dist/helper/report/customiseReport.d.ts +1 -0
- package/dist/helper/report/customiseReport.js +55 -0
- package/dist/helper/report/init.d.ts +1 -0
- package/dist/helper/report/init.js +14 -0
- package/dist/helper/report/report.d.ts +1 -0
- package/dist/helper/report/report.js +102 -0
- package/dist/helper/util/dataLoader.d.ts +10 -0
- package/dist/helper/util/dataLoader.js +73 -0
- package/dist/helper/util/logger.d.ts +4 -0
- package/dist/helper/util/logger.js +61 -0
- package/dist/helper/util/session/sessionUtil.d.ts +26 -0
- package/dist/helper/util/session/sessionUtil.js +729 -0
- package/dist/helper/util/stepHelpers.d.ts +2 -0
- package/dist/helper/util/stepHelpers.js +16 -0
- package/dist/helper/util/test-data/dataLoader.d.ts +7 -0
- package/dist/helper/util/test-data/dataLoader.js +145 -0
- package/dist/helper/util/test-data/dataTest.d.ts +10 -0
- package/dist/helper/util/test-data/dataTest.js +216 -0
- package/dist/helper/util/totp/totpHelper.d.ts +38 -0
- package/dist/helper/util/totp/totpHelper.js +117 -0
- package/dist/helper/util/utilities/cryptoUtil.d.ts +2 -0
- package/dist/helper/util/utilities/cryptoUtil.js +53 -0
- package/dist/helper/util/utilities/schemaGeneratorUtil.d.ts +2 -0
- package/dist/helper/util/utilities/schemaGeneratorUtil.js +129 -0
- package/dist/helper/util/utils.d.ts +2 -0
- package/dist/helper/util/utils.js +22 -0
- package/dist/helper/wrapper/PlaywrightWrappers.d.ts +8 -0
- package/dist/helper/wrapper/PlaywrightWrappers.js +26 -0
- package/dist/helper/wrapper/assert.d.ts +9 -0
- package/dist/helper/wrapper/assert.js +23 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +57 -0
- package/dist/scripts/get-versions.d.ts +1 -0
- package/dist/scripts/get-versions.js +98 -0
- package/dist/scripts/posttest.d.ts +1 -0
- package/dist/scripts/posttest.js +29 -0
- package/dist/scripts/pretest.d.ts +1 -0
- package/dist/scripts/pretest.js +57 -0
- package/dist/scripts/util.d.ts +1 -0
- package/dist/scripts/util.js +376 -0
- package/package.json +68 -0
- package/src/exec/featureFileCache.ts +80 -0
- package/src/exec/featureFilePreProcess.ts +239 -0
- package/src/exec/preLoader.ts +72 -0
- package/src/exec/preProcessEntry.ts +59 -0
- package/src/exec/preProcess_old_todelete.ts +289 -0
- package/src/exec/runner.ts +241 -0
- package/src/exec/runnerCuke.js +90 -0
- package/src/exec/runner_orchestrator.ts +91 -0
- package/src/exec/sgGenerator.ts +373 -0
- package/src/global.ts +130 -0
- package/src/helper/actions/api/apiRequestActions.ts +362 -0
- package/src/helper/actions/api/apiValidationActions.ts +594 -0
- package/src/helper/actions/apiActions.ts +18 -0
- package/src/helper/actions/apiStepDefs.ts +80 -0
- package/src/helper/actions/comm/commonActions.ts +165 -0
- package/src/helper/actions/comm/utilityActions.ts +344 -0
- package/src/helper/actions/commActions.ts +18 -0
- package/src/helper/actions/commStepDefs.ts +72 -0
- package/src/helper/actions/stepGroupStepDefs.ts +17 -0
- package/src/helper/actions/web/alertActions.ts +179 -0
- package/src/helper/actions/web/cookieActions.ts +124 -0
- package/src/helper/actions/web/downloadActions.ts +129 -0
- package/src/helper/actions/web/elementReaderActions.ts +323 -0
- package/src/helper/actions/web/formActions.ts +469 -0
- package/src/helper/actions/web/iframeActions.ts +67 -0
- package/src/helper/actions/web/javascriptActions.ts +38 -0
- package/src/helper/actions/web/keyboardActions.ts +101 -0
- package/src/helper/actions/web/localStorageActions.ts +109 -0
- package/src/helper/actions/web/mouseActions.ts +864 -0
- package/src/helper/actions/web/reportingActions.ts +53 -0
- package/src/helper/actions/web/screenshotActions.ts +124 -0
- package/src/helper/actions/web/testDataActions.ts +162 -0
- package/src/helper/actions/web/validationActions.ts +2287 -0
- package/src/helper/actions/web/waitActions.ts +757 -0
- package/src/helper/actions/web/webNavigation.ts +313 -0
- package/src/helper/actions/webActions.ts +33 -0
- package/src/helper/actions/webStepDefs.ts +505 -0
- package/src/helper/browsers/browserManager.ts +23 -0
- package/src/helper/bundle/defaultEntries.ts +208 -0
- package/src/helper/bundle/env.ts +119 -0
- package/src/helper/bundle/vars.ts +368 -0
- package/src/helper/faker/customFaker.ts +107 -0
- package/src/helper/faker/modules/data/postcodes_valid_sg.json +17 -0
- package/src/helper/faker/modules/dateTime.ts +121 -0
- package/src/helper/faker/modules/mobile.ts +58 -0
- package/src/helper/faker/modules/nric.ts +109 -0
- package/src/helper/faker/modules/passport.ts +34 -0
- package/src/helper/faker/modules/person.ts +93 -0
- package/src/helper/faker/modules/postcode.ts +57 -0
- package/src/helper/fixtures/locAggregate.ts +61 -0
- package/src/helper/fixtures/logFixture.ts +57 -0
- package/src/helper/fixtures/webFixture.ts +206 -0
- package/src/helper/fixtures/webLocFixture.ts +143 -0
- package/src/helper/report/allureStepLogger.ts +26 -0
- package/src/helper/report/customiseReport.ts +61 -0
- package/src/helper/report/init.ts +18 -0
- package/src/helper/report/report.ts +72 -0
- package/src/helper/util/dataLoader.ts +42 -0
- package/src/helper/util/logger.ts +32 -0
- package/src/helper/util/session/sessionUtil.ts +839 -0
- package/src/helper/util/stepHelpers.ts +14 -0
- package/src/helper/util/test-data/dataLoader.ts +108 -0
- package/src/helper/util/test-data/dataTest.ts +191 -0
- package/src/helper/util/test-data/registerUser.json +7 -0
- package/src/helper/util/totp/totpHelper.ts +102 -0
- package/src/helper/util/utilities/cryptoUtil.ts +53 -0
- package/src/helper/util/utilities/schemaGeneratorUtil.ts +143 -0
- package/src/helper/util/utils.ts +28 -0
- package/src/helper/wrapper/PlaywrightWrappers.ts +28 -0
- package/src/helper/wrapper/assert.ts +25 -0
- package/src/index.ts +17 -0
- package/src/scripts/get-versions.ts +68 -0
- package/src/scripts/posttest.ts +32 -0
- package/src/scripts/pretest.ts +48 -0
- package/src/scripts/util.ts +406 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file apiValidationActions.ts
|
|
3
|
+
*
|
|
4
|
+
* Validation utilities for API testing across Playwright and Cucumber runners.
|
|
5
|
+
* Provides value and JSON path assertions with variable interpolation.
|
|
6
|
+
*
|
|
7
|
+
* Key Features:
|
|
8
|
+
* - Hybrid runner support with Allure-compatible steps.
|
|
9
|
+
* - Flexible path resolution for body, headers, status, and status text.
|
|
10
|
+
* - Optional partial text checks and assertion control.
|
|
11
|
+
*
|
|
12
|
+
* Authors: Renish Kozhithottathil [Lead Automation Principal, NCS]
|
|
13
|
+
* Date: 2025-07-01
|
|
14
|
+
* Version: v1.0.0
|
|
15
|
+
*
|
|
16
|
+
* Note: This file adheres to the PlayQ Enterprise Automation Standards.
|
|
17
|
+
*/
|
|
18
|
+
import * as allure from "allure-js-commons";
|
|
19
|
+
import { vars, comm } from "../../../global";
|
|
20
|
+
import { test } from "@playwright/test";
|
|
21
|
+
|
|
22
|
+
// Inline runner helpers
|
|
23
|
+
function isPlaywrightRunner() { return process.env.TEST_RUNNER === 'playwright'; }
|
|
24
|
+
function isCucumberRunner() { return process.env.TEST_RUNNER === 'cucumber'; }
|
|
25
|
+
const __allureAny_api: any = allure as any;
|
|
26
|
+
if (typeof __allureAny_api.step !== 'function') {
|
|
27
|
+
__allureAny_api.step = async (_name: string, fn: any) => await fn();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Api: Verify value -actual: {param} -expected: {param} -options: {param}
|
|
32
|
+
*
|
|
33
|
+
* Verifies that a given value matches the expected value.
|
|
34
|
+
* Throws an error if the values do not match.
|
|
35
|
+
*
|
|
36
|
+
* @param actual - The actual value to verify.
|
|
37
|
+
* @param expected - The expected value to compare against.
|
|
38
|
+
* @param message - Optional. Custom error message on failure.
|
|
39
|
+
*/
|
|
40
|
+
export async function verifyValue(actual: any, expected: string, options?: string | Record<string, any>) {
|
|
41
|
+
const resolvedActual = vars.replaceVariables(String(actual));
|
|
42
|
+
const resolvedExpected = vars.replaceVariables(expected);
|
|
43
|
+
|
|
44
|
+
const options_json =
|
|
45
|
+
typeof options === "string" ? vars.parseLooseJson(options) : options || {};
|
|
46
|
+
const {
|
|
47
|
+
assert = true,
|
|
48
|
+
partial_text = false
|
|
49
|
+
} = options_json;
|
|
50
|
+
|
|
51
|
+
// Input validation
|
|
52
|
+
if (expected === undefined || expected === null) {
|
|
53
|
+
throw new Error("Expected value must be provided for verification.");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const stepName = `Api: Verify value -actual: ${resolvedActual} -expected: ${resolvedExpected} -options: ${JSON.stringify(options_json)}`;
|
|
57
|
+
|
|
58
|
+
if (isPlaywrightRunner()) {
|
|
59
|
+
await test.step(stepName, async () => {
|
|
60
|
+
await doVerifyValue();
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
await doVerifyValue();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function doVerifyValue() {
|
|
67
|
+
// Debug log for type and value
|
|
68
|
+
console.debug(`[verifyValue] actual (type: ${typeof actual}):`, actual);
|
|
69
|
+
if (partial_text) {
|
|
70
|
+
if (!resolvedActual.includes(resolvedExpected)) {
|
|
71
|
+
console.warn(`❌ Verification failed (partial_text): expected '${resolvedActual}' to include '${resolvedExpected}'`);
|
|
72
|
+
await comm.attachLog(`❌ Verification failed (partial_text): expected '${resolvedActual}' to include '${resolvedExpected}'`, "text/plain", "Verification Details");
|
|
73
|
+
if (assert) throw new Error(`Verification failed (partial_text): expected '${resolvedActual}' to include '${resolvedExpected}'`);
|
|
74
|
+
} else {
|
|
75
|
+
console.info(`✅ Verification passed (partial_text): expected '${resolvedActual}' to include '${resolvedExpected}'`);
|
|
76
|
+
await comm.attachLog(`✅ Verification passed (partial_text): expected '${resolvedActual}' to include '${resolvedExpected}'`, "text/plain", "Verification Details");
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (resolvedActual !== resolvedExpected) {
|
|
81
|
+
console.warn(`❌ Verification failed: expected: '${resolvedExpected}', actual: '${resolvedActual}'`);
|
|
82
|
+
await comm.attachLog(`❌ Verification failed: expected: '${resolvedExpected}', actual: '${resolvedActual}'`, "text/plain", "Verification Details");
|
|
83
|
+
if (assert) throw new Error(`Verification failed: expected '${resolvedExpected}', but got '${resolvedActual}'`);
|
|
84
|
+
} else {
|
|
85
|
+
console.info(`✅ Verification passed: expected: '${resolvedExpected}', actual: '${resolvedActual}'`);
|
|
86
|
+
await comm.attachLog(`✅ Verification passed: expected: '${resolvedExpected}', actual: '${resolvedActual}'`, "text/plain", "Verification Details");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Api: Verify api path value in last response -path: {param} -expected: {param} -options: {param}
|
|
94
|
+
*
|
|
95
|
+
* Verifies that a value at a given JSON path in the last API response matches the expected value.
|
|
96
|
+
* Throws an error if the values do not match.
|
|
97
|
+
*
|
|
98
|
+
* @param path - The JSON path to extract from the last response body/header/status.
|
|
99
|
+
* @param expected - The expected value to compare against.
|
|
100
|
+
* @param options - Optional. Supports { assert: boolean, partial_text: boolean }
|
|
101
|
+
*/
|
|
102
|
+
export async function verifyPathValue(path: string, expected: string, options?: string | Record<string, any>) {
|
|
103
|
+
const resolvedExpected = vars.replaceVariables(expected);
|
|
104
|
+
const options_json =
|
|
105
|
+
typeof options === "string" ? vars.parseLooseJson(options) : options || {};
|
|
106
|
+
const {
|
|
107
|
+
assert = true,
|
|
108
|
+
partial_text = false
|
|
109
|
+
} = options_json;
|
|
110
|
+
|
|
111
|
+
// Input validation
|
|
112
|
+
if (!path || typeof path !== 'string') {
|
|
113
|
+
throw new Error("Path must be a non-empty string for verification.");
|
|
114
|
+
}
|
|
115
|
+
if (expected === undefined || expected === null) {
|
|
116
|
+
throw new Error("Expected value must be provided for verification.");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const sources = {
|
|
120
|
+
body: vars.getValue("playq.api.last.resBody"),
|
|
121
|
+
header: vars.getValue("playq.api.last.resHeader"),
|
|
122
|
+
status: vars.getValue("playq.api.last.resStatus"),
|
|
123
|
+
statusText: vars.getValue("playq.api.last.resStatusText")
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
let actual: any;
|
|
127
|
+
let allureMsg = ""
|
|
128
|
+
|
|
129
|
+
const stepName = `Api: Verify api path value in last response -path: ${path} -expected: ${resolvedExpected} -options: ${JSON.stringify(options_json)}`;
|
|
130
|
+
|
|
131
|
+
if (isPlaywrightRunner()) {
|
|
132
|
+
await test.step(stepName, async () => {
|
|
133
|
+
await doVerifyPathValue();
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
await doVerifyPathValue();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function doVerifyPathValue() {
|
|
140
|
+
actual = getPathValue(path, sources);
|
|
141
|
+
// Debug log for extracted value
|
|
142
|
+
console.debug(`[verifyPathValue] path: ${path}, extracted value (type: ${typeof actual}):`, actual, `Expected: ${resolvedExpected}`);
|
|
143
|
+
// verifyValue(actual, resolvedExpected, options);
|
|
144
|
+
if (partial_text) {
|
|
145
|
+
if (!actual || !actual.includes(resolvedExpected)) {
|
|
146
|
+
console.warn(`❌ Verification failed (partial_text): expected '${actual}' to include '${resolvedExpected}'`);
|
|
147
|
+
await comm.attachLog(`❌ Verification failed (partial_text): expected '${actual}' to include '${resolvedExpected}'`, "text/plain", "Verification Details");
|
|
148
|
+
if (assert) throw new Error(`Verification failed (partial_text): expected '${actual}' to include '${resolvedExpected}'`);
|
|
149
|
+
} else {
|
|
150
|
+
await comm.attachLog(`✅ Verification passed (partial_text): expected '${actual}' to include '${resolvedExpected}'`, "text/plain", "Verification Details");
|
|
151
|
+
console.assert(`✅ Verification passed (partial_text): expected '${actual}' to include '${resolvedExpected}'`);
|
|
152
|
+
}
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Handle null/undefined actual values
|
|
157
|
+
const actualString = (actual !== null && actual !== undefined) ? actual.toString() : String(actual);
|
|
158
|
+
|
|
159
|
+
if (actualString !== resolvedExpected) {
|
|
160
|
+
console.warn(`❌ Verification failed: expected: '${resolvedExpected}', actual: '${actual}'`);
|
|
161
|
+
await comm.attachLog(`❌ Verification failed: expected: '${resolvedExpected}', actual: '${actual}'`, "text/plain", "Verification Details");
|
|
162
|
+
if (assert) throw new Error(`Verification failed: expected '${resolvedExpected}', but got '${actual}'`);
|
|
163
|
+
} else {
|
|
164
|
+
await comm.attachLog(`✅ Verification passed: expected: '${resolvedExpected}', actual: '${actual}'`, "text/plain", "Verification Details");
|
|
165
|
+
console.assert(`✅ Verification passed: expected: '${resolvedExpected}', actual: '${actual}'`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Api: Get last response JSON path value -path: {param} -storeTo: {param}
|
|
172
|
+
*
|
|
173
|
+
* Get the value at a given JSON path from the last API response body.
|
|
174
|
+
* Example: path = "data[0].id" will return the id of the first item in data array.
|
|
175
|
+
* Supports paths like "x.data[0].id" (where "x" is treated as the root).
|
|
176
|
+
*
|
|
177
|
+
* @param path - JSON path string; supports leading "x." as body alias.
|
|
178
|
+
* @returns The extracted value or undefined if not found.
|
|
179
|
+
* @throws Error if `path` is not a non-empty string.
|
|
180
|
+
|
|
181
|
+
*/
|
|
182
|
+
export async function getLastResponseJsonPathValue(path: string) {
|
|
183
|
+
// Input validation
|
|
184
|
+
if (!path || typeof path !== 'string') {
|
|
185
|
+
throw new Error("Path must be a non-empty string for JSON path extraction.");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const stepName = `Api: Get last response JSON path value -path: ${path}`;
|
|
189
|
+
|
|
190
|
+
if (isPlaywrightRunner()) {
|
|
191
|
+
return await test.step(stepName, async () => {
|
|
192
|
+
return await doGetLastResponseJsonPathValue();
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return await doGetLastResponseJsonPathValue();
|
|
196
|
+
|
|
197
|
+
async function doGetLastResponseJsonPathValue() {
|
|
198
|
+
const resBody = vars.getValue("playq.api.last.resBody");
|
|
199
|
+
if (!resBody) return undefined;
|
|
200
|
+
|
|
201
|
+
const json = JSON.parse(resBody);
|
|
202
|
+
|
|
203
|
+
// Remove leading "x." if present
|
|
204
|
+
const normalisedPath = path.startsWith('x.') ? path.slice(2) : path;
|
|
205
|
+
|
|
206
|
+
// Simple dot/bracket notation path resolver
|
|
207
|
+
const segments = normalisedPath
|
|
208
|
+
.replace(/^\[(\d+)\]/, '$1') // handle root array [0]
|
|
209
|
+
.replace(/\[(\d+)\]/g, '.$1') // handle nested arrays
|
|
210
|
+
.split('.')
|
|
211
|
+
.filter(Boolean);
|
|
212
|
+
let result: any = json;
|
|
213
|
+
for (const seg of segments) {
|
|
214
|
+
if (result == null) return undefined;
|
|
215
|
+
result = result[seg];
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Api: Store last response JSON paths to variables -paths: {param} -vars: {param}
|
|
223
|
+
*
|
|
224
|
+
* Assign multiple values from the last API response body, header, status, or status text to variables.
|
|
225
|
+
* @param pathVarString Comma-separated JSON paths (e.g. "x.data[1].email,h.content-type,s.,t.")
|
|
226
|
+
* @param varKeyString Comma-separated variable keys (e.g. "var.email,var.contentType,var.status,var.statusText")
|
|
227
|
+
* Prefixes (case-insensitive):
|
|
228
|
+
* x. / (body). = response body (default if no prefix)
|
|
229
|
+
* h. / (header). = response header
|
|
230
|
+
* s / (status) / (statusCode) = response status code
|
|
231
|
+
* t / (statusText) = response status text
|
|
232
|
+
* Examples:
|
|
233
|
+
* "x.data[0].id" or "(body).data[0].id" → from body
|
|
234
|
+
* "h.content-type" or "(header).content-type" → from header
|
|
235
|
+
* "s" or "(status)" or "(statusCode)" → response status code
|
|
236
|
+
* "t" or "(statusText)" → response status text
|
|
237
|
+
* "data[0].id" → from body (default)
|
|
238
|
+
*
|
|
239
|
+
* @throws Error if inputs are missing or the number of paths and variable keys differ.
|
|
240
|
+
*/
|
|
241
|
+
export async function storeLastResponseJsonPathsToVariables(pathVarString: string, varKeyString: string) {
|
|
242
|
+
// Input validation
|
|
243
|
+
if (!pathVarString || !varKeyString) {
|
|
244
|
+
throw new Error("Both pathVarString and varKeyString must be provided.");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const execute = async () => {
|
|
248
|
+
const resBody = vars.getValue("playq.api.last.resBody");
|
|
249
|
+
const resHeader = vars.getValue("playq.api.last.resHeader");
|
|
250
|
+
const resStatus = vars.getValue("playq.api.last.resStatus");
|
|
251
|
+
const resStatusText = vars.getValue("playq.api.last.resStatusText");
|
|
252
|
+
if (!resBody && !resHeader && !resStatus && !resStatusText) return;
|
|
253
|
+
|
|
254
|
+
const jsonBody = resBody ? JSON.parse(resBody) : {};
|
|
255
|
+
const jsonHeader = resHeader ? JSON.parse(resHeader) : {};
|
|
256
|
+
|
|
257
|
+
const paths = pathVarString.split(',').map(s => s.trim());
|
|
258
|
+
const varKeys = varKeyString.split(',').map(s => s.trim());
|
|
259
|
+
|
|
260
|
+
if (paths.length !== varKeys.length) {
|
|
261
|
+
throw new Error("Number of paths and variable keys must match.");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (let i = 0; i < paths.length; i++) {
|
|
265
|
+
let path = paths[i];
|
|
266
|
+
const varKey = varKeys[i];
|
|
267
|
+
let result: any;
|
|
268
|
+
|
|
269
|
+
const lowerPath = path.toLowerCase();
|
|
270
|
+
|
|
271
|
+
if (lowerPath.startsWith('h.')) {
|
|
272
|
+
// Header: h.header-name
|
|
273
|
+
const headerKey = path.slice(2).toLowerCase();
|
|
274
|
+
result = Object.entries(jsonHeader).find(([k]) => k.toLowerCase() === headerKey)?.[1];
|
|
275
|
+
} else if (lowerPath.startsWith('(header).')) {
|
|
276
|
+
// Header: (header).header-name
|
|
277
|
+
const headerKey = path.slice(9).toLowerCase();
|
|
278
|
+
result = Object.entries(jsonHeader).find(([k]) => k.toLowerCase() === headerKey)?.[1];
|
|
279
|
+
} else if (lowerPath.startsWith('s') || lowerPath.startsWith('s.')) {
|
|
280
|
+
// Status: s.
|
|
281
|
+
result = resStatus;
|
|
282
|
+
} else if (lowerPath.startsWith('(status)') || lowerPath.startsWith('(statuscode)') || lowerPath.startsWith('(status).') || lowerPath.startsWith('(statuscode).')) {
|
|
283
|
+
// Status: (status).
|
|
284
|
+
result = resStatus;
|
|
285
|
+
} else if (lowerPath.startsWith('t.')) {
|
|
286
|
+
// Status Text: t.
|
|
287
|
+
result = resStatusText;
|
|
288
|
+
} else if (lowerPath.startsWith('(statustext)') || lowerPath.startsWith('(statustext).')) {
|
|
289
|
+
// Status Text: (statusText).
|
|
290
|
+
result = resStatusText;
|
|
291
|
+
} else {
|
|
292
|
+
// Body: x., (body)., or no prefix
|
|
293
|
+
let normalisedPath = path;
|
|
294
|
+
if (lowerPath.startsWith('x.')) normalisedPath = path.slice(2);
|
|
295
|
+
else if (lowerPath.startsWith('(body).')) normalisedPath = path.slice(7);
|
|
296
|
+
|
|
297
|
+
const segments = normalisedPath
|
|
298
|
+
.replace(/^\[(\d+)\]/, '$1')
|
|
299
|
+
.replace(/\[(\d+)\]/g, '.$1')
|
|
300
|
+
.split('.')
|
|
301
|
+
.filter(Boolean);
|
|
302
|
+
result = jsonBody;
|
|
303
|
+
for (const seg of segments) {
|
|
304
|
+
if (result == null) break;
|
|
305
|
+
result = result[seg];
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (result !== undefined && result !== null) {
|
|
309
|
+
vars.setValue(varKey, result.toString());
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
if (isPlaywrightRunner()) {
|
|
315
|
+
await test.step(
|
|
316
|
+
`Api: Store last response JSON paths to variables -paths: ${pathVarString} -vars: ${varKeyString}`,
|
|
317
|
+
async () => {
|
|
318
|
+
await execute();
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
} else {
|
|
322
|
+
await execute();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Internal helper used only by verifyPathValue
|
|
327
|
+
function getPathValueInternal(
|
|
328
|
+
path: string,
|
|
329
|
+
sources: { body?: string | object, header?: string | object, status?: string, statusText?: string }
|
|
330
|
+
): any {
|
|
331
|
+
const lowerPath = path.toLowerCase();
|
|
332
|
+
const jsonBody = typeof sources.body === "string" && sources.body ? JSON.parse(sources.body) : sources.body || {};
|
|
333
|
+
const jsonHeader = typeof sources.header === "string" && sources.header ? JSON.parse(sources.header) : sources.header || {};
|
|
334
|
+
|
|
335
|
+
if (lowerPath.startsWith('h.')) {
|
|
336
|
+
const headerKey = path.slice(2).toLowerCase();
|
|
337
|
+
return Object.entries(jsonHeader).find(([k]) => k.toLowerCase() === headerKey)?.[1];
|
|
338
|
+
}
|
|
339
|
+
if (lowerPath.startsWith('(header).')) {
|
|
340
|
+
const headerKey = path.slice(9).toLowerCase();
|
|
341
|
+
return Object.entries(jsonHeader).find(([k]) => k.toLowerCase() === headerKey)?.[1];
|
|
342
|
+
}
|
|
343
|
+
if (lowerPath === 's' || lowerPath === 's.' || lowerPath.startsWith('s.') || lowerPath.startsWith('(status)') || lowerPath.startsWith('(statuscode)') || lowerPath.startsWith('(status).') || lowerPath.startsWith('(statuscode).')) {
|
|
344
|
+
return sources.status;
|
|
345
|
+
}
|
|
346
|
+
if (lowerPath === 't' || lowerPath === 't.' || lowerPath.startsWith('(statustext)') || lowerPath.startsWith('(statustext).')) {
|
|
347
|
+
return sources.statusText;
|
|
348
|
+
}
|
|
349
|
+
let normalisedPath = path;
|
|
350
|
+
if (lowerPath.startsWith('x.')) normalisedPath = path.slice(2);
|
|
351
|
+
else if (lowerPath.startsWith('(body).')) normalisedPath = path.slice(7);
|
|
352
|
+
const segments = normalisedPath
|
|
353
|
+
.replace(/^\[(\d+)\]/, '$1') // handle root array [0]
|
|
354
|
+
.replace(/\[(\d+)\]/g, '.$1') // handle nested arrays
|
|
355
|
+
.split('.')
|
|
356
|
+
.filter(Boolean);
|
|
357
|
+
let result: any = jsonBody;
|
|
358
|
+
for (const seg of segments) {
|
|
359
|
+
if (result == null) return undefined;
|
|
360
|
+
result = result[seg];
|
|
361
|
+
}
|
|
362
|
+
return result;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Generic internal function to extract a value from a JSON object, header, status, or status text
|
|
367
|
+
* using a flexible path syntax.
|
|
368
|
+
*
|
|
369
|
+
* @param path - Path string with optional prefix (e.g., "x.data[0].id", "h.content-type", "s", "t")
|
|
370
|
+
* @param sources - Object containing { body, header, status, statusText }
|
|
371
|
+
* @returns The extracted value or undefined if not found.
|
|
372
|
+
*/
|
|
373
|
+
function getPathValue(
|
|
374
|
+
path: string,
|
|
375
|
+
sources: {
|
|
376
|
+
body?: string | object,
|
|
377
|
+
header?: string | object,
|
|
378
|
+
status?: string,
|
|
379
|
+
statusText?: string
|
|
380
|
+
}
|
|
381
|
+
): any {
|
|
382
|
+
const lowerPath = path.toLowerCase();
|
|
383
|
+
|
|
384
|
+
// Prepare parsed objects
|
|
385
|
+
const jsonBody = typeof sources.body === "string" && sources.body
|
|
386
|
+
? JSON.parse(sources.body)
|
|
387
|
+
: sources.body || {};
|
|
388
|
+
const jsonHeader = typeof sources.header === "string" && sources.header
|
|
389
|
+
? JSON.parse(sources.header)
|
|
390
|
+
: sources.header || {};
|
|
391
|
+
|
|
392
|
+
if (lowerPath.startsWith('h.')) {
|
|
393
|
+
// Header: h.header-name
|
|
394
|
+
const headerKey = path.slice(2).toLowerCase();
|
|
395
|
+
return Object.entries(jsonHeader).find(([k]) => k.toLowerCase() === headerKey)?.[1];
|
|
396
|
+
}
|
|
397
|
+
if (lowerPath.startsWith('(header).')) {
|
|
398
|
+
// Header: (header).header-name
|
|
399
|
+
const headerKey = path.slice(9).toLowerCase();
|
|
400
|
+
return Object.entries(jsonHeader).find(([k]) => k.toLowerCase() === headerKey)?.[1];
|
|
401
|
+
}
|
|
402
|
+
if (lowerPath === 's' || lowerPath === 's.' ||
|
|
403
|
+
lowerPath.startsWith('s.') ||
|
|
404
|
+
lowerPath.startsWith('(status)') || lowerPath.startsWith('(statuscode)') ||
|
|
405
|
+
lowerPath.startsWith('(status).') || lowerPath.startsWith('(statuscode).')) {
|
|
406
|
+
return sources.status;
|
|
407
|
+
}
|
|
408
|
+
if (lowerPath === 't' || lowerPath === 't.' ||
|
|
409
|
+
lowerPath.startsWith('(statustext)') || lowerPath.startsWith('(statustext).')) {
|
|
410
|
+
return sources.statusText;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Body: x., (body)., or no prefix
|
|
414
|
+
let normalisedPath = path;
|
|
415
|
+
if (lowerPath.startsWith('x.')) normalisedPath = path.slice(2);
|
|
416
|
+
else if (lowerPath.startsWith('(body).')) normalisedPath = path.slice(7);
|
|
417
|
+
|
|
418
|
+
const segments = normalisedPath
|
|
419
|
+
.replace(/^\[(\d+)\]/, '$1') // handle root array [0]
|
|
420
|
+
.replace(/\[(\d+)\]/g, '.$1') // handle nested arrays
|
|
421
|
+
.split('.')
|
|
422
|
+
.filter(Boolean);
|
|
423
|
+
let result: any = jsonBody;
|
|
424
|
+
for (const seg of segments) {
|
|
425
|
+
if (result == null) return undefined;
|
|
426
|
+
result = result[seg];
|
|
427
|
+
}
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Api: Get path value from last response -path: {param} -storeTo: {param}
|
|
433
|
+
*
|
|
434
|
+
* Get a value from the last API response using a flexible path.
|
|
435
|
+
* Supports body, header, status, and statusText via prefixes:
|
|
436
|
+
* - Body: default or `x.` or `(body).`
|
|
437
|
+
* - Header: `h.` or `(header).`
|
|
438
|
+
* - Status code: `s` or `(status)` or `(statusCode)`
|
|
439
|
+
* - Status text: `t` or `(statusText)`
|
|
440
|
+
*
|
|
441
|
+
* @param path - Path string (e.g., `title`, `x.data[0].id`, `h.content-type`, `s`, `t`)
|
|
442
|
+
* @param options - Optional JSON string or object (reserved)
|
|
443
|
+
* @returns Extracted value or undefined
|
|
444
|
+
* @throws Error if `path` is not a non-empty string
|
|
445
|
+
*/
|
|
446
|
+
export async function getPathValueFromLastResponse(path: string, options?: string | Record<string, any>) {
|
|
447
|
+
if (!path || typeof path !== 'string') {
|
|
448
|
+
throw new Error("Path must be a non-empty string");
|
|
449
|
+
}
|
|
450
|
+
const options_json = typeof options === 'string' ? vars.parseLooseJson(options) : options || {};
|
|
451
|
+
const sources = {
|
|
452
|
+
body: vars.getValue("playq.api.last.resBody"),
|
|
453
|
+
header: vars.getValue("playq.api.last.resHeader"),
|
|
454
|
+
status: vars.getValue("playq.api.last.resStatus"),
|
|
455
|
+
statusText: vars.getValue("playq.api.last.resStatusText")
|
|
456
|
+
};
|
|
457
|
+
const stepName = `Api: Get path value from last response -path: ${path} -options: ${JSON.stringify(options_json)}`;
|
|
458
|
+
const run = async () => getPathValue(path, sources);
|
|
459
|
+
if (isPlaywrightRunner()) {
|
|
460
|
+
return await test.step(stepName, run);
|
|
461
|
+
}
|
|
462
|
+
return run();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Friendly aliases
|
|
466
|
+
export const assertValue = verifyValue;
|
|
467
|
+
export const assertPathValue = verifyPathValue;
|
|
468
|
+
export const storePaths = storeLastResponseJsonPathsToVariables;
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Api: Extract the entire root-level array from the last API response body.
|
|
472
|
+
* @returns {any[]} The parsed array, or throws if not an array.
|
|
473
|
+
*/
|
|
474
|
+
export async function getLastResponseArray() {
|
|
475
|
+
const resBody = vars.getValue("playq.api.last.resBody");
|
|
476
|
+
if (!resBody) throw new Error("No last response body found.");
|
|
477
|
+
const arr = JSON.parse(resBody);
|
|
478
|
+
if (!Array.isArray(arr)) throw new Error("Last response body is not an array.");
|
|
479
|
+
return arr;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Api: Extract a property from each object in the root-level array from the last API response body.
|
|
484
|
+
* @param {string} property - The property name to extract from each object.
|
|
485
|
+
* @returns {any[]} Array of property values.
|
|
486
|
+
*/
|
|
487
|
+
export async function getPropertyFromLastResponseArray(property: string) {
|
|
488
|
+
const arr = await getLastResponseArray();
|
|
489
|
+
return arr.map(obj => obj && typeof obj === 'object' ? obj[property] : undefined);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Api: Generic extractor for last response body (array, object, or primitive).
|
|
494
|
+
* If a path is provided, will attempt to extract using dot/bracket notation.
|
|
495
|
+
* If no path is provided, returns the parsed body (array/object/primitive).
|
|
496
|
+
* @param {string} [path] - Optional dot/bracket path (e.g., 'data[0].id').
|
|
497
|
+
* @returns {any} Extracted value or the parsed body.
|
|
498
|
+
*/
|
|
499
|
+
export async function extractFromLastResponse(path?: string) {
|
|
500
|
+
const resBody = vars.getValue("playq.api.last.resBody");
|
|
501
|
+
if (!resBody) throw new Error("No last response body found.");
|
|
502
|
+
const json = JSON.parse(resBody);
|
|
503
|
+
if (!path) return json;
|
|
504
|
+
// Simple dot/bracket notation path resolver
|
|
505
|
+
const segments = path.replace(/\[(\w+)\]/g, '.$1').split('.').filter(Boolean);
|
|
506
|
+
let result = json;
|
|
507
|
+
for (const seg of segments) {
|
|
508
|
+
if (result == null) return undefined;
|
|
509
|
+
result = result[seg];
|
|
510
|
+
}
|
|
511
|
+
return result;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Api: Assert last response status code.
|
|
516
|
+
* @param expectedStatus - Expected HTTP status code (string or number).
|
|
517
|
+
* @param options - Optional: { assert: boolean }
|
|
518
|
+
*/
|
|
519
|
+
export async function verifyStatus(expectedStatus: string | number, options?: string | Record<string, any>) {
|
|
520
|
+
const actual = vars.getValue("playq.api.last.resStatus");
|
|
521
|
+
const expected = String(expectedStatus);
|
|
522
|
+
const options_json = typeof options === 'string' ? vars.parseLooseJson(options) : options || {};
|
|
523
|
+
const { assert = true } = options_json;
|
|
524
|
+
if (actual !== expected) {
|
|
525
|
+
await comm.attachLog(`❌ Status code mismatch: expected ${expected}, got ${actual}`, "text/plain");
|
|
526
|
+
if (assert) throw new Error(`Status code mismatch: expected ${expected}, got ${actual}`);
|
|
527
|
+
} else {
|
|
528
|
+
await comm.attachLog(`✅ Status code matched: ${expected}`, "text/plain");
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Api: Assert last response header value.
|
|
534
|
+
* @param header - Header name (case-insensitive).
|
|
535
|
+
* @param expectedValue - Expected value (string).
|
|
536
|
+
* @param options - Optional: { assert: boolean, partial_text: boolean }
|
|
537
|
+
*/
|
|
538
|
+
export async function verifyHeader(header: string, expectedValue: string, options?: string | Record<string, any>) {
|
|
539
|
+
const resHeader = vars.getValue("playq.api.last.resHeader");
|
|
540
|
+
const jsonHeader = resHeader ? JSON.parse(resHeader) : {};
|
|
541
|
+
const actual = Object.entries(jsonHeader).find(([k]) => k.toLowerCase() === header.toLowerCase())?.[1];
|
|
542
|
+
const options_json = typeof options === 'string' ? vars.parseLooseJson(options) : options || {};
|
|
543
|
+
const { assert = true, partial_text = false } = options_json;
|
|
544
|
+
if (
|
|
545
|
+
partial_text
|
|
546
|
+
? !(typeof actual === "string" && actual.includes(expectedValue))
|
|
547
|
+
: actual !== expectedValue
|
|
548
|
+
) {
|
|
549
|
+
await comm.attachLog(`❌ Header mismatch: ${header} expected ${expectedValue}, got ${actual}`, "text/plain");
|
|
550
|
+
if (assert) throw new Error(`Header mismatch: ${header} expected ${expectedValue}, got ${actual}`);
|
|
551
|
+
} else {
|
|
552
|
+
await comm.attachLog(`✅ Header matched: ${header} = ${expectedValue}`, "text/plain");
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Api: Assert last response body matches JSON schema.
|
|
558
|
+
* @param schema - JSON schema object.
|
|
559
|
+
* @param options - Optional: { assert: boolean }
|
|
560
|
+
*/
|
|
561
|
+
export async function verifySchema(schema: object, options?: string | Record<string, any>) {
|
|
562
|
+
const resBody = vars.getValue("playq.api.last.resBody");
|
|
563
|
+
const options_json = typeof options === 'string' ? vars.parseLooseJson(options) : options || {};
|
|
564
|
+
const { assert = true } = options_json;
|
|
565
|
+
if (!resBody) throw new Error("No last response body found.");
|
|
566
|
+
const Ajv = require('ajv');
|
|
567
|
+
const ajv = new Ajv();
|
|
568
|
+
const json = JSON.parse(resBody);
|
|
569
|
+
const valid = ajv.validate(schema, json);
|
|
570
|
+
if (!valid) {
|
|
571
|
+
await comm.attachLog(`❌ Schema validation failed: ${JSON.stringify(ajv.errors)}`, "application/json");
|
|
572
|
+
if (assert) throw new Error(`Schema validation failed: ${JSON.stringify(ajv.errors)}`);
|
|
573
|
+
} else {
|
|
574
|
+
await comm.attachLog(`✅ Schema validation passed.`, "text/plain");
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Api: Assert last response is error/timeout (negative test).
|
|
580
|
+
* @param expectedStatus - Expected error status code (string or number).
|
|
581
|
+
* @param options - Optional: { assert: boolean }
|
|
582
|
+
*/
|
|
583
|
+
export async function verifyError(expectedStatus: string | number, options?: string | Record<string, any>) {
|
|
584
|
+
const actual = vars.getValue("playq.api.last.resStatus");
|
|
585
|
+
const expected = String(expectedStatus);
|
|
586
|
+
const options_json = typeof options === 'string' ? vars.parseLooseJson(options) : options || {};
|
|
587
|
+
const { assert = true } = options_json;
|
|
588
|
+
if (actual !== expected) {
|
|
589
|
+
await comm.attachLog(`❌ Expected error status ${expected}, got ${actual}`, "text/plain");
|
|
590
|
+
if (assert) throw new Error(`Expected error status ${expected}, got ${actual}`);
|
|
591
|
+
} else {
|
|
592
|
+
await comm.attachLog(`✅ Error status matched: ${expected}`, "text/plain");
|
|
593
|
+
}
|
|
594
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file apiActions.ts
|
|
3
|
+
*
|
|
4
|
+
* Unified API actions bridging Playwright and Cucumber.
|
|
5
|
+
* Provides request helpers and validation utilities with reporting.
|
|
6
|
+
*
|
|
7
|
+
* Key Features:
|
|
8
|
+
* - Hybrid context: Playwright Runner (page) and Cucumber World (webFixture)
|
|
9
|
+
* - Rich options for assertions, timeouts, and attachments
|
|
10
|
+
* - Robust error handling, logging, and Allure-compatible step wrappers
|
|
11
|
+
*
|
|
12
|
+
* Authors: Renish Kozhithottathil [Lead Automation Principal, NCS]
|
|
13
|
+
* Version: v1.0.0
|
|
14
|
+
*
|
|
15
|
+
* Note: This file adheres to the PlayQ Enterprise Automation Standards.
|
|
16
|
+
*/
|
|
17
|
+
export * from './api/apiRequestActions';
|
|
18
|
+
export * from './api/apiValidationActions';
|