@zest-pw/test 1.0.0
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/LICENSE +22 -0
- package/README.md +415 -0
- package/dist/config.d.ts +127 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +140 -0
- package/dist/fixtures/fixtures.d.ts +3 -0
- package/dist/fixtures/fixtures.d.ts.map +1 -0
- package/dist/fixtures/fixtures.js +24 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/reporter/custom-reporter.d.ts +11 -0
- package/dist/reporter/custom-reporter.d.ts.map +1 -0
- package/dist/reporter/custom-reporter.js +19 -0
- package/dist/reporter/result-processor.d.ts +11 -0
- package/dist/reporter/result-processor.d.ts.map +1 -0
- package/dist/reporter/result-processor.js +52 -0
- package/dist/reporter/test-results-store.d.ts +31 -0
- package/dist/reporter/test-results-store.d.ts.map +1 -0
- package/dist/reporter/test-results-store.js +38 -0
- package/dist/utils/add-file-names.d.ts +9 -0
- package/dist/utils/add-file-names.d.ts.map +1 -0
- package/dist/utils/add-file-names.js +39 -0
- package/dist/utils/enrich-test-results.d.ts +6 -0
- package/dist/utils/enrich-test-results.d.ts.map +1 -0
- package/dist/utils/enrich-test-results.js +113 -0
- package/dist/utils/parse-test-steps.d.ts +8 -0
- package/dist/utils/parse-test-steps.d.ts.map +1 -0
- package/dist/utils/parse-test-steps.js +110 -0
- package/dist/utils/save-json-report.d.ts +17 -0
- package/dist/utils/save-json-report.d.ts.map +1 -0
- package/dist/utils/save-json-report.js +75 -0
- package/dist/utils/save-screenshots.d.ts +9 -0
- package/dist/utils/save-screenshots.d.ts.map +1 -0
- package/dist/utils/save-screenshots.js +66 -0
- package/dist/utils/take-screenshots.d.ts +13 -0
- package/dist/utils/take-screenshots.d.ts.map +1 -0
- package/dist/utils/take-screenshots.js +34 -0
- package/dist/utils/terminal-reporter.d.ts +8 -0
- package/dist/utils/terminal-reporter.d.ts.map +1 -0
- package/dist/utils/terminal-reporter.js +140 -0
- package/dist/utils/test-result-transformer.d.ts +13 -0
- package/dist/utils/test-result-transformer.d.ts.map +1 -0
- package/dist/utils/test-result-transformer.js +109 -0
- package/dist/utils/test-step-wrapper.d.ts +13 -0
- package/dist/utils/test-step-wrapper.d.ts.map +1 -0
- package/dist/utils/test-step-wrapper.js +48 -0
- package/dist/zephyr-api/get-results-from-json.d.ts +2 -0
- package/dist/zephyr-api/get-results-from-json.d.ts.map +1 -0
- package/dist/zephyr-api/get-results-from-json.js +71 -0
- package/dist/zephyr-api/update-execution-result.d.ts +2 -0
- package/dist/zephyr-api/update-execution-result.d.ts.map +1 -0
- package/dist/zephyr-api/update-execution-result.js +26 -0
- package/dist/zephyr-api/zephyr-api.d.ts +19 -0
- package/dist/zephyr-api/zephyr-api.d.ts.map +1 -0
- package/dist/zephyr-api/zephyr-api.js +89 -0
- package/package.json +69 -0
- package/scripts/install-config.js +92 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.saveTestResultsToJson = saveTestResultsToJson;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Saves test results to JSON file
|
|
41
|
+
*
|
|
42
|
+
* This function creates a JSON report from test results and saves it to the specified directory.
|
|
43
|
+
* If the directory doesn't exist, it will be created automatically.
|
|
44
|
+
*
|
|
45
|
+
* @param result - Object with test results to be saved
|
|
46
|
+
* @param outputDir - Directory for saving (default 'test-results')
|
|
47
|
+
* @returns Full path to the saved JSON file
|
|
48
|
+
* @throws Error if file cannot be saved
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* const filepath = saveTestResultsToJson(testResults);
|
|
52
|
+
* console.log(`Saved to: ${filepath}`);
|
|
53
|
+
*/
|
|
54
|
+
function saveTestResultsToJson(result, outputDir = 'test-results') {
|
|
55
|
+
try {
|
|
56
|
+
// Build the full path to the results directory
|
|
57
|
+
const resultsPath = path.join(process.cwd(), outputDir);
|
|
58
|
+
// Create directory if it doesn't exist (recursive: true creates parent dirs too)
|
|
59
|
+
if (!fs.existsSync(resultsPath)) {
|
|
60
|
+
fs.mkdirSync(resultsPath, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
// Define the output filename
|
|
63
|
+
const filename = `test-results.json`;
|
|
64
|
+
// Build the full file path
|
|
65
|
+
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
|
+
fs.writeFileSync(filepath, JSON.stringify(result, null, 2), 'utf-8');
|
|
69
|
+
return filepath;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error(`\n⚠️ Error saving JSON report: ${error}`);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Зберігає скріншот з base64 рядка
|
|
3
|
+
* @param base64String - Base64 рядок скріншоту
|
|
4
|
+
* @param filename - Назва файлу
|
|
5
|
+
* @param outputDir - Базова директорія (за замовчуванням 'screenshots')
|
|
6
|
+
* @param testTitle - Назва тесту (опціонально, створює підпапку для організації)
|
|
7
|
+
*/
|
|
8
|
+
export declare function saveBase64Screenshot(base64String: string, filename: string, outputDir?: string, testTitle?: string): string;
|
|
9
|
+
//# sourceMappingURL=save-screenshots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"save-screenshots.d.ts","sourceRoot":"","sources":["../../utils/save-screenshots.ts"],"names":[],"mappings":"AAUA;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAsB,EACjC,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAkBR"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.saveBase64Screenshot = saveBase64Screenshot;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Декодує base64 рядок в Buffer
|
|
41
|
+
*/
|
|
42
|
+
function decodeBase64ToBuffer(base64String) {
|
|
43
|
+
return Buffer.from(base64String, 'base64');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Зберігає скріншот з base64 рядка
|
|
47
|
+
* @param base64String - Base64 рядок скріншоту
|
|
48
|
+
* @param filename - Назва файлу
|
|
49
|
+
* @param outputDir - Базова директорія (за замовчуванням 'screenshots')
|
|
50
|
+
* @param testTitle - Назва тесту (опціонально, створює підпапку для організації)
|
|
51
|
+
*/
|
|
52
|
+
function saveBase64Screenshot(base64String, filename, outputDir = 'screenshots', testTitle) {
|
|
53
|
+
let screenshotsPath = path.join(process.cwd(), outputDir);
|
|
54
|
+
// Якщо вказано назву тесту, створюємо окрему підпапку
|
|
55
|
+
if (testTitle) {
|
|
56
|
+
const safeTestTitle = testTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase();
|
|
57
|
+
screenshotsPath = path.join(screenshotsPath, safeTestTitle);
|
|
58
|
+
}
|
|
59
|
+
if (!fs.existsSync(screenshotsPath)) {
|
|
60
|
+
fs.mkdirSync(screenshotsPath, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
const filepath = path.join(screenshotsPath, filename);
|
|
63
|
+
const buffer = decodeBase64ToBuffer(base64String);
|
|
64
|
+
fs.writeFileSync(filepath, buffer);
|
|
65
|
+
return filepath;
|
|
66
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Утиліта для створення скріншотів після кроків тесту
|
|
3
|
+
*/
|
|
4
|
+
import type { Page, TestInfo } from "@playwright/test";
|
|
5
|
+
/**
|
|
6
|
+
* Робить скріншот після кожного кроку тесту (завжди, незалежно від результату)
|
|
7
|
+
*
|
|
8
|
+
* Для збереження скріншотів на диск (з base64 результатів):
|
|
9
|
+
* 1. Додайте в .env файл: SAVE_SCREENSHOTS=true
|
|
10
|
+
* 2. Або передайте через команду: SAVE_SCREENSHOTS=true npx playwright test
|
|
11
|
+
*/
|
|
12
|
+
export declare function takeScreenshotAfterStep(page: Page, stepInfo: any, testInfo: TestInfo, stepTitle?: string): Promise<void>;
|
|
13
|
+
//# sourceMappingURL=take-screenshots.d.ts.map
|
|
@@ -0,0 +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;;;;;;GAMG;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,CAqBf"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Утиліта для створення скріншотів після кроків тесту
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.takeScreenshotAfterStep = takeScreenshotAfterStep;
|
|
7
|
+
/**
|
|
8
|
+
* Робить скріншот після кожного кроку тесту (завжди, незалежно від результату)
|
|
9
|
+
*
|
|
10
|
+
* Для збереження скріншотів на диск (з base64 результатів):
|
|
11
|
+
* 1. Додайте в .env файл: SAVE_SCREENSHOTS=true
|
|
12
|
+
* 2. Або передайте через команду: SAVE_SCREENSHOTS=true npx playwright test
|
|
13
|
+
*/
|
|
14
|
+
async function takeScreenshotAfterStep(page, stepInfo, testInfo, stepTitle) {
|
|
15
|
+
try {
|
|
16
|
+
if (page && testInfo) {
|
|
17
|
+
// Робимо скріншот без збереження на диск (тільки в буфер)
|
|
18
|
+
const screenshotBuffer = await page.screenshot({
|
|
19
|
+
fullPage: true
|
|
20
|
+
});
|
|
21
|
+
// Додаємо скріншот як attachment через testInfo
|
|
22
|
+
// Attachment автоматично прикріплюється до поточного кроку як substep
|
|
23
|
+
await testInfo.attach(stepTitle || stepInfo?.title || 'screenshot', {
|
|
24
|
+
body: screenshotBuffer,
|
|
25
|
+
contentType: 'image/png',
|
|
26
|
+
});
|
|
27
|
+
// Примітка: Збереження на диск відбувається в terminal-reporter.ts (якщо PRINT_TEST_RESULTS=true)
|
|
28
|
+
// або можна зберегти з JSON звіту вручну
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error('Помилка при створенні скріншота кроку:', error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Форматує та виводить результати тестів після їх завершення
|
|
3
|
+
* Вивід контролюється через змінну оточення PRINT_TEST_RESULTS
|
|
4
|
+
*
|
|
5
|
+
* Очікує що result вже збагачений запланованими кроками через enrichTestResultsWithPlannedSteps
|
|
6
|
+
*/
|
|
7
|
+
export declare function printTestResults(result: any): void;
|
|
8
|
+
//# sourceMappingURL=terminal-reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-reporter.d.ts","sourceRoot":"","sources":["../../utils/terminal-reporter.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAyBlD"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.printTestResults = printTestResults;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const save_screenshots_1 = require("./save-screenshots");
|
|
39
|
+
const config_1 = require("../config");
|
|
40
|
+
/**
|
|
41
|
+
* Форматує та виводить результати тестів після їх завершення
|
|
42
|
+
* Вивід контролюється через змінну оточення PRINT_TEST_RESULTS
|
|
43
|
+
*
|
|
44
|
+
* Очікує що result вже збагачений запланованими кроками через enrichTestResultsWithPlannedSteps
|
|
45
|
+
*/
|
|
46
|
+
function printTestResults(result) {
|
|
47
|
+
if (!result.tests || !Array.isArray(result.tests)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
console.log('\n=== Деталі по тестах та їх кроках ===');
|
|
51
|
+
result.tests.forEach((test) => {
|
|
52
|
+
printTestInfo(test);
|
|
53
|
+
// test.steps вже містить всі кроки (виконані + заплановані) після enrichTestResultsWithPlannedSteps
|
|
54
|
+
const allSteps = test.steps || [];
|
|
55
|
+
const executedSteps = allSteps.filter((step) => step.statusName !== 'In Progress');
|
|
56
|
+
// Створюємо outputDir точно як Playwright: test-results/{filename}-{test-title}-{project}
|
|
57
|
+
// test.testCaseKey тепер без розширення (наприклад "TC-002")
|
|
58
|
+
const testFileName = test.testCaseKey || 'test';
|
|
59
|
+
const sanitizedTitle = test.testTitle.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
60
|
+
const outputDir = path.join('test-results', `${testFileName}-${sanitizedTitle}-chromium`);
|
|
61
|
+
printTestSteps(executedSteps.length, allSteps, test.testTitle, outputDir);
|
|
62
|
+
});
|
|
63
|
+
console.log('\n=== Фінальне завершення ===');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Виводить загальну інформацію про тест
|
|
67
|
+
*/
|
|
68
|
+
function printTestInfo(test) {
|
|
69
|
+
console.log(`\n${test.testCaseKey}: ${test.testTitle}`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Виводить інформацію про кроки тесту
|
|
73
|
+
*/
|
|
74
|
+
function printTestSteps(executedCount, allSteps, testTitle, outputDir) {
|
|
75
|
+
if (allSteps.length === 0) {
|
|
76
|
+
console.log(' Steps: none');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const totalCount = allSteps.length;
|
|
80
|
+
console.log(` Steps (${executedCount}/${totalCount}):`);
|
|
81
|
+
allSteps.forEach((step, stepIndex) => {
|
|
82
|
+
const statusEmoji = step.statusName === 'passed' ? 'passed - ✅' : step.statusName === 'failed' ? 'failed - ❌' : step.statusName === 'In Progress' ? 'skipped - ⏭️' : '⏱️';
|
|
83
|
+
console.log(` ${stepIndex + 1}. ${step.stepTitle}`);
|
|
84
|
+
// Спочатку показуємо помилку, якщо є
|
|
85
|
+
if (step.error) {
|
|
86
|
+
console.log(` ❌ Error: ${step.error.message}`);
|
|
87
|
+
if (step.error.stack) {
|
|
88
|
+
const stackLines = step.error.stack.split('\n').slice(0, 3);
|
|
89
|
+
stackLines.forEach((line) => console.log(` ${line}`));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
printStepAttachments(step, testTitle, outputDir, stepIndex + 1);
|
|
93
|
+
console.log(` Status: ${statusEmoji}`);
|
|
94
|
+
// Додаємо порожній рядок після кожного кроку для кращої читабельності
|
|
95
|
+
console.log('');
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Виводить actualResult кроку
|
|
100
|
+
*/
|
|
101
|
+
function printStepAttachments(step, testTitle, outputDir, _stepNumber) {
|
|
102
|
+
if (!step.actualResult || step.actualResult.length === 0) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log(` Screenshot:`);
|
|
106
|
+
step.actualResult.forEach((att) => {
|
|
107
|
+
const isErrorScreenshot = att.fileName?.includes('ERROR');
|
|
108
|
+
const emoji = isErrorScreenshot ? '💥' : att.image === 'image/png' ? '📸' : '📄';
|
|
109
|
+
// Для консолі виводимо "Decode: Base64"
|
|
110
|
+
const displayName = att.image === 'image/png' ? 'Decode: Base64' : att.fileName;
|
|
111
|
+
console.log(` ${emoji} ${displayName}`);
|
|
112
|
+
if (att.body && att.image === 'text/plain') {
|
|
113
|
+
// Для текстових actualResult виводимо повний текст
|
|
114
|
+
console.log(` ${att.body}`);
|
|
115
|
+
}
|
|
116
|
+
// Зберігаємо скріншот на диск, якщо увімкнено в конфігурації або через змінну оточення
|
|
117
|
+
const config = (0, config_1.getZestConfig)();
|
|
118
|
+
const shouldSaveScreenshots = config.screenshots.saveToDisk || process.env.SAVE_SCREENSHOTS === 'true';
|
|
119
|
+
if (att.body && shouldSaveScreenshots && att.image === 'image/png') {
|
|
120
|
+
try {
|
|
121
|
+
// Використовуємо fileName з actualResult
|
|
122
|
+
const filename = att.fileName;
|
|
123
|
+
// Використовуємо outputDir від Playwright або fallback на screenshots/
|
|
124
|
+
if (outputDir) {
|
|
125
|
+
// Зберігаємо в папку тесту, яку створив Playwright
|
|
126
|
+
(0, save_screenshots_1.saveBase64Screenshot)(att.body, filename, outputDir);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Fallback: зберігаємо в screenshots/ з підпапкою тесту
|
|
130
|
+
(0, save_screenshots_1.saveBase64Screenshot)(att.body, filename, 'screenshots', testTitle);
|
|
131
|
+
}
|
|
132
|
+
console.log(` 💾 File saved: locally`);
|
|
133
|
+
console.log(` 📄 File name: ${filename}`);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
console.error(` ⚠️ Error saving screenshot: ${error}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FullResult, TestCase, TestResult } from '@playwright/test/reporter';
|
|
2
|
+
/**
|
|
3
|
+
* Transforms test results from Playwright Reporter API into extended format with detailed information about steps and attachments
|
|
4
|
+
*
|
|
5
|
+
* @param fullResult - Full test execution result from Playwright containing overall status and statistics
|
|
6
|
+
* @param testResults - Array of individual test results, each containing test case information and execution result
|
|
7
|
+
* @returns Object with 'tests' array containing transformed test cases
|
|
8
|
+
*/
|
|
9
|
+
export declare function transformTestResults(_fullResult: FullResult, testResults: Array<{
|
|
10
|
+
test: TestCase;
|
|
11
|
+
result: TestResult;
|
|
12
|
+
}>): any;
|
|
13
|
+
//# sourceMappingURL=test-result-transformer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-result-transformer.d.ts","sourceRoot":"","sources":["../../utils/test-result-transformer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAElF;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,UAAU,EACvB,WAAW,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,GACzD,GAAG,CAIL"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transformTestResults = transformTestResults;
|
|
4
|
+
/**
|
|
5
|
+
* Transforms test results from Playwright Reporter API into extended format with detailed information about steps and attachments
|
|
6
|
+
*
|
|
7
|
+
* @param fullResult - Full test execution result from Playwright containing overall status and statistics
|
|
8
|
+
* @param testResults - Array of individual test results, each containing test case information and execution result
|
|
9
|
+
* @returns Object with 'tests' array containing transformed test cases
|
|
10
|
+
*/
|
|
11
|
+
function transformTestResults(_fullResult, testResults) {
|
|
12
|
+
return {
|
|
13
|
+
tests: testResults.map(({ test, result }) => transformTestCase(test, result))
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Transforms individual test case with its result
|
|
18
|
+
*
|
|
19
|
+
* @param test - Test case information from Playwright
|
|
20
|
+
* @param result - Test execution result containing steps and status
|
|
21
|
+
* @returns Transformed test object with title, key, and steps
|
|
22
|
+
*/
|
|
23
|
+
function transformTestCase(test, result) {
|
|
24
|
+
return {
|
|
25
|
+
testTitle: test.title,
|
|
26
|
+
testCaseKey: transformLocation(test.location),
|
|
27
|
+
_fullPath: test.location?.file, // Used in enrich-test-results.ts to find planned test steps
|
|
28
|
+
steps: result.steps?.map(step => transformStep(step)) || []
|
|
29
|
+
};
|
|
30
|
+
}
|
|
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
|
+
/**
|
|
46
|
+
* Transforms error information
|
|
47
|
+
*
|
|
48
|
+
* @param error - Error object from test execution
|
|
49
|
+
* @returns Object with error message and stack trace, or undefined if no error
|
|
50
|
+
*/
|
|
51
|
+
function transformError(error) {
|
|
52
|
+
if (!error) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
message: error.message,
|
|
57
|
+
stack: error.stack
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Transforms test step with attachments
|
|
62
|
+
*
|
|
63
|
+
* @param step - Step object from test execution containing title, status, and attachments
|
|
64
|
+
* @returns Transformed step object with title, actual results, status, and error (if any)
|
|
65
|
+
*/
|
|
66
|
+
function transformStep(step) {
|
|
67
|
+
// Collect attachments from the step itself
|
|
68
|
+
let attachments = step.attachments?.map((att) => transformAttachment(att)) || [];
|
|
69
|
+
// Collect attachments from substeps (Playwright creates substeps for testInfo.attach())
|
|
70
|
+
if (step.steps && Array.isArray(step.steps)) {
|
|
71
|
+
step.steps.forEach((substep) => {
|
|
72
|
+
if (substep.attachments && substep.attachments.length > 0) {
|
|
73
|
+
const substepAttachments = substep.attachments.map((att) => transformAttachment(att));
|
|
74
|
+
attachments = attachments.concat(substepAttachments);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
stepTitle: step.title,
|
|
80
|
+
actualResult: attachments,
|
|
81
|
+
statusName: determineStepStatus(step),
|
|
82
|
+
error: transformError(step.error)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Determines step status
|
|
87
|
+
*
|
|
88
|
+
* @param step - Step object from test execution
|
|
89
|
+
* @returns Step status ('failed' if has error, otherwise step.status or 'passed')
|
|
90
|
+
*/
|
|
91
|
+
function determineStepStatus(step) {
|
|
92
|
+
if (step.error) {
|
|
93
|
+
return 'failed';
|
|
94
|
+
}
|
|
95
|
+
return step.status || 'passed';
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Transforms attachment (screenshot, video, etc.)
|
|
99
|
+
*
|
|
100
|
+
* @param att - Attachment object containing name, content type, and body
|
|
101
|
+
* @returns Transformed attachment with fileName, image (content type), and base64 encoded body
|
|
102
|
+
*/
|
|
103
|
+
function transformAttachment(att) {
|
|
104
|
+
return {
|
|
105
|
+
fileName: att.name,
|
|
106
|
+
image: att.contentType,
|
|
107
|
+
body: att.body ? att.body.toString('base64') : undefined
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TestInfo, TestType, Page } from '@playwright/test';
|
|
2
|
+
/**
|
|
3
|
+
* Застосовує обгортку до test.step для автоматичного створення скріншотів
|
|
4
|
+
* після кожного кроку тесту
|
|
5
|
+
*
|
|
6
|
+
* @param test - Об'єкт test з Playwright
|
|
7
|
+
* @param getCurrentContext - Функція для отримання поточного контексту тесту
|
|
8
|
+
*/
|
|
9
|
+
export declare function wrapTestStepWithScreenshots(test: TestType<any, any>, getCurrentContext: () => {
|
|
10
|
+
testInfo: TestInfo;
|
|
11
|
+
page: Page;
|
|
12
|
+
} | null): void;
|
|
13
|
+
//# sourceMappingURL=test-step-wrapper.d.ts.map
|
|
@@ -0,0 +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;;;;;;GAMG;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,CA2CN"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapTestStepWithScreenshots = wrapTestStepWithScreenshots;
|
|
4
|
+
const take_screenshots_1 = require("./take-screenshots");
|
|
5
|
+
/**
|
|
6
|
+
* Застосовує обгортку до test.step для автоматичного створення скріншотів
|
|
7
|
+
* після кожного кроку тесту
|
|
8
|
+
*
|
|
9
|
+
* @param test - Об'єкт test з Playwright
|
|
10
|
+
* @param getCurrentContext - Функція для отримання поточного контексту тесту
|
|
11
|
+
*/
|
|
12
|
+
function wrapTestStepWithScreenshots(test, getCurrentContext) {
|
|
13
|
+
const originalTestStep = test.step.bind(test);
|
|
14
|
+
const originalTestStepSkip = test.step.skip?.bind(test);
|
|
15
|
+
const stepWrapper = async function (title, body, options) {
|
|
16
|
+
return originalTestStep(title, async (stepInfo) => {
|
|
17
|
+
try {
|
|
18
|
+
// Виконуємо крок
|
|
19
|
+
const result = await body(stepInfo);
|
|
20
|
+
// Робимо скріншот після успішного виконання кроку
|
|
21
|
+
const context = getCurrentContext();
|
|
22
|
+
if (context?.page && context?.testInfo) {
|
|
23
|
+
await (0, take_screenshots_1.takeScreenshotAfterStep)(context.page, stepInfo, context.testInfo, title);
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
// Якщо крок падає, все одно робимо скріншот
|
|
29
|
+
const context = getCurrentContext();
|
|
30
|
+
if (context?.page && context?.testInfo) {
|
|
31
|
+
try {
|
|
32
|
+
await (0, take_screenshots_1.takeScreenshotAfterStep)(context.page, { ...stepInfo, error }, context.testInfo, title);
|
|
33
|
+
}
|
|
34
|
+
catch (screenshotError) {
|
|
35
|
+
console.error('Помилка при створенні скріншота після помилки кроку:', screenshotError);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}, options);
|
|
41
|
+
};
|
|
42
|
+
// Додаємо метод skip, якщо він існує
|
|
43
|
+
if (originalTestStepSkip) {
|
|
44
|
+
stepWrapper.skip = originalTestStepSkip;
|
|
45
|
+
}
|
|
46
|
+
// Замінюємо оригінальний test.step на обгортку
|
|
47
|
+
test.step = stepWrapper;
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-results-from-json.d.ts","sourceRoot":"","sources":["../../zephyr-api/get-results-from-json.ts"],"names":[],"mappings":"AAsBA,wBAAsB,kBAAkB,iBAuBvC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getResultsFromJson = getResultsFromJson;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
// Допоміжна функція для читання результатів тестів
|
|
40
|
+
function readTestResults() {
|
|
41
|
+
// Шлях до файлу з результатами тестів
|
|
42
|
+
const testResultsPath = path.join(process.cwd(), 'test-results', 'test-results.json');
|
|
43
|
+
// Перевіряємо, чи існує файл
|
|
44
|
+
if (!fs.existsSync(testResultsPath)) {
|
|
45
|
+
console.error('Test results file not found:', testResultsPath);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
// Читаємо та парсимо JSON файл
|
|
49
|
+
const testResultsContent = fs.readFileSync(testResultsPath, 'utf-8');
|
|
50
|
+
const testResults = JSON.parse(testResultsContent);
|
|
51
|
+
return testResults;
|
|
52
|
+
}
|
|
53
|
+
async function getResultsFromJson() {
|
|
54
|
+
console.log('Getting results for Zephyr...');
|
|
55
|
+
const testResults = readTestResults();
|
|
56
|
+
if (!testResults) {
|
|
57
|
+
console.error('No test results found');
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
// Виключаємо testTitle та testCaseKey з кожного тесту, stepTitle з кожного кроку
|
|
61
|
+
// та перетворюємо масив тестів в об'єкт з ключами testCaseKey
|
|
62
|
+
const processedResults = testResults.tests.reduce((acc, test) => {
|
|
63
|
+
const { testTitle, testCaseKey, ...testData } = test;
|
|
64
|
+
acc[testCaseKey] = {
|
|
65
|
+
...testData,
|
|
66
|
+
steps: test.steps.map(({ stepTitle, ...step }) => step)
|
|
67
|
+
};
|
|
68
|
+
return acc;
|
|
69
|
+
}, {});
|
|
70
|
+
return processedResults;
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-execution-result.d.ts","sourceRoot":"","sources":["../../zephyr-api/update-execution-result.ts"],"names":[],"mappings":"AAOA,wBAAsB,gBAAgB,kBAyBrC"}
|