Visualize Playwright E2E test metrics, history, and status directly in Jira issues.

This Atlassian Forge app bridges the gap between QA and Project Management. It allows you to push test results from your CI/CD pipeline (or local machine) directly to related Jira tickets using a simple custom reporter.

Features

Installation & Setup

1. Install the App

Install Playwright Test Results from the Atlassian Marketplace into your Jira instance.

2. Get your Credentials

  1. Open Jira and navigate to Apps in the top menu.
  2. Select Playwright to Jira Config.
  3. Copy your Webhook URL and Security Token.

3. Configure Playwright

Create a file named jira-reporter.js in your Playwright project root and paste the code below. (Note: You can also copy this code directly from the App's Config page).

  const reporterCode = `// playwright-jira-reporter.js
class JiraReporter {
  constructor(options = {}) {
    this.issueId = options.issueId || process.env.JIRA_ISSUE_ID;
  }

  onBegin(config, suite) {
    this.suite = suite;
    this.startTime = Date.now();
  }

  async onEnd(result) {
    const duration = Date.now() - this.startTime;
    const stats = { passed: 0, failed: 0, skipped: 0, duration: duration };
    const tests = [];

    const traverse = (suite) => {
      for (const test of suite.tests) {
        const outcome = test.results[test.results.length - 1];
        if (!outcome) continue;

        let mappedStatus = 'skipped';
        if (outcome.status === 'passed') {
          stats.passed++;
          mappedStatus = 'passed';
        } else if (outcome.status === 'failed' || outcome.status === 'timedOut' || outcome.status === 'interrupted') {
          stats.failed++;
          mappedStatus = 'failed';
        } else {
          stats.skipped++;
        }

        tests.push({
          title: test.title,
          status: mappedStatus,
          duration: outcome.duration || 0
        });
      }
      for (const child of suite.suites) traverse(child);
    };
    
    traverse(this.suite);

    const overallStatus = stats.failed > 0 ? 'failed' : 'passed';

    const payload = {
      token: "${token}", // Auto-injected by Jira
      issueId: this.issueId,
      status: overallStatus,
      summary: stats,
      tests: tests
    };

    if (!this.issueId) {
       console.warn('\\\\n[Jira Reporter] No JIRA_ISSUE_ID provided. Skipping Jira update.');
       return;
    }

    console.log('\\\\n[Jira Reporter] Sending report to Jira issue:', this.issueId);
    
    try {
      await fetch('${webhookUrl}', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload)
      });
      console.log('[Jira Reporter] ✅ Successfully sent to Jira!');
    } catch (e) {
      console.error('[Jira Reporter] ❌ Failed to send to Jira:', e);
    }
  }
}

module.exports = JiraReporter;`;