@zest-pw/test 1.0.0 → 1.0.1
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 +10 -17
- package/dist/fixtures/fixtures.d.ts.map +1 -1
- package/dist/fixtures/fixtures.js +1 -8
- package/dist/utils/enrich-test-results.d.ts +4 -2
- package/dist/utils/enrich-test-results.d.ts.map +1 -1
- package/dist/utils/enrich-test-results.js +17 -8
- package/dist/utils/parse-test-steps.d.ts +4 -4
- package/dist/utils/parse-test-steps.d.ts.map +1 -1
- package/dist/utils/parse-test-steps.js +5 -14
- package/dist/utils/save-json-report.d.ts.map +1 -1
- package/dist/utils/save-json-report.js +0 -6
- package/dist/utils/save-screenshots.d.ts +6 -5
- package/dist/utils/save-screenshots.d.ts.map +1 -1
- package/dist/utils/save-screenshots.js +9 -7
- package/dist/utils/take-screenshots.d.ts +10 -5
- package/dist/utils/take-screenshots.d.ts.map +1 -1
- package/dist/utils/take-screenshots.js +11 -11
- package/dist/utils/terminal-reporter.d.ts +3 -4
- package/dist/utils/terminal-reporter.d.ts.map +1 -1
- package/dist/utils/terminal-reporter.js +18 -23
- package/dist/utils/test-result-transformer.js +5 -18
- package/dist/utils/test-step-wrapper.d.ts +3 -5
- package/dist/utils/test-step-wrapper.d.ts.map +1 -1
- package/dist/utils/test-step-wrapper.js +4 -11
- package/dist/zephyr-api/get-results-from-json.d.ts +6 -0
- package/dist/zephyr-api/get-results-from-json.d.ts.map +1 -1
- package/dist/zephyr-api/get-results-from-json.js +11 -7
- package/dist/zephyr-api/update-execution-result.d.ts +4 -0
- package/dist/zephyr-api/update-execution-result.d.ts.map +1 -1
- package/dist/zephyr-api/update-execution-result.js +4 -1
- package/dist/zephyr-api/zephyr-api.d.ts +4 -3
- package/dist/zephyr-api/zephyr-api.d.ts.map +1 -1
- package/dist/zephyr-api/zephyr-api.js +6 -5
- package/package.json +53 -68
- package/scripts/install-config.js +0 -92
package/README.md
CHANGED
|
@@ -13,6 +13,11 @@ Advanced Playwright test framework with automatic screenshots, custom reporting,
|
|
|
13
13
|
|
|
14
14
|
## 🚀 Quick Start
|
|
15
15
|
|
|
16
|
+
### Requirements
|
|
17
|
+
|
|
18
|
+
- Node.js >= 18.0.0
|
|
19
|
+
- Playwright (peer dependency)
|
|
20
|
+
|
|
16
21
|
### 1. Install the Package
|
|
17
22
|
|
|
18
23
|
```bash
|
|
@@ -21,11 +26,7 @@ npm install --save-dev @zest-pw/test
|
|
|
21
26
|
|
|
22
27
|
### 2. Configuration
|
|
23
28
|
|
|
24
|
-
The configuration file `zest.config.ts` will be automatically created in your project root after installation.
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npx zest-pw-init
|
|
28
|
-
```
|
|
29
|
+
The configuration file `zest.config.ts` will be automatically created in your project root after installation.
|
|
29
30
|
|
|
30
31
|
The default configuration:
|
|
31
32
|
|
|
@@ -170,13 +171,14 @@ Test results are automatically saved to `test-results/test-results.json`:
|
|
|
170
171
|
{
|
|
171
172
|
"tests": [
|
|
172
173
|
{
|
|
174
|
+
"projectName": "chromium",
|
|
173
175
|
"testTitle": "Check the title",
|
|
174
176
|
"testCaseKey": "TC-001",
|
|
175
177
|
"steps": [
|
|
176
178
|
{
|
|
177
179
|
"stepTitle": "Go to the playwright website",
|
|
178
180
|
"actualResult": [...],
|
|
179
|
-
"statusName": "
|
|
181
|
+
"statusName": "pass"
|
|
180
182
|
}
|
|
181
183
|
]
|
|
182
184
|
}
|
|
@@ -281,9 +283,6 @@ tests/
|
|
|
281
283
|
## 💡 Commands
|
|
282
284
|
|
|
283
285
|
```bash
|
|
284
|
-
# Initialize configuration (if not created automatically)
|
|
285
|
-
npx zest-pw-init
|
|
286
|
-
|
|
287
286
|
# Run all tests
|
|
288
287
|
npx playwright test
|
|
289
288
|
|
|
@@ -349,15 +348,9 @@ export default defineZestConfig({
|
|
|
349
348
|
|
|
350
349
|
## 🐛 Troubleshooting
|
|
351
350
|
|
|
352
|
-
### Configuration file
|
|
353
|
-
|
|
354
|
-
If `zest.config.ts` wasn't created after installation, run:
|
|
355
|
-
|
|
356
|
-
```bash
|
|
357
|
-
npx zest-pw-init
|
|
358
|
-
```
|
|
351
|
+
### Configuration file setup
|
|
359
352
|
|
|
360
|
-
|
|
353
|
+
The `zest.config.ts` file is automatically created when you install the package. If it wasn't created, you can create it manually using the template from the Configuration section.
|
|
361
354
|
|
|
362
355
|
### Screenshots not appearing in report
|
|
363
356
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../fixtures/fixtures.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../fixtures/fixtures.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,IAAI,6OAQf,CAAC;AAKH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -3,22 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.expect = exports.test = void 0;
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
5
|
const test_step_wrapper_1 = require("../utils/test-step-wrapper");
|
|
6
|
-
// Глобальна змінна для зберігання поточного контексту тесту
|
|
7
6
|
let currentTestContext = null;
|
|
8
|
-
// Розширюємо базовий test з кастомним fixture для зберігання контексту
|
|
9
7
|
exports.test = test_1.test.extend({
|
|
10
8
|
page: async ({ page }, use, testInfo) => {
|
|
11
|
-
// Зберігаємо контекст перед використанням page
|
|
12
9
|
currentTestContext = { testInfo, page };
|
|
13
10
|
await use(page);
|
|
14
|
-
// Очищаємо контекст після використання
|
|
15
11
|
currentTestContext = null;
|
|
16
12
|
},
|
|
17
13
|
});
|
|
18
|
-
//
|
|
14
|
+
// Apply wrapper to test.step for automatic screenshots
|
|
19
15
|
(0, test_step_wrapper_1.wrapTestStepWithScreenshots)(exports.test, () => currentTestContext);
|
|
20
|
-
// Примітка: Контекст тесту (page та testInfo) зберігається через custom fixture
|
|
21
|
-
// Скріншоти створюються автоматично після кожного test.step()
|
|
22
|
-
// Виведення результатів тестів відбувається через кастомний Reporter (custom-reporter.ts)
|
|
23
16
|
var test_2 = require("@playwright/test");
|
|
24
17
|
Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return test_2.expect; } });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Enriches test results with planned steps from files
|
|
3
|
+
* Adds steps that were not executed (e.g., after test failure)
|
|
4
|
+
* @param results - Test results object containing tests array
|
|
5
|
+
* @returns Enriched test results with planned steps included
|
|
4
6
|
*/
|
|
5
7
|
export declare function enrichTestResultsWithPlannedSteps(results: any): any;
|
|
6
8
|
//# sourceMappingURL=enrich-test-results.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrich-test-results.d.ts","sourceRoot":"","sources":["../../utils/enrich-test-results.ts"],"names":[],"mappings":"AAGA
|
|
1
|
+
{"version":3,"file":"enrich-test-results.d.ts","sourceRoot":"","sources":["../../utils/enrich-test-results.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,CASnE"}
|
|
@@ -37,8 +37,10 @@ exports.enrichTestResultsWithPlannedSteps = enrichTestResultsWithPlannedSteps;
|
|
|
37
37
|
const path = __importStar(require("path"));
|
|
38
38
|
const parse_test_steps_1 = require("./parse-test-steps");
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* Enriches test results with planned steps from files
|
|
41
|
+
* Adds steps that were not executed (e.g., after test failure)
|
|
42
|
+
* @param results - Test results object containing tests array
|
|
43
|
+
* @returns Enriched test results with planned steps included
|
|
42
44
|
*/
|
|
43
45
|
function enrichTestResultsWithPlannedSteps(results) {
|
|
44
46
|
if (!results.tests || !Array.isArray(results.tests)) {
|
|
@@ -50,13 +52,14 @@ function enrichTestResultsWithPlannedSteps(results) {
|
|
|
50
52
|
};
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
|
-
*
|
|
55
|
+
* Enriches a single test with planned steps
|
|
56
|
+
* @param test - Test object containing steps and file path
|
|
57
|
+
* @returns Test object with enriched steps
|
|
54
58
|
*/
|
|
55
59
|
function enrichTestWithPlannedSteps(test) {
|
|
56
60
|
const userSteps = filterUserSteps(test.steps || []);
|
|
57
61
|
const plannedSteps = getPlannedSteps(test);
|
|
58
62
|
const allSteps = combineSteps(userSteps, plannedSteps);
|
|
59
|
-
// Видаляємо _fullPath перед поверненням (використовувався тільки для getPlannedSteps)
|
|
60
63
|
const { _fullPath, ...testWithoutFullPath } = test;
|
|
61
64
|
return {
|
|
62
65
|
...testWithoutFullPath,
|
|
@@ -64,7 +67,9 @@ function enrichTestWithPlannedSteps(test) {
|
|
|
64
67
|
};
|
|
65
68
|
}
|
|
66
69
|
/**
|
|
67
|
-
*
|
|
70
|
+
* Filters only user-defined steps (hides system hooks)
|
|
71
|
+
* @param steps - Array of test steps
|
|
72
|
+
* @returns Filtered array containing only user steps
|
|
68
73
|
*/
|
|
69
74
|
function filterUserSteps(steps) {
|
|
70
75
|
return steps.filter((step) => {
|
|
@@ -83,10 +88,11 @@ function filterUserSteps(steps) {
|
|
|
83
88
|
});
|
|
84
89
|
}
|
|
85
90
|
/**
|
|
86
|
-
*
|
|
91
|
+
* Gets planned steps from test file
|
|
92
|
+
* @param test - Test object containing _fullPath property
|
|
93
|
+
* @returns Array of planned step titles
|
|
87
94
|
*/
|
|
88
95
|
function getPlannedSteps(test) {
|
|
89
|
-
// Використовуємо _fullPath який створюється в transformTestCase
|
|
90
96
|
const fullPath = test._fullPath;
|
|
91
97
|
if (!fullPath) {
|
|
92
98
|
return [];
|
|
@@ -97,7 +103,10 @@ function getPlannedSteps(test) {
|
|
|
97
103
|
return (0, parse_test_steps_1.parsePlannedStepsFromFile)(testFilePath, test.testTitle);
|
|
98
104
|
}
|
|
99
105
|
/**
|
|
100
|
-
*
|
|
106
|
+
* Combines executed and unexecuted steps
|
|
107
|
+
* @param executedSteps - Array of executed step objects
|
|
108
|
+
* @param plannedSteps - Array of planned step titles
|
|
109
|
+
* @returns Combined array with executed steps and unexecuted steps marked as 'In Progress'
|
|
101
110
|
*/
|
|
102
111
|
function combineSteps(executedSteps, plannedSteps) {
|
|
103
112
|
const executedStepTitles = executedSteps.map((step) => step.stepTitle);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param filePath -
|
|
4
|
-
* @param testTitle -
|
|
5
|
-
* @returns
|
|
2
|
+
* Parses planned test steps from file
|
|
3
|
+
* @param filePath - Path to test file
|
|
4
|
+
* @param testTitle - Test title
|
|
5
|
+
* @returns Array of planned step titles
|
|
6
6
|
*/
|
|
7
7
|
export declare function parsePlannedStepsFromFile(filePath: string, testTitle: string): string[];
|
|
8
8
|
//# sourceMappingURL=parse-test-steps.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-test-steps.d.ts","sourceRoot":"","sources":["../../utils/parse-test-steps.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"parse-test-steps.d.ts","sourceRoot":"","sources":["../../utils/parse-test-steps.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAmEvF"}
|
|
@@ -36,15 +36,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.parsePlannedStepsFromFile = parsePlannedStepsFromFile;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
40
|
-
* @param filePath -
|
|
41
|
-
* @param testTitle -
|
|
42
|
-
* @returns
|
|
39
|
+
* Parses planned test steps from file
|
|
40
|
+
* @param filePath - Path to test file
|
|
41
|
+
* @param testTitle - Test title
|
|
42
|
+
* @returns Array of planned step titles
|
|
43
43
|
*/
|
|
44
44
|
function parsePlannedStepsFromFile(filePath, testTitle) {
|
|
45
45
|
const steps = [];
|
|
46
46
|
try {
|
|
47
|
-
// Перевіряємо, чи файл існує
|
|
48
47
|
if (!fs.existsSync(filePath)) {
|
|
49
48
|
return steps;
|
|
50
49
|
}
|
|
@@ -54,16 +53,13 @@ function parsePlannedStepsFromFile(filePath, testTitle) {
|
|
|
54
53
|
let insideTargetTest = false;
|
|
55
54
|
let braceCount = 0;
|
|
56
55
|
let testStartLine = -1;
|
|
57
|
-
// Регулярні вирази для пошуку тестів та кроків
|
|
58
56
|
const testTitleRegex = /\btest(\.only|\.skip|\.fixme)?\s*\(\s*(["'`])([^"'`]+)\2\s*,/;
|
|
59
57
|
const stepRegex = /\b(?:test|await\s+test)\.step\s*\(\s*(["'`])([^"'`]+)\1\s*,/;
|
|
60
58
|
for (let i = 0; i < lines.length; i++) {
|
|
61
59
|
const line = lines[i];
|
|
62
|
-
// Перевіряємо, чи це початок нового тесту
|
|
63
60
|
const testMatch = line.match(testTitleRegex);
|
|
64
61
|
if (testMatch) {
|
|
65
62
|
const foundTitle = testMatch[3];
|
|
66
|
-
// Якщо ми були всередині іншого тесту, скидаємо стан
|
|
67
63
|
if (insideTargetTest && foundTitle !== testTitle) {
|
|
68
64
|
insideTargetTest = false;
|
|
69
65
|
braceCount = 0;
|
|
@@ -74,7 +70,6 @@ function parsePlannedStepsFromFile(filePath, testTitle) {
|
|
|
74
70
|
steps.length = 0;
|
|
75
71
|
testStartLine = i;
|
|
76
72
|
braceCount = 0;
|
|
77
|
-
// Починаємо рахувати відкриваючі дужки
|
|
78
73
|
const openBraces = (line.match(/\{/g) || []).length;
|
|
79
74
|
const closeBraces = (line.match(/\}/g) || []).length;
|
|
80
75
|
braceCount += openBraces - closeBraces;
|
|
@@ -85,13 +80,10 @@ function parsePlannedStepsFromFile(filePath, testTitle) {
|
|
|
85
80
|
continue;
|
|
86
81
|
}
|
|
87
82
|
}
|
|
88
|
-
// Якщо ми знаходимось всередині потрібного тесту, шукаємо кроки
|
|
89
83
|
if (insideTargetTest && currentTestTitle === testTitle) {
|
|
90
|
-
// Рахуємо дужки для визначення меж тесту
|
|
91
84
|
const openBraces = (line.match(/\{/g) || []).length;
|
|
92
85
|
const closeBraces = (line.match(/\}/g) || []).length;
|
|
93
86
|
braceCount += openBraces - closeBraces;
|
|
94
|
-
// Якщо дужки закрилися, тест закінчився
|
|
95
87
|
if (braceCount <= 0 && i > testStartLine) {
|
|
96
88
|
break;
|
|
97
89
|
}
|
|
@@ -103,8 +95,7 @@ function parsePlannedStepsFromFile(filePath, testTitle) {
|
|
|
103
95
|
}
|
|
104
96
|
}
|
|
105
97
|
catch (error) {
|
|
106
|
-
|
|
107
|
-
console.error(`Помилка парсингу файлу ${filePath}:`, error);
|
|
98
|
+
console.error(`Error parsing file ${filePath}:`, error);
|
|
108
99
|
}
|
|
109
100
|
return steps;
|
|
110
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"save-json-report.d.ts","sourceRoot":"","sources":["../../utils/save-json-report.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,GAAE,MAAuB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"save-json-report.d.ts","sourceRoot":"","sources":["../../utils/save-json-report.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,GAAE,MAAuB,GAAG,MAAM,CAiB7F"}
|
|
@@ -53,18 +53,12 @@ const path = __importStar(require("path"));
|
|
|
53
53
|
*/
|
|
54
54
|
function saveTestResultsToJson(result, outputDir = 'test-results') {
|
|
55
55
|
try {
|
|
56
|
-
// Build the full path to the results directory
|
|
57
56
|
const resultsPath = path.join(process.cwd(), outputDir);
|
|
58
|
-
// Create directory if it doesn't exist (recursive: true creates parent dirs too)
|
|
59
57
|
if (!fs.existsSync(resultsPath)) {
|
|
60
58
|
fs.mkdirSync(resultsPath, { recursive: true });
|
|
61
59
|
}
|
|
62
|
-
// Define the output filename
|
|
63
60
|
const filename = `test-results.json`;
|
|
64
|
-
// Build the full file path
|
|
65
61
|
const filepath = path.join(resultsPath, filename);
|
|
66
|
-
// Convert object to JSON string with pretty formatting (2 space indentation)
|
|
67
|
-
// and save to file with UTF-8 encoding
|
|
68
62
|
fs.writeFileSync(filepath, JSON.stringify(result, null, 2), 'utf-8');
|
|
69
63
|
return filepath;
|
|
70
64
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @param base64String - Base64
|
|
4
|
-
* @param filename -
|
|
5
|
-
* @param outputDir -
|
|
6
|
-
* @param testTitle -
|
|
2
|
+
* Saves screenshot from base64 string to file
|
|
3
|
+
* @param base64String - Base64 encoded screenshot string
|
|
4
|
+
* @param filename - File name for the screenshot
|
|
5
|
+
* @param outputDir - Base output directory (default 'screenshots')
|
|
6
|
+
* @param testTitle - Optional test title, creates subdirectory for organization
|
|
7
|
+
* @returns Full path to the saved screenshot file
|
|
7
8
|
*/
|
|
8
9
|
export declare function saveBase64Screenshot(base64String: string, filename: string, outputDir?: string, testTitle?: string): string;
|
|
9
10
|
//# sourceMappingURL=save-screenshots.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"save-screenshots.d.ts","sourceRoot":"","sources":["../../utils/save-screenshots.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"save-screenshots.d.ts","sourceRoot":"","sources":["../../utils/save-screenshots.ts"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAsB,EACjC,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAiBR"}
|
|
@@ -37,21 +37,23 @@ exports.saveBase64Screenshot = saveBase64Screenshot;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* Decodes base64 string to Buffer
|
|
41
|
+
* @param base64String - Base64 encoded string
|
|
42
|
+
* @returns Buffer containing decoded data
|
|
41
43
|
*/
|
|
42
44
|
function decodeBase64ToBuffer(base64String) {
|
|
43
45
|
return Buffer.from(base64String, 'base64');
|
|
44
46
|
}
|
|
45
47
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @param base64String - Base64
|
|
48
|
-
* @param filename -
|
|
49
|
-
* @param outputDir -
|
|
50
|
-
* @param testTitle -
|
|
48
|
+
* Saves screenshot from base64 string to file
|
|
49
|
+
* @param base64String - Base64 encoded screenshot string
|
|
50
|
+
* @param filename - File name for the screenshot
|
|
51
|
+
* @param outputDir - Base output directory (default 'screenshots')
|
|
52
|
+
* @param testTitle - Optional test title, creates subdirectory for organization
|
|
53
|
+
* @returns Full path to the saved screenshot file
|
|
51
54
|
*/
|
|
52
55
|
function saveBase64Screenshot(base64String, filename, outputDir = 'screenshots', testTitle) {
|
|
53
56
|
let screenshotsPath = path.join(process.cwd(), outputDir);
|
|
54
|
-
// Якщо вказано назву тесту, створюємо окрему підпапку
|
|
55
57
|
if (testTitle) {
|
|
56
58
|
const safeTestTitle = testTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
57
59
|
screenshotsPath = path.join(screenshotsPath, safeTestTitle);
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Utility for creating screenshots after test steps
|
|
3
3
|
*/
|
|
4
4
|
import type { Page, TestInfo } from "@playwright/test";
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Takes a screenshot after each test step (always, regardless of result)
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
8
|
+
* To save screenshots to disk (from base64 results):
|
|
9
|
+
* 1. Add to .env file: SAVE_SCREENSHOTS=true
|
|
10
|
+
* 2. Or pass via command: SAVE_SCREENSHOTS=true npx playwright test
|
|
11
|
+
*
|
|
12
|
+
* @param page - Playwright Page object
|
|
13
|
+
* @param stepInfo - Step information object
|
|
14
|
+
* @param testInfo - Playwright TestInfo object for attaching screenshots
|
|
15
|
+
* @param stepTitle - Optional step title for attachment name
|
|
11
16
|
*/
|
|
12
17
|
export declare function takeScreenshotAfterStep(page: Page, stepInfo: any, testInfo: TestInfo, stepTitle?: string): Promise<void>;
|
|
13
18
|
//# sourceMappingURL=take-screenshots.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"take-screenshots.d.ts","sourceRoot":"","sources":["../../utils/take-screenshots.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD
|
|
1
|
+
{"version":3,"file":"take-screenshots.d.ts","sourceRoot":"","sources":["../../utils/take-screenshots.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;;;;;;;;GAWG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,GAAG,EACb,QAAQ,EAAE,QAAQ,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAef"}
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Utility for creating screenshots after test steps
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.takeScreenshotAfterStep = takeScreenshotAfterStep;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Takes a screenshot after each test step (always, regardless of result)
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* 1.
|
|
12
|
-
* 2.
|
|
10
|
+
* To save screenshots to disk (from base64 results):
|
|
11
|
+
* 1. Add to .env file: SAVE_SCREENSHOTS=true
|
|
12
|
+
* 2. Or pass via command: SAVE_SCREENSHOTS=true npx playwright test
|
|
13
|
+
*
|
|
14
|
+
* @param page - Playwright Page object
|
|
15
|
+
* @param stepInfo - Step information object
|
|
16
|
+
* @param testInfo - Playwright TestInfo object for attaching screenshots
|
|
17
|
+
* @param stepTitle - Optional step title for attachment name
|
|
13
18
|
*/
|
|
14
19
|
async function takeScreenshotAfterStep(page, stepInfo, testInfo, stepTitle) {
|
|
15
20
|
try {
|
|
16
21
|
if (page && testInfo) {
|
|
17
|
-
// Робимо скріншот без збереження на диск (тільки в буфер)
|
|
18
22
|
const screenshotBuffer = await page.screenshot({
|
|
19
23
|
fullPage: true
|
|
20
24
|
});
|
|
21
|
-
// Додаємо скріншот як attachment через testInfo
|
|
22
|
-
// Attachment автоматично прикріплюється до поточного кроку як substep
|
|
23
25
|
await testInfo.attach(stepTitle || stepInfo?.title || 'screenshot', {
|
|
24
26
|
body: screenshotBuffer,
|
|
25
27
|
contentType: 'image/png',
|
|
26
28
|
});
|
|
27
|
-
// Примітка: Збереження на диск відбувається в terminal-reporter.ts (якщо PRINT_TEST_RESULTS=true)
|
|
28
|
-
// або можна зберегти з JSON звіту вручну
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
catch (error) {
|
|
32
|
-
console.error('
|
|
32
|
+
console.error('Error taking step screenshot:', error);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Очікує що result вже збагачений запланованими кроками через enrichTestResultsWithPlannedSteps
|
|
2
|
+
* Formats and prints test results after completion
|
|
3
|
+
* Expects result to be enriched with planned steps via enrichTestResultsWithPlannedSteps
|
|
4
|
+
* @param result - Test results object containing tests array with steps information
|
|
6
5
|
*/
|
|
7
6
|
export declare function printTestResults(result: any): void;
|
|
8
7
|
//# sourceMappingURL=terminal-reporter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-reporter.d.ts","sourceRoot":"","sources":["../../utils/terminal-reporter.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"terminal-reporter.d.ts","sourceRoot":"","sources":["../../utils/terminal-reporter.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAoBlD"}
|
|
@@ -38,38 +38,38 @@ const path = __importStar(require("path"));
|
|
|
38
38
|
const save_screenshots_1 = require("./save-screenshots");
|
|
39
39
|
const config_1 = require("../config");
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* Очікує що result вже збагачений запланованими кроками через enrichTestResultsWithPlannedSteps
|
|
41
|
+
* Formats and prints test results after completion
|
|
42
|
+
* Expects result to be enriched with planned steps via enrichTestResultsWithPlannedSteps
|
|
43
|
+
* @param result - Test results object containing tests array with steps information
|
|
45
44
|
*/
|
|
46
45
|
function printTestResults(result) {
|
|
47
46
|
if (!result.tests || !Array.isArray(result.tests)) {
|
|
48
47
|
return;
|
|
49
48
|
}
|
|
50
|
-
console.log('\n=== Деталі по тестах та їх кроках ===');
|
|
51
49
|
result.tests.forEach((test) => {
|
|
52
50
|
printTestInfo(test);
|
|
53
|
-
// test.steps вже містить всі кроки (виконані + заплановані) після enrichTestResultsWithPlannedSteps
|
|
54
51
|
const allSteps = test.steps || [];
|
|
55
52
|
const executedSteps = allSteps.filter((step) => step.statusName !== 'In Progress');
|
|
56
|
-
// Створюємо outputDir точно як Playwright: test-results/{filename}-{test-title}-{project}
|
|
57
|
-
// test.testCaseKey тепер без розширення (наприклад "TC-002")
|
|
58
53
|
const testFileName = test.testCaseKey || 'test';
|
|
59
54
|
const sanitizedTitle = test.testTitle.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
60
|
-
const
|
|
55
|
+
const projectName = test.projectName || 'chromium';
|
|
56
|
+
const outputDir = path.join('test-results', `${testFileName}-${sanitizedTitle}-${projectName}`);
|
|
61
57
|
printTestSteps(executedSteps.length, allSteps, test.testTitle, outputDir);
|
|
62
58
|
});
|
|
63
|
-
console.log('\n=== Фінальне завершення ===');
|
|
64
59
|
}
|
|
65
60
|
/**
|
|
66
|
-
*
|
|
61
|
+
* Prints general test information
|
|
62
|
+
* @param test - Test object containing testCaseKey and testTitle
|
|
67
63
|
*/
|
|
68
64
|
function printTestInfo(test) {
|
|
69
65
|
console.log(`\n${test.testCaseKey}: ${test.testTitle}`);
|
|
70
66
|
}
|
|
71
67
|
/**
|
|
72
|
-
*
|
|
68
|
+
* Prints test step information
|
|
69
|
+
* @param executedCount - Number of executed steps
|
|
70
|
+
* @param allSteps - Array of all steps (executed and planned)
|
|
71
|
+
* @param testTitle - Title of the test
|
|
72
|
+
* @param outputDir - Optional output directory path for saving screenshots
|
|
73
73
|
*/
|
|
74
74
|
function printTestSteps(executedCount, allSteps, testTitle, outputDir) {
|
|
75
75
|
if (allSteps.length === 0) {
|
|
@@ -79,9 +79,8 @@ function printTestSteps(executedCount, allSteps, testTitle, outputDir) {
|
|
|
79
79
|
const totalCount = allSteps.length;
|
|
80
80
|
console.log(` Steps (${executedCount}/${totalCount}):`);
|
|
81
81
|
allSteps.forEach((step, stepIndex) => {
|
|
82
|
-
const statusEmoji = step.statusName === '
|
|
82
|
+
const statusEmoji = step.statusName === 'pass' ? 'passed - ✅' : step.statusName === 'failed' ? 'fail - ❌' : step.statusName === 'In Progress' ? 'skipped - ⏭️' : '⏱️';
|
|
83
83
|
console.log(` ${stepIndex + 1}. ${step.stepTitle}`);
|
|
84
|
-
// Спочатку показуємо помилку, якщо є
|
|
85
84
|
if (step.error) {
|
|
86
85
|
console.log(` ❌ Error: ${step.error.message}`);
|
|
87
86
|
if (step.error.stack) {
|
|
@@ -91,12 +90,15 @@ function printTestSteps(executedCount, allSteps, testTitle, outputDir) {
|
|
|
91
90
|
}
|
|
92
91
|
printStepAttachments(step, testTitle, outputDir, stepIndex + 1);
|
|
93
92
|
console.log(` Status: ${statusEmoji}`);
|
|
94
|
-
// Додаємо порожній рядок після кожного кроку для кращої читабельності
|
|
95
93
|
console.log('');
|
|
96
94
|
});
|
|
97
95
|
}
|
|
98
96
|
/**
|
|
99
|
-
*
|
|
97
|
+
* Prints step attachments (screenshots, etc.)
|
|
98
|
+
* @param step - Step object containing actualResult with attachments
|
|
99
|
+
* @param testTitle - Title of the test
|
|
100
|
+
* @param outputDir - Optional output directory path for saving screenshots
|
|
101
|
+
* @param _stepNumber - Step number (unused, kept for compatibility)
|
|
100
102
|
*/
|
|
101
103
|
function printStepAttachments(step, testTitle, outputDir, _stepNumber) {
|
|
102
104
|
if (!step.actualResult || step.actualResult.length === 0) {
|
|
@@ -106,27 +108,20 @@ function printStepAttachments(step, testTitle, outputDir, _stepNumber) {
|
|
|
106
108
|
step.actualResult.forEach((att) => {
|
|
107
109
|
const isErrorScreenshot = att.fileName?.includes('ERROR');
|
|
108
110
|
const emoji = isErrorScreenshot ? '💥' : att.image === 'image/png' ? '📸' : '📄';
|
|
109
|
-
// Для консолі виводимо "Decode: Base64"
|
|
110
111
|
const displayName = att.image === 'image/png' ? 'Decode: Base64' : att.fileName;
|
|
111
112
|
console.log(` ${emoji} ${displayName}`);
|
|
112
113
|
if (att.body && att.image === 'text/plain') {
|
|
113
|
-
// Для текстових actualResult виводимо повний текст
|
|
114
114
|
console.log(` ${att.body}`);
|
|
115
115
|
}
|
|
116
|
-
// Зберігаємо скріншот на диск, якщо увімкнено в конфігурації або через змінну оточення
|
|
117
116
|
const config = (0, config_1.getZestConfig)();
|
|
118
117
|
const shouldSaveScreenshots = config.screenshots.saveToDisk || process.env.SAVE_SCREENSHOTS === 'true';
|
|
119
118
|
if (att.body && shouldSaveScreenshots && att.image === 'image/png') {
|
|
120
119
|
try {
|
|
121
|
-
// Використовуємо fileName з actualResult
|
|
122
120
|
const filename = att.fileName;
|
|
123
|
-
// Використовуємо outputDir від Playwright або fallback на screenshots/
|
|
124
121
|
if (outputDir) {
|
|
125
|
-
// Зберігаємо в папку тесту, яку створив Playwright
|
|
126
122
|
(0, save_screenshots_1.saveBase64Screenshot)(att.body, filename, outputDir);
|
|
127
123
|
}
|
|
128
124
|
else {
|
|
129
|
-
// Fallback: зберігаємо в screenshots/ з підпапкою тесту
|
|
130
125
|
(0, save_screenshots_1.saveBase64Screenshot)(att.body, filename, 'screenshots', testTitle);
|
|
131
126
|
}
|
|
132
127
|
console.log(` 💾 File saved: locally`);
|
|
@@ -22,26 +22,13 @@ function transformTestResults(_fullResult, testResults) {
|
|
|
22
22
|
*/
|
|
23
23
|
function transformTestCase(test, result) {
|
|
24
24
|
return {
|
|
25
|
+
projectName: test.parent.parent.title || 'chromium',
|
|
25
26
|
testTitle: test.title,
|
|
26
|
-
testCaseKey:
|
|
27
|
+
testCaseKey: test.parent.title.split('.')[0],
|
|
27
28
|
_fullPath: test.location?.file, // Used in enrich-test-results.ts to find planned test steps
|
|
28
29
|
steps: result.steps?.map(step => transformStep(step)) || []
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Transforms test location information - returns file name as testCaseKey
|
|
33
|
-
*
|
|
34
|
-
* @param location - Location object containing file path information
|
|
35
|
-
* @returns Test case key (file name without .spec.ts extension) or undefined
|
|
36
|
-
*/
|
|
37
|
-
function transformLocation(location) {
|
|
38
|
-
if (!location || !location.file) {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
// Extract only the file name from the full path and remove .spec.ts extension
|
|
42
|
-
const fileName = location.file.split('/').pop();
|
|
43
|
-
return fileName?.replace('.spec.ts', '');
|
|
44
|
-
}
|
|
45
32
|
/**
|
|
46
33
|
* Transforms error information
|
|
47
34
|
*
|
|
@@ -86,13 +73,13 @@ function transformStep(step) {
|
|
|
86
73
|
* Determines step status
|
|
87
74
|
*
|
|
88
75
|
* @param step - Step object from test execution
|
|
89
|
-
* @returns Step status ('
|
|
76
|
+
* @returns Step status ('fail' if has error, otherwise 'pass')
|
|
90
77
|
*/
|
|
91
78
|
function determineStepStatus(step) {
|
|
92
79
|
if (step.error) {
|
|
93
|
-
return '
|
|
80
|
+
return 'fail';
|
|
94
81
|
}
|
|
95
|
-
return
|
|
82
|
+
return 'pass';
|
|
96
83
|
}
|
|
97
84
|
/**
|
|
98
85
|
* Transforms attachment (screenshot, video, etc.)
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { TestInfo, TestType, Page } from '@playwright/test';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @param test - Об'єкт test з Playwright
|
|
7
|
-
* @param getCurrentContext - Функція для отримання поточного контексту тесту
|
|
3
|
+
* Wraps test.step to automatically create screenshots after each test step
|
|
4
|
+
* @param test - Playwright test object
|
|
5
|
+
* @param getCurrentContext - Function to get current test context (testInfo and page)
|
|
8
6
|
*/
|
|
9
7
|
export declare function wrapTestStepWithScreenshots(test: TestType<any, any>, getCurrentContext: () => {
|
|
10
8
|
testInfo: TestInfo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-step-wrapper.d.ts","sourceRoot":"","sources":["../../utils/test-step-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGjE
|
|
1
|
+
{"version":3,"file":"test-step-wrapper.d.ts","sourceRoot":"","sources":["../../utils/test-step-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGjE;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EACxB,iBAAiB,EAAE,MAAM;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG,IAAI,GACjE,IAAI,CAsCN"}
|
|
@@ -3,11 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.wrapTestStepWithScreenshots = wrapTestStepWithScreenshots;
|
|
4
4
|
const take_screenshots_1 = require("./take-screenshots");
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @param test - Об'єкт test з Playwright
|
|
10
|
-
* @param getCurrentContext - Функція для отримання поточного контексту тесту
|
|
6
|
+
* Wraps test.step to automatically create screenshots after each test step
|
|
7
|
+
* @param test - Playwright test object
|
|
8
|
+
* @param getCurrentContext - Function to get current test context (testInfo and page)
|
|
11
9
|
*/
|
|
12
10
|
function wrapTestStepWithScreenshots(test, getCurrentContext) {
|
|
13
11
|
const originalTestStep = test.step.bind(test);
|
|
@@ -15,9 +13,7 @@ function wrapTestStepWithScreenshots(test, getCurrentContext) {
|
|
|
15
13
|
const stepWrapper = async function (title, body, options) {
|
|
16
14
|
return originalTestStep(title, async (stepInfo) => {
|
|
17
15
|
try {
|
|
18
|
-
// Виконуємо крок
|
|
19
16
|
const result = await body(stepInfo);
|
|
20
|
-
// Робимо скріншот після успішного виконання кроку
|
|
21
17
|
const context = getCurrentContext();
|
|
22
18
|
if (context?.page && context?.testInfo) {
|
|
23
19
|
await (0, take_screenshots_1.takeScreenshotAfterStep)(context.page, stepInfo, context.testInfo, title);
|
|
@@ -25,24 +21,21 @@ function wrapTestStepWithScreenshots(test, getCurrentContext) {
|
|
|
25
21
|
return result;
|
|
26
22
|
}
|
|
27
23
|
catch (error) {
|
|
28
|
-
// Якщо крок падає, все одно робимо скріншот
|
|
29
24
|
const context = getCurrentContext();
|
|
30
25
|
if (context?.page && context?.testInfo) {
|
|
31
26
|
try {
|
|
32
27
|
await (0, take_screenshots_1.takeScreenshotAfterStep)(context.page, { ...stepInfo, error }, context.testInfo, title);
|
|
33
28
|
}
|
|
34
29
|
catch (screenshotError) {
|
|
35
|
-
console.error('
|
|
30
|
+
console.error('Error taking screenshot after step error:', screenshotError);
|
|
36
31
|
}
|
|
37
32
|
}
|
|
38
33
|
throw error;
|
|
39
34
|
}
|
|
40
35
|
}, options);
|
|
41
36
|
};
|
|
42
|
-
// Додаємо метод skip, якщо він існує
|
|
43
37
|
if (originalTestStepSkip) {
|
|
44
38
|
stepWrapper.skip = originalTestStepSkip;
|
|
45
39
|
}
|
|
46
|
-
// Замінюємо оригінальний test.step на обгортку
|
|
47
40
|
test.step = stepWrapper;
|
|
48
41
|
}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets test results from JSON file and processes them for Zephyr
|
|
3
|
+
* Excludes testTitle and testCaseKey from each test, stepTitle from each step,
|
|
4
|
+
* and transforms tests array into an object with testCaseKey as keys
|
|
5
|
+
* @returns Processed test results object with testCaseKey as keys, or null if no results found
|
|
6
|
+
*/
|
|
1
7
|
export declare function getResultsFromJson(): Promise<any>;
|
|
2
8
|
//# sourceMappingURL=get-results-from-json.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-results-from-json.d.ts","sourceRoot":"","sources":["../../zephyr-api/get-results-from-json.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"get-results-from-json.d.ts","sourceRoot":"","sources":["../../zephyr-api/get-results-from-json.ts"],"names":[],"mappings":"AAqBA;;;;;GAKG;AACH,wBAAsB,kBAAkB,iBAoBvC"}
|
|
@@ -36,20 +36,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.getResultsFromJson = getResultsFromJson;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Helper function to read test results from JSON file
|
|
41
|
+
* @returns Test results object or null if file doesn't exist
|
|
42
|
+
*/
|
|
40
43
|
function readTestResults() {
|
|
41
|
-
// Шлях до файлу з результатами тестів
|
|
42
44
|
const testResultsPath = path.join(process.cwd(), 'test-results', 'test-results.json');
|
|
43
|
-
// Перевіряємо, чи існує файл
|
|
44
45
|
if (!fs.existsSync(testResultsPath)) {
|
|
45
46
|
console.error('Test results file not found:', testResultsPath);
|
|
46
47
|
return null;
|
|
47
48
|
}
|
|
48
|
-
// Читаємо та парсимо JSON файл
|
|
49
49
|
const testResultsContent = fs.readFileSync(testResultsPath, 'utf-8');
|
|
50
50
|
const testResults = JSON.parse(testResultsContent);
|
|
51
51
|
return testResults;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Gets test results from JSON file and processes them for Zephyr
|
|
55
|
+
* Excludes testTitle and testCaseKey from each test, stepTitle from each step,
|
|
56
|
+
* and transforms tests array into an object with testCaseKey as keys
|
|
57
|
+
* @returns Processed test results object with testCaseKey as keys, or null if no results found
|
|
58
|
+
*/
|
|
53
59
|
async function getResultsFromJson() {
|
|
54
60
|
console.log('Getting results for Zephyr...');
|
|
55
61
|
const testResults = readTestResults();
|
|
@@ -57,10 +63,8 @@ async function getResultsFromJson() {
|
|
|
57
63
|
console.error('No test results found');
|
|
58
64
|
return null;
|
|
59
65
|
}
|
|
60
|
-
// Виключаємо testTitle та testCaseKey з кожного тесту, stepTitle з кожного кроку
|
|
61
|
-
// та перетворюємо масив тестів в об'єкт з ключами testCaseKey
|
|
62
66
|
const processedResults = testResults.tests.reduce((acc, test) => {
|
|
63
|
-
const { testTitle, testCaseKey, ...testData } = test;
|
|
67
|
+
const { projectName, testTitle, testCaseKey, ...testData } = test;
|
|
64
68
|
acc[testCaseKey] = {
|
|
65
69
|
...testData,
|
|
66
70
|
steps: test.steps.map(({ stepTitle, ...step }) => step)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-execution-result.d.ts","sourceRoot":"","sources":["../../zephyr-api/update-execution-result.ts"],"names":[],"mappings":"AAOA,wBAAsB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"update-execution-result.d.ts","sourceRoot":"","sources":["../../zephyr-api/update-execution-result.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,wBAAsB,gBAAgB,kBAwBrC"}
|
|
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.updateTestResult = updateTestResult;
|
|
4
4
|
const get_results_from_json_1 = require("./get-results-from-json");
|
|
5
5
|
const zephyr_api_1 = require("./zephyr-api");
|
|
6
|
+
/**
|
|
7
|
+
* Updates test execution results in Zephyr Scale
|
|
8
|
+
* Reads test results from JSON file and updates each test case in Zephyr
|
|
9
|
+
*/
|
|
6
10
|
async function updateTestResult() {
|
|
7
11
|
console.log('Updating test result in Zephyr...');
|
|
8
12
|
const testResults = await (0, get_results_from_json_1.getResultsFromJson)();
|
|
@@ -10,7 +14,6 @@ async function updateTestResult() {
|
|
|
10
14
|
console.error('No test results found');
|
|
11
15
|
return;
|
|
12
16
|
}
|
|
13
|
-
// Iterate over object with testCaseKey as keys
|
|
14
17
|
for (const testCaseKey in testResults) {
|
|
15
18
|
const steps = testResults[testCaseKey];
|
|
16
19
|
const testCaseId = await (0, zephyr_api_1.getTestCaseId)(testCaseKey);
|
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
* @param testCaseKey - Test case key (e.g., "TC-001")
|
|
4
4
|
* @returns Test case ID or undefined in case of error
|
|
5
5
|
*/
|
|
6
|
-
export declare function getTestCaseId(testCaseKey: string): Promise<string
|
|
6
|
+
export declare function getTestCaseId(testCaseKey: string): Promise<string>;
|
|
7
7
|
/**
|
|
8
8
|
* Gets test execution key from Zephyr by test case ID
|
|
9
9
|
* @param testCaseId - Test case ID
|
|
10
10
|
* @returns Test execution key or null in case of error
|
|
11
11
|
*/
|
|
12
|
-
export declare function getTestExecutionKey(testCaseId: string): Promise<string
|
|
12
|
+
export declare function getTestExecutionKey(testCaseId: string): Promise<string>;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Updates test execution steps in Zephyr
|
|
15
15
|
* @param testExecutionKey - Test execution key
|
|
16
16
|
* @param steps - Array of test steps to update
|
|
17
|
+
* @throws Error if request fails
|
|
17
18
|
*/
|
|
18
19
|
export declare function putTestExecution(testExecutionKey: string, steps: string[]): Promise<void>;
|
|
19
20
|
//# sourceMappingURL=zephyr-api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zephyr-api.d.ts","sourceRoot":"","sources":["../../zephyr-api/zephyr-api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"zephyr-api.d.ts","sourceRoot":"","sources":["../../zephyr-api/zephyr-api.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,mBAkBtD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,UAAU,EAAE,MAAM,mBA0B3D;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,iBAyB/E"}
|
|
@@ -3,7 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getTestCaseId = getTestCaseId;
|
|
4
4
|
exports.getTestExecutionKey = getTestExecutionKey;
|
|
5
5
|
exports.putTestExecution = putTestExecution;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Gets common headers for all Zephyr API requests
|
|
8
|
+
* @returns Headers object with Content-Type and Authorization
|
|
9
|
+
*/
|
|
7
10
|
const getHeaders = () => {
|
|
8
11
|
return {
|
|
9
12
|
'Content-Type': 'application/json',
|
|
@@ -21,7 +24,6 @@ async function getTestCaseId(testCaseKey) {
|
|
|
21
24
|
method: 'GET',
|
|
22
25
|
headers: getHeaders(),
|
|
23
26
|
});
|
|
24
|
-
// Return only ID
|
|
25
27
|
const data = await response.json();
|
|
26
28
|
const id = data.id;
|
|
27
29
|
console.log('------------------------------------------');
|
|
@@ -44,7 +46,6 @@ async function getTestExecutionKey(testCaseId) {
|
|
|
44
46
|
method: 'GET',
|
|
45
47
|
headers: getHeaders()
|
|
46
48
|
});
|
|
47
|
-
// Find object in array by testCase.id
|
|
48
49
|
const data = await response.json();
|
|
49
50
|
const testExecution = data.values.find((execution) => execution.testCase.id === testCaseId);
|
|
50
51
|
if (testExecution) {
|
|
@@ -62,9 +63,10 @@ async function getTestExecutionKey(testCaseId) {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
/**
|
|
65
|
-
*
|
|
66
|
+
* Updates test execution steps in Zephyr
|
|
66
67
|
* @param testExecutionKey - Test execution key
|
|
67
68
|
* @param steps - Array of test steps to update
|
|
69
|
+
* @throws Error if request fails
|
|
68
70
|
*/
|
|
69
71
|
async function putTestExecution(testExecutionKey, steps) {
|
|
70
72
|
try {
|
|
@@ -78,7 +80,6 @@ async function putTestExecution(testExecutionKey, steps) {
|
|
|
78
80
|
});
|
|
79
81
|
console.log('Successfully sent test steps to Zephyr ✅');
|
|
80
82
|
console.log('------------------------------------------');
|
|
81
|
-
// Add 3 second pause after updating steps
|
|
82
83
|
console.log('Waiting 3 seconds before continuing... ⏳');
|
|
83
84
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
84
85
|
}
|
package/package.json
CHANGED
|
@@ -1,69 +1,54 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
"peerDependencies": {
|
|
57
|
-
"@playwright/test": "^1.40.0"
|
|
58
|
-
},
|
|
59
|
-
"peerDependenciesMeta": {
|
|
60
|
-
"@playwright/test": {
|
|
61
|
-
"optional": false
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
"devDependencies": {
|
|
65
|
-
"@playwright/test": "^1.40.0",
|
|
66
|
-
"@types/node": "^20.0.0",
|
|
67
|
-
"typescript": "^5.0.0"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
2
|
+
"name": "@zest-pw/test",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Advanced Playwright test framework with automatic screenshots, custom reporting, and Zephyr Scale integration",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"default": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./reporter": "./dist/reporter/custom-reporter.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"type-check": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"playwright",
|
|
26
|
+
"testing",
|
|
27
|
+
"automation",
|
|
28
|
+
"zephyr",
|
|
29
|
+
"zephyr-scale",
|
|
30
|
+
"screenshots",
|
|
31
|
+
"test-framework",
|
|
32
|
+
"test-reporter",
|
|
33
|
+
"e2e",
|
|
34
|
+
"end-to-end"
|
|
35
|
+
],
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/Capuchin33/zest-pw.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/Capuchin33/zest-pw#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/Capuchin33/zest-pw/issues"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@playwright/test": "^1.40.0",
|
|
51
|
+
"@types/node": "^20.0.0",
|
|
52
|
+
"typescript": "^5.0.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Post-install script to automatically create zest.config.ts in the project root
|
|
5
|
-
* This script runs after npm install and creates a default configuration file
|
|
6
|
-
* if it doesn't already exist.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
|
|
12
|
-
const configTemplate = `import { defineZestConfig } from '@zest-pw/test';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Zest Playwright Configuration
|
|
16
|
-
*
|
|
17
|
-
* Configure test reporting, screenshots, and Zephyr integration
|
|
18
|
-
*/
|
|
19
|
-
export default defineZestConfig({
|
|
20
|
-
reporter: {
|
|
21
|
-
// Save test results to JSON file
|
|
22
|
-
saveJsonReport: true,
|
|
23
|
-
// Output directory for reports
|
|
24
|
-
outputDir: 'test-results',
|
|
25
|
-
// Print test results to console
|
|
26
|
-
printToConsole: true,
|
|
27
|
-
// Verbose output (includes all step details)
|
|
28
|
-
verbose: false,
|
|
29
|
-
},
|
|
30
|
-
screenshots: {
|
|
31
|
-
// Enable screenshot capture
|
|
32
|
-
enabled: true,
|
|
33
|
-
// Include screenshots in JSON report
|
|
34
|
-
includeInReport: true,
|
|
35
|
-
// Capture screenshots only on failure
|
|
36
|
-
onlyOnFailure: false,
|
|
37
|
-
// Save screenshots to disk as files
|
|
38
|
-
saveToDisk: false,
|
|
39
|
-
},
|
|
40
|
-
zephyr: {
|
|
41
|
-
// Enable Zephyr Scale integration
|
|
42
|
-
enabled: false,
|
|
43
|
-
// Update test results in Zephyr after test run
|
|
44
|
-
updateResults: false,
|
|
45
|
-
// API credentials (uses environment variables by default)
|
|
46
|
-
// apiUrl: process.env.ZEPHYR_API_URL,
|
|
47
|
-
// apiKey: process.env.ZEPHYR_API_KEY,
|
|
48
|
-
// testCycleKey: process.env.ZEPHYR_TEST_CYCLE_KEY,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
`;
|
|
52
|
-
|
|
53
|
-
const configFileName = 'zest.config.ts';
|
|
54
|
-
|
|
55
|
-
// When postinstall runs, process.cwd() is the project root where npm install was executed
|
|
56
|
-
// This is the correct directory where we want to create zest.config.ts
|
|
57
|
-
const projectRoot = process.cwd();
|
|
58
|
-
const configPath = path.join(projectRoot, configFileName);
|
|
59
|
-
|
|
60
|
-
// Skip if we're in the zest-pw package development directory itself
|
|
61
|
-
// (when running npm install in the package repo)
|
|
62
|
-
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
63
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
64
|
-
try {
|
|
65
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
66
|
-
if (packageJson.name === '@zest-pw/test') {
|
|
67
|
-
// We're in the package development directory, skip
|
|
68
|
-
process.exit(0);
|
|
69
|
-
}
|
|
70
|
-
} catch (e) {
|
|
71
|
-
// If we can't read package.json, continue anyway
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Перевіряємо, чи файл вже існує
|
|
76
|
-
if (fs.existsSync(configPath)) {
|
|
77
|
-
console.log(`[@zest-pw/test] ✓ ${configFileName} already exists, skipping...`);
|
|
78
|
-
process.exit(0);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Створюємо файл конфігурації
|
|
82
|
-
try {
|
|
83
|
-
fs.writeFileSync(configPath, configTemplate, 'utf8');
|
|
84
|
-
console.log(`[@zest-pw/test] ✓ Created ${configFileName} in project root`);
|
|
85
|
-
console.log(`[@zest-pw/test] Location: ${configPath}`);
|
|
86
|
-
} catch (error) {
|
|
87
|
-
// Don't break the installation process, but show the error
|
|
88
|
-
console.error(`[@zest-pw/test] ⚠ Failed to create ${configFileName}:`, error.message);
|
|
89
|
-
console.error(`[@zest-pw/test] Target directory: ${projectRoot}`);
|
|
90
|
-
process.exit(0); // Exit with 0 to not break npm install
|
|
91
|
-
}
|
|
92
|
-
|