341 lines
13 KiB
Python
Raw Normal View History

2026-05-30 20:16:39 +02:00
import re
2026-06-05 05:36:18 +02:00
from uuid import uuid4
from datetime import datetime, timedelta, timezone
2026-05-30 20:16:39 +02:00
2026-05-27 21:06:18 +02:00
from playwright.sync_api import expect
2026-05-23 03:21:55 +02:00
from tests.conftest import BASE_URL, assert_share_copies
2026-06-05 05:36:18 +02:00
from devplacepy.database import get_table
from devplacepy.utils import make_combined_slug
def _seed_projects(count):
owner = str(uuid4())
get_table("users").insert({
"uid": owner, "username": f"pag_{owner[:8]}", "email": f"{owner[:8]}@test.devplace",
"password_hash": "x", "role": "Member", "is_active": True,
"created_at": datetime.now(timezone.utc).isoformat(),
})
base = datetime(2026, 1, 1, tzinfo=timezone.utc)
projects = get_table("projects")
for i in range(count):
uid = str(uuid4())
title = f"Pag Project {i}"
projects.insert({
"uid": uid, "user_uid": owner, "slug": make_combined_slug(title, uid),
"title": title, "description": "paginated", "project_type": "software",
"status": "In Development", "stars": 0,
"created_at": (base - timedelta(seconds=i)).isoformat(),
})
return owner
2026-05-23 03:21:55 +02:00
2026-05-23 05:44:04 +02:00
def _create_project(page, title):
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", title)
page.fill("#description", "Project for testing")
page.fill("#release_date", "2026-06-01")
page.check("input[value='game']")
page.locator("#platforms-input").fill("PC")
page.locator("#platforms-input").press("Enter")
page.click("button:has-text('Create Project')")
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
def test_project_vote(alice):
page, _ = alice
_create_project(page, "Votable Project")
star = "form[action*='/votes/project/'] button"
2026-05-27 21:06:18 +02:00
count = "form[action*='/votes/project/'] .vote-count-value"
2026-05-23 05:44:04 +02:00
before = int(page.locator(star).first.inner_text().strip("☆ "))
page.locator(star).first.click()
2026-05-27 21:06:18 +02:00
expect(page.locator(count).first).to_have_text(str(before + 1))
2026-05-23 05:44:04 +02:00
after = int(page.locator(star).first.inner_text().strip("☆ "))
assert after == before + 1
2026-05-30 20:16:39 +02:00
def test_project_voted_state_persists(alice):
page, _ = alice
_create_project(page, "Voted State Project")
star = "form[action*='/votes/project/'] button"
page.locator(star).first.click()
page.wait_for_timeout(500)
page.reload(wait_until="domcontentloaded")
expect(page.locator(star).first).to_have_class(re.compile(r"\bvoted\b"))
2026-05-23 05:44:04 +02:00
def test_delete_own_project(alice):
page, _ = alice
_create_project(page, "Deletable Project XYZ")
proj_url = page.url
page.once("dialog", lambda d: d.accept())
page.locator("form[action*='/projects/delete/'] button").click()
page.wait_for_url(f"{BASE_URL}/projects", wait_until="domcontentloaded")
resp = page.goto(proj_url, wait_until="domcontentloaded")
assert resp.status == 404
2026-05-23 03:21:55 +02:00
def test_project_detail_share_button(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "Share Project")
page.fill("#description", "A project created for the share button test")
page.fill("#release_date", "2026-06-01")
page.check("input[value='game']")
page.locator("#platforms-input").fill("PC")
page.locator("#platforms-input").press("Enter")
page.click("button:has-text('Create Project')")
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
assert_share_copies(page, "/projects/")
2026-05-10 09:08:12 +02:00
def test_projects_page_loads(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-11 05:30:51 +02:00
assert page.is_visible("h1:has-text('Projects')")
2026-05-10 09:08:12 +02:00
assert page.is_visible("text=Discover amazing projects")
def test_projects_search_bar(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
search = page.locator("input[placeholder='Search projects...']")
assert search.is_visible()
def test_projects_tabs(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
tabs = ["Recently Released", "Most Popular", "New This Week"]
for tab in tabs:
assert page.is_visible(f"text={tab}")
def test_projects_tab_switch(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.click("a:has-text('Most Popular')")
2026-05-11 00:41:41 +02:00
page.wait_for_url(f"{BASE_URL}/projects?tab=popular", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.click("a:has-text('New This Week')")
2026-05-11 00:41:41 +02:00
page.wait_for_url(f"{BASE_URL}/projects?tab=new", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
def test_create_project_button(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
fab = page.locator("#create-project-btn")
assert fab.is_visible()
fab.click()
assert page.is_visible("h3:has-text('Create Project')")
def test_create_project_full(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.locator("#create-project-btn").click()
page.fill("#title", "Playwright Test Game")
page.fill("#description", "A game created by Playwright integration tests")
page.fill("#release_date", "2026-06-01")
page.check("input[value='game']")
page.locator("#platforms-input").fill("PC")
page.locator("#platforms-input").press("Enter")
page.click("button:has-text('Create Project')")
2026-05-11 20:49:45 +02:00
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
assert page.is_visible("text=Playwright Test Game")
def test_create_project_software(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.locator("#create-project-btn").click()
page.fill("#title", "CLI Tool")
page.fill("#description", "A command line tool")
page.check("input[value='software']")
page.check("input[value='Released']")
page.click("button:has-text('Create Project')")
2026-05-11 20:49:45 +02:00
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
assert page.is_visible("text=CLI Tool")
def test_create_project_mobile_app(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.locator("#create-project-btn").click()
page.fill("#title", "Mobile Messenger")
page.fill("#description", "A cross-platform messaging app")
page.check("input[value='mobile_app']")
page.locator("#platforms-input").fill("iOS")
page.locator("#platforms-input").press("Enter")
page.locator("#platforms-input").fill("Android")
page.locator("#platforms-input").press("Enter")
page.click("button:has-text('Create Project')")
2026-05-11 20:49:45 +02:00
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
assert page.is_visible("text=Mobile Messenger")
def test_project_search(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.locator("#create-project-btn").click()
page.fill("#title", "SearchableProject")
page.fill("#description", "Find me via search")
page.click("button:has-text('Create Project')")
2026-05-11 20:49:45 +02:00
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.fill("input[placeholder='Search projects...']", "SearchableProject")
page.locator("input[placeholder='Search projects...']").press("Enter")
page.wait_for_timeout(500)
assert page.is_visible("text=SearchableProject")
def test_project_cancel_modal(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.locator("#create-project-btn").click()
cancel = page.locator("button:has-text('Cancel')").first
cancel.click()
modal = page.locator("#create-project-modal")
assert not modal.is_visible()
def test_project_platform_presets(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
page.locator("#create-project-btn").click()
presets = page.locator(".platform-preset")
assert presets.count() >= 5
presets.first.click()
tag = page.locator("#platforms-tags .platform-tag")
assert tag.is_visible()
2026-06-05 05:36:18 +02:00
def test_project_pagination_first_page(alice):
page, _ = alice
owner = _seed_projects(26)
page.goto(f"{BASE_URL}/projects?user_uid={owner}", wait_until="domcontentloaded")
assert page.locator(".project-card").count() == 25
assert page.is_visible(".load-more-wrap")
def test_project_pagination_load_more(alice):
page, _ = alice
owner = _seed_projects(26)
page.goto(f"{BASE_URL}/projects?user_uid={owner}", wait_until="domcontentloaded")
page.click(".load-more-wrap a")
page.wait_for_url(lambda url: "before=" in url, wait_until="domcontentloaded")
assert f"user_uid={owner}" in page.url
assert page.locator(".project-card").count() == 1
assert not page.is_visible(".load-more-wrap")
def test_project_pagination_count_reflects_total(alice):
page, _ = alice
owner = _seed_projects(26)
page.goto(f"{BASE_URL}/projects?user_uid={owner}", wait_until="domcontentloaded")
count_text = page.text_content(".projects-count")
assert "Showing 25 of 26 projects" in count_text
2026-05-10 09:08:12 +02:00
def test_projects_count(alice):
page, _ = alice
2026-05-11 00:41:41 +02:00
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-10 09:08:12 +02:00
count_text = page.text_content(".projects-count")
assert "Showing" in count_text
assert "projects" in count_text
2026-05-11 07:02:06 +02:00
def test_project_detail_page(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "DetailTestProject")
page.fill("#description", "Project detail page test")
page.click("button:has-text('Create Project')")
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
assert page.is_visible("text=DetailTestProject")
assert page.is_visible("text=Back to Projects")
def test_project_detail_unauth(page):
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
def test_project_detail_back_link(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "BackLinkProject")
page.fill("#description", "Test back link")
page.click("button:has-text('Create Project')")
2026-05-11 20:49:45 +02:00
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
2026-05-11 07:02:06 +02:00
back = page.locator("a:has-text('Back to Projects')")
assert back.is_visible()
back.click()
page.wait_for_url(f"{BASE_URL}/projects", wait_until="domcontentloaded")
2026-05-11 20:49:45 +02:00
def test_project_comments_form_visible(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "CommentProject")
page.fill("#description", "Project for testing comments")
page.click("button:has-text('Create Project')")
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
assert page.is_visible("text=Comments")
assert page.is_visible("text=No comments yet")
assert page.is_visible("textarea[name='content']")
def test_project_comment_create(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "CommentCreateProj")
page.fill("#description", "Project for creating a comment")
page.click("button:has-text('Create Project')")
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
page.fill("textarea[name='content']", "Great project!")
page.click("button:has-text('Post')")
page.wait_for_timeout(500)
assert page.is_visible("text=Great project!")
def test_project_comment_reply(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "CommentReplyProj")
page.fill("#description", "Project for testing reply")
page.click("button:has-text('Create Project')")
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
page.fill("textarea[name='content']", "First comment")
page.click("button:has-text('Post')")
page.wait_for_timeout(500)
assert page.is_visible("text=First comment")
page.click("button:has-text('Reply')")
2026-06-05 19:22:29 +02:00
reply_form = page.locator(".comment-reply-form").first
reply_form.locator("textarea[name='content']").fill("Reply to comment")
reply_form.locator("button:has-text('Post')").click()
2026-05-11 20:49:45 +02:00
page.wait_for_timeout(500)
assert page.is_visible("text=Reply to comment")
def test_project_comment_delete(alice):
page, _ = alice
page.goto(f"{BASE_URL}/projects", wait_until="domcontentloaded")
page.locator("#create-project-btn").click()
page.fill("#title", "CommentDeleteProj")
page.fill("#description", "Project for testing delete")
page.click("button:has-text('Create Project')")
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
page.fill("textarea[name='content']", "Comment to delete")
page.click("button:has-text('Post')")
page.wait_for_timeout(500)
assert page.is_visible("text=Comment to delete")
page.locator(".comment-action-btn:has-text('Delete')").click()
page.wait_for_timeout(500)
assert not page.is_visible("text=Comment to delete")