12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
+
15
16
import pytest
16
17
17
- from playwright .sync_api import Error , Page , expect
18
+ from playwright .sync_api import Error , Locator , Page , expect
18
19
from tests .server import Server
19
20
from tests .utils import TARGET_CLOSED_ERROR_MESSAGE
20
21
@@ -25,16 +26,18 @@ def test_should_work(page: Page, server: Server) -> None:
25
26
before_count = 0
26
27
after_count = 0
27
28
28
- def handler () -> None :
29
+ original_locator = page .get_by_text ("This interstitial covers the button" )
30
+
31
+ def handler (locator : Locator ) -> None :
32
+ nonlocal original_locator
33
+ assert locator == original_locator
29
34
nonlocal before_count
30
35
nonlocal after_count
31
36
before_count += 1
32
37
page .locator ("#close" ).click ()
33
38
after_count += 1
34
39
35
- page .add_locator_handler (
36
- page .locator ("text=This interstitial covers the button" ), handler
37
- )
40
+ page .add_locator_handler (original_locator , handler )
38
41
39
42
for args in [
40
43
["mouseover" , 1 ],
@@ -70,7 +73,7 @@ def handler() -> None:
70
73
if page .get_by_text ("This interstitial covers the button" ).is_visible ():
71
74
page .locator ("#close" ).click ()
72
75
73
- page .add_locator_handler (page .locator ("body" ), handler )
76
+ page .add_locator_handler (page .locator ("body" ), handler , no_wait_after = True )
74
77
75
78
for args in [
76
79
["mouseover" , 2 ],
@@ -152,7 +155,7 @@ def handler() -> None:
152
155
# Deliberately timeout.
153
156
try :
154
157
page .wait_for_timeout (9999999 )
155
- except Error :
158
+ except Exception :
156
159
pass
157
160
158
161
page .add_locator_handler (
@@ -195,3 +198,190 @@ def handler() -> None:
195
198
expect (page .locator ("#target" )).to_be_visible ()
196
199
expect (page .locator ("#interstitial" )).not_to_be_visible ()
197
200
assert called == 1
201
+
202
+
203
+ def test_should_work_when_owner_frame_detaches (page : Page , server : Server ) -> None :
204
+ page .goto (server .EMPTY_PAGE )
205
+ page .evaluate (
206
+ """
207
+ () => {
208
+ const iframe = document.createElement('iframe');
209
+ iframe.src = 'data:text/html,<body>hello from iframe</body>';
210
+ document.body.append(iframe);
211
+
212
+ const target = document.createElement('button');
213
+ target.textContent = 'Click me';
214
+ target.id = 'target';
215
+ target.addEventListener('click', () => window._clicked = true);
216
+ document.body.appendChild(target);
217
+
218
+ const closeButton = document.createElement('button');
219
+ closeButton.textContent = 'close';
220
+ closeButton.id = 'close';
221
+ closeButton.addEventListener('click', () => iframe.remove());
222
+ document.body.appendChild(closeButton);
223
+ }
224
+ """
225
+ )
226
+ page .add_locator_handler (
227
+ page .frame_locator ("iframe" ).locator ("body" ),
228
+ lambda : page .locator ("#close" ).click (),
229
+ )
230
+ page .locator ("#target" ).click ()
231
+ assert page .query_selector ("iframe" ) is None
232
+ assert page .evaluate ("window._clicked" ) is True
233
+
234
+
235
+ def test_should_work_with_times_option (page : Page , server : Server ) -> None :
236
+ page .goto (server .PREFIX + "/input/handle-locator.html" )
237
+ called = 0
238
+
239
+ def _handler () -> None :
240
+ nonlocal called
241
+ called += 1
242
+
243
+ page .add_locator_handler (
244
+ page .locator ("body" ), _handler , no_wait_after = True , times = 2
245
+ )
246
+ page .locator ("#aside" ).hover ()
247
+ page .evaluate (
248
+ """
249
+ () => {
250
+ window.clicked = 0;
251
+ window.setupAnnoyingInterstitial('mouseover', 4);
252
+ }
253
+ """
254
+ )
255
+ with pytest .raises (Error ) as exc_info :
256
+ page .locator ("#target" ).click (timeout = 3000 )
257
+ assert called == 2
258
+ assert page .evaluate ("window.clicked" ) == 0
259
+ expect (page .locator ("#interstitial" )).to_be_visible ()
260
+ assert "Timeout 3000ms exceeded" in exc_info .value .message
261
+ assert (
262
+ '<div>This interstitial covers the button</div> from <div class="visible" id="interstitial">…</div> subtree intercepts pointer events'
263
+ in exc_info .value .message
264
+ )
265
+
266
+
267
+ def test_should_wait_for_hidden_by_default (page : Page , server : Server ) -> None :
268
+ page .goto (server .PREFIX + "/input/handle-locator.html" )
269
+ called = 0
270
+
271
+ def _handler (button : Locator ) -> None :
272
+ nonlocal called
273
+ called += 1
274
+ button .click ()
275
+
276
+ page .add_locator_handler (page .get_by_role ("button" , name = "close" ), _handler )
277
+ page .locator ("#aside" ).hover ()
278
+ page .evaluate (
279
+ """
280
+ () => {
281
+ window.clicked = 0;
282
+ window.setupAnnoyingInterstitial('timeout', 1);
283
+ }
284
+ """
285
+ )
286
+ page .locator ("#target" ).click ()
287
+ assert page .evaluate ("window.clicked" ) == 1
288
+ expect (page .locator ("#interstitial" )).not_to_be_visible ()
289
+ assert called == 1
290
+
291
+
292
+ def test_should_wait_for_hidden_by_default_2 (page : Page , server : Server ) -> None :
293
+ page .goto (server .PREFIX + "/input/handle-locator.html" )
294
+ called = 0
295
+
296
+ def _handler () -> None :
297
+ nonlocal called
298
+ called += 1
299
+
300
+ page .add_locator_handler (page .get_by_role ("button" , name = "close" ), _handler )
301
+ page .locator ("#aside" ).hover ()
302
+ page .evaluate (
303
+ """
304
+ () => {
305
+ window.clicked = 0;
306
+ window.setupAnnoyingInterstitial('hide', 1);
307
+ }
308
+ """
309
+ )
310
+ with pytest .raises (Error ) as exc_info :
311
+ page .locator ("#target" ).click (timeout = 3000 )
312
+ assert page .evaluate ("window.clicked" ) == 0
313
+ expect (page .locator ("#interstitial" )).to_be_visible ()
314
+ assert called == 1
315
+ assert (
316
+ 'locator handler has finished, waiting for get_by_role("button", name="close") to be hidden'
317
+ in exc_info .value .message
318
+ )
319
+
320
+
321
+ def test_should_work_with_noWaitAfter (page : Page , server : Server ) -> None :
322
+ page .goto (server .PREFIX + "/input/handle-locator.html" )
323
+ called = 0
324
+
325
+ def _handler (button : Locator ) -> None :
326
+ nonlocal called
327
+ called += 1
328
+ if called == 1 :
329
+ button .click ()
330
+ else :
331
+ page .locator ("#interstitial" ).wait_for (state = "hidden" )
332
+
333
+ page .add_locator_handler (
334
+ page .get_by_role ("button" , name = "close" ), _handler , no_wait_after = True
335
+ )
336
+ page .locator ("#aside" ).hover ()
337
+ page .evaluate (
338
+ """
339
+ () => {
340
+ window.clicked = 0;
341
+ window.setupAnnoyingInterstitial('timeout', 1);
342
+ }
343
+ """
344
+ )
345
+ page .locator ("#target" ).click ()
346
+ assert page .evaluate ("window.clicked" ) == 1
347
+ expect (page .locator ("#interstitial" )).not_to_be_visible ()
348
+ assert called == 2
349
+
350
+
351
+ def test_should_removeLocatorHandler (page : Page , server : Server ) -> None :
352
+ page .goto (server .PREFIX + "/input/handle-locator.html" )
353
+ called = 0
354
+
355
+ def _handler (locator : Locator ) -> None :
356
+ nonlocal called
357
+ called += 1
358
+ locator .click ()
359
+
360
+ page .add_locator_handler (page .get_by_role ("button" , name = "close" ), _handler )
361
+ page .evaluate (
362
+ """
363
+ () => {
364
+ window.clicked = 0;
365
+ window.setupAnnoyingInterstitial('hide', 1);
366
+ }
367
+ """
368
+ )
369
+ page .locator ("#target" ).click ()
370
+ assert called == 1
371
+ assert page .evaluate ("window.clicked" ) == 1
372
+ expect (page .locator ("#interstitial" )).not_to_be_visible ()
373
+ page .evaluate (
374
+ """
375
+ () => {
376
+ window.clicked = 0;
377
+ window.setupAnnoyingInterstitial('hide', 1);
378
+ }
379
+ """
380
+ )
381
+ page .remove_locator_handler (page .get_by_role ("button" , name = "close" ))
382
+ with pytest .raises (Error ) as error :
383
+ page .locator ("#target" ).click (timeout = 3000 )
384
+ assert called == 1
385
+ assert page .evaluate ("window.clicked" ) == 0
386
+ expect (page .locator ("#interstitial" )).to_be_visible ()
387
+ assert "Timeout 3000ms exceeded" in error .value .message
0 commit comments