Skip to content
Open
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
1 change: 0 additions & 1 deletion .github/workflows/.reusable-docker-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ jobs:
cd e2e
zip -r playwright-report.zip playwright-report/ || echo "Failed to zip report"
cd ..
npm install --no-save @slack/web-api
npx -y tsx e2e/slack-e2e-reporter.ts
env:
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
Expand Down
20 changes: 12 additions & 8 deletions frontend/api/slack-client.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
const { WebClient } = require('@slack/web-api')

if (!process.env.SLACK_TOKEN) {
return
}

const web = new WebClient(process.env.SLACK_TOKEN)
const SLACK_TOKEN = process.env.SLACK_TOKEN

async function toChannel(message, channel) {
// eslint-disable-next-line
console.log(`sending to channel: ${channel} message: ${message}`);
console.log(`sending to channel: ${channel} message: ${message}`)
try {
await web.chat.postMessage({
channel: `#${channel}`,
text: message,
const res = await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
Authorization: `Bearer ${SLACK_TOKEN}`,
},
body: JSON.stringify({ channel: `#${channel}`, text: message }),
})
const data = await res.json()
if (!data.ok) throw new Error(data.error)
} catch (error) {
// eslint-disable-next-line
console.log(`Error posting to Slack:${error}`);
console.log(`Error posting to Slack:${error}`)
}
}

Expand Down
56 changes: 35 additions & 21 deletions frontend/e2e/slack-e2e-reporter.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import * as fs from 'fs';
import * as path from 'path';
import { WebClient } from '@slack/web-api';

const SLACK_TOKEN = process.env.SLACK_TOKEN;
const CHANNEL_ID = 'C0102JZRG3G'; // infra_tests channel ID
const failedJsonPath = path.join(__dirname, 'test-results', 'failed.json');
const failedData = JSON.parse(fs.readFileSync(failedJsonPath, 'utf-8'));
const failedCount = failedData.failedTests?.length || 0;

async function slackPost(endpoint: string, body: Record<string, unknown>): Promise<unknown> {
const res = await fetch(`https://slack.com/api/${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
Authorization: `Bearer ${SLACK_TOKEN}`,
},
body: JSON.stringify(body),
});
const data = await res.json() as { ok: boolean; error?: string };
if (!data.ok) throw new Error(`Slack ${endpoint} error: ${data.error}`);
return data;
}

async function uploadFile(filePath: string): Promise<void> {
if (!SLACK_TOKEN) {
console.log('Slack token not specified, skipping upload');
Expand All @@ -16,14 +29,28 @@ async function uploadFile(filePath: string): Promise<void> {

const epoch = Date.now();
const filename = `playwright-report-${epoch}.zip`;
const fileBytes = fs.readFileSync(filePath);
const fileSize = fileBytes.byteLength;

console.log(`Uploading ${filePath}`);
const urlRes = await slackPost('files.getUploadURLExternal', {
filename,
length: fileSize,
}) as { upload_url: string; file_id: string };


const slackClient = new WebClient(SLACK_TOKEN);
await slackClient.files.uploadV2({
const uploadRes = await fetch(urlRes.upload_url, {
method: 'POST',
headers: { 'Content-Type': 'application/octet-stream' },
body: fileBytes,
});
if (!uploadRes.ok) {
throw new Error(`Upload to pre-signed URL failed: ${uploadRes.status} ${uploadRes.statusText}`);
}

await slackPost('files.completeUploadExternal', {
files: [{ id: urlRes.file_id, title: filename }],
channel_id: CHANNEL_ID,
file: fs.createReadStream(filePath),
filename,
});
}

Expand All @@ -33,17 +60,10 @@ function postMessage(message: string): Promise<unknown> {
return Promise.resolve();
}

const slackClient = new WebClient(SLACK_TOKEN);
return slackClient.chat.postMessage({
channel: CHANNEL_ID,
text: message,
});
return slackPost('chat.postMessage', { channel: CHANNEL_ID, text: message });
}

function notifyFailure(
failedCount: number,
failedTests: any[],
): Promise<unknown> {
function notifyFailure(failedCount: number, failedTests: any[]): Promise<unknown> {
const actionUrl = process.env.GITHUB_ACTION_URL || '';
if (!actionUrl) {
console.log('No GITHUB_ACTION_URL set, skipping Slack notification');
Expand All @@ -56,16 +76,11 @@ function notifyFailure(
const prTitle = process.env.PR_TITLE;
const prUrl = process.env.PR_URL;

// Build PR info line
const prInfo = prNumber && prUrl
? `*PR:* <${prUrl}|#${prNumber}>${prTitle ? ` - ${prTitle}` : ''}\n`
: '';

// Build failed tests list (inline, limit to first 3)
const testNames = failedTests
.slice(0, 3)
.map((test) => test.title)
.join(', ');
const testNames = failedTests.slice(0, 3).map((t) => t.title).join(', ');
const moreTests = failedCount > 3 ? ` +${failedCount - 3} more` : '';

const message = `❌ E2E Tests Failed
Expand Down Expand Up @@ -94,7 +109,6 @@ async function main() {
await notifyFailure(failedCount, failedData.failedTests || []);
console.log('Slack notification sent successfully');

// Upload HTML report if zip file exists
const reportZipPath = path.join(__dirname, 'playwright-report.zip');
if (fs.existsSync(reportZipPath)) {
console.log('Uploading HTML report...');
Expand Down
4 changes: 1 addition & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@
"react": "$react",
"react-dom": "$react-dom",
"@types/react": "$@types/react",
"@types/react-dom": "$@types/react-dom",
"axios": "^1.16.0"
"@types/react-dom": "$@types/react-dom"
},
"dependencies": {
"@amplitude/analytics-browser": "^2.11.3",
Expand Down Expand Up @@ -74,7 +73,6 @@
"@rspack/plugin-react-refresh": "^1.6.2",
"@sentry/browser": "^7.119.1",
"@sentry/webpack-plugin": "2.22.7",
"@slack/web-api": "^6.9.1",
"array-find-index": "^1.0.2",
"body-parser": "^2.2.2",
"bootstrap": "5.2.2",
Expand Down
Loading