diff --git a/tests/conftest.py b/tests/conftest.py index dbfc8ef0..9a5be4cd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -290,6 +290,14 @@ def assert_share_copies(page, expected_fragment): ) +def vote_glyph(locator): + value = locator.evaluate("el => getComputedStyle(el, '::before').content") + value = value.strip().strip('"').strip("'") + if value.startswith("\\") and len(value) == 5 and value[1:].isdigit(): + value = chr(int(value[1:], 16)) + return value + + def assert_no_horizontal_overflow(page, layout): report = page.evaluate( """(selector) => { diff --git a/tests/e2e/leaderboard.py b/tests/e2e/leaderboard.py index b7c4499f..0633cdca 100644 --- a/tests/e2e/leaderboard.py +++ b/tests/e2e/leaderboard.py @@ -39,7 +39,7 @@ def test_leaderboard_ranks_after_upvote(app_server, browser, seeded_db): pa.goto(post_url, wait_until="domcontentloaded") pa.wait_for_timeout(1000) - vote_btn = pa.locator("button.post-action-btn").filter(has_text="+").first + vote_btn = pa.locator("button.post-action-btn.vote-up").first vote_btn.wait_for(state="visible", timeout=10000) vote_btn.click() pa.wait_for_timeout(1500) diff --git a/tests/e2e/notifications/index.py b/tests/e2e/notifications/index.py index 9634ba38..e24834fa 100644 --- a/tests/e2e/notifications/index.py +++ b/tests/e2e/notifications/index.py @@ -310,7 +310,7 @@ def test_vote_notification_on_post(app_server, browser, seeded_db): pa.goto(post_url, wait_until="domcontentloaded") pa.wait_for_timeout(1000) - vote_btn = pa.locator("button.post-action-btn").filter(has_text="+").first + vote_btn = pa.locator("button.post-action-btn.vote-up").first vote_btn.wait_for(state="visible", timeout=10000) vote_btn.click() pa.wait_for_timeout(1500) @@ -796,7 +796,7 @@ def test_vote_notification_click_opens_target(app_server, browser, seeded_db): pa.goto(f"{BASE_URL}{post_path}", wait_until="domcontentloaded") pa.wait_for_timeout(1000) - vote_btn = pa.locator("button.post-action-btn").filter(has_text="+").first + vote_btn = pa.locator("button.post-action-btn.vote-up").first vote_btn.wait_for(state="visible", timeout=10000) vote_btn.click() pa.wait_for_timeout(1500) diff --git a/tests/e2e/post.py b/tests/e2e/post.py index cc1747ff..4729f598 100644 --- a/tests/e2e/post.py +++ b/tests/e2e/post.py @@ -2,7 +2,7 @@ import re from playwright.sync_api import expect -from tests.conftest import BASE_URL, assert_share_copies +from tests.conftest import BASE_URL, assert_share_copies, vote_glyph def create_post(page, topic="random", content="Test post content", title=None): page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded") page.locator(".feed-fab").first.wait_for(state="visible", timeout=10000) @@ -74,35 +74,57 @@ def test_post_vote_increment(alice): def test_post_upvote_voted_state_persists(alice): page, _ = alice create_post(page, "showcase", "Upvote persistence test") + vote_up = page.locator(".post-action-btn.vote-up").first + assert vote_glyph(vote_up) == "+" with page.expect_response( lambda r: "/votes/" in r.url and r.request.method == "POST" ): - page.locator(".post-action-btn.vote-up").first.click() + vote_up.click() expect(page.locator(".post-vote-count").first).to_have_text("1") + expect(vote_up).to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(vote_up) == "\u2295" page.reload(wait_until="domcontentloaded") - expect(page.locator(".post-action-btn.vote-up").first).to_have_class( - re.compile(r"\bvoted\b") - ) - assert "voted" not in ( - page.locator(".post-action-btn.vote-down").first.get_attribute("class") or "" - ) + vote_up = page.locator(".post-action-btn.vote-up").first + expect(vote_up).to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(vote_up) == "\u2295" + vote_down = page.locator(".post-action-btn.vote-down").first + assert "voted" not in (vote_down.get_attribute("class") or "") + assert vote_glyph(vote_down) == "\u2212" + with page.expect_response( + lambda r: "/votes/" in r.url and r.request.method == "POST" + ): + vote_up.click() + expect(page.locator(".post-vote-count").first).to_have_text("0") + expect(vote_up).not_to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(vote_up) == "+" def test_post_downvote_voted_state_persists(alice): page, _ = alice create_post(page, "showcase", "Downvote persistence test") + vote_down = page.locator(".post-action-btn.vote-down").first + assert vote_glyph(vote_down) == "\u2212" with page.expect_response( lambda r: "/votes/" in r.url and r.request.method == "POST" ): - page.locator(".post-action-btn.vote-down").first.click() + vote_down.click() expect(page.locator(".post-vote-count").first).to_have_text("-1") + expect(vote_down).to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(vote_down) == "\u2296" page.reload(wait_until="domcontentloaded") - expect(page.locator(".post-action-btn.vote-down").first).to_have_class( - re.compile(r"\bvoted\b") - ) - assert "voted" not in ( - page.locator(".post-action-btn.vote-up").first.get_attribute("class") or "" - ) + vote_down = page.locator(".post-action-btn.vote-down").first + expect(vote_down).to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(vote_down) == "\u2296" + vote_up = page.locator(".post-action-btn.vote-up").first + assert "voted" not in (vote_up.get_attribute("class") or "") + assert vote_glyph(vote_up) == "+" + with page.expect_response( + lambda r: "/votes/" in r.url and r.request.method == "POST" + ): + vote_down.click() + expect(page.locator(".post-vote-count").first).to_have_text("0") + expect(vote_down).not_to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(vote_down) == "\u2212" def test_comment_voted_state_persists(alice): @@ -114,14 +136,25 @@ def test_comment_voted_state_persists(alice): expect( page.locator(".comment-text:has-text('Comment whose vote should persist')") ).to_be_visible() - page.locator(".comment-vote-btn").first.click() - expect(page.locator(".comment-vote-btn").first).to_have_class( - re.compile(r"\bvoted\b") - ) + comment_vote = page.locator(".comment-vote-btn.vote-up").first + assert vote_glyph(comment_vote) == "+" + with page.expect_response( + lambda r: "/votes/" in r.url and r.request.method == "POST" + ): + comment_vote.click() + expect(comment_vote).to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(comment_vote) == "\u2295" page.reload(wait_until="domcontentloaded") - expect(page.locator(".comment-vote-btn").first).to_have_class( - re.compile(r"\bvoted\b") - ) + comment_vote = page.locator(".comment-vote-btn.vote-up").first + expect(comment_vote).to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(comment_vote) == "\u2295" + with page.expect_response( + lambda r: "/votes/" in r.url and r.request.method == "POST" + ): + comment_vote.click() + expect(page.locator(".comment-vote-count").first).to_have_text("0") + expect(comment_vote).not_to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(comment_vote) == "+" def test_mention_first_match_highlighted(alice): diff --git a/tests/e2e/posts/index.py b/tests/e2e/posts/index.py index 17a03294..c0e053ef 100644 --- a/tests/e2e/posts/index.py +++ b/tests/e2e/posts/index.py @@ -14,7 +14,7 @@ def _create_plain_post_engagement_ui(page, content): page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded") from uuid import uuid4 from datetime import datetime, timedelta, timezone -from tests.conftest import BASE_URL, assert_share_copies +from tests.conftest import BASE_URL, assert_share_copies, vote_glyph from devplacepy.database import get_table from devplacepy.utils import make_combined_slug def _seed_posts(count): @@ -404,15 +404,19 @@ def test_feed_vote_voted_state_persists(alice): card = page.locator( ".post-card", has_text="Feed vote persistence test content" ).first - card.locator(".post-action-btn.vote-up").click() + vote_up = card.locator(".post-action-btn.vote-up") + assert vote_glyph(vote_up) == "+" + vote_up.click() expect(card.locator(".post-vote-count")).to_have_text("1") + expect(vote_up).to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(vote_up) == "\u2295" page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded") card = page.locator( ".post-card", has_text="Feed vote persistence test content" ).first - expect(card.locator(".post-action-btn.vote-up")).to_have_class( - re.compile(r"\bvoted\b") - ) + vote_up = card.locator(".post-action-btn.vote-up") + expect(vote_up).to_have_class(re.compile(r"\bvoted\b")) + assert vote_glyph(vote_up) == "\u2295" def test_feed_card_share_button(alice):