Skip to content
Merged
Show file tree
Hide file tree
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
7 changes: 7 additions & 0 deletions .github/workflows/ci-test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ on:
required: false
CODECOV_TOKEN:
required: false
REPORTER_JIRA_ROCKETCHAT_API_KEY:
required: false

env:
MONGO_URL: mongodb://localhost:27017/rocketchat?replicaSet=rs0&directConnection=true
Expand Down Expand Up @@ -250,10 +252,15 @@ jobs:
IS_EE: ${{ inputs.release == 'ee' && 'true' || '' }}
REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }}
REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }}
REPORTER_JIRA_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_JIRA_ROCKETCHAT_API_KEY }}
REPORTER_ROCKETCHAT_REPORT: ${{ github.event.pull_request.draft != 'true' && 'true' || '' }}
REPORTER_ROCKETCHAT_RUN: ${{ github.run_number }}
REPORTER_ROCKETCHAT_BRANCH: ${{ github.ref }}
REPORTER_ROCKETCHAT_DRAFT: ${{ github.event.pull_request.draft }}
REPORTER_ROCKETCHAT_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
REPORTER_ROCKETCHAT_AUTHOR: ${{ github.event.pull_request.user.login }}
REPORTER_ROCKETCHAT_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
REPORTER_ROCKETCHAT_PR: ${{ github.event.pull_request.number }}
QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }}
QASE_REPORT: ${{ github.ref == 'refs/heads/develop' && 'true' || '' }}
CI: true
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ jobs:
QASE_API_TOKEN: ${{ secrets.QASE_API_TOKEN }}
REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }}
REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }}
REPORTER_JIRA_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_JIRA_ROCKETCHAT_API_KEY }}

test-api-ee:
name: 🔨 Test API (EE)
Expand Down Expand Up @@ -400,6 +401,7 @@ jobs:
REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }}
REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
REPORTER_JIRA_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_JIRA_ROCKETCHAT_API_KEY }}

test-ui-ee-no-watcher:
name: 🔨 Test UI (EE)
Expand Down Expand Up @@ -430,6 +432,7 @@ jobs:
REPORTER_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_ROCKETCHAT_API_KEY }}
REPORTER_ROCKETCHAT_URL: ${{ secrets.REPORTER_ROCKETCHAT_URL }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
REPORTER_JIRA_ROCKETCHAT_API_KEY: ${{ secrets.REPORTER_JIRA_ROCKETCHAT_API_KEY }}

tests-done:
name: ✅ Tests Done
Expand Down
15 changes: 15 additions & 0 deletions apps/meteor/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ export default {
branch: process.env.REPORTER_ROCKETCHAT_BRANCH,
run: Number(process.env.REPORTER_ROCKETCHAT_RUN),
draft: process.env.REPORTER_ROCKETCHAT_DRAFT === 'true',
headSha: process.env.REPORTER_ROCKETCHAT_HEAD_SHA,
},
],
process.env.REPORTER_ROCKETCHAT_REPORT === 'true' && [
'./reporters/jira.ts',
{
url: `https://rocketchat.atlassian.net`,
apiKey: process.env.REPORTER_JIRA_ROCKETCHAT_API_KEY ?? process.env.JIRA_TOKEN,
branch: process.env.REPORTER_ROCKETCHAT_BRANCH,
run: Number(process.env.REPORTER_ROCKETCHAT_RUN),
headSha: process.env.REPORTER_ROCKETCHAT_HEAD_SHA,
author: process.env.REPORTER_ROCKETCHAT_AUTHOR,
run_url: process.env.REPORTER_ROCKETCHAT_RUN_URL,
pr: Number(process.env.REPORTER_ROCKETCHAT_PR),
draft: process.env.REPORTER_ROCKETCHAT_DRAFT === 'true',
},
],
[
Expand Down
186 changes: 186 additions & 0 deletions apps/meteor/reporters/jira.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import type { Reporter, TestCase, TestResult } from '@playwright/test/reporter';
import fetch from 'node-fetch';

class JIRAReporter implements Reporter {
private url: string;

private apiKey: string;

private branch: string;

private draft: boolean;

private run: number;

private headSha: string;

private author: string;

private run_url: string;

private pr: number;

constructor(options: {
url: string;
apiKey: string;
branch: string;
draft: boolean;
run: number;
headSha: string;
author: string;
run_url: string;
pr: number;
}) {
this.url = options.url;
this.apiKey = options.apiKey;
this.branch = options.branch;
this.draft = options.draft;
this.run = options.run;
this.headSha = options.headSha;
this.author = options.author;
this.run_url = options.run_url;
this.pr = options.pr;
}

async onTestEnd(test: TestCase, result: TestResult) {
if (process.env.REPORTER_ROCKETCHAT_REPORT !== 'true') {
return;
}

if (this.draft === true) {
return;
}

if (result.status === 'passed' || result.status === 'skipped') {
return;
}

const payload = {
name: test.title,
status: result.status,
duration: result.duration,
branch: this.branch,
draft: this.draft,
run: this.run,
headSha: this.headSha,
};

console.log(`Sending test result to JIRA: ${JSON.stringify(payload)}`);

// first search and check if there is an existing issue

const search = await fetch(
`${this.url}/rest/api/2/search?${new URLSearchParams({
jql: `project = FLAKY AND summary ~ '${payload.name}'`,
})}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${this.apiKey}`,
},
},
);

if (!search.ok) {
throw new Error(
`JIRA: Failed to search for existing issue: ${search.statusText}.` +
`${this.url}/rest/api/2/search${new URLSearchParams({
jql: `project = FLAKY AND summary ~ '${payload.name}'`,
})}`,
);
}

const { issues } = await search.json();

const existing = issues.find(
(issue: {
fields: {
summary: string;
};
}) => issue.fields.summary === payload.name,
);

if (existing) {
const { location } = test;

await fetch(`${this.url}/rest/api/2/issue/${existing.key}/comment`, {
method: 'POST',
body: JSON.stringify({
body: `Test run ${payload.run} failed
author: ${this.author}
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}
${this.run_url}
`,
}),
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${this.apiKey}`,
},
});
return;
}

const data: {
fields: {
summary: string;
description: string;
issuetype: {
name: string;
};
project: {
key: string;
};
};
} = {
fields: {
summary: payload.name,
description: '',
issuetype: {
name: 'Tech Debt',
},
project: {
key: 'FLAKY',
},
},
};

const responseIssue = await fetch(`${this.url}/rest/api/2/issue`, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${this.apiKey}`,
},
});

const issue = (await responseIssue.json()).key;

const { location } = test;

await fetch(`${this.url}/rest/api/2/issue/${issue}/comment`, {
method: 'POST',
body: JSON.stringify({
body: `Test run ${payload.run} failed
author: ${this.author}
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},
${this.run_url}
`,
}),
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${this.apiKey}`,
},
});
}
}

export default JIRAReporter;