Test Output and Artifacts

Back

Loading concept...

🎯 Test Output and Artifacts in Playwright

The Detective’s Evidence Locker 🔍

Imagine you’re a detective solving a mystery. After every investigation, you collect evidence — photos, fingerprints, notes, and recordings. You put them in a special evidence locker so you can look back later and understand exactly what happened.

Playwright works the same way!

When your tests run, Playwright can save artifacts — screenshots, videos, logs, and traces. These go into a special folder called the test output directory. If a test fails, you can open the locker and see exactly what went wrong!


🗂️ Test Output Directory

What Is It?

The test output directory is like a filing cabinet for your test evidence. Every time Playwright collects artifacts, it puts them here.

By default, this folder is called test-results/ and lives in your project root.

See It In Action

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  outputDir: './my-test-evidence',
});

What happens:

  • All screenshots, videos, and traces go into ./my-test-evidence/
  • Each test gets its own subfolder!

Real Life Example 🏠

Think of a school where every student has their own locker:

my-test-evidence/
├── login-test-chromium/
│   ├── screenshot.png
│   └── video.webm
├── cart-test-firefox/
│   └── trace.zip

Each test (student) has its own space (locker) inside the main cabinet!


📦 Artifact Collection

What Are Artifacts?

Artifacts are the evidence your tests collect:

Artifact What It Does Like In Real Life
Screenshot A picture of the page Taking a photo
Video Recording of the whole test Security camera
Trace Step-by-step replay A detailed diary

Collecting Screenshots

import { test, expect } from '@playwright/test';

test('take a photo', async ({ page }) => {
  await page.goto('https://example.com');

  // Take a screenshot!
  await page.screenshot({
    path: 'my-screenshot.png'
  });
});

Automatic Collection (The Easy Way!)

// playwright.config.ts
export default defineConfig({
  use: {
    // Screenshot on failure
    screenshot: 'only-on-failure',

    // Record video always
    video: 'on',

    // Save trace on first retry
    trace: 'on-first-retry',
  },
});

Options explained:

  • 'off' — Never collect
  • 'on' — Always collect
  • 'only-on-failure' — Only when test fails
  • 'on-first-retry' — Only on retry attempts
graph TD A["Test Runs"] --> B{Did it pass?} B -->|Yes| C["Keep going!"] B -->|No| D["Collect Evidence"] D --> E["Screenshot 📸"] D --> F["Video 🎬"] D --> G["Trace 📋"]

🔧 The test.info() Method

Your Test’s Memory Book

test.info() is like a personal notebook that each test carries. You can:

  • Read information about the test
  • Add notes and attachments
  • Check the test’s status

Basic Usage

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

test('my test', async ({ page }) => {
  // Get test info
  const info = test.info();

  // What's the test called?
  console.log(info.title);
  // Output: "my test"

  // What file is it in?
  console.log(info.file);

  // How long can it run?
  console.log(info.timeout);
});

Adding Your Own Attachments

test('save evidence', async ({ page }) => {
  await page.goto('https://example.com');

  // Attach a screenshot to the report
  const screenshot = await page.screenshot();

  await test.info().attach('homepage', {
    body: screenshot,
    contentType: 'image/png',
  });
});

Attaching Text Notes

test('save notes', async ({ page }) => {
  const apiResponse = await page.request
    .get('/api/data');
  const data = await apiResponse.text();

  // Attach the API response as a note
  await test.info().attach('api-response', {
    body: data,
    contentType: 'text/plain',
  });
});

Why do this? When you view your test report, you’ll see these attachments right there — like sticky notes in your evidence file!


🏷️ Test Metadata

Labels for Your Tests

Metadata is like putting stickers on your test files:

  • “This is a smoke test” 🔥
  • “This is slow” 🐢
  • “This tests login” 🔐

Adding Annotations

test('login test',
  { tag: ['@smoke', '@auth'] },
  async ({ page }) => {
    // Your test code here
});

Built-in Annotation Types

test('my test', async ({ page }) => {
  // Mark as slow (3x timeout)
  test.slow();

  // Skip this test
  test.skip();

  // Mark as failing (expected to fail)
  test.fail();

  // Add a custom note
  test.info().annotations.push({
    type: 'issue',
    description: 'Bug #123',
  });
});

Conditional Annotations

test('windows only', async ({ page }) => {
  // Skip on Mac and Linux
  test.skip(
    process.platform !== 'win32',
    'Windows only feature'
  );

  // Now test the feature...
});
graph TD A["Test Starts"] --> B{Check Conditions} B -->|Skip?| C["Mark Skipped ⏭️"] B -->|Slow?| D["Extend Timeout 🐢"] B -->|Run!| E["Execute Test ▶️"] E --> F["Add Annotations 🏷️"]

Reading Metadata

test('check my labels', async ({ page }) => {
  const info = test.info();

  // See all annotations
  console.log(info.annotations);

  // Check tags
  console.log(info.tags);
  // ['@smoke', '@auth']
});

🖥️ Browser Console Logs

Listening to the Browser Talk

The browser’s console is like a chatty friend. It tells you:

  • Errors and warnings ⚠️
  • Debug messages 📝
  • What JavaScript is doing

Capturing Console Logs

test('hear the browser', async ({ page }) => {
  // Listen for ALL console messages
  page.on('console', msg => {
    console.log(`Browser says: ${msg.text()}`);
  });

  await page.goto('https://example.com');
});

Filtering by Type

test('errors only', async ({ page }) => {
  const errors = [];

  page.on('console', msg => {
    if (msg.type() === 'error') {
      errors.push(msg.text());
    }
  });

  await page.goto('https://example.com');

  // Check no errors happened
  expect(errors).toHaveLength(0);
});

Console Message Types

Type What It Means Example
log Regular message console.log()
error Something broke! console.error()
warning Heads up! console.warn()
info FYI console.info()

Saving Console Logs as Artifacts

test('save all logs', async ({ page }) => {
  const logs = [];

  page.on('console', msg => {
    logs.push({
      type: msg.type(),
      text: msg.text(),
      time: new Date().toISOString(),
    });
  });

  await page.goto('https://example.com');

  // Attach logs to report
  await test.info().attach('console-logs', {
    body: JSON.stringify(logs, null, 2),
    contentType: 'application/json',
  });
});
graph TD A["Page Loads"] --> B["Browser Console"] B --> C{Message Type?} C -->|log| D["📝 Info"] C -->|error| E["❌ Error"] C -->|warning| F["⚠️ Warning"] D --> G["Save to Array"] E --> G F --> G G --> H["Attach to Report"]

🎯 Putting It All Together

Here’s a complete example using everything we learned:

import { test, expect } from '@playwright/test';

test('complete detective work',
  { tag: ['@smoke'] },
  async ({ page }) => {
    const consoleMessages = [];

    // 1. Listen to browser console
    page.on('console', msg => {
      consoleMessages.push(msg.text());
    });

    // 2. Go to the page
    await page.goto('https://example.com');

    // 3. Take a screenshot
    const screenshot = await page.screenshot();

    // 4. Attach it using test.info()
    await test.info().attach('evidence', {
      body: screenshot,
      contentType: 'image/png',
    });

    // 5. Attach console logs
    await test.info().attach('browser-logs', {
      body: consoleMessages.join('\n'),
      contentType: 'text/plain',
    });

    // 6. Add metadata annotation
    test.info().annotations.push({
      type: 'note',
      description: 'Collected all evidence!',
    });
});

🚀 Quick Summary

Concept What It Does Remember As
Output Directory Where artifacts go Filing cabinet 🗄️
Artifact Collection Screenshots, videos, traces Evidence collection 📦
test.info() Test’s personal notebook Memory book 📓
Test Metadata Labels and annotations Sticky notes 🏷️
Console Logs Browser’s messages Chatty friend 💬

You’re now a test detective! 🕵️‍♀️

Every time a test fails, you’ll have all the evidence you need to solve the mystery and fix the bug!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.