Allure Reports (deep)

41Allure Reports (deep)

What you will master here

  • What Allure is, what it gives you over plain reports
  • Installing for Playwright, Java (TestNG/JUnit), Pytest
  • Annotations: @Feature, @Story, @Severity, @Step, @Description, @Issue, @TmsLink
  • Attachments: screenshots, logs, traces
  • History, trends, categories of defects
  • Hosting reports (S3, Allure TestOps SaaS)
  • CI integration

41.1 Why Allure

41.2 Setup — Playwright

npm i -D allure-playwright allure-commandline
// playwright.config.ts
reporter: [
  ['line'],
  ['allure-playwright', {
    detail: true,
    outputFolder: 'allure-results',
    suiteTitle: false,
    environmentInfo: {
      env: process.env.ENV ?? 'staging',
      browser: 'chromium',
      node: process.version,
    },
  }],
],
npx playwright test
npx allure generate ./allure-results -o ./allure-report --clean
npx allure open ./allure-report

41.3 Annotations (Playwright)

import { test } from '@playwright/test';
import { allure } from 'allure-playwright';

test('login - happy path', async ({ page }) => {
  await allure.epic('Authentication');
  await allure.feature('Login');
  await allure.story('Happy path with valid credentials');
  await allure.severity('critical');
  await allure.tag('smoke');
  await allure.issue('JIRA-1234', 'https://acme.atlassian.net/browse/JIRA-1234');
  await allure.tms('TC-5678');

  await allure.step('Open login page', async () => {
    await page.goto('/login');
  });
  await allure.step('Submit credentials', async () => {
    await page.getByLabel('Email').fill('alice@test.com');
    await page.getByLabel('Password').fill('p4ssw0rd');
    await page.getByRole('button', { name: 'Sign in' }).click();
  });
  await allure.step('Verify dashboard loaded', async () => {
    await expect(page.getByText('Dashboard')).toBeVisible();
  });
});

41.4 Java (TestNG) version

@Epic("Authentication")
@Feature("Login")
public class LoginTest {
  @Test(description = "Happy path")
  @Story("Happy path with valid credentials")
  @Severity(SeverityLevel.CRITICAL)
  @TmsLink("TC-5678")
  @Issue("JIRA-1234")
  public void happyLogin() {
    Allure.step("Open login page", () -> driver.get("/login"));
    Allure.step("Submit credentials", () -> { /* ... */ });
    Allure.step("Verify dashboard", () -> { /* ... */ });
  }
}

41.5 Attachments

// Screenshot
const buf = await page.screenshot();
await allure.attach('failure-screenshot', buf, 'image/png');

// HTML
await allure.attach('response', JSON.stringify(body, null, 2), 'application/json');

// Trace
await allure.attach('trace.zip', fs.readFileSync('trace.zip'), 'application/zip');

41.6 History & trends

Allure keeps a history/ folder. To get trends across runs, copy the previous report's history/ into the next run's allure-results/ before generating.

# In CI
- name: Download previous history
  uses: actions/download-artifact@v4
  with: { name: allure-history }
- run: cp -r allure-history/* allure-results/history/ || true
- run: npx allure generate allure-results -o allure-report --clean
- name: Upload new history
  uses: actions/upload-artifact@v4
  with: { name: allure-history, path: allure-report/history }

41.7 Categories (defect classification)

// allure-results/categories.json
[
  { "name": "Product defects", "matchedStatuses": ["failed"] },
  { "name": "Test defects",    "matchedStatuses": ["broken"] },
  { "name": "Outdated tests",  "matchedStatuses": ["skipped"] },
  { "name": "Infrastructure",  "matchedStatuses": ["failed","broken"],
    "messageRegex": ".*ECONNREFUSED.*|.*ETIMEDOUT.*" }
]

41.8 Hosting

Module 64 — Allure Q&A

Why Allure over the default reporter?
Stakeholder-readable hierarchy (Epic/Feature/Story/Test), step-level visibility, trends across runs, defect categorisation, attachments inline. Plain reports are pass/fail; Allure is "what happened and why".
What's allure.step for?
Wraps an action so the report shows it as a named step with its own status, duration, and attachments. Makes long tests readable. Failures point at the exact step.
How do you get history/trends?
Copy the previous run's history/ folder into the current allure-results/ before generating. CI workflow saves history as an artifact and restores on next run.
What are Categories?
Rules in categories.json that group failures by message regex/status. Lets you classify "product defect" vs "test defect" vs "infra flake" — actionable triage.
How do you attach a Playwright trace to Allure?
await allure.attach('trace.zip', fs.readFileSync(tracePath), 'application/zip'). Open in Allure → download → npx playwright show-trace locally. Some integrations link the trace viewer inline.
Severity field values?
blocker, critical, normal, minor, trivial. Use blocker for fundamental functionality, critical for major flows, normal for typical, minor for cosmetic.
What's Allure TestOps?
Paid SaaS / on-prem product from the Allure team. Aggregates results across many projects, supports manual test cases, links to Jira, history, defect tracking. Useful at scale.

Allure with Playwright Python — Full Setup

pip install allure-pytest pytest-playwright

# Run tests and generate Allure results
pytest tests/ --alluredir=allure-results -v

# Generate HTML report (requires Allure CLI)
allure generate allure-results --clean -o allure-report

# Serve report locally
allure serve allure-results

# Install Allure CLI (macOS)
brew install allure
import allure
import pytest
from playwright.sync_api import Page

@allure.epic("E-Commerce")
@allure.feature("Checkout")
@allure.story("Payment Processing")
class TestCheckout:

    @allure.title("User can complete checkout with valid card")
    @allure.severity(allure.severity_level.CRITICAL)
    @allure.description("Verify complete checkout flow from cart to order confirmation")
    def test_checkout_success(self, page: Page):

        with allure.step("Navigate to product and add to cart"):
            page.goto("https://shop.example.com/product/123")
            page.click('[data-testid="add-to-cart"]')
            allure.attach(page.screenshot(), "product-page.png", allure.attachment_type.PNG)

        with allure.step("Proceed to checkout"):
            page.click('[data-testid="checkout-btn"]')
            assert page.url.contains("/checkout")

        with allure.step("Fill payment details"):
            page.fill('[name="cardNumber"]', "4242424242424242")
            page.fill('[name="expiry"]', "12/28")
            page.fill('[name="cvv"]', "123")
            allure.attach(page.screenshot(), "payment-form.png", allure.attachment_type.PNG)

        with allure.step("Submit order and verify confirmation"):
            page.click('[type="submit"]')
            page.wait_for_selector('[data-testid="order-confirmation"]')
            order_id = page.text_content('[data-testid="order-id"]')
            allure.attach(
                f"Order ID: {order_id}".encode(),
                "order-details.txt",
                allure.attachment_type.TEXT
            )
            assert order_id.startswith("ORD-")

    @allure.title("Payment fails with expired card")
    @allure.severity(allure.severity_level.NORMAL)
    @pytest.mark.xfail(reason="Known issue: error message text changes monthly")
    def test_checkout_expired_card(self, page: Page):
        page.goto("https://shop.example.com/checkout")
        page.fill('[name="cardNumber"]', "4000000000000002")
        page.fill('[name="expiry"]', "01/20")  # expired
        page.click('[type="submit"]')

        with allure.step("Verify error message"):
            error = page.locator('[data-testid="payment-error"]')
            allure.attach(page.screenshot(), "error-state.png", allure.attachment_type.PNG)
            assert error.is_visible()
            assert "card" in error.text_content().lower()

Allure Custom Categories & Environment Info

// allure-results/categories.json — classify test failures
[
  {
    "name": "Product defects",
    "matchedStatuses": ["failed"],
    "messageRegex": ".*AssertionError.*expected.*"
  },
  {
    "name": "Test infrastructure problems",
    "matchedStatuses": ["broken"],
    "messageRegex": ".*TimeoutError.*|.*ConnectionError.*"
  },
  {
    "name": "Known flaky tests",
    "matchedStatuses": ["failed", "broken"],
    "traceRegex": ".*flaky.*"
  }
]
// allure-results/environment.properties
{
  "Browser": "Chromium 124",
  "Environment": "Staging",
  "Base URL": "https://staging.example.com",
  "Build": "2.14.0",
  "Python": "3.11.4",
  "Playwright": "1.44.0"
}