playwright
playwright copied to clipboard
[Feature] Make test execution speed (option slowMo) adjustable during test execution
Currently, you can only artificially lower Playwright's execution speed for the entirety of a test execution, using the browserType.launch option "slowMo" (see https://playwright.dev/docs/api/class-browsertype#browsertypelaunchoptions):

However, in many debugging situations you're only interested in watching Playwright doing it slowly during specific parts of your test while the remainder of your test execution could very well be executed at normal (warp) speed in order to save your precious test development/debugging time (e.g. you're testing an app consisting of 6 pages and you only want to watch the last page being tested in slow motion).
Therefore, it would be very handy if you could slow Playwright temporarily down for specific parts of your test code only, for example via await page.slow(30) and then resetting it via await page.slow(0) or something comparable.
That sounds great! Could be used with the Inspector if one has also set await page.paus() to then directly in the Inspector set the slow speed (without even having to use await page.slow(30). I.e. you could use that, but when you hit that location the Inspector has a field where you see the normal speed change to 30, but you can then also further change it if wanted to another speed direclty.
Actually, API-wise, maybe something like page.debug( { slowMo: 60 } ) would be better in order to cater for future debug-related options.
If so, then I assume page.pause() should be put into that as well. I'd maybe rather have page.debug.slowMo(60) then as I find it faster to type and easier for auto-completion. But I suppose all this is up to the Playwright peeps :)
@thernstig Yes, indeed, in that case it would make sense to integrate page.pause() into page.debug(...) as well, good hint!
Jep, for us, a slowMo per page + (optionally that you can set dynamically) would be what we are looking for.
While this is collecting feedback, I'd like to discuss something related: it'd be amazing if I could have a set of configuration options which automatically get called when I call page.pause().
Use-case: I call pause so that I can experiment and figure out the best selector for an element. I will be poking around the page and the test fails because it hits the 30 second timeout. Right now, I either disable the timeout on the cli npx playwright test --timeout=0 or I set the test timeout in the test:
const { test, expect } = require('@playwright/test');
test('my test name here', async ({ page }) => {
await page.goto('http://example.com');
test.setTimeout(0);
await page.pause();
});
Example implementation:
module.exports = {
timeout: 30000,
pauseOptions: {
timeout: 0,
slowMo: 200,
},
};
+1. the above would be awesome ^
Do you have any updates deadlines for this?
It seems like this affects typing into input fields, so a slowmo of 2000 makes typing extremely slow.
Overriding typing speed is not possible with this parameter:
*locator*.type(text, {
delay: 10
})
would be really helpful +1
Definitely a must! +1 Currently getting trained in Playwright and my training lead me here.
Since this issue is approaching is first birthday, I'm using this opportunity to ping @pavelfeldman, @arjunattam and @mxschmitt to highlight people's interest in this feature. :eyes: :pray:
Here's a hack I wrote to get this functionality going without any changes required to the Playwright codebase.
For context, I wanted to "slow down" certain operations in the tests so I could record videos that didn't blaze through the operations so they could be used to show people for training purposes.
Waiting per-operation is easy in a test by just wrapping the request in a Promise that delays a certain amount of time, but I wanted to be able to enable/disable the delay with a small code change instead of having to change every line of the test to wait a certain amount of time.
Here's what I came up with (it's in TypeScript). It wraps the page.locator function with a version that replaces click and fill to be artificially slowed.
import { Locator, Page } from "@playwright/test";
// Return a "slow" page locator that waits before 'click' and 'fill' requests
function slowLocator(
page: Page,
waitInMs: number
): (...args: any[]) => Locator {
// Grab original
const l = page.locator.bind(page);
// Return a new function that uses the original locator but remaps certain functions
return (locatorArgs) => {
const locator = l(locatorArgs);
locator.click = async (args) => {
await new Promise((r) => setTimeout(r, waitInMs));
return l(locatorArgs).click(args);
};
locator.fill = async (args) => {
await new Promise((r) => setTimeout(r, waitInMs));
return l(locatorArgs).fill(args);
};
return locator;
};
}
Now, I just put this at the start of my test:
test("sample test", async ({ page }) => {
// Overwrite page.locator to give us back a version that waits 500ms before `click` and `fill`
page.locator = slowLocator(page, 500);
await page.locator('input).fill();
await page.locator('button').click();
});
It should be pretty trivial to add delays to methods on the locator other than click and fill by following the pattern.
Now if you want the original/fast version of the locator, just comment out the one line that resets the page.locator and you get back your speedy tests. If you want to record a slowed-down video you can just uncomment the line (or add it in temporarily) to get a more relaxed version.
page.locator = slowLocator(page, 500);
Can I get the same for expect assertions with tobevisible or tobeenabled combination
does anyone know if this feature has been added to the options when running test from command line?
Here's a hack I wrote to get this functionality going without any changes required to the Playwright codebase.
For context, I wanted to "slow down" certain operations in the tests so I could record videos that didn't blaze through the operations so they could be used to show people for training purposes.
Waiting per-operation is easy in a test by just wrapping the request in a Promise that delays a certain amount of time, but I wanted to be able to enable/disable the delay with a small code change instead of having to change every line of the test to wait a certain amount of time.
Here's what I came up with (it's in TypeScript). It wraps the
page.locatorfunction with a version that replacesclickandfillto be artificially slowed.import { Locator, Page } from "@playwright/test"; // Return a "slow" page locator that waits before 'click' and 'fill' requests function slowLocator( page: Page, waitInMs: number ): (...args: any[]) => Locator { // Grab original const l = page.locator.bind(page); // Return a new function that uses the original locator but remaps certain functions return (locatorArgs) => { const locator = l(locatorArgs); locator.click = async (args) => { await new Promise((r) => setTimeout(r, waitInMs)); return l(locatorArgs).click(args); }; locator.fill = async (args) => { await new Promise((r) => setTimeout(r, waitInMs)); return l(locatorArgs).fill(args); }; return locator; }; }Now, I just put this at the start of my test:
test("sample test", async ({ page }) => { // Overwrite page.locator to give us back a version that waits 500ms before `click` and `fill` page.locator = slowLocator(page, 500); await page.locator('input).fill(); await page.locator('button').click(); });It should be pretty trivial to add delays to methods on the locator other than
clickandfillby following the pattern.Now if you want the original/fast version of the locator, just comment out the one line that resets the
page.locatorand you get back your speedy tests. If you want to record a slowed-down video you can just uncomment the line (or add it in temporarily) to get a more relaxed version.
Do you know if there's a way to do this with the python bindings?
+1 for this issue. I'm using getAttribute to get all the attributes of 90+ elements to verify if they are filled in or not, while the rest is just ordinary navigating and clicking. The getAttribute loop grinds to a halt with a slowmo of 1 second, while the rest could benefit from taking a second to be able to see what is going on. Being able to selectively pass a slowmo for a test step or certain sections would be useful rather than having to toggle it for a test.
Here's a hack I wrote to get this functionality going without any changes required to the Playwright codebase.
For context, I wanted to "slow down" certain operations in the tests so I could record videos that didn't blaze through the operations so they could be used to show people for training purposes.
Waiting per-operation is easy in a test by just wrapping the request in a Promise that delays a certain amount of time, but I wanted to be able to enable/disable the delay with a small code change instead of having to change every line of the test to wait a certain amount of time.
Here's what I came up with (it's in TypeScript). It wraps the
page.locatorfunction with a version that replacesclickandfillto be artificially slowed.import { Locator, Page } from "@playwright/test"; // Return a "slow" page locator that waits before 'click' and 'fill' requests function slowLocator( page: Page, waitInMs: number ): (...args: any[]) => Locator { // Grab original const l = page.locator.bind(page); // Return a new function that uses the original locator but remaps certain functions return (locatorArgs) => { const locator = l(locatorArgs); locator.click = async (args) => { await new Promise((r) => setTimeout(r, waitInMs)); return l(locatorArgs).click(args); }; locator.fill = async (args) => { await new Promise((r) => setTimeout(r, waitInMs)); return l(locatorArgs).fill(args); }; return locator; }; }Now, I just put this at the start of my test:
test("sample test", async ({ page }) => { // Overwrite page.locator to give us back a version that waits 500ms before `click` and `fill` page.locator = slowLocator(page, 500); await page.locator('input).fill(); await page.locator('button').click(); });It should be pretty trivial to add delays to methods on the locator other than
clickandfillby following the pattern.Now if you want the original/fast version of the locator, just comment out the one line that resets the
page.locatorand you get back your speedy tests. If you want to record a slowed-down video you can just uncomment the line (or add it in temporarily) to get a more relaxed version.
Is it possible to write similar wrapper for getByRole ?
When running a test manually and watching the browser in headed mode, it would be very convenient to set the "speed" (slowMo) in the command line, rather than editing the test or creating a config file.
does anyone know if this feature has been added to the options when running test from command line?
This is my workaround to automatically set slowMo when running in --headed mode (that is usually helpful):
export default defineConfig({
use: {
launchOptions: {
slowMo: isHeadedMode() ? 1000 : undefined
}
}
});
function isHeadedMode() {
// important to use env var - for workers
if (process.argv.includes('--headed')) process.env.HEADED_MODE = '1';
return Boolean(process.env.HEADED_MODE);
}
Has anyone solved this with Python?
Bumping, this would be super useful. I currently have concurrent process happening. I have a MutationObserver that looks for DOM changes and then takes a screenshot via an exposed function. The screenshot however is too slow as the UI changes and updates the UI. I'd like a hook or setting that would be able to slow down all commands except the screenshot which is running in another thread.