-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: mouse and keyboard indicators #3526
feat: mouse and keyboard indicators #3526
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
06e5d25
to
4f5f533
Compare
docs/api.md
Outdated
@@ -4135,6 +4135,8 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'. | |||
- `options` <[Object]> | |||
- `wsEndpoint` <[string]> A browser websocket endpoint to connect to. **required** | |||
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. | |||
- `showMouseIndicator` <[boolean]> Show Playwright's mouse cursor. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider doing this under PWDEBUG instead? How do you see these options being used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's land this behind env instead.
cb88ddb
to
b4815fa
Compare
@dgozman and I talked offline about this.
By myself I had a change of heart about Instead I was inspired by typescript's "strict": true, which has related options "strictBindCallApply", "strictNullChecks", etc. I've renamed |
src/server/input.ts
Outdated
function createKeyboardIndicator() { | ||
const element = document.createElement('playwright-keyboard-indicator'); | ||
const shadow = element.attachShadow({mode: 'closed'}); | ||
shadow.innerHTML = ` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
our injected script already has html library for debug overlays, let's align those implementations.
src/server/input.ts
Outdated
@@ -50,14 +51,98 @@ export class Keyboard { | |||
this._page = page; | |||
} | |||
|
|||
async _updateKeyboardIndicator(description?: KeyDescription) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's get some code modularity going. This is a debugging code, it needs to be strictly separate from the code functionality. See what Dmitry did for tracing - make sure your auxiliary feature is using apis / dependency injection where necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requesting changes for modularity, we can't pollute core.
e16de46
to
c937ef0
Compare
This has been updated. ptal. |
c937ef0
to
dc0305d
Compare
Actually, still needs work. It is disconnected from the existing debugging features and cli, we should get some consistency going there. |
Just chiming in to say that I would love to see this feature get integrated 👍 |
dc0305d
to
aa97293
Compare
Merged all of the options into |
docs/src/api/class-browsertype.md
Outdated
@@ -157,6 +157,11 @@ headless`] option will be set `false`. | |||
|
|||
Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. | |||
|
|||
### option: BrowserType.launch.showUiserInput |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo in the option name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like that we lost linting for these
src/server/browserContext.ts
Outdated
} | ||
|
||
export type InputEvent = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like instrumentation has grown large enough to justify a separate file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I made a contextListeners file.
src/debug/showUserInput.ts
Outdated
shadow.appendChild(div); | ||
} | ||
if (timeout) | ||
clearInterval(timeout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This uses setTimeout + clearInterval. Let's decide whether it's a timeout or an interval 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/debug/showUserInput.ts
Outdated
timeout = setTimeout(() => { | ||
div.remove(); | ||
timeout = null; | ||
}, 200); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Played with this, 200ms is too short for me. WDYT about reusing the slowMo value is present? Same for tap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. (slowMo || 150) + 50
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For tap I think always 500ms is fine. We treat tap as an atomic action and dont slowMo inbetween touchstart and touchend.
src/debug/showUserInput.ts
Outdated
text = ''; | ||
} else if (!modifiers.size && key.trim().length === 1) { | ||
const currentText = (textForPage.get(page) || '') + key; | ||
textForPage.set(page, currentText); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should reset this somewhere, otherwise multiple texts will coalesce. Perhaps on other forms of input? Navigations? Timeout?
More generally, I think the text should increase while the tooltip is shown, and be reset once the tooltip is hidden. wdyt?
await page.fill('username', 'John Doe');
await page.check('remember be');
await page.fill('password', 'foo');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I think theres a lot we can do by taking into account the specific method that called into the keyboard. I was looking at a karaoke style https://youtu.be/OU3699R53rs?t=23 for page.type. And we can remove the indicator on page.fill if its filling uninterrupted into a textarea. It's just redundant there.
I'd rather tackle these in a follow up patch because they involve a lot of api changes and bikeshedding.
c401499
to
6d87cf7
Compare
6d87cf7
to
c28b9c1
Compare
@@ -197,6 +197,11 @@ Specify environment variables that will be visible to the browser. Defaults to ` | |||
|
|||
Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. | |||
|
|||
### option: BrowserType.launch.showUserInput |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am hesitant with a public option. We are growing a list of options nobody will ever know about. Let's think of "one to rule them all" approach?
@@ -290,6 +295,14 @@ Specify environment variables that will be visible to the browser. Defaults to ` | |||
Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. | |||
Defaults to 0. | |||
|
|||
### option: BrowserType.launchPersistentContext.showUserInput |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will I see these indicators on the screenshots? Does that mean all my screenshot tests will just fail? Let's hide them when screenshotting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dgozman as a counter argument to this, what if I want indicators in my screenshots? To make it more obvious what the screenshot is demonstrating?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I would prefer to have them as well
c28b9c1
to
dd3556c
Compare
export class ShowUserInput implements ContextListener { | ||
async onContextCreated(context: BrowserContext) { | ||
context._inputListeners.add(async (page, event) => { | ||
if (!page.context()._browser.options.showUserInput) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just not add the input listener in the first place.
dd3556c
to
58a5f6b
Compare
@@ -332,7 +334,7 @@ export type BrowserTypeLaunchPersistentContextOptions = { | |||
downloadsPath?: string, | |||
chromiumSandbox?: boolean, | |||
slowMo?: number, | |||
noDefaultViewport?: boolean, | |||
showUserInput?: boolean, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- what happened to the noDefaultViewport?
- I would not want to add another API like
headless: false
that requires modifications of source for every debugging session.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are missing some doc checks these days.
@@ -322,7 +308,7 @@ export abstract class BrowserContext extends EventEmitter { | |||
|
|||
// Bookkeeping. | |||
for (const listener of this._browser.options.contextListeners) | |||
await listener.onContextDidDestroy(this); | |||
await listener.onContextDidDestroy?.(this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we do this ?. already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of this patch yes. See previous comments about tsconfig.json
@@ -57,7 +57,15 @@ export class Keyboard { | |||
if (kModifiers.includes(description.key as types.KeyboardModifier)) | |||
this._pressedModifiers.add(description.key as types.KeyboardModifier); | |||
const text = description.text; | |||
await this._raw.keydown(this._pressedModifiers, description.code, description.keyCode, description.keyCodeWithoutLocation, description.key, description.location, autoRepeat, text); | |||
await Promise.all([ | |||
this._page.context().notifyInputListeners(this._page, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now conflicts with both pw:api and trace viewer. I wish we could have it all aligned.
58a5f6b
to
05ba4a4
Compare
Closing this in favor of the new |
Adds an overlay that shows the current mouse position and typed text. I have a test but its waiting on some fixture refactorings I'm working on.
The overlays are added into the dom. While their guts are hidden with a shadow root, they still can be detected by JavaScript on the page. In practice I don't expect this to be an issue.
I'd like future patches to animate the mouse cursor and show "to-be-typed" text for a smoother experience.