diff --git a/qase-javascript-commons/README.md b/qase-javascript-commons/README.md index 6aa42ca8..ed126112 100644 --- a/qase-javascript-commons/README.md +++ b/qase-javascript-commons/README.md @@ -37,6 +37,7 @@ All configuration options are listed in the table below: | Mode of reporter | `mode` | `QASE_MODE` | `testops` | No | `testops`, `report`, `off` | | Fallback mode of reporter | `fallback` | `QASE_FALLBACK` | `off` | No | `testops`, `report`, `off` | | Environment | `environment` | `QASE_ENVIRONMENT` | `local` | No | Any string | +| Root suite | `rootSuite` | `QASE_ROOT_SUITE` | | No | Any string | | Enable debug logs | `debug` | `QASE_DEBUG` | `False` | No | `True`, `False` | | Enable capture logs from `stdout` and `stderr` | `testops.defect` | `QASE_CAPTURE_LOGS` | `False` | No | `True`, `False` | | **Qase Report configuration** | | | | | | @@ -45,7 +46,7 @@ All configuration options are listed in the table below: | Local report format | `report.connection.format` | `QASE_REPORT_CONNECTION_FORMAT` | `json` | | `json`, `jsonp` | | **Qase TestOps configuration** | | | | | | | Token for [API access](https://developers.qase.io/#authentication) | `testops.api.token` | `QASE_TESTOPS_API_TOKEN` | | Yes | Any string | -| Qase API host | `testops.api.host` | `QASE_TESTOPS_API_HOST` | `qase.io` | No | Any string | +| Qase API host. For enterprise users, specify full address: `api-example.qase.io` | `testops.api.host` | `QASE_TESTOPS_API_HOST` | `qase.io` | No | Any string | | Qase enterprise environment | `testops.api.enterprise` | `QASE_TESTOPS_API_ENTERPRISE` | `False` | No | `True`, `False` | | Code of your project, which you can take from the URL: `https://app.qase.io/project/DEMOTR` - `DEMOTR` is the project code | `testops.project` | `QASE_TESTOPS_PROJECT` | | Yes | Any string | | Qase test run ID | `testops.run.id` | `QASE_TESTOPS_RUN_ID` | | No | Any integer | diff --git a/qase-javascript-commons/changelog.md b/qase-javascript-commons/changelog.md index 0a30887f..472a3b90 100644 --- a/qase-javascript-commons/changelog.md +++ b/qase-javascript-commons/changelog.md @@ -2,20 +2,15 @@ ## What's new -Added new configuration options `enterprise` for the Qase TestOps reporter in the `qase.config.json` file and environment variables. -Support enterprise API URL. Now the reporters will upload test results to your own Qase instance. +Added new configuration option `rootSuite` to specify a root suite. +This option is available in the config file and the `QASE_ROOT_SUITE` env variable ```diff { - ... - "testops": { - "api": { - "token": "token", - "host": "qase.io", -+ "enterprise": true - } - ... - }, + "mode": "testops", + "fallback": "report", + "environment": "local", ++ "rootSuite": "Root Suite", ... } ``` diff --git a/qase-javascript-commons/package.json b/qase-javascript-commons/package.json index f0493274..2320dc15 100644 --- a/qase-javascript-commons/package.json +++ b/qase-javascript-commons/package.json @@ -33,7 +33,7 @@ "lodash.merge": "^4.6.2", "lodash.mergewith": "^4.6.2", "mime-types": "^2.1.33", - "qaseio": "^2.1.0", + "qaseio": "^2.1.3", "strip-ansi": "^6.0.1", "uuid": "^9.0.0" }, diff --git a/qase-javascript-commons/src/config/config-validation-schema.ts b/qase-javascript-commons/src/config/config-validation-schema.ts index 5cac8c26..96d3eb4a 100644 --- a/qase-javascript-commons/src/config/config-validation-schema.ts +++ b/qase-javascript-commons/src/config/config-validation-schema.ts @@ -33,6 +33,10 @@ export const configValidationSchema: JSONSchemaType = { type: 'boolean', nullable: true, }, + rootSuite: { + type: 'string', + nullable: true, + }, testops: { type: 'object', diff --git a/qase-javascript-commons/src/env/env-enum.ts b/qase-javascript-commons/src/env/env-enum.ts index 564a1e61..b404ab1a 100644 --- a/qase-javascript-commons/src/env/env-enum.ts +++ b/qase-javascript-commons/src/env/env-enum.ts @@ -7,6 +7,7 @@ export enum EnvEnum { debug = 'QASE_DEBUG', environment = 'QASE_ENVIRONMENT', captureLogs = 'QASE_CAPTURE_LOGS', + rootSuite = 'QASE_ROOT_SUITE', } /** diff --git a/qase-javascript-commons/src/env/env-to-config.ts b/qase-javascript-commons/src/env/env-to-config.ts index 31a9af53..93506633 100644 --- a/qase-javascript-commons/src/env/env-to-config.ts +++ b/qase-javascript-commons/src/env/env-to-config.ts @@ -20,6 +20,7 @@ export const envToConfig = (env: EnvType): ConfigType => ({ debug: env[EnvEnum.debug], environment: env[EnvEnum.environment], captureLogs: env[EnvEnum.captureLogs], + rootSuite: env[EnvEnum.rootSuite], testops: { project: env[EnvTestOpsEnum.project], diff --git a/qase-javascript-commons/src/env/env-type.ts b/qase-javascript-commons/src/env/env-type.ts index 208b0c16..8cd4e359 100644 --- a/qase-javascript-commons/src/env/env-type.ts +++ b/qase-javascript-commons/src/env/env-type.ts @@ -16,6 +16,7 @@ export type EnvType = { [EnvEnum.debug]?: boolean; [EnvEnum.environment]?: string; [EnvEnum.captureLogs]?: boolean; + [EnvEnum.rootSuite]?: string; [EnvTestOpsEnum.project]?: string; [EnvTestOpsEnum.uploadAttachments]?: boolean; diff --git a/qase-javascript-commons/src/env/env-validation-schema.ts b/qase-javascript-commons/src/env/env-validation-schema.ts index 3eac4752..c544bc46 100644 --- a/qase-javascript-commons/src/env/env-validation-schema.ts +++ b/qase-javascript-commons/src/env/env-validation-schema.ts @@ -42,6 +42,10 @@ export const envValidationSchema: JSONSchemaType = { type: 'boolean', nullable: true, }, + [EnvEnum.rootSuite]: { + type: 'string', + nullable: true, + }, [EnvTestOpsEnum.project]: { type: 'string', diff --git a/qase-javascript-commons/src/options/options-type.ts b/qase-javascript-commons/src/options/options-type.ts index 6f13d4d9..bd29d5c6 100644 --- a/qase-javascript-commons/src/options/options-type.ts +++ b/qase-javascript-commons/src/options/options-type.ts @@ -31,6 +31,7 @@ export type OptionsType = { captureLogs?: boolean | undefined; debug?: boolean | undefined; environment?: string | undefined; + rootSuite?: string | undefined; testops?: | (RecursivePartial & AdditionalTestOpsOptionsType) | undefined; diff --git a/qase-javascript-commons/src/qase.ts b/qase-javascript-commons/src/qase.ts index 9be8a96f..ef32c892 100644 --- a/qase-javascript-commons/src/qase.ts +++ b/qase-javascript-commons/src/qase.ts @@ -341,6 +341,7 @@ export class QaseReporter implements ReporterInterface { frameworkName, reporterName, environment, + rootSuite, report = {}, testops = {}, } = options; @@ -408,6 +409,7 @@ export class QaseReporter implements ReporterInterface { }, apiClient, environment, + rootSuite, ); } @@ -419,6 +421,7 @@ export class QaseReporter implements ReporterInterface { this.logger, writer, environment, + rootSuite, testops.run?.id); } diff --git a/qase-javascript-commons/src/reporters/report-reporter.ts b/qase-javascript-commons/src/reporters/report-reporter.ts index cec3ab83..0780025c 100644 --- a/qase-javascript-commons/src/reporters/report-reporter.ts +++ b/qase-javascript-commons/src/reporters/report-reporter.ts @@ -14,23 +14,27 @@ import { LoggerInterface } from '../utils/logger'; export class ReportReporter extends AbstractReporter { private readonly environment: string | undefined; private readonly runId: number | undefined; + private readonly rootSuite: string | undefined; private startTime: number = Date.now(); /** * @param {LoggerInterface} logger * @param {WriterInterface} writer * @param {string | undefined} environment + * @param {string | undefined} rootSuite * @param {number | undefined} runId */ constructor( logger: LoggerInterface, private writer: WriterInterface, environment?: string, + rootSuite?: string, runId?: number, ) { super(logger); this.environment = environment; this.runId = runId; + this.rootSuite = rootSuite; } /** @@ -111,6 +115,26 @@ export class ReportReporter extends AbstractReporter { result.steps = this.copyStepAttachments(result.steps); result.run_id = this.runId ?? null; + if (result.relations != null && this.rootSuite != null) { + const data = { + title: this.rootSuite, + public_id: null, + }; + + result.relations.suite?.data.unshift(data); + } else if (this.rootSuite != null) { + result.relations = { + suite: { + data: [ + { + title: this.rootSuite, + public_id: null, + }, + ], + }, + }; + } + await this.writer.writeTestResult(result); } diff --git a/qase-javascript-commons/src/reporters/testops-reporter.ts b/qase-javascript-commons/src/reporters/testops-reporter.ts index 4110a24f..0dc2c275 100644 --- a/qase-javascript-commons/src/reporters/testops-reporter.ts +++ b/qase-javascript-commons/src/reporters/testops-reporter.ts @@ -139,6 +139,12 @@ export class TestOpsReporter extends AbstractReporter { */ private readonly defect: boolean; + /** + * @type {string | undefined} + * @private + */ + private readonly rootSuite: string | undefined; + /** * @type {number} * @private @@ -155,13 +161,15 @@ export class TestOpsReporter extends AbstractReporter { * @param {LoggerInterface} logger * @param {ReporterOptionsType & TestOpsOptionsType} options * @param {QaseApiInterface} api - * @param {number} environment + * @param {string | undefined} environment + * @param {string | undefined} rootSuite */ constructor( logger: LoggerInterface, options: TestOpsOptionsType, private api: QaseApiInterface, environment?: string, + rootSuite?: string, ) { const { project, @@ -181,6 +189,7 @@ export class TestOpsReporter extends AbstractReporter { this.batchSize = options.batch?.size ?? defaultChunkSize; this.useV2 = options.useV2 ?? false; this.defect = options.defect ?? false; + this.rootSuite = rootSuite; } /** @@ -370,9 +379,10 @@ export class TestOpsReporter extends AbstractReporter { return resultCreate; } + const rootSuite = this.rootSuite ? `${this.rootSuite}\t` : ''; resultCreate.case = { title: result.title, - suite_title: result.relations?.suite ? result.relations?.suite?.data.map((suite) => suite.title).join('\t') : null, + suite_title: result.relations?.suite ? `${rootSuite}${result.relations?.suite?.data.map((suite) => suite.title).join('\t')}` : rootSuite, }; this.logger.logDebug(`Transformed result: ${JSON.stringify(resultCreate)}`); @@ -402,11 +412,31 @@ export class TestOpsReporter extends AbstractReporter { * @private */ private getRelation(relation: Relation | null): ResultRelations { - if (!relation || !relation.suite) { - return {}; + if (!relation?.suite) { + if (this.rootSuite == undefined) { + return {}; + } + + return { + suite: { + data: [ + { + public_id: null, + title: this.rootSuite, + }, + ], + }, + }; } const suiteData: SuiteData[] = []; + if (this.rootSuite != undefined) { + suiteData.push({ + public_id: null, + title: this.rootSuite, + }); + } + for (const data of relation.suite.data) { suiteData.push({ public_id: null,