transit-core-taf 1.0.21 → 1.0.23
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/dist/global-setup.js +0 -61
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/dist/reporters/testrail-reporter.d.ts +2 -6
- package/dist/reporters/testrail-reporter.js +14 -32
- package/dist/specs/excel-utils.spec.d.ts +1 -0
- package/dist/specs/excel-utils.spec.js +51 -0
- package/dist/utils/excel/excel-utils.d.ts +25 -0
- package/dist/utils/excel/excel-utils.js +87 -0
- package/dist/utils/testrail/client.d.ts +2 -1
- package/dist/utils/testrail/client.js +12 -9
- package/dist/utils/testrail-importer.d.ts +4 -5
- package/dist/utils/testrail-importer.js +51 -8
- package/package.json +5 -3
package/dist/global-setup.js
CHANGED
|
@@ -1,67 +1,6 @@
|
|
|
1
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
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
const dotenv = __importStar(require("dotenv"));
|
|
40
|
-
const path_1 = __importDefault(require("path"));
|
|
41
|
-
const fs = __importStar(require("fs"));
|
|
42
|
-
// Function to find the project root
|
|
43
|
-
const findProjectRoot = (startPath) => {
|
|
44
|
-
let currentPath = startPath;
|
|
45
|
-
while (currentPath !== path_1.default.parse(currentPath).root) {
|
|
46
|
-
if (fs.existsSync(path_1.default.join(currentPath, 'package.json'))) {
|
|
47
|
-
return currentPath;
|
|
48
|
-
}
|
|
49
|
-
currentPath = path_1.default.dirname(currentPath);
|
|
50
|
-
}
|
|
51
|
-
return startPath; // Fallback to the original directory if not found
|
|
52
|
-
};
|
|
53
3
|
async function globalSetup() {
|
|
54
|
-
const env = process.env.ENV || 'stage';
|
|
55
|
-
const projectRoot = path_1.default.resolve(__dirname, '..');
|
|
56
|
-
const envPath = path_1.default.join(projectRoot, `.env.${env}`);
|
|
57
|
-
// Load env file
|
|
58
|
-
if (fs.existsSync(envPath)) {
|
|
59
|
-
dotenv.config({ path: envPath, override: true });
|
|
60
|
-
console.log(`✅ Loaded ${envPath} from project root ${projectRoot} ✅`);
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
throw new Error(`❌ Environment file .env.${env} not found in project root ${projectRoot} ❌`);
|
|
64
|
-
}
|
|
65
4
|
const argv = process.argv;
|
|
66
5
|
// Find all arguments related to project selection
|
|
67
6
|
const projectArgs = argv.map((arg, i) => {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,10 +6,10 @@ export * from './baseui/commonpagevalidations';
|
|
|
6
6
|
export * from './baseui/commonpagewaits';
|
|
7
7
|
export * from './baseui/logger';
|
|
8
8
|
export * from './utils/Utils';
|
|
9
|
+
export * from './utils/excel/excel-utils';
|
|
9
10
|
export * from './baseapi/baseapihelpers';
|
|
10
11
|
export * from './baseapi/apiutils';
|
|
11
12
|
export * from './constants/test-tags';
|
|
12
|
-
export
|
|
13
|
+
export { TestRailClient, TestRailClientOptions, TestRailResult, } from './utils/testrail/client';
|
|
13
14
|
export * from './utils/testrail-importer';
|
|
14
15
|
export { default as TestRailReporter } from './reporters/testrail-reporter';
|
|
15
|
-
export type { TestRailReporterOptions } from './reporters/testrail-reporter';
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.TestRailReporter = void 0;
|
|
20
|
+
exports.TestRailReporter = exports.TestRailClient = void 0;
|
|
21
21
|
__exportStar(require("./baseui/basepagevalidations"), exports);
|
|
22
22
|
__exportStar(require("./baseui/basepageactions"), exports);
|
|
23
23
|
__exportStar(require("./baseui/basepagewaits"), exports);
|
|
@@ -26,10 +26,12 @@ __exportStar(require("./baseui/commonpagevalidations"), exports);
|
|
|
26
26
|
__exportStar(require("./baseui/commonpagewaits"), exports);
|
|
27
27
|
__exportStar(require("./baseui/logger"), exports);
|
|
28
28
|
__exportStar(require("./utils/Utils"), exports);
|
|
29
|
+
__exportStar(require("./utils/excel/excel-utils"), exports);
|
|
29
30
|
__exportStar(require("./baseapi/baseapihelpers"), exports);
|
|
30
31
|
__exportStar(require("./baseapi/apiutils"), exports);
|
|
31
32
|
__exportStar(require("./constants/test-tags"), exports);
|
|
32
|
-
|
|
33
|
+
var client_1 = require("./utils/testrail/client");
|
|
34
|
+
Object.defineProperty(exports, "TestRailClient", { enumerable: true, get: function () { return client_1.TestRailClient; } });
|
|
33
35
|
__exportStar(require("./utils/testrail-importer"), exports);
|
|
34
36
|
var testrail_reporter_1 = require("./reporters/testrail-reporter");
|
|
35
37
|
Object.defineProperty(exports, "TestRailReporter", { enumerable: true, get: function () { return __importDefault(testrail_reporter_1).default; } });
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { Reporter, FullConfig, Suite, FullResult } from '@playwright/test/reporter';
|
|
2
2
|
import { TestRailJunitImporterOptions } from '../utils/testrail-importer';
|
|
3
|
-
export interface TestRailReporterOptions extends Omit<TestRailJunitImporterOptions, 'junitPath' | 'onstart' | 'onend'> {
|
|
4
|
-
outputFile?: string;
|
|
5
|
-
}
|
|
6
3
|
declare class TestRailReporter implements Reporter {
|
|
7
4
|
private options;
|
|
8
|
-
private
|
|
9
|
-
constructor(options:
|
|
5
|
+
private runId;
|
|
6
|
+
constructor(options: TestRailJunitImporterOptions);
|
|
10
7
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
11
8
|
onEnd(result: FullResult): Promise<void>;
|
|
12
|
-
private getJunitReporter;
|
|
13
9
|
}
|
|
14
10
|
export default TestRailReporter;
|
|
@@ -3,51 +3,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const testrail_importer_1 = require("../utils/testrail-importer");
|
|
4
4
|
class TestRailReporter {
|
|
5
5
|
options;
|
|
6
|
-
|
|
6
|
+
runId;
|
|
7
7
|
constructor(options) {
|
|
8
8
|
if (!options.runId) {
|
|
9
9
|
throw new Error('TestRail Reporter requires a `runId` to be specified.');
|
|
10
10
|
}
|
|
11
11
|
this.options = options;
|
|
12
|
+
this.runId = options.runId;
|
|
12
13
|
console.log('Initialized TestRail Reporter.');
|
|
13
14
|
}
|
|
14
15
|
onBegin(config, suite) {
|
|
15
|
-
this.config = config;
|
|
16
16
|
console.log(`Starting the run with ${suite.allTests().length} tests`);
|
|
17
17
|
}
|
|
18
18
|
async onEnd(result) {
|
|
19
19
|
console.log(`Run finished with status: ${result.status}`);
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
if (this.options.junitPath) {
|
|
21
|
+
try {
|
|
22
|
+
await (0, testrail_importer_1.uploadJunitToTestRail)({
|
|
23
|
+
...this.options,
|
|
24
|
+
runId: this.runId,
|
|
25
|
+
junitPath: this.options.junitPath,
|
|
26
|
+
});
|
|
27
|
+
console.log('Successfully uploaded test results to TestRail.');
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.error('Failed to upload results to TestRail:', error);
|
|
31
|
+
}
|
|
23
32
|
}
|
|
24
|
-
// Find the JUnit reporter's output file path from the Playwright config
|
|
25
|
-
const junitReporter = this.getJunitReporter(this.config);
|
|
26
|
-
if (!junitReporter) {
|
|
27
|
-
console.error('Error: JUnit reporter not found in Playwright config. The TestRail reporter depends on the output of the JUnit reporter.');
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const junitPath = junitReporter.outputFile || junitReporter._outputFile;
|
|
31
|
-
if (!junitPath) {
|
|
32
|
-
console.error('Error: Could not determine the output file path for the JUnit reporter.');
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
console.log(`Using JUnit report from: ${junitPath}`);
|
|
36
|
-
try {
|
|
37
|
-
await (0, testrail_importer_1.uploadJunitToTestRail)({
|
|
38
|
-
...this.options,
|
|
39
|
-
junitPath,
|
|
40
|
-
});
|
|
41
|
-
console.log('Successfully uploaded test results to TestRail.');
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
console.error('Failed to upload results to TestRail:', error);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
getJunitReporter(config) {
|
|
48
|
-
// Playwright wraps reporters in an array, e.g., ['junit', { outputFile: 'results.xml' }]
|
|
49
|
-
const reporter = config.reporter.find((r) => r[0] === 'junit');
|
|
50
|
-
return reporter ? reporter[1] : undefined;
|
|
51
33
|
}
|
|
52
34
|
}
|
|
53
35
|
exports.default = TestRailReporter;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
const test_1 = require("@playwright/test");
|
|
37
|
+
const excel_utils_1 = require("../utils/excel/excel-utils");
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const testDataPath = path.resolve(process.cwd(), 'src/testdata/bookTripData.xlsx');
|
|
40
|
+
test_1.test.describe('Excel Utility Tests', () => {
|
|
41
|
+
(0, test_1.test)('should read test data from an Excel file', () => {
|
|
42
|
+
const testData = (0, excel_utils_1.getTestDataFromExcel)(testDataPath, 'TripData');
|
|
43
|
+
(0, test_1.expect)(testData).toBeInstanceOf(Array);
|
|
44
|
+
(0, test_1.expect)(testData.length).toBeGreaterThan(0);
|
|
45
|
+
console.log(JSON.stringify(testData, null, 2));
|
|
46
|
+
// Example of a more specific assertion
|
|
47
|
+
const firstRecord = testData[0];
|
|
48
|
+
(0, test_1.expect)(firstRecord).toHaveProperty('execute');
|
|
49
|
+
(0, test_1.expect)(firstRecord).toHaveProperty('testCaseName');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A generic interface for a row of test data.
|
|
3
|
+
* It allows for any number of properties of any type.
|
|
4
|
+
*/
|
|
5
|
+
interface TestDataRow {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* An interface for test data grouped by TestCaseId.
|
|
10
|
+
* The key is the TestCaseId (string), and the value is an array of test data rows.
|
|
11
|
+
*/
|
|
12
|
+
interface GroupedTestData {
|
|
13
|
+
[testCaseId: string]: TestDataRow[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reads an Excel file and returns the test data grouped by TestCaseId.
|
|
17
|
+
* The column headers are sanitized by removing non-alphanumeric characters and converting to camel case.
|
|
18
|
+
* e.g., "Pickup location" becomes "pickupLocation", "FundingSource" becomes "fundingSource".
|
|
19
|
+
*
|
|
20
|
+
* @param filePath - The path to the Excel file.
|
|
21
|
+
* @param sheetName - The name of the sheet to read from.
|
|
22
|
+
* @returns An object with TestCaseId as the key and an array of test data rows as the value.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getTestDataFromExcel(filePath: string, sheetName: string): GroupedTestData;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
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.getTestDataFromExcel = getTestDataFromExcel;
|
|
37
|
+
const xlsx = __importStar(require("xlsx"));
|
|
38
|
+
/**
|
|
39
|
+
* Reads an Excel file and returns the test data grouped by TestCaseId.
|
|
40
|
+
* The column headers are sanitized by removing non-alphanumeric characters and converting to camel case.
|
|
41
|
+
* e.g., "Pickup location" becomes "pickupLocation", "FundingSource" becomes "fundingSource".
|
|
42
|
+
*
|
|
43
|
+
* @param filePath - The path to the Excel file.
|
|
44
|
+
* @param sheetName - The name of the sheet to read from.
|
|
45
|
+
* @returns An object with TestCaseId as the key and an array of test data rows as the value.
|
|
46
|
+
*/
|
|
47
|
+
function getTestDataFromExcel(filePath, sheetName) {
|
|
48
|
+
const workbook = xlsx.readFile(filePath);
|
|
49
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
50
|
+
if (!worksheet) {
|
|
51
|
+
throw new Error(`Sheet "${sheetName}" not found in the Excel file.`);
|
|
52
|
+
}
|
|
53
|
+
const jsonData = xlsx.utils.sheet_to_json(worksheet, {
|
|
54
|
+
// This will ensure that if a column is empty, it will be blank instead of being undefined
|
|
55
|
+
defval: '',
|
|
56
|
+
});
|
|
57
|
+
const groupedData = jsonData.reduce((acc, row) => {
|
|
58
|
+
const testCaseIdKey = Object.keys(row).find(key => key.toLowerCase().replace(/[^a-z0-9]/gi, '') === 'testcaseid');
|
|
59
|
+
if (!testCaseIdKey) {
|
|
60
|
+
console.warn('Row does not have a TestCaseId column, skipping.', row);
|
|
61
|
+
return acc;
|
|
62
|
+
}
|
|
63
|
+
const testCaseId = row[testCaseIdKey];
|
|
64
|
+
if (!testCaseId) {
|
|
65
|
+
return acc;
|
|
66
|
+
}
|
|
67
|
+
const sanitizedRow = {};
|
|
68
|
+
for (const key in row) {
|
|
69
|
+
if (Object.prototype.hasOwnProperty.call(row, key)) {
|
|
70
|
+
// Sanitize key to camelCase
|
|
71
|
+
const sanitizedKey = key
|
|
72
|
+
.trim()
|
|
73
|
+
.replace(/[^a-zA-Z0-9\s]/g, '') // remove special chars except space
|
|
74
|
+
.replace(/\s(.)/g, (_match, group1) => group1.toUpperCase())
|
|
75
|
+
.replace(/\s/g, '')
|
|
76
|
+
.replace(/^(.)/, (match) => match.toLowerCase());
|
|
77
|
+
sanitizedRow[sanitizedKey] = row[key];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!acc[testCaseId]) {
|
|
81
|
+
acc[testCaseId] = [];
|
|
82
|
+
}
|
|
83
|
+
acc[testCaseId].push(sanitizedRow);
|
|
84
|
+
return acc;
|
|
85
|
+
}, {});
|
|
86
|
+
return groupedData;
|
|
87
|
+
}
|
|
@@ -6,6 +6,7 @@ export interface TestRailClientOptions {
|
|
|
6
6
|
password?: string;
|
|
7
7
|
}
|
|
8
8
|
export interface TestRailResult {
|
|
9
|
+
case_id: number;
|
|
9
10
|
status_id: number;
|
|
10
11
|
comment?: string;
|
|
11
12
|
version?: string;
|
|
@@ -22,6 +23,6 @@ export declare class TestRailClient {
|
|
|
22
23
|
constructor(options: TestRailClientOptions);
|
|
23
24
|
private apiPost;
|
|
24
25
|
createTestRun(suiteId: number, name: string, description: string): Promise<any>;
|
|
25
|
-
|
|
26
|
+
addResultsForCases(runId: number, results: TestRailResult[]): Promise<any>;
|
|
26
27
|
addResultForCase(runId: number, caseId: number, result: TestRailResult): Promise<any>;
|
|
27
28
|
}
|
|
@@ -61,15 +61,18 @@ class TestRailClient {
|
|
|
61
61
|
console.log(`Successfully created Test Run with ID: ${run.id}. View at: ${run.url}`);
|
|
62
62
|
return run;
|
|
63
63
|
}
|
|
64
|
-
async
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
async addResultsForCases(runId, results) {
|
|
65
|
+
const endpoint = `add_results_for_cases/${runId}`;
|
|
66
|
+
console.log(`Adding ${results.length} results to Test Run ID: ${runId}`);
|
|
67
|
+
try {
|
|
68
|
+
const response = await this.apiPost(endpoint, { results });
|
|
69
|
+
console.log(`Successfully added ${results.length} results.`);
|
|
70
|
+
return response;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error(`Failed to add results for Test Run ID: ${runId}.`, error);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
73
76
|
}
|
|
74
77
|
async addResultForCase(runId, caseId, result) {
|
|
75
78
|
const endpoint = `add_result_for_case/${runId}/${caseId}`;
|
|
@@ -4,13 +4,12 @@ export interface TestRailJunitImporterOptions extends TestRailClientOptions {
|
|
|
4
4
|
junitPath: string;
|
|
5
5
|
onstart?: () => void;
|
|
6
6
|
onend?: () => void;
|
|
7
|
+
statusMap?: {
|
|
8
|
+
[key: string]: number;
|
|
9
|
+
};
|
|
7
10
|
}
|
|
8
11
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* IMPORTANT: For this to work correctly, your test names must include the TestRail Case ID.
|
|
12
|
-
* The TestRail JUnit parser uses the test name to map results to cases.
|
|
13
|
-
* Example: test('C12345 My test description', async ({ page }) => { ... });
|
|
12
|
+
* Parses a JUnit XML report, extracts test results, and uploads them to a TestRail test run.
|
|
14
13
|
*
|
|
15
14
|
* @param options - The configuration options for the TestRail client and the JUnit import.
|
|
16
15
|
*/
|
|
@@ -36,17 +36,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.uploadJunitToTestRail = uploadJunitToTestRail;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const client_1 = require("./testrail/client");
|
|
39
|
+
const xmlJs = __importStar(require("xml-js"));
|
|
40
|
+
const defaultStatusMap = {
|
|
41
|
+
passed: 1,
|
|
42
|
+
failed: 5,
|
|
43
|
+
skipped: 2, // Mapped to 'Blocked'
|
|
44
|
+
};
|
|
45
|
+
const caseIdRegex = /C(\d+)/g;
|
|
39
46
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* IMPORTANT: For this to work correctly, your test names must include the TestRail Case ID.
|
|
43
|
-
* The TestRail JUnit parser uses the test name to map results to cases.
|
|
44
|
-
* Example: test('C12345 My test description', async ({ page }) => { ... });
|
|
47
|
+
* Parses a JUnit XML report, extracts test results, and uploads them to a TestRail test run.
|
|
45
48
|
*
|
|
46
49
|
* @param options - The configuration options for the TestRail client and the JUnit import.
|
|
47
50
|
*/
|
|
48
51
|
async function uploadJunitToTestRail(options) {
|
|
49
|
-
const { host, user, projectId, token, password, runId, junitPath, onstart, onend } = options;
|
|
52
|
+
const { host, user, projectId, token, password, runId, junitPath, onstart, onend, statusMap = defaultStatusMap, } = options;
|
|
50
53
|
onstart?.();
|
|
51
54
|
if (!fs.existsSync(junitPath)) {
|
|
52
55
|
throw new Error(`JUnit report not found at path: ${junitPath}`);
|
|
@@ -55,8 +58,48 @@ async function uploadJunitToTestRail(options) {
|
|
|
55
58
|
try {
|
|
56
59
|
console.log(`Reading JUnit report from: ${junitPath}`);
|
|
57
60
|
const xmlContent = fs.readFileSync(junitPath, 'utf-8');
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
const report = xmlJs.xml2js(xmlContent, { compact: true });
|
|
62
|
+
const results = [];
|
|
63
|
+
const testsuites = Array.isArray(report.testsuites.testsuite)
|
|
64
|
+
? report.testsuites.testsuite
|
|
65
|
+
: [report.testsuites.testsuite];
|
|
66
|
+
for (const testsuite of testsuites) {
|
|
67
|
+
if (!testsuite.testcase)
|
|
68
|
+
continue;
|
|
69
|
+
const testcases = Array.isArray(testsuite.testcase)
|
|
70
|
+
? testsuite.testcase
|
|
71
|
+
: [testsuite.testcase];
|
|
72
|
+
for (const testcase of testcases) {
|
|
73
|
+
const caseIds = testcase._attributes.name.match(caseIdRegex);
|
|
74
|
+
if (!caseIds)
|
|
75
|
+
continue;
|
|
76
|
+
const statusName = testcase.failure
|
|
77
|
+
? 'failed'
|
|
78
|
+
: testcase.skipped
|
|
79
|
+
? 'skipped'
|
|
80
|
+
: 'passed';
|
|
81
|
+
const statusId = statusMap[statusName];
|
|
82
|
+
if (statusId === undefined)
|
|
83
|
+
continue;
|
|
84
|
+
const comment = testcase.failure
|
|
85
|
+
? `Test failed: ${testcase.failure._attributes.message}`
|
|
86
|
+
: `Test ${statusName}`;
|
|
87
|
+
for (const caseId of caseIds) {
|
|
88
|
+
results.push({
|
|
89
|
+
case_id: parseInt(caseId.substring(1), 10),
|
|
90
|
+
status_id: statusId,
|
|
91
|
+
comment,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (results.length > 0) {
|
|
97
|
+
await client.addResultsForCases(runId, results);
|
|
98
|
+
console.log('Successfully uploaded test results to TestRail.');
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log('No test results with case IDs found in the JUnit report.');
|
|
102
|
+
}
|
|
60
103
|
}
|
|
61
104
|
catch (error) {
|
|
62
105
|
console.error('Failed to upload JUnit report to TestRail.', error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transit-core-taf",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"description": "Transit Core Automation Framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"xlsx": "^0.18.5"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"
|
|
27
|
+
"@types/xml-js": "^0.9.0",
|
|
28
|
+
"axios": "^1.7.2",
|
|
29
|
+
"xml-js": "^1.6.11"
|
|
28
30
|
},
|
|
29
31
|
"peerDependencies": {
|
|
30
32
|
"playwright": "^1.54.1"
|
|
@@ -35,4 +37,4 @@
|
|
|
35
37
|
},
|
|
36
38
|
"author": "Transit Core",
|
|
37
39
|
"license": "ISC"
|
|
38
|
-
}
|
|
40
|
+
}
|