Skip to content

Commit 257a6ae

Browse files
authored
fix(assertions): error messages from negated matchers (#2619)
1 parent 3352d85 commit 257a6ae

File tree

3 files changed

+195
-27
lines changed

3 files changed

+195
-27
lines changed

playwright/_impl/_assertions.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -511,15 +511,14 @@ async def to_be_attached(
511511
timeout: float = None,
512512
) -> None:
513513
__tracebackhide__ = True
514+
if attached is None:
515+
attached = True
516+
attached_string = "attached" if attached else "detached"
514517
await self._expect_impl(
515-
(
516-
"to.be.attached"
517-
if (attached is None or attached is True)
518-
else "to.be.detached"
519-
),
518+
("to.be.attached" if attached else "to.be.detached"),
520519
FrameExpectOptions(timeout=timeout),
521520
None,
522-
"Locator expected to be attached",
521+
f"Locator expected to be {attached_string}",
523522
)
524523

525524
async def to_be_checked(
@@ -528,15 +527,14 @@ async def to_be_checked(
528527
checked: bool = None,
529528
) -> None:
530529
__tracebackhide__ = True
530+
if checked is None:
531+
checked = True
532+
checked_string = "checked" if checked else "unchecked"
531533
await self._expect_impl(
532-
(
533-
"to.be.checked"
534-
if checked is None or checked is True
535-
else "to.be.unchecked"
536-
),
534+
("to.be.checked" if checked else "to.be.unchecked"),
537535
FrameExpectOptions(timeout=timeout),
538536
None,
539-
"Locator expected to be checked",
537+
f"Locator expected to be {checked_string}",
540538
)
541539

542540
async def not_to_be_attached(
@@ -581,11 +579,12 @@ async def to_be_editable(
581579
__tracebackhide__ = True
582580
if editable is None:
583581
editable = True
582+
editable_string = "editable" if editable else "readonly"
584583
await self._expect_impl(
585584
"to.be.editable" if editable else "to.be.readonly",
586585
FrameExpectOptions(timeout=timeout),
587586
None,
588-
"Locator expected to be editable",
587+
f"Locator expected to be {editable_string}",
589588
)
590589

591590
async def not_to_be_editable(
@@ -623,11 +622,12 @@ async def to_be_enabled(
623622
__tracebackhide__ = True
624623
if enabled is None:
625624
enabled = True
625+
enabled_string = "enabled" if enabled else "disabled"
626626
await self._expect_impl(
627627
"to.be.enabled" if enabled else "to.be.disabled",
628628
FrameExpectOptions(timeout=timeout),
629629
None,
630-
"Locator expected to be enabled",
630+
f"Locator expected to be {enabled_string}",
631631
)
632632

633633
async def not_to_be_enabled(
@@ -665,11 +665,12 @@ async def to_be_visible(
665665
__tracebackhide__ = True
666666
if visible is None:
667667
visible = True
668+
visible_string = "visible" if visible else "hidden"
668669
await self._expect_impl(
669670
"to.be.visible" if visible else "to.be.hidden",
670671
FrameExpectOptions(timeout=timeout),
671672
None,
672-
"Locator expected to be visible",
673+
f"Locator expected to be {visible_string}",
673674
)
674675

675676
async def not_to_be_visible(

tests/async/test_assertions.py

Lines changed: 138 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -510,14 +510,14 @@ async def test_assertions_locator_to_be_checked(page: Page, server: Server) -> N
510510
await page.set_content("<input type=checkbox>")
511511
my_checkbox = page.locator("input")
512512
await expect(my_checkbox).not_to_be_checked()
513-
with pytest.raises(AssertionError):
513+
with pytest.raises(AssertionError, match="Locator expected to be checked"):
514514
await expect(my_checkbox).to_be_checked(timeout=100)
515515
await expect(my_checkbox).to_be_checked(timeout=100, checked=False)
516516
with pytest.raises(AssertionError):
517517
await expect(my_checkbox).to_be_checked(timeout=100, checked=True)
518518
await my_checkbox.check()
519519
await expect(my_checkbox).to_be_checked(timeout=100, checked=True)
520-
with pytest.raises(AssertionError):
520+
with pytest.raises(AssertionError, match="Locator expected to be unchecked"):
521521
await expect(my_checkbox).to_be_checked(timeout=100, checked=False)
522522
await expect(my_checkbox).to_be_checked()
523523

@@ -534,19 +534,91 @@ async def test_assertions_locator_to_be_disabled_enabled(
534534
await expect(my_checkbox).to_be_disabled(timeout=100)
535535
await my_checkbox.evaluate("e => e.disabled = true")
536536
await expect(my_checkbox).to_be_disabled()
537-
with pytest.raises(AssertionError):
537+
with pytest.raises(AssertionError, match="Locator expected to be enabled"):
538538
await expect(my_checkbox).to_be_enabled(timeout=100)
539539

540540

541+
async def test_assertions_locator_to_be_enabled_with_true(page: Page) -> None:
542+
await page.set_content("<button>Text</button>")
543+
await expect(page.locator("button")).to_be_enabled(enabled=True)
544+
545+
546+
async def test_assertions_locator_to_be_enabled_with_false_throws_good_exception(
547+
page: Page,
548+
) -> None:
549+
await page.set_content("<button>Text</button>")
550+
with pytest.raises(AssertionError, match="Locator expected to be disabled"):
551+
await expect(page.locator("button")).to_be_enabled(enabled=False)
552+
553+
554+
async def test_assertions_locator_to_be_enabled_with_false(page: Page) -> None:
555+
await page.set_content("<button disabled>Text</button>")
556+
await expect(page.locator("button")).to_be_enabled(enabled=False)
557+
558+
559+
async def test_assertions_locator_to_be_enabled_with_not_and_false(page: Page) -> None:
560+
await page.set_content("<button>Text</button>")
561+
await expect(page.locator("button")).not_to_be_enabled(enabled=False)
562+
563+
564+
async def test_assertions_locator_to_be_enabled_eventually(page: Page) -> None:
565+
await page.set_content("<button disabled>Text</button>")
566+
await page.eval_on_selector(
567+
"button",
568+
"""
569+
button => setTimeout(() => {
570+
button.removeAttribute('disabled');
571+
}, 700);
572+
""",
573+
)
574+
await expect(page.locator("button")).to_be_enabled()
575+
576+
577+
async def test_assertions_locator_to_be_enabled_eventually_with_not(page: Page) -> None:
578+
await page.set_content("<button>Text</button>")
579+
await page.eval_on_selector(
580+
"button",
581+
"""
582+
button => setTimeout(() => {
583+
button.setAttribute('disabled', '');
584+
}, 700);
585+
""",
586+
)
587+
await expect(page.locator("button")).not_to_be_enabled()
588+
589+
541590
async def test_assertions_locator_to_be_editable(page: Page, server: Server) -> None:
542591
await page.goto(server.EMPTY_PAGE)
543592
await page.set_content("<input></input><button disabled>Text</button>")
544593
await expect(page.locator("button")).not_to_be_editable()
545594
await expect(page.locator("input")).to_be_editable()
546-
with pytest.raises(AssertionError):
595+
with pytest.raises(AssertionError, match="Locator expected to be editable"):
547596
await expect(page.locator("button")).to_be_editable(timeout=100)
548597

549598

599+
async def test_assertions_locator_to_be_editable_with_true(page: Page) -> None:
600+
await page.set_content("<input></input>")
601+
await expect(page.locator("input")).to_be_editable(editable=True)
602+
603+
604+
async def test_assertions_locator_to_be_editable_with_false(page: Page) -> None:
605+
await page.set_content("<input readonly></input>")
606+
await expect(page.locator("input")).to_be_editable(editable=False)
607+
608+
609+
async def test_assertions_locator_to_be_editable_with_false_and_throw_good_exception(
610+
page: Page,
611+
) -> None:
612+
await page.set_content("<input></input>")
613+
with pytest.raises(AssertionError, match="Locator expected to be readonly"):
614+
await expect(page.locator("input")).to_be_editable(editable=False)
615+
616+
617+
async def test_assertions_locator_to_be_editable_with_not_and_false(page: Page) -> None:
618+
await page.set_content("<input></input>")
619+
await expect(page.locator("input")).not_to_be_editable(editable=False)
620+
621+
550622
async def test_assertions_locator_to_be_empty(page: Page, server: Server) -> None:
551623
await page.goto(server.EMPTY_PAGE)
552624
await page.set_content(
@@ -579,10 +651,59 @@ async def test_assertions_locator_to_be_hidden_visible(
579651
await expect(my_checkbox).to_be_hidden(timeout=100)
580652
await my_checkbox.evaluate("e => e.style.display = 'none'")
581653
await expect(my_checkbox).to_be_hidden()
582-
with pytest.raises(AssertionError):
654+
with pytest.raises(AssertionError, match="Locator expected to be visible"):
583655
await expect(my_checkbox).to_be_visible(timeout=100)
584656

585657

658+
async def test_assertions_locator_to_be_visible_with_true(page: Page) -> None:
659+
await page.set_content("<button>hello</button>")
660+
await expect(page.locator("button")).to_be_visible(visible=True)
661+
662+
663+
async def test_assertions_locator_to_be_visible_with_false(page: Page) -> None:
664+
await page.set_content("<button hidden>hello</button>")
665+
await expect(page.locator("button")).to_be_visible(visible=False)
666+
667+
668+
async def test_assertions_locator_to_be_visible_with_false_throws_good_exception(
669+
page: Page,
670+
) -> None:
671+
await page.set_content("<button>hello</button>")
672+
with pytest.raises(AssertionError, match="Locator expected to be hidden"):
673+
await expect(page.locator("button")).to_be_visible(visible=False)
674+
675+
676+
async def test_assertions_locator_to_be_visible_with_not_and_false(page: Page) -> None:
677+
await page.set_content("<button>hello</button>")
678+
await expect(page.locator("button")).not_to_be_visible(visible=False)
679+
680+
681+
async def test_assertions_locator_to_be_visible_eventually(page: Page) -> None:
682+
await page.set_content("<div></div>")
683+
await page.eval_on_selector(
684+
"div",
685+
"""
686+
div => setTimeout(() => {
687+
div.innerHTML = '<span>Hello</span>';
688+
}, 700);
689+
""",
690+
)
691+
await expect(page.locator("span")).to_be_visible()
692+
693+
694+
async def test_assertions_locator_to_be_visible_eventually_with_not(page: Page) -> None:
695+
await page.set_content("<div><span>Hello</span></div>")
696+
await page.eval_on_selector(
697+
"span",
698+
"""
699+
span => setTimeout(() => {
700+
span.textContent = '';
701+
}, 700);
702+
""",
703+
)
704+
await expect(page.locator("span")).not_to_be_visible()
705+
706+
586707
async def test_assertions_should_serialize_regexp_correctly(
587708
page: Page, server: Server
588709
) -> None:
@@ -746,6 +867,15 @@ async def test_should_be_attached_with_attached_false(page: Page) -> None:
746867
await expect(locator).to_be_attached(attached=False)
747868

748869

870+
async def test_should_be_attached_with_attached_false_and_throw_good_error(
871+
page: Page,
872+
) -> None:
873+
await page.set_content("<button>hello</button>")
874+
locator = page.locator("button")
875+
with pytest.raises(AssertionError, match="Locator expected to be detached"):
876+
await expect(locator).to_be_attached(attached=False, timeout=1)
877+
878+
749879
async def test_should_be_attached_with_not_and_attached_false(page: Page) -> None:
750880
await page.set_content("<button>hello</button>")
751881
locator = page.locator("button")
@@ -773,7 +903,9 @@ async def test_should_be_attached_eventually_with_not(page: Page) -> None:
773903
async def test_should_be_attached_fail(page: Page) -> None:
774904
await page.set_content("<button>Hello</button>")
775905
locator = page.locator("input")
776-
with pytest.raises(AssertionError) as exc_info:
906+
with pytest.raises(
907+
AssertionError, match="Locator expected to be attached"
908+
) as exc_info:
777909
await expect(locator).to_be_attached(timeout=1000)
778910
assert "locator resolved to" not in exc_info.value.args[0]
779911

tests/sync/test_assertions.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -490,14 +490,14 @@ def test_assertions_locator_to_be_checked(page: Page, server: Server) -> None:
490490
page.set_content("<input type=checkbox>")
491491
my_checkbox = page.locator("input")
492492
expect(my_checkbox).not_to_be_checked()
493-
with pytest.raises(AssertionError):
493+
with pytest.raises(AssertionError, match="Locator expected to be checked"):
494494
expect(my_checkbox).to_be_checked(timeout=100)
495495
expect(my_checkbox).to_be_checked(timeout=100, checked=False)
496496
with pytest.raises(AssertionError):
497497
expect(my_checkbox).to_be_checked(timeout=100, checked=True)
498498
my_checkbox.check()
499499
expect(my_checkbox).to_be_checked(timeout=100, checked=True)
500-
with pytest.raises(AssertionError):
500+
with pytest.raises(AssertionError, match="Locator expected to be unchecked"):
501501
expect(my_checkbox).to_be_checked(timeout=100, checked=False)
502502
expect(my_checkbox).to_be_checked()
503503

@@ -512,7 +512,7 @@ def test_assertions_locator_to_be_disabled_enabled(page: Page, server: Server) -
512512
expect(my_checkbox).to_be_disabled(timeout=100)
513513
my_checkbox.evaluate("e => e.disabled = true")
514514
expect(my_checkbox).to_be_disabled()
515-
with pytest.raises(AssertionError):
515+
with pytest.raises(AssertionError, match="Locator expected to be enabled"):
516516
expect(my_checkbox).to_be_enabled(timeout=100)
517517

518518

@@ -521,6 +521,14 @@ def test_assertions_locator_to_be_enabled_with_true(page: Page) -> None:
521521
expect(page.locator("button")).to_be_enabled(enabled=True)
522522

523523

524+
def test_assertions_locator_to_be_enabled_with_false_throws_good_exception(
525+
page: Page,
526+
) -> None:
527+
page.set_content("<button>Text</button>")
528+
with pytest.raises(AssertionError, match="Locator expected to be disabled"):
529+
expect(page.locator("button")).to_be_enabled(enabled=False)
530+
531+
524532
def test_assertions_locator_to_be_enabled_with_false(page: Page) -> None:
525533
page.set_content("<button disabled>Text</button>")
526534
expect(page.locator("button")).to_be_enabled(enabled=False)
@@ -562,7 +570,7 @@ def test_assertions_locator_to_be_editable(page: Page, server: Server) -> None:
562570
page.set_content("<input></input><button disabled>Text</button>")
563571
expect(page.locator("button")).not_to_be_editable()
564572
expect(page.locator("input")).to_be_editable()
565-
with pytest.raises(AssertionError):
573+
with pytest.raises(AssertionError, match="Locator expected to be editable"):
566574
expect(page.locator("button")).to_be_editable(timeout=100)
567575

568576

@@ -576,6 +584,14 @@ def test_assertions_locator_to_be_editable_with_false(page: Page) -> None:
576584
expect(page.locator("input")).to_be_editable(editable=False)
577585

578586

587+
def test_assertions_locator_to_be_editable_with_false_and_throw_good_exception(
588+
page: Page,
589+
) -> None:
590+
page.set_content("<input></input>")
591+
with pytest.raises(AssertionError, match="Locator expected to be readonly"):
592+
expect(page.locator("input")).to_be_editable(editable=False)
593+
594+
579595
def test_assertions_locator_to_be_editable_with_not_and_false(page: Page) -> None:
580596
page.set_content("<input></input>")
581597
expect(page.locator("input")).not_to_be_editable(editable=False)
@@ -611,7 +627,7 @@ def test_assertions_locator_to_be_hidden_visible(page: Page, server: Server) ->
611627
expect(my_checkbox).to_be_hidden(timeout=100)
612628
my_checkbox.evaluate("e => e.style.display = 'none'")
613629
expect(my_checkbox).to_be_hidden()
614-
with pytest.raises(AssertionError):
630+
with pytest.raises(AssertionError, match="Locator expected to be visible"):
615631
expect(my_checkbox).to_be_visible(timeout=100)
616632

617633

@@ -625,6 +641,14 @@ def test_assertions_locator_to_be_visible_with_false(page: Page) -> None:
625641
expect(page.locator("button")).to_be_visible(visible=False)
626642

627643

644+
def test_assertions_locator_to_be_visible_with_false_throws_good_exception(
645+
page: Page,
646+
) -> None:
647+
page.set_content("<button>hello</button>")
648+
with pytest.raises(AssertionError, match="Locator expected to be hidden"):
649+
expect(page.locator("button")).to_be_visible(visible=False)
650+
651+
628652
def test_assertions_locator_to_be_visible_with_not_and_false(page: Page) -> None:
629653
page.set_content("<button>hello</button>")
630654
expect(page.locator("button")).not_to_be_visible(visible=False)
@@ -813,6 +837,15 @@ def test_should_be_attached_with_attached_false(page: Page) -> None:
813837
expect(locator).to_be_attached(attached=False)
814838

815839

840+
def test_should_be_attached_with_attached_false_and_throw_good_error(
841+
page: Page,
842+
) -> None:
843+
page.set_content("<button>hello</button>")
844+
locator = page.locator("button")
845+
with pytest.raises(AssertionError, match="Locator expected to be detached"):
846+
expect(locator).to_be_attached(attached=False, timeout=1)
847+
848+
816849
def test_should_be_attached_with_not_and_attached_false(page: Page) -> None:
817850
page.set_content("<button>hello</button>")
818851
locator = page.locator("button")
@@ -838,7 +871,9 @@ def test_should_be_attached_eventually_with_not(page: Page) -> None:
838871
def test_should_be_attached_fail(page: Page) -> None:
839872
page.set_content("<button>Hello</button>")
840873
locator = page.locator("input")
841-
with pytest.raises(AssertionError) as exc_info:
874+
with pytest.raises(
875+
AssertionError, match="Locator expected to be attached"
876+
) as exc_info:
842877
expect(locator).to_be_attached(timeout=1000)
843878
assert "locator resolved to" not in exc_info.value.args[0]
844879

0 commit comments

Comments
 (0)