Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 44 additions & 24 deletions apps/meteor/reporters/jira.ts
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -42,11 +44,20 @@ class JIRAReporter implements Reporter {
this.pr = options.pr;
}

private static async ensureJiraOk(response: Awaited<ReturnType<typeof fetch>>, context: string): Promise<void> {
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);
}
}

Expand Down Expand Up @@ -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',
Expand All @@ -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: {
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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}
`,
}),
Expand All @@ -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}.`);
}
}

Expand Down
Loading