Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issues setting up video record in playwright - docs have been followed

Playwright Tests Run Successfully but No Video Recordings Generated

I'm running Playwright tests for my web application. The tests execute successfully, but no video recordings are being generated. I've configured video recording in my playwright.config.js, but the handleTestVideo function in my afterEach hook always reports that no video was recorded.

Environment

  • Playwright version: 1.47.0
  • Operating System: Windows
  • Node.js version: 16
  • full repo: github.com/lmcrean/odyssey-api

Code

playwright.config.js


export default defineConfig({
  use: {
    video: {
      mode: 'on',
      size: { width: 640, height: 480 }
    },
  },

});

see full code FYI

// @ts-check
import { defineConfig, devices } from '@playwright/test';



export default defineConfig({
  testDir: './playwright',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [['list'], ['json', { outputFile: 'test-results.json' }]],
  outputDir: 'test-results',
  use: {
    baseURL: 'http://localhost:8080',
    trace: 'on-first-retry',
    screenshot: 'on',
    video: {
      mode: 'on',
      size: { width: 640, height: 480 }
    },
    headless: true,
  },
  projects: [
    {
      name: 'chromium',
      use: { 
        ...devices['Desktop Chrome'],
      },
    },
  ],
  webServer: {
    command: 'python3 manage.py runserver',
    url: 'http://localhost:8080',
    reuseExistingServer: !process.env.CI,
    timeout: 120 * 1000
  },
  preserveOutput: 'never',
});

Test file

test('Validate message send form and capture screenshots', async ({ page }) => {
  // Test actions, pass as expected...
});

test.afterEach(async ({ }, testInfo) => {
  await handleTestVideo(testInfo);
});

testUtils.js

Looking at the official playwright docs it should be unnecessary to use any utils, the solution should be much more simple than this.

I am aware that this debugging step is almost worthless (be gentle on me I am quite junior...) But my main point is that there is no video.

export async function handleTestVideo(testInfo) {
  console.log('Handling test video...');
  console.log('Test status:', testInfo.status);
  
  // Wait for the video to be ready
  await testInfo.attachments.find(attachment => attachment.name === 'video');
  console.log('Video attachment found:', testInfo.attachments.find(attachment => attachment.name === 'video'));

  const videoAttachment = testInfo.attachments.find(attachment => attachment.name === 'video');
  console.log('Video attachment:', videoAttachment);
  if (videoAttachment) {
    const videoPath = videoAttachment.path;
    console.log('Video path:', videoPath);
    
    if (videoPath) {
      const videoDir = path.join('playwright', 'videos', testInfo.title);
      const newPath = path.join(videoDir, `${testInfo.title}.webm`);
      
      // Ensure the directory exists
      fs.mkdirSync(videoDir, { recursive: true });

      // Wait for the video file to be fully written
      await new Promise(resolve => setTimeout(resolve, 1000));

      // Move the video file
      fs.renameSync(videoPath, newPath);
      console.log(`Video saved to: ${newPath}`);
    } else {
      console.log('Video path is undefined.');
    }
  } else {
    console.log('No video attachment found for this test.');
  }
}

console log

[test logs as expected, pass]
Handling test video...
Test status: passed
Video attachment found: undefined
Video attachment: undefined
No video attachment found for this test

Test File

import { test, expect } from '@playwright/test';
import { login, captureScreenshot, handleTestVideo, baseURL, handleFailedTestVideo } from './testUtils';
import path from 'path';

const BASE_URL = baseURL;

test.describe('Message Detail Send Form Validation', () => {
  test('Validate message send form and capture screenshots', async ({ page }) => {
    await login(page);

    console.log(`Navigating to ${BASE_URL}/messages/1`);
    await page.goto(`${BASE_URL}/messages/1`);

    console.log('Starting message send form validation tests');

    await page.waitForSelector('form', { state: 'visible' });

    // Test: Empty submission
    console.log('Testing empty submission');
    await page.click('button:has-text("Send")');
    
    // Wait for the alert to appear
    await page.waitForSelector('.alert-warning', { state: 'visible', timeout: 5000 });
    
    const alertText = await page.textContent('.alert-warning');
    expect(alertText).toContain('Please enter a message or upload an image');
    
    await captureScreenshot(page, 'message-send', '1-empty-submission');
    console.log('Captured screenshot for empty submission');

    // Test: Message too long
    console.log('Testing message too long');
    await page.fill('textarea[name="content"]', 'a'.repeat(1001));
    await page.click('button:has-text("Send")');
    
    // Wait for the alert to appear
    await page.waitForSelector('.alert-warning', { state: 'visible', timeout: 5000 });
    
    const longMessageAlertText = await page.textContent('.alert-warning');
    expect(longMessageAlertText).toContain('Message should not exceed 1000 characters');
    
    await captureScreenshot(page, 'message-send', '2-message-too-long');
    console.log('Captured screenshot for message too long');

    // Test: Message with only spaces
    console.log('Testing message with only spaces');
    await page.fill('textarea[name="content"]', '   ');
    await page.click('button:has-text("Send")');
    
    // Wait for the alert to appear
    await page.waitForSelector('.alert-warning', { state: 'visible', timeout: 5000 });
    
    const spacesAlertText = await page.textContent('.alert-warning');
    expect(spacesAlertText).toContain('Please enter a message or upload an image');
    
    await captureScreenshot(page, 'message-send', '3-spaces-only');
    console.log('Captured screenshot for spaces-only message');
    

    // Test: Image file too large
    console.log('Testing image file too large');
    const largeImagePath = path.join(__dirname, 'test-assets', 'large-image.jpg');
    await page.setInputFiles('input[type="file"]', largeImagePath);
    
    // Wait for the alert to appear
    await page.waitForSelector('.alert-warning', { state: 'visible', timeout: 5000 });
    
    const largeImageAlertText = await page.textContent('.alert-warning');
    expect(largeImageAlertText).toContain('Image file size should not exceed 5MB');
    
    await captureScreenshot(page, 'message-send', '4-image-too-large');
    console.log('Captured screenshot for image too large');

    // Test: invalid image type
    console.log('Testing invalid image type');
    const invalidImagePath = path.join(__dirname, 'test-assets', 'test-file.txt');
    await page.setInputFiles('input[type="file"]', invalidImagePath);
    
    // Wait for the alert to appear
    await page.waitForSelector('.alert-warning', { state: 'visible', timeout: 5000 });
    
    const invalidImageAlertText = await page.textContent('.alert-warning');
    expect(invalidImageAlertText).toContain('Only JPEG, PNG, and GIF images are allowed');
    
    await captureScreenshot(page, 'message-send', '5-invalid-image-type');
    console.log('Captured screenshot for invalid image type');

    console.log('Message send form validation tests completed');

     // Test: Valid image with no message
     console.log('Testing valid image with no message');
     const validImagePath = path.join(__dirname, 'test-assets', 'test-image.jpg');
     await page.fill('textarea[name="content"]', '');
     await page.setInputFiles('input[type="file"]', validImagePath);
     await captureScreenshot(page, 'message-send', '6.1-valid-image-no-message-preview');
     await page.click('button:has-text("Send")');
     await page.waitForTimeout(200);
     
     // Check if there are no error messages
     const errorMessages = await page.$$('.alert-warning');
     expect(errorMessages.length).toBe(0);
     await page.waitForTimeout(2000);
     await captureScreenshot(page, 'message-send', '6-valid-image-no-message');
      console.log('Captured screenshot for valid image with no message');

    // Test: Valid message with no image
    console.log('Testing valid message with no image');
    await page.fill('textarea[name="content"]', 'Hello, world!');
    await page.click('button:has-text("Send")');
    // wait for network idle
    await page.waitForTimeout(2000);
    await captureScreenshot(page, 'message-send', '7-valid-message-no-image');
  });
});

test.afterEach(async ({ }, testInfo) => {
  await handleTestVideo(testInfo);
});

Has anyone out there got video recording in playwright and how did they do it? What am I missing?

docs suggest it is very simple - my screenshots are working perfectly.

https://playwright.dev/docs/videos#record-video

like image 941
Laurie Crean Avatar asked Jan 21 '26 22:01

Laurie Crean


1 Answers

This can be fixed in playwright.config.ts and there's no need for a custom handleTestVideo method. You don't need afterAll either, unless you created the browser context yourself.

When using the default JavaScript/TypeScript test runner, Playwright will always record videos when playwright.config.ts includes this:

use: {
  video: {
    mode: 'on',
  },
  contextOptions: {
    recordVideo: {
      dir: 'test-results',
    }
  }
}

The playwright documentation doesn't make it clear that you need to add recordVideo under contextOptions, but it won't work without it. I recommend using mode: 'retain-on-failure' instead of mode: 'on' so that it deletes your videos whenever the test passes.

https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-video

https://playwright.dev/docs/api/class-testoptions#test-options-context-options https://playwright.dev/docs/videos

like image 153
emery Avatar answered Jan 25 '26 14:01

emery



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!