playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[Question] How to name video files

Open celeryclub opened this issue 4 years ago • 48 comments

Hi team. I love the new video API in 1.5.0. Very clean. I don't see any information about how to name video files in the examples though. Seems like each file is just assigned a random name with no way to change that. When the API was in beta, we could get a handle to the file in order to rename it. Am I missing something in the docs? https://playwright.dev/docs/videos

Thanks!

celeryclub avatar Oct 13 '20 21:10 celeryclub

Ouch, the name is in the trace, which is not yet public :/ Let us see what we can do to mitigate. Thanks for the early heads up!

pavelfeldman avatar Oct 13 '20 22:10 pavelfeldman

Thanks for investigating @pavelfeldman. 👍

celeryclub avatar Oct 13 '20 22:10 celeryclub

@celeryclub: we have a split in the team in terms of how we should handle this, please help us with sharing more details on your use case. We are going to have a trace info soon that will include the video name, so whatever API we add now is likely to not be needed in the future. So one of the options is to create contexts with unique folders and then scrape them for the video files. Does this work for you or you also need to know which page this video belongs to?

pavelfeldman avatar Oct 13 '20 23:10 pavelfeldman

Still interested to know the use case!

pavelfeldman avatar Oct 14 '20 05:10 pavelfeldman

@pavelfeldman We have built some functionality using playwright-video that deletes videos for successful tests. Sometimes, tests are run in parallel which makes it difficult to determine which video file belongs to which test when using the new built-in video API in [email protected]. Being able to name each video recording when launching a new Page or Context would help use solve this issue.

When using playwright-video, we do the naming in a custom Jest test environment using the following logic:

  getVideoFilename(test: Circus.TestEntry): string {
    const parentName = test.parent.name.replace(/\W/g, "-");
    const specName = test.name.replace(/\W/g, "-");
    return `${parentName}_${specName}`;
  }

Additionally, let's say you run 10 tests at once and 5 of them fail. You will get 5 video files with randomly generated names. You'll have to look through all the videos in order to find which recording belongs to which test. I do not know what features you have planned here, but it would be a real pain not to be able to name the files. Could you maybe elaborate a bit on what you have planned around this going forward?

Snikanes avatar Oct 14 '20 09:10 Snikanes

My use case is pretty much the same as @Snikanes. I use playwright-video currently and I need to know the names in order to conditionally delete the video if the test passes, and in order to label the video if the test fails.

celeryclub avatar Oct 14 '20 16:10 celeryclub

@Snikanes , @celeryclub : could you give playwright@next a try and tell us if the API in #4132 works for you? If yes, we will include it in 1.5.1 right away.

pavelfeldman avatar Oct 14 '20 17:10 pavelfeldman

@pavelfeldman that solution will work for us, although I'm curious as to what you have planned long term with regard to the API. In the PR you mentioned, the following comment was made, where saveAs is mentioned. I'm not sure if I should read anything into this comment, but I personally believe that providing the path rather than having to rename the file on page/context close is a saner API. Could you shed some light on what the plans are going forward here?

Also, thanks for the quick reply :100:

Snikanes avatar Oct 15 '20 06:10 Snikanes

Thanks for the fast follow-up! After further investigation, my first target will be using the official test runner, and then I'll revisit official video support since it appears to be built into the test runner.

celeryclub avatar Oct 15 '20 21:10 celeryclub

Could you shed some light on what the plans are going forward here?

Providing the path is not always feasible - imagine a popup with the animation that appears upon user action. We need to start saving it right away, but we don't know the name yet. To eliminate this dependency, we are letting the browser pick the name and start recording in a non-blocking manner.

We assume that you create a new browser context for each test and specify a new folder for each of these contexts, so the idea is that you get a guid instead of a name, but it should be easy to associate it with the test run.

pavelfeldman avatar Oct 19 '20 06:10 pavelfeldman

Hi all. I have a lot of test suites, every test suite usually creates one context and do some actions in one or two of tabs. So in my case probably the best way to divide video files is to create videos in separate folders.

But another feature that will be awesome to have - is an optional ability to set some pattern for video files. For example: TestSuite1-<auto-generated-id>.avi

DJ-Glock avatar Oct 23 '20 15:10 DJ-Glock

For naming the videos using playwright-video I use the names of the scenario's (using cucumber).

this.scenarioContext['scenarioName'] = scenario.pickle.name;

async startVideoCapturing(world: World, page: Page): Promise<void> {
    const scenarioName = world.scenarioContext['scenarioName'];
    const videoCaptureName = scenarioName.replace(/\s/g, '_');
    const dateTimeString = this.dateToDateTimeString(new Date());
    const videoCapture = await saveVideo(page, `${VideoCapturePath}/${videoCaptureName}_${dateTimeString}.mp4`);
    world.testCleaner.add(async () => await this.stopVideo(videoCapture));
  }

  async stopVideo(videoCapture: PageVideoCapture): Promise<void> {
    await videoCapture.stop();
  }

mortelee avatar Dec 30 '20 12:12 mortelee

@mortelee this issue is about the Playwright integrated video recording feature which was added a few versions ago. Your code snippet shows the usage with playwright-video, a third-party solution to record Chromium videos with Playwright.

mxschmitt avatar Dec 30 '20 12:12 mxschmitt

@mortelee this issue is about the Playwright integrated video recording feature which was added a few versions ago. Your code snippet shows the usage with playwright-video, a third-party solution to record Chromium videos with Playwright.

@mxschmitt true, just wanted to make clear that I would like to see that we can name video files.

mortelee avatar Dec 30 '20 12:12 mortelee

could be missing something (using pytest-playwright) but..

 "recordHar": {"path": "test.har"},
 "recordVideo": {"dir": "videos"}

What?

Why one accepts path and the other dir (with random filename?)

hampsterx avatar Jan 19 '21 20:01 hampsterx

Is there any update on this? We're liking playwright but having the uuid for the video file name is proving annoying. It's difficult to detect which video is for the failed test

csxero avatar Feb 14 '21 21:02 csxero

You can get the video path by executing: const videoFileName = await page.video().path(); Then, just rename it using fs.rename( videoFilePath, newPath, callback )

hananmalka avatar Feb 14 '21 21:02 hananmalka

You can get the video path by executing: const videoFileName = await page.video().path(); Then, just rename it using fs.rename( videoFilePath, newPath, callback )

This works, but it shouldn't have to be this janky. You should get a more readable name for free instead of uuid

csxero avatar Feb 17 '21 00:02 csxero

You can get the video path by executing: const videoFileName = await page.video().path(); Then, just rename it using fs.rename( videoFilePath, newPath, callback )

I've been trying to use this approach in my jest-playwright suite for a couple of days.

I'm facing following issues:

  1. fs.rename() requires context to be closed. Some of my tests use jest-playwright-preset's global context object; and when I use a global context in all tests and run tests in parallel, then I get issues in saving some but not other video files (probably because of closing context after one test while other parallel test is still running using global context).

  2. I sometimes use a non-global context (i.e. create my own context), I noticed playwright creates two video files one of them is empty file (approx 2KB in size) and other is a proper file with recorded video.

ychaudhari avatar Feb 17 '21 18:02 ychaudhari

Together with the great advice of @mxschmitt on the Playwright's slack channel, I came up with the following, fairly simple and robust solution:

  1. I created a custom 'testEnvironment': https://github.com/playwright-community/jest-playwright#usage-with-custom-testenvironment
  2. I used globally available page.video().path() to get the supposed video output path
  3. Then I used fs.rename with globally available test name as this.global.expect.getState().currentTestName
  • Important: files are written only after await super.teardown(), so file rename action must happen only after super.teardown (when file exists)

CustomEnvironment.js

const fs = require('fs');

const PlaywrightEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvironment')
  .default

class CustomEnvironment extends PlaywrightEnvironment {
  async setup() {
    await super.setup()
    // Your setup
  }

  async teardown() {
    await super.teardown()
    const videoFileName = await this.global.page.video().path();
    fs.rename(videoFileName, `test-run-recordings/${this.global.expect.getState().currentTestName}.webm`, (() => console.log('File renamed!')))
  }

  async handleTestEvent(event) {
  }
}

module.exports = CustomEnvironment

barakbs1 avatar Apr 03 '21 01:04 barakbs1

The workaround stopped working:

const videoFileName = await this.global.page.video().path();

This now throws an error:

Path is not available when using browserType.connect(). Use saveAs() to save a local copy.

I guess it is possible to use saveAs but it makes less and less sense...


Actually, no, now it is broken completely:

.saveAs: Browser has been closed

kemsky avatar May 11 '21 14:05 kemsky

Try

await this.global.page.video().saveAs(`test-run-recordings/${this.global.expect.getState().currentTestName}.webm`);

which should work.

mxschmitt avatar May 11 '21 14:05 mxschmitt

@mxschmitt, it turns out that after upgrading from 1.10.0 to 1.11.0 (playwright-core + playwright) it stopped recording videos at all, saveAs and path methods throw errors. I'm using it in combination with jest-playwright-preset + custom JestPlaywrightEnvironment to rename videos.

Probably this is a different bug.

kemsky avatar May 11 '21 14:05 kemsky

Hi all, I don't have the same issues as @kemsky but the new delete() and saveAs() don't work for us/me as well. It seems to never resolve when you call it (depending on if you call it when the test throws an error and if you call it before or after the page gets destroyed). In the end I gave up getting it to work and we rename the file ourselves:

  async renameFileLoop(page: Page, testFileName: string) {
    let succeededRename: boolean = false;
    while (!succeededRename) {
      try {
        const videoFilePath = await page.video().path();
        const newVideoPath = videoFilePath.replace(/\\(\w)*.webm/g, `\\${testFileName}.webm`);
        fs.renameSync(videoFilePath, newVideoPath);
        // console.log('rename success');
        succeededRename = true;
      } catch (e) {
        // console.log('rename fail');
        await this.sleep(1000);
      }
    }
  }

LanderBeeuwsaert avatar May 16 '21 08:05 LanderBeeuwsaert

Hi all, video saving works for me. I've added code snippet of how I use it:

private async saveVideo(world: CustomWorld, page: Page): Promise { const scenarioName = world.scenarioContext['scenarioName'] as string; const videoCaptureName = scenarioName.replace(/\s/g, '_'); const dateTimeString = this.dateToDateTimeString(new Date()); await page.video()?.saveAs(${VideoCapturePath}/${videoCaptureName}_${dateTimeString}.mp4); // Delete original video (saveAs creates a second video file). await page.video()?.delete(); }

private async stopBrowser(world: CustomWorld, browser: Browser, page: Page): Promise { await page.close(); await this.saveVideo(world, page); await browser.close(); }

Maybe it helps.

mortelee avatar May 19 '21 13:05 mortelee

@mxschmitt, I've created repo https://github.com/kemsky/playwright-video-bug and a corresponding bug https://github.com/playwright-community/jest-playwright/issues/708

kemsky avatar May 21 '21 16:05 kemsky

has anyone suggested optimized solution? I'm eager to find the way how rename videos. It's really hard to figure out specific tests among tens of randomly named videos.

danteYoon avatar Jul 29 '21 10:07 danteYoon

UP

orihomie avatar Oct 11 '21 11:10 orihomie

Is that possible to add to a BrowserContext (or where could I access test specific info about browser, viewport, etc...) some event listener like

on("destructor", listener: (worker: Worker) => void): this;

for one to be able to rename video at this moment?

UPD I've tried this for jest as @mortelee suggested

newContext.on('close', (c) => {
      c.pages().forEach(async function (p) {
        await p.video().saveAs(
          path.join(
            `${config.recordVideo.dir}`,
            `${test.name}_${new Date().toString()}_${p.viewportSize().height}x${p.viewportSize().width}`,
            ))

       await page.video()?.delete();
      })

But strangely this doesnt work :\ c.pages() returns empty array.

UPD What worked for me (I'm using jest runner, jest pw preset) is:

newPage.addListener('close', async (p) => {
      let videSize = `${p.viewportSize().height}x${p.viewportSize().width}`;
      let fileName =
        `${expect.getState().currentTestName}_${new Date().toString()}_${videSize}`;
      await p.video().saveAs(
        path.join(
          `${config.recordVideo.dir}`,
          fileName,
        ))

     await p.video()?.delete();
    });

orihomie avatar Oct 12 '21 07:10 orihomie

Also I've noticed that deleting video after saving it not working properly, it just wont be deleted, so I've hooked it this way:

 let video = page.video();

  if (!video) return;

  try {
    await video.saveAs(path.join(dir, fileName));
  } catch {}

  try {
    let videoPath = await video.path();
    unlink(videoPath, () => {});
  } catch {}

orihomie avatar Oct 15 '21 07:10 orihomie