diff --git a/apps/meteor/reporters/jira.ts b/apps/meteor/reporters/jira.ts index e2b86ab52161d..390116dbabfd7 100644 --- a/apps/meteor/reporters/jira.ts +++ b/apps/meteor/reporters/jira.ts @@ -1,6 +1,8 @@ import type { Reporter, TestCase, TestResult } from '@playwright/test/reporter'; import fetch from 'node-fetch'; +const LOG = '[JIRA reporter]'; + class JIRAReporter implements Reporter { private url: string; @@ -42,11 +44,20 @@ class JIRAReporter implements Reporter { this.pr = options.pr; } + private static async ensureJiraOk(response: Awaited>, context: string): Promise { + if (response.ok) { + return; + } + const text = await response.text(); + const preview = text.length > 800 ? `${text.slice(0, 800)}...` : text; + throw new Error(`${LOG} ${context}: HTTP ${response.status} ${response.statusText}. Body: ${preview}`); + } + async onTestEnd(test: TestCase, result: TestResult) { try { await this._onTestEnd(test, result); } catch (error) { - console.error('Error sending test result to JIRA', error); + console.error(`${LOG} Error sending test result to JIRA`, error); } } @@ -77,13 +88,13 @@ class JIRAReporter implements Reporter { headSha: this.headSha, }; - console.log(`Sending test result to JIRA: ${JSON.stringify(payload)}`); + console.log(`${LOG} preparing notification for flaky/unexpected failure: ${JSON.stringify(payload)}`); // first search and check if there is an existing issue // replace all ()[]- with nothing const search = await fetch( `${this.url}/rest/api/3/search/jql?${new URLSearchParams({ - jql: `project = FLAKY AND summary ~ '${payload.name.replace(/[\(\)\[\]-]/g, '')}'`, + jql: `project = FLAKY AND summary ~ '${payload.name.replace(/[()[\]-]/g, '')}'`, })}`, { method: 'GET', @@ -94,30 +105,23 @@ class JIRAReporter implements Reporter { }, ); - if (!search.ok) { - throw new Error( - `JIRA: Failed to search for existing issue: ${search.statusText}.` + - `${this.url}/rest/api/3/search/jql?${new URLSearchParams({ - jql: `project = FLAKY AND summary ~ '${payload.name}'`, - })}`, - ); - } + await JIRAReporter.ensureJiraOk(search, 'search for existing issue'); - const { issues } = await search.json(); + const { issues } = JSON.parse(await search.text()) as { + issues: { key: string; fields: { summary: string } }[]; + }; - const existing = issues.find( - (issue: { - fields: { - summary: string; - }; - }) => issue.fields.summary === payload.name, - ); + console.log(`${LOG} JQL search returned ${issues.length} candidate issue(s) (exact summary match is applied next).`); + + const existing = issues.find((issue) => issue.fields.summary === payload.name); if (existing) { + console.log(`${LOG} exact summary match on ${existing.key}; no new issue will be created (comment / label only).`); + const { location } = test; if (this.pr === 0) { - await fetch(`${this.url}/rest/api/3/issue/${existing.key}`, { + const labelRes = await fetch(`${this.url}/rest/api/3/issue/${existing.key}`, { method: 'PUT', body: JSON.stringify({ update: { @@ -133,9 +137,11 @@ class JIRAReporter implements Reporter { 'Authorization': `Basic ${this.apiKey}`, }, }); + await JIRAReporter.ensureJiraOk(labelRes, `add label flaky_Develop on ${existing.key}`); + console.log(`${LOG} label update OK for ${existing.key}`); } - await fetch(`${this.url}/rest/api/3/issue/${existing.key}/comment`, { + const commentRes = await fetch(`${this.url}/rest/api/3/issue/${existing.key}/comment`, { method: 'POST', body: JSON.stringify({ body: `Test run ${payload.run} failed @@ -153,9 +159,13 @@ ${this.run_url} 'Authorization': `Basic ${this.apiKey}`, }, }); + await JIRAReporter.ensureJiraOk(commentRes, `comment on ${existing.key}`); + console.log(`${LOG} comment posted on ${existing.key} for run ${payload.run}.`); return; } + console.log(`${LOG} no issue with identical summary; creating new FLAKY issue.`); + const data: { fields: { summary: string; @@ -190,11 +200,19 @@ ${this.run_url} }, }); - const issue = (await responseIssue.json()).key; + await JIRAReporter.ensureJiraOk(responseIssue, 'create issue'); + + const created = JSON.parse(await responseIssue.text()) as { key?: string }; + const issue = created.key; + if (!issue) { + throw new Error(`${LOG} create issue response had no key: ${JSON.stringify(created)}`); + } + + console.log(`${LOG} created issue ${issue}.`); const { location } = test; - await fetch(`${this.url}/rest/api/3/issue/${issue}/comment`, { + const commentRes = await fetch(`${this.url}/rest/api/3/issue/${issue}/comment`, { method: 'POST', body: JSON.stringify({ body: `Test run ${payload.run} failed @@ -203,7 +221,7 @@ PR: ${this.pr} https://github.com/RocketChat/Rocket.Chat/blob/${payload.headSha}/${location.file.replace( '/home/runner/work/Rocket.Chat/Rocket.Chat', '', - )}#L${location.line}:${location.column}, + )}#L${location.line}:${location.column} ${this.run_url} `, }), @@ -212,6 +230,8 @@ ${this.run_url} 'Authorization': `Basic ${this.apiKey}`, }, }); + await JIRAReporter.ensureJiraOk(commentRes, `comment on ${issue}`); + console.log(`${LOG} comment posted on ${issue}; done for run ${payload.run}.`); } }