Skip to content

Commit de5f967

Browse files
authored
feat(tap): support tap (microsoft#261)
1 parent 1d15fba commit de5f967

13 files changed

+753
-9
lines changed

playwright/async_api.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
)
5656
from playwright.input import Keyboard as KeyboardImpl
5757
from playwright.input import Mouse as MouseImpl
58+
from playwright.input import Touchscreen as TouchscreenImpl
5859
from playwright.js_handle import JSHandle as JSHandleImpl
5960
from playwright.network import Request as RequestImpl
6061
from playwright.network import Response as ResponseImpl
@@ -678,6 +679,26 @@ async def dblclick(
678679
mapping.register(MouseImpl, Mouse)
679680

680681

682+
class Touchscreen(AsyncBase):
683+
def __init__(self, obj: TouchscreenImpl):
684+
super().__init__(obj)
685+
686+
async def tap(self, x: float, y: float) -> NoneType:
687+
"""Touchscreen.tap
688+
689+
Dispatches a `touchstart` and `touchend` event with a single touch at the position (`x`,`y`).
690+
691+
Parameters
692+
----------
693+
x : float
694+
y : float
695+
"""
696+
return mapping.from_maybe_impl(await self._impl_obj.tap(x=x, y=y))
697+
698+
699+
mapping.register(TouchscreenImpl, Touchscreen)
700+
701+
681702
class JSHandle(AsyncBase):
682703
def __init__(self, obj: JSHandleImpl):
683704
super().__init__(obj)
@@ -1130,6 +1151,53 @@ async def selectOption(
11301151
)
11311152
)
11321153

1154+
async def tap(
1155+
self,
1156+
modifiers: typing.Union[
1157+
typing.List[Literal["Alt", "Control", "Meta", "Shift"]]
1158+
] = None,
1159+
position: MousePosition = None,
1160+
timeout: int = None,
1161+
force: bool = None,
1162+
noWaitAfter: bool = None,
1163+
) -> NoneType:
1164+
"""ElementHandle.tap
1165+
1166+
This method taps the element by performing the following steps:
1167+
1168+
Wait for actionability checks on the element, unless `force` option is set.
1169+
Scroll the element into view if needed.
1170+
Use page.touchscreen to tap in the center of the element, or the specified `position`.
1171+
Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
1172+
1173+
If the element is detached from the DOM at any moment during the action, this method rejects.
1174+
When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
1175+
1176+
**NOTE** `elementHandle.tap()` requires that the `hasTouch` option of the browser context be set to true.
1177+
1178+
Parameters
1179+
----------
1180+
modifiers : Optional[List[Literal['Alt', 'Control', 'Meta', 'Shift']]]
1181+
Modifier keys to press. Ensures that only these modifiers are pressed during the tap, and then restores current modifiers back. If not specified, currently pressed modifiers are used.
1182+
position : Optional[{"x": float, "y": float}]
1183+
A point to tap relative to the top-left corner of element padding box. If not specified, taps some visible point of the element.
1184+
timeout : Optional[int]
1185+
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
1186+
force : Optional[bool]
1187+
Whether to bypass the actionability checks. Defaults to `false`.
1188+
noWaitAfter : Optional[bool]
1189+
Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to inaccessible pages. Defaults to `false`.
1190+
"""
1191+
return mapping.from_maybe_impl(
1192+
await self._impl_obj.tap(
1193+
modifiers=modifiers,
1194+
position=position,
1195+
timeout=timeout,
1196+
force=force,
1197+
noWaitAfter=noWaitAfter,
1198+
)
1199+
)
1200+
11331201
async def fill(
11341202
self, value: str, timeout: int = None, noWaitAfter: bool = None
11351203
) -> NoneType:
@@ -2344,6 +2412,57 @@ async def dblclick(
23442412
)
23452413
)
23462414

2415+
async def tap(
2416+
self,
2417+
selector: str,
2418+
modifiers: typing.Union[
2419+
typing.List[Literal["Alt", "Control", "Meta", "Shift"]]
2420+
] = None,
2421+
position: MousePosition = None,
2422+
timeout: int = None,
2423+
force: bool = None,
2424+
noWaitAfter: bool = None,
2425+
) -> NoneType:
2426+
"""Frame.tap
2427+
2428+
This method taps an element matching `selector` by performing the following steps:
2429+
2430+
Find an element match matching `selector`. If there is none, wait until a matching element is attached to the DOM.
2431+
Wait for actionability checks on the matched element, unless `force` option is set. If the element is detached during the checks, the whole action is retried.
2432+
Scroll the element into view if needed.
2433+
Use page.touchscreen to tap the center of the element, or the specified `position`.
2434+
Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
2435+
2436+
When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
2437+
2438+
**NOTE** `frame.tap()` requires that the `hasTouch` option of the browser context be set to true.
2439+
2440+
Parameters
2441+
----------
2442+
selector : str
2443+
A selector to search for element to tap. If there are multiple elements satisfying the selector, the first will be tapped. See working with selectors for more details.
2444+
modifiers : Optional[List[Literal['Alt', 'Control', 'Meta', 'Shift']]]
2445+
Modifier keys to press. Ensures that only these modifiers are pressed during the tap, and then restores current modifiers back. If not specified, currently pressed modifiers are used.
2446+
position : Optional[{"x": float, "y": float}]
2447+
A point to tap relative to the top-left corner of element padding box. If not specified, taps some visible point of the element.
2448+
timeout : Optional[int]
2449+
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
2450+
force : Optional[bool]
2451+
Whether to bypass the actionability checks. Defaults to `false`.
2452+
noWaitAfter : Optional[bool]
2453+
Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to inaccessible pages. Defaults to `false`.
2454+
"""
2455+
return mapping.from_maybe_impl(
2456+
await self._impl_obj.tap(
2457+
selector=selector,
2458+
modifiers=modifiers,
2459+
position=position,
2460+
timeout=timeout,
2461+
force=force,
2462+
noWaitAfter=noWaitAfter,
2463+
)
2464+
)
2465+
23472466
async def fill(
23482467
self, selector: str, value: str, timeout: int = None, noWaitAfter: bool = None
23492468
) -> NoneType:
@@ -3194,6 +3313,16 @@ def mouse(self) -> "Mouse":
31943313
"""
31953314
return mapping.from_impl(self._impl_obj.mouse)
31963315

3316+
@property
3317+
def touchscreen(self) -> "Touchscreen":
3318+
"""Page.touchscreen
3319+
3320+
Returns
3321+
-------
3322+
Touchscreen
3323+
"""
3324+
return mapping.from_impl(self._impl_obj.touchscreen)
3325+
31973326
@property
31983327
def context(self) -> "BrowserContext":
31993328
"""Page.context
@@ -4404,6 +4533,59 @@ async def dblclick(
44044533
)
44054534
)
44064535

4536+
async def tap(
4537+
self,
4538+
selector: str,
4539+
modifiers: typing.Union[
4540+
typing.List[Literal["Alt", "Control", "Meta", "Shift"]]
4541+
] = None,
4542+
position: MousePosition = None,
4543+
timeout: int = None,
4544+
force: bool = None,
4545+
noWaitAfter: bool = None,
4546+
) -> NoneType:
4547+
"""Page.tap
4548+
4549+
This method taps an element matching `selector` by performing the following steps:
4550+
4551+
Find an element match matching `selector`. If there is none, wait until a matching element is attached to the DOM.
4552+
Wait for actionability checks on the matched element, unless `force` option is set. If the element is detached during the checks, the whole action is retried.
4553+
Scroll the element into view if needed.
4554+
Use page.touchscreen to tap the center of the element, or the specified `position`.
4555+
Wait for initiated navigations to either succeed or fail, unless `noWaitAfter` option is set.
4556+
4557+
When all steps combined have not finished during the specified `timeout`, this method rejects with a TimeoutError. Passing zero timeout disables this.
4558+
4559+
**NOTE** `page.tap()` requires that the `hasTouch` option of the browser context be set to true.
4560+
4561+
Shortcut for page.mainFrame().tap().
4562+
4563+
Parameters
4564+
----------
4565+
selector : str
4566+
A selector to search for element to tap. If there are multiple elements satisfying the selector, the first will be tapped. See working with selectors for more details.
4567+
modifiers : Optional[List[Literal['Alt', 'Control', 'Meta', 'Shift']]]
4568+
Modifier keys to press. Ensures that only these modifiers are pressed during the tap, and then restores current modifiers back. If not specified, currently pressed modifiers are used.
4569+
position : Optional[{"x": float, "y": float}]
4570+
A point to tap relative to the top-left corner of element padding box. If not specified, taps some visible point of the element.
4571+
timeout : Optional[int]
4572+
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
4573+
force : Optional[bool]
4574+
Whether to bypass the actionability checks. Defaults to `false`.
4575+
noWaitAfter : Optional[bool]
4576+
Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can opt out of waiting via setting this flag. You would only need this option in the exceptional cases such as navigating to inaccessible pages. Defaults to `false`.
4577+
"""
4578+
return mapping.from_maybe_impl(
4579+
await self._impl_obj.tap(
4580+
selector=selector,
4581+
modifiers=modifiers,
4582+
position=position,
4583+
timeout=timeout,
4584+
force=force,
4585+
noWaitAfter=noWaitAfter,
4586+
)
4587+
)
4588+
44074589
async def fill(
44084590
self, selector: str, value: str, timeout: int = None, noWaitAfter: bool = None
44094591
) -> NoneType:

playwright/element_handle.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ async def selectOption(
130130
params = {**params, **convert_select_option_values(values)}
131131
return await self._channel.send("selectOption", params)
132132

133+
async def tap(
134+
self,
135+
modifiers: List[KeyboardModifier] = None,
136+
position: MousePosition = None,
137+
timeout: int = None,
138+
force: bool = None,
139+
noWaitAfter: bool = None,
140+
) -> None:
141+
await self._channel.send("tap", locals_to_params(locals()))
142+
133143
async def fill(
134144
self, value: str, timeout: int = None, noWaitAfter: bool = None
135145
) -> None:

playwright/frame.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,17 @@ async def dblclick(
376376
) -> None:
377377
await self._channel.send("dblclick", locals_to_params(locals()))
378378

379+
async def tap(
380+
self,
381+
selector: str,
382+
modifiers: List[KeyboardModifier] = None,
383+
position: MousePosition = None,
384+
timeout: int = None,
385+
force: bool = None,
386+
noWaitAfter: bool = None,
387+
) -> None:
388+
await self._channel.send("tap", locals_to_params(locals()))
389+
379390
async def fill(
380391
self, selector: str, value: str, timeout: int = None, noWaitAfter: bool = None
381392
) -> None:

playwright/helper.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,23 @@ class MousePosition(TypedDict):
5656
y: float
5757

5858

59+
class FrameTapParams(TypedDict):
60+
selector: str
61+
force: Optional[bool]
62+
noWaitAfter: bool
63+
modifiers: Optional[List[KeyboardModifier]]
64+
position: Optional[MousePosition]
65+
timeout: int
66+
67+
68+
class FrameTapOptions(TypedDict):
69+
force: Optional[bool]
70+
noWaitAfter: bool
71+
modifiers: Optional[List[KeyboardModifier]]
72+
position: Optional[MousePosition]
73+
timeout: int
74+
75+
5976
class FilePayload(TypedDict):
6077
name: str
6178
mimeType: str

playwright/input.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,12 @@ async def dblclick(
7777
button: MouseButton = None,
7878
) -> None:
7979
await self.click(x, y, delay=delay, button=button, clickCount=2)
80+
81+
82+
class Touchscreen:
83+
def __init__(self, channel: Channel) -> None:
84+
self._channel = channel
85+
self._loop = channel._connection._loop
86+
87+
async def tap(self, x: float, y: float) -> None:
88+
await self._channel.send("touchscreenTap", locals_to_params(locals()))

playwright/page.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
parse_error,
5252
serialize_error,
5353
)
54-
from playwright.input import Keyboard, Mouse
54+
from playwright.input import Keyboard, Mouse, Touchscreen
5555
from playwright.js_handle import (
5656
JSHandle,
5757
Serializable,
@@ -96,6 +96,7 @@ class Page(ChannelOwner):
9696
accessibility: Accessibility
9797
keyboard: Keyboard
9898
mouse: Mouse
99+
touchscreen: Touchscreen
99100

100101
def __init__(
101102
self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
@@ -104,6 +105,7 @@ def __init__(
104105
self.accessibility = Accessibility(self._channel)
105106
self.keyboard = Keyboard(self._channel)
106107
self.mouse = Mouse(self._channel)
108+
self.touchscreen = Touchscreen(self._channel)
107109

108110
self._main_frame: Frame = from_channel(initializer["mainFrame"])
109111
self._main_frame._page = self
@@ -631,6 +633,17 @@ async def dblclick(
631633
) -> None:
632634
return await self._main_frame.dblclick(**locals_to_params(locals()))
633635

636+
async def tap(
637+
self,
638+
selector: str,
639+
modifiers: List[KeyboardModifier] = None,
640+
position: MousePosition = None,
641+
timeout: int = None,
642+
force: bool = None,
643+
noWaitAfter: bool = None,
644+
) -> None:
645+
return await self._main_frame.tap(**locals_to_params(locals()))
646+
634647
async def fill(
635648
self, selector: str, value: str, timeout: int = None, noWaitAfter: bool = None
636649
) -> None:

0 commit comments

Comments
 (0)