DEV Community

Orbit A
Orbit A

Posted on

Bridging the Gap Between Appium and Jira: A Native Approach Using Atlassian Forge

Introduction: The Context-Switching Problem

In mobile automation, the distance between “a test failed” and “a developer fixed it” is often measured in manual steps. You run your Appium suite, wait for the CI/CD pipeline to finish, and then embark on a quest through thousands of lines of logs to find the culprit.

At Orbit A, we’ve seen this bottleneck slow down teams repeatedly. Having already built reporting tools for Playwright, Cypress, JMeter and k6, we set out to solve the mobile reporting puzzle. Our goal was to create a system where test results aren’t just an artifact in a CI console, but a living part of a Jira Issue.

The Architecture: Why Forge?

Most Jira integrations rely on external backends. You send data to a third-party server, which then talks to Jira via REST API.
We decided to go native. By building on Atlassian Forge, we eliminated the “middleman” entirely.

  • Data Residency: Your test metrics never leave the Atlassian infrastructure. They are stored in Forge Storage, complying with enterprise security standards.
  • Security: There are no external databases to breach. Authentication is handled via a tenant-specific Security Token, ensuring that only your authorized CI environment can push updates.

Step-by-Step: Integrating Appium with Jira

To make this work for any mobile project, we focused on WebdriverIO (WDIO) as the primary framework. Here is how we structured the integration.

1. Preparing the Environment
First, you need the standard WDIO stack. If you are starting fresh or adding to an existing project:

npm install @wdio/cli @wdio/local-runner @wdio/mocha-framework @wdio/spec-reporter --save-dev
Enter fullscreen mode Exit fullscreen mode

2. The Custom Reporter Logic
The core of the integration is a custom reporter. We’ve designed a template that handles the collection of test states (passed, failed, skipped) and sends a single, optimized payload at the end of the run.

Inside your project, create wdio-jira-reporter.js. Here is the logic we implemented to ensure data integrity:

const WDIOReporter = require('@wdio/reporter').default;
class JiraReporter extends WDIOReporter {
    constructor(options) {
        options = Object.assign(options, { stdout: true });
        super(options);
        // We pull the Jira ID from an env variable to keep it dynamic
        this.issueId = options.issueId || process.env.JIRA_ISSUE_ID;
        this.testResults = [];
        this.isDone = true;
    }
    onTestPass(test) {
        this.testResults.push({ title: test.title, status: 'passed', duration: test._duration || 0 });
    }
    onTestFail(test) {
        this.testResults.push({ title: test.title, status: 'failed', duration: test._duration || 0 });
    }
    async onRunnerEnd(runner) {
        this.isDone = false;
        const failedCount = this.testResults.filter(t => t.status === 'failed').length;

        const payload = {
            token: "YOUR_ORBIT_A_TOKEN", // Generated in Jira Config
            issueId: this.issueId,
            status: failedCount > 0 ? 'failed' : 'passed',
            summary: {
                passed: this.testResults.filter(t => t.status === 'passed').length,
                failed: failedCount,
                duration: runner.duration || 0
            },
            tests: this.testResults
        };
        try {
            await fetch('YOUR_FORGE_WEBTRIGGER_URL', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload)
            });
            console.log('[Orbit A] ✅ Results synced to Jira.');
        } catch (e) {
            console.error('[Orbit A] ❌ Sync failed:', e);
        } finally {
            this.isDone = true;
        }
    }
}
module.exports = JiraReporter;
Enter fullscreen mode Exit fullscreen mode

3. Execution via CLI
Linking a run to a specific Jira ticket is handled via environment variables. This makes it incredibly easy to use within GitHub Actions, GitLab CI, or Bitrise:

JIRA_ISSUE_ID=MOB-10010 npx wdio run wdio.conf.js
Enter fullscreen mode Exit fullscreen mode

NotaBene! How to find the Numeric Issue ID:

  1. Open the Jira Issue you are testing.
  2. Look at the URL in your browser. — If you are on a Board: You will see ...&selectedIssue=10010. The number is the ID. — If you are in the direct issue view: You can navigate to Jira ticket → click ‘Actions’ button → click ‘Export XML’ → copy value of ‘key_id’, i.e. {some-name} (here you’ll need to copy 10010)

The Result: A Unified QA View
When the payload hits the Forge Webtrigger, the app validates the token and stores the metrics. Inside Jira, we provide a custom Issue Panel.
When a developer or manager opens 10010, they don’t see a link to a log file - they see a rendered table.

hero snapshot

  • High-level metrics: Overall status and duration.
  • Test Breakdown: A searchable list of all test cases within that specific run.

Key Takeaway: By surfacing this data directly in the ticket, we’ve seen teams reduce “clarification time” between QA and Dev by up to 40%.

Conclusion: The Orbit A Ecosystem

The release of Appium Test Results for Jira completes our current vision of a unified QA reporting stack. Whether you are running Cypress or Playwright for web, JMeter or k6 for performance, or Appium for mobile, you now have a single, secure way to report back to Jira.

Automated testing shouldn’t just find bugs; it should make them easier to fix.

Explore the Orbit A suite on the Atlassian Marketplace:

If you found this helpful, follow Orbit A on LinkedIn. We are building a suite of native Atlassian apps for QA and DevOps teams!

Happy testing,
The Orbit A Team

Top comments (0)