|
|
|
|
@@ -7,7 +7,7 @@ from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
|
|
|
|
|
from pytfm.web import BasePage
|
|
|
|
|
from pytfm.web import BasePage, SmartLocator
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from playwright.sync_api import Page
|
|
|
|
|
@@ -22,9 +22,21 @@ class HomePage(BasePage):
|
|
|
|
|
|
|
|
|
|
path = "/web/"
|
|
|
|
|
|
|
|
|
|
def __init__(self, page: Page, base_url: str) -> None:
|
|
|
|
|
"""Initialize the home page object.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
page: Playwright Page instance.
|
|
|
|
|
base_url: Application base URL.
|
|
|
|
|
"""
|
|
|
|
|
super().__init__(page, base_url)
|
|
|
|
|
self._create_post_btn = SmartLocator.by_testid("btn-create-post-header")
|
|
|
|
|
self._post_list = SmartLocator.by_testid("post-list")
|
|
|
|
|
self._empty_state = SmartLocator.by_testid("empty-state")
|
|
|
|
|
|
|
|
|
|
def create_post(self) -> None:
|
|
|
|
|
"""Click the 'Write a Post' button to navigate to the form."""
|
|
|
|
|
self.loc("btn-create-post-header").click()
|
|
|
|
|
self._create_post_btn.click(self.page)
|
|
|
|
|
|
|
|
|
|
def has_post_with_title(self, title: str) -> bool:
|
|
|
|
|
"""Check if a post with the given title is present in the list.
|
|
|
|
|
@@ -67,7 +79,7 @@ class HomePage(BasePage):
|
|
|
|
|
Returns:
|
|
|
|
|
True if the empty state is visible.
|
|
|
|
|
"""
|
|
|
|
|
return self.loc("empty-state")._get_locator().is_visible()
|
|
|
|
|
return self._empty_state.is_visible(self.page)
|
|
|
|
|
|
|
|
|
|
def count_posts(self) -> int:
|
|
|
|
|
"""Count the number of post cards on the page.
|
|
|
|
|
@@ -126,6 +138,20 @@ class PostFormPage(BasePage):
|
|
|
|
|
|
|
|
|
|
path = "/web/posts/new"
|
|
|
|
|
|
|
|
|
|
def __init__(self, page: Page, base_url: str) -> None:
|
|
|
|
|
"""Initialize the post form page object.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
page: Playwright Page instance.
|
|
|
|
|
base_url: Application base URL.
|
|
|
|
|
"""
|
|
|
|
|
super().__init__(page, base_url)
|
|
|
|
|
self._title_input = SmartLocator.by_testid("input-title")
|
|
|
|
|
self._content_input = SmartLocator.by_testid("textarea-content")
|
|
|
|
|
self._tags_input = SmartLocator.by_testid("input-tags")
|
|
|
|
|
self._publish_btn = SmartLocator.by_testid("btn-publish-post")
|
|
|
|
|
self._save_draft_btn = SmartLocator.by_testid("btn-save-draft")
|
|
|
|
|
|
|
|
|
|
def fill_form(self, title: str, content: str, tags: str) -> None:
|
|
|
|
|
"""Fill the post creation form.
|
|
|
|
|
|
|
|
|
|
@@ -134,8 +160,8 @@ class PostFormPage(BasePage):
|
|
|
|
|
content: Post content (markdown).
|
|
|
|
|
tags: Comma-separated tags string.
|
|
|
|
|
"""
|
|
|
|
|
self.loc("input-title").fill(title)
|
|
|
|
|
self.loc("input-tags").fill(tags)
|
|
|
|
|
self._title_input.fill(self.page, title)
|
|
|
|
|
self._tags_input.fill(self.page, tags)
|
|
|
|
|
|
|
|
|
|
self.page.evaluate(
|
|
|
|
|
"(content) => {"
|
|
|
|
|
@@ -151,11 +177,11 @@ class PostFormPage(BasePage):
|
|
|
|
|
|
|
|
|
|
def publish(self) -> None:
|
|
|
|
|
"""Click the publish button to submit the form."""
|
|
|
|
|
self.loc("btn-publish-post").click()
|
|
|
|
|
self._publish_btn.click(self.page)
|
|
|
|
|
|
|
|
|
|
def save_draft(self) -> None:
|
|
|
|
|
"""Click the 'Save as Draft' button."""
|
|
|
|
|
self.loc("btn-save-draft").click()
|
|
|
|
|
self._save_draft_btn.click(self.page)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PostDetailPage(BasePage):
|
|
|
|
|
@@ -177,6 +203,12 @@ class PostDetailPage(BasePage):
|
|
|
|
|
"""
|
|
|
|
|
super().__init__(page, base_url)
|
|
|
|
|
self.slug = slug
|
|
|
|
|
self._title = SmartLocator.by_testid("post-detail-title")
|
|
|
|
|
self._status = SmartLocator.by_testid("post-detail-status")
|
|
|
|
|
self._content = SmartLocator.by_testid("post-detail-content")
|
|
|
|
|
self._edit_btn = SmartLocator.by_testid("btn-edit-post")
|
|
|
|
|
self._delete_btn = SmartLocator.by_testid("btn-delete-post")
|
|
|
|
|
self._like_button = SmartLocator.by_testid("like-button")
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def url(self) -> str:
|
|
|
|
|
@@ -202,7 +234,7 @@ class PostDetailPage(BasePage):
|
|
|
|
|
Returns:
|
|
|
|
|
Post title string.
|
|
|
|
|
"""
|
|
|
|
|
return self.loc("post-detail-title").text_content() or ""
|
|
|
|
|
return self._title.get_text(self.page)
|
|
|
|
|
|
|
|
|
|
def get_status(self) -> str:
|
|
|
|
|
"""Get the post status badge text.
|
|
|
|
|
@@ -210,7 +242,7 @@ class PostDetailPage(BasePage):
|
|
|
|
|
Returns:
|
|
|
|
|
Status text ('Published' or 'Draft').
|
|
|
|
|
"""
|
|
|
|
|
return self.loc("post-detail-status").text_content() or ""
|
|
|
|
|
return self._status.get_text(self.page)
|
|
|
|
|
|
|
|
|
|
def is_published(self) -> bool:
|
|
|
|
|
"""Check if the post status is 'Published'.
|
|
|
|
|
@@ -222,7 +254,7 @@ class PostDetailPage(BasePage):
|
|
|
|
|
|
|
|
|
|
def edit(self) -> None:
|
|
|
|
|
"""Click the edit button to navigate to the edit form."""
|
|
|
|
|
self.loc("btn-edit-post").click()
|
|
|
|
|
self._edit_btn.click(self.page)
|
|
|
|
|
|
|
|
|
|
def can_edit(self) -> bool:
|
|
|
|
|
"""Check if the edit button is visible.
|
|
|
|
|
@@ -230,7 +262,7 @@ class PostDetailPage(BasePage):
|
|
|
|
|
Returns:
|
|
|
|
|
True if edit button is present.
|
|
|
|
|
"""
|
|
|
|
|
return self.loc("btn-edit-post")._get_locator().is_visible()
|
|
|
|
|
return self._edit_btn.is_visible(self.page)
|
|
|
|
|
|
|
|
|
|
def can_delete(self) -> bool:
|
|
|
|
|
"""Check if the delete button is visible.
|
|
|
|
|
@@ -238,12 +270,12 @@ class PostDetailPage(BasePage):
|
|
|
|
|
Returns:
|
|
|
|
|
True if delete button is present.
|
|
|
|
|
"""
|
|
|
|
|
return self.loc("btn-delete-post")._get_locator().is_visible()
|
|
|
|
|
return self._delete_btn.is_visible(self.page)
|
|
|
|
|
|
|
|
|
|
def delete(self) -> None:
|
|
|
|
|
"""Click the delete button and accept the confirmation dialog."""
|
|
|
|
|
self.page.on("dialog", lambda dialog: dialog.accept())
|
|
|
|
|
self.loc("btn-delete-post").click()
|
|
|
|
|
self._delete_btn.click(self.page)
|
|
|
|
|
|
|
|
|
|
def get_like_count(self) -> int:
|
|
|
|
|
"""Get the current like count from the detail page.
|
|
|
|
|
@@ -256,4 +288,4 @@ class PostDetailPage(BasePage):
|
|
|
|
|
|
|
|
|
|
def click_like(self) -> None:
|
|
|
|
|
"""Click the like/unlike button to toggle the like state."""
|
|
|
|
|
self.loc("like-button").click()
|
|
|
|
|
self._like_button.click(self.page)
|
|
|
|
|
|