playwright
playwright copied to clipboard
[Question] How to name video files
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!
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!
Thanks for investigating @pavelfeldman. 👍
@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?
Still interested to know the use case!
@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?
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.
@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 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:
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.
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.
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
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 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.
@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.
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?)
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
You can get the video path by executing:
const videoFileName = await page.video().path();
Then, just rename it using fs.rename( videoFilePath, newPath, callback )
You can get the video path by executing:
const videoFileName = await page.video().path();
Then, just rename it usingfs.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
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:
-
fs.rename()
requires context to be closed. Some of my tests usejest-playwright-preset
's globalcontext
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). -
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.
Together with the great advice of @mxschmitt on the Playwright's slack channel, I came up with the following, fairly simple and robust solution:
- I created a custom 'testEnvironment': https://github.com/playwright-community/jest-playwright#usage-with-custom-testenvironment
- I used globally available
page.video().path()
to get the supposed video output path - Then I used
fs.rename
with globally available test name asthis.global.expect.getState().currentTestName
- Important: files are written only after
await super.teardown()
, so file rename action must happen only aftersuper.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
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
Try
await this.global.page.video().saveAs(`test-run-recordings/${this.global.expect.getState().currentTestName}.webm`);
which should work.
@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.
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);
}
}
}
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${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
Maybe it helps.
@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
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.
UP
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();
});
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 {}