diff --git a/.changeset/pr-286.md b/.changeset/pr-286.md new file mode 100644 index 00000000..aecbe5cc --- /dev/null +++ b/.changeset/pr-286.md @@ -0,0 +1,5 @@ +--- +"@wdio/browserstack-service": minor +--- + +- `testManagementOptions` set in the BrowserStack service options is now forwarded in full, so `issues` and `issueTracker` can link Jira issues to the build. The same JSON can be passed with the `BROWSERSTACK_TEST_MANAGEMENT_OPTIONS` environment variable or the `--browserstack.testManagementOptions` CLI argument (Mocha and Cucumber). diff --git a/packages/browserstack-service/src/cli/cliUtils.ts b/packages/browserstack-service/src/cli/cliUtils.ts index 9944df42..3a3a5f72 100644 --- a/packages/browserstack-service/src/cli/cliUtils.ts +++ b/packages/browserstack-service/src/cli/cliUtils.ts @@ -89,10 +89,7 @@ export class CLIUtils { const isNonBstackA11y = isTurboScale(options) || !shouldAddServiceVersion(config as Options.Testrunner, options.testObservability) const observabilityOptions: TestObservabilityOptions = options.testObservabilityOptions || {} - const testManagementOptions: TestManagementOptions = options.testManagementOptions || {} - const testPlanId = typeof testManagementOptions.testPlanId === 'string' - ? testManagementOptions.testPlanId.trim() - : '' + const testManagementOptions = this.getTestManagementOptions(options.testManagementOptions) const binconfig: Record = { userName: observabilityOptions.user || config.user, accessKey: observabilityOptions.key || config.key, @@ -105,10 +102,8 @@ export class CLIUtils { binconfig.buildName = observabilityOptions.buildName || binconfig.buildName binconfig.projectName = observabilityOptions.projectName || binconfig.projectName binconfig.buildTag = this.getObservabilityBuildTags(observabilityOptions, buildTag) || [] - if (testPlanId.length > 0) { - binconfig.testManagementOptions = { - testPlanId - } + if (testManagementOptions) { + binconfig.testManagementOptions = testManagementOptions } let caps = capabilities @@ -142,6 +137,22 @@ export class CLIUtils { return JSON.stringify(binconfig) } + /** + * testManagementOptions forwarded to the binary as configured, with testPlanId trimmed and + * dropped when blank; undefined when nothing is left to send. + */ + static getTestManagementOptions(testManagementOptions?: TestManagementOptions): TestManagementOptions | undefined { + if (!testManagementOptions || typeof testManagementOptions !== 'object' || Array.isArray(testManagementOptions)) { + return undefined + } + const { testPlanId, ...otherOptions } = testManagementOptions + const trimmedTestPlanId = typeof testPlanId === 'string' ? testPlanId.trim() : '' + const resolved: TestManagementOptions = trimmedTestPlanId.length > 0 + ? { testPlanId: trimmedTestPlanId, ...otherOptions } + : otherOptions + return Object.keys(resolved).length > 0 ? resolved : undefined + } + static getSdkVersion() { return bstackServiceVersion } diff --git a/packages/browserstack-service/src/types.ts b/packages/browserstack-service/src/types.ts index 7c9e45d8..af270fdb 100644 --- a/packages/browserstack-service/src/types.ts +++ b/packages/browserstack-service/src/types.ts @@ -57,8 +57,32 @@ export interface TestReportingOptions { key?: string } +export interface IssueTrackerOptions { + /** + * Issue tracker the linked issues live in, e.g. 'jira'. + */ + name?: string, + /** + * Base URL of the issue tracker, e.g. 'https://your-domain.atlassian.net'. + */ + host?: string, + [key: string]: unknown +} + export interface TestManagementOptions { + /** + * Test plan to add this run's test cases to. + */ testPlanId?: string, + /** + * Issue keys to link to the build, e.g. ['XYZ-1', 'XYZ-2']. + */ + issues?: string[], + /** + * Issue tracker that `issues` belong to. + */ + issueTracker?: IssueTrackerOptions, + [key: string]: unknown } export interface RunSmartSelectionOptions { @@ -110,8 +134,14 @@ export interface BrowserstackConfig { */ testReportingOptions?: TestReportingOptions; /** - * Set the Test Management related config options under this key. - * Currently supports testPlanId. + * Set the Test Management related config options under this key, e.g. testPlanId, issues + * and issueTracker. + * + * For Mocha and Cucumber runs every key is forwarded to Test Management as is, and the same + * JSON object can also be passed through the BROWSERSTACK_TEST_MANAGEMENT_OPTIONS environment + * variable or the --browserstack.testManagementOptions CLI argument; each top-level key is + * taken from the CLI argument first, then the environment variable, then this option. + * Other runs use testPlanId only. */ testManagementOptions?: TestManagementOptions; /** diff --git a/packages/browserstack-service/tests/cli/cliUtils.test.ts b/packages/browserstack-service/tests/cli/cliUtils.test.ts index 32bab846..987e6b3a 100644 --- a/packages/browserstack-service/tests/cli/cliUtils.test.ts +++ b/packages/browserstack-service/tests/cli/cliUtils.test.ts @@ -195,7 +195,7 @@ describe('CLIUtils', () => { }) }) - it('omits empty testManagementOptions and strips unrelated keys', () => { + it('drops a blank testPlanId and keeps the other testManagementOptions keys', () => { const capabilities = [ { browserName: 'chrome' } ] @@ -215,13 +215,16 @@ describe('CLIUtils', () => { const emptyPlanResult = CLIUtils.getBinConfig(mockConfig, capabilities, emptyPlanOptions) const emptyPlanParsed = JSON.parse(emptyPlanResult) - expect(emptyPlanParsed.testManagementOptions).toBeUndefined() + expect(emptyPlanParsed.testManagementOptions).toEqual({ + ignoredKey: 'ignored' + }) const validPlanResult = CLIUtils.getBinConfig(mockConfig, capabilities, validPlanOptions) const validPlanParsed = JSON.parse(validPlanResult) expect(validPlanParsed.testManagementOptions).toEqual({ - testPlanId: 'tm-plan-456' + testPlanId: 'tm-plan-456', + ignoredKey: 'ignored' }) }) })