@lytics/playwright-slack
v0.3.1
Published
Slack notifications for Playwright test results with rich formatting and automatic section handling
Readme
@lytics/playwright-slack
Slack notifications for Playwright test results with rich formatting and automatic section handling.
Features
- 🎨 Rich formatting using Slack Block Kit via fluent MessageBuilder API
- 📊 Test result summary with pass/fail counts and percentages
- ❌ Failed test details with error messages and report links
- ⚠️ Flaky test tracking for tests that passed after retry
- 🔗 Dashboard and action links for quick access
- 🏷️ Environment labels (Production, Staging, etc.)
- 🎯 Automatic section hiding for empty results
Installation
npm install @lytics/playwright-slack
# or
pnpm add @lytics/playwright-slack
# or
yarn add @lytics/playwright-slackUsage
Basic Example
import { PlaywrightNotifier } from "@lytics/playwright-slack";
import { SlackClient } from "@lytics/slack-client";
// Create Slack client
const slackClient = new SlackClient({
webhook_url: process.env.SLACK_WEBHOOK_URL!,
});
// Create notifier with environment label
const notifier = new PlaywrightNotifier(slackClient, {
environment: "Production",
});
// Send test results
await notifier.sendTestResults({
total: 50,
passed: 45,
failed: [
{
name: "Login Flow",
error: "Timeout waiting for element .submit-button",
reportUrl: "https://dashboard.com/reports/login-flow",
},
{
name: "Checkout Process",
error: "Assertion failed: expected 200, got 500",
reportUrl: "https://dashboard.com/reports/checkout",
},
],
flaky: [
{
name: "Search Results",
retries: 2,
},
],
duration: 120,
trigger: "schedule",
dashboardUrl: "https://dashboard.com",
actionUrl: "https://github.com/org/repo/actions/runs/123",
});Integration with Playwright
import { test, expect } from "@playwright/test";
import { PlaywrightNotifier } from "@lytics/playwright-slack";
import { SlackClient } from "@lytics/slack-client";
// After test run
test.afterAll(async () => {
// Parse Playwright test results
const results = {
total: testResults.allTests().length,
passed: testResults.passed().length,
failed: testResults.failed().map((test) => ({
name: test.title,
error: test.error?.message || "Unknown error",
reportUrl: `https://dashboard.com/reports/${test.id}`,
})),
flaky: testResults.flaky().map((test) => ({
name: test.title,
retries: test.results.length - 1,
})),
duration: Math.round(testResults.duration / 1000),
trigger: process.env.GITHUB_EVENT_NAME || "manual",
actionUrl: process.env.GITHUB_SERVER_URL
? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`
: undefined,
};
// Send to Slack
const slackClient = new SlackClient({
webhook_url: process.env.SLACK_WEBHOOK_URL!,
});
const notifier = new PlaywrightNotifier(slackClient, {
environment: process.env.TEST_ENV || "Test",
});
await notifier.sendTestResults(results);
});GitHub Actions Integration
name: E2E Tests
on:
schedule:
- cron: "0 9 * * 1-5" # 9 AM weekdays
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: pnpm install
- name: Run Playwright tests
run: pnpm exec playwright test
- name: Send Slack notification
if: always()
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
TEST_ENV: Production
run: node notify-results.jsCustom Formatters
The package supports custom message formatting through the formatter pattern. This allows you to customize the message layout while maintaining clean, testable code.
Using Default Formatter (with skipped/flaky counts)
The default formatter now includes skipped and flaky test counts in the summary:
await notifier.sendTestResults({
total: 62,
passed: 61,
failed: [],
flaky: [{ name: 'Flaky Test', retries: 1 }],
skipped: 1,
duration: 643,
trigger: 'schedule',
dashboardUrl: 'https://dashboard.com',
});
// Summary: "61/62 passed (98%) • 1 skipped • 1 flaky • ⏱️ 643s • View Dashboard"Extending Default Formatter
Override specific formatting methods:
import { DefaultPlaywrightFormatter, PlaywrightNotifier } from '@lytics/playwright-slack';
import { SlackClient } from '@lytics/slack-client';
class MyFormatter extends DefaultPlaywrightFormatter {
// Customize the summary line
protected formatSummary(results: TestResults): string {
const percent = Math.round((results.passed / results.total) * 100);
const parts = [
`${results.passed}/${results.total} passed (${percent}%)`,
];
if (results.skipped > 0) {
parts.push(`${results.skipped} skipped`);
}
if (results.flaky.length > 0) {
parts.push(`${results.flaky.length} flaky`);
}
parts.push(`:stopwatch: ${results.duration}s`);
if (results.dashboardUrl) {
parts.push(`<${results.dashboardUrl}|View Dashboard>`);
}
return parts.join(' • ');
}
// Customize the header
protected formatHeader(results: TestResults, options?: PlaywrightNotifierOptions): string {
const emoji = results.failed.length === 0 ? '🎭' : '💥';
return `${emoji} Custom Test Report - ${options?.environment || 'Dev'}`;
}
}
const slackClient = new SlackClient({
webhook_url: process.env.SLACK_WEBHOOK_URL!,
});
const notifier = new PlaywrightNotifier(
slackClient,
{ environment: 'Production' },
new MyFormatter() // Use custom formatter
);
await notifier.sendTestResults(results);Complete Custom Formatter
For complete control, implement the PlaywrightFormatter interface:
import { PlaywrightFormatter, type TestResults } from '@lytics/playwright-slack';
import { MessageBuilder, type SlackMessage } from '@lytics/slack-client';
class RadicallyDifferentFormatter implements PlaywrightFormatter {
formatMessage(results: TestResults): SlackMessage {
return new MessageBuilder()
.setHeader('🎭 My Custom Format')
.setSummary(`Results: ${results.passed}/${results.total}`)
.addSection(
'Details',
'Custom section',
[`${results.failed.length} failed`, `${results.flaky.length} flaky`]
)
.setFooter('_Custom footer_')
.build();
}
}
const notifier = new PlaywrightNotifier(
slackClient,
options,
new RadicallyDifferentFormatter()
);Available Protected Methods
When extending DefaultPlaywrightFormatter, you can override:
formatHeader(results, options)- Message header with status emojiformatSummary(results)- Pass/fail summary with counts and linksformatFailedTests(tests)- Failed test list formattingformatFlakyTests(tests)- Flaky test list formattingformatFooter(results)- Footer with trigger and linksformatFallbackText(results)- Plain text fallback for notifications
API Reference
PlaywrightNotifier
Main class for sending Playwright test results to Slack.
Constructor
new PlaywrightNotifier(
slackClient: SlackClient,
options?: PlaywrightNotifierOptions,
formatter?: PlaywrightFormatter
)Parameters:
slackClient: SlackClient instance for sending messagesoptions.environment: Optional environment label (e.g., 'Production', 'Staging')formatter: Optional custom formatter (defaults toDefaultPlaywrightFormatter)
Methods
sendTestResults(results: TestResults): Promise<void>
Send test results notification to Slack.
Parameters:
results: Test results data (see TestResults)
Example:
await notifier.sendTestResults({
total: 50,
passed: 45,
failed: [...],
flaky: [...],
skipped: 2,
duration: 120,
trigger: 'schedule',
});Types
TestResults
interface TestResults {
/** Total number of tests */
total: number;
/** Number of passed tests */
passed: number;
/** Array of failed test details */
failed: FailedTest[];
/** Array of flaky test details (passed after retry) */
flaky: FlakyTest[];
/** Number of skipped tests */
skipped: number;
/** Test duration in seconds */
duration: number;
/** What triggered the test run */
trigger: string;
/** Optional URL to test dashboard */
dashboardUrl?: string;
/** Optional URL to GitHub Actions run */
actionUrl?: string;
}FailedTest
interface FailedTest {
/** Test name */
name: string;
/** Error message or stack trace */
error: string;
/** Optional URL to detailed test report */
reportUrl?: string;
}FlakyTest
interface FlakyTest {
/** Test name */
name: string;
/** Number of retries before passing */
retries: number;
}PlaywrightNotifierOptions
interface PlaywrightNotifierOptions {
/** Environment label (e.g., 'Production', 'Staging', 'Development') */
environment?: string;
}Message Format
The generated Slack message includes:
Header
- ✅ Success emoji (all tests pass) or ❌ failure emoji
- Environment label
- Example: "✅ E2E Test Results - Production"
Summary
- Pass/fail counts with percentage
- Skipped count (if present)
- Flaky count (if present)
- Test duration
- Dashboard link (if provided)
- Example: "61/62 passed (98%) • 1 skipped • 1 flaky • ⏱️ 643s • View Dashboard"
Failed Tests Section (auto-hidden if empty)
- Test name (bold)
- Error message
- Link to detailed report (if provided)
Flaky Tests Section (auto-hidden if empty)
- Test name (bold)
- Number of retries before passing
Footer
- Trigger source
- GitHub Actions link (if provided)
- Example: "Triggered by schedule • GitHub Actions"
Testing
Run tests:
pnpm testRun tests in watch mode:
pnpm test:watchDevelopment
Build the package:
pnpm buildType check:
pnpm typecheckLint:
pnpm lintRelated Packages
@lytics/slack-client- Generic Slack client with fluent MessageBuilder API
License
MIT
