120 lines
4.7 KiB
Python
120 lines
4.7 KiB
Python
from playwright.async_api import Page, Locator
|
|
|
|
|
|
class LundukeForum:
|
|
def __init__(self, page: Page, root_url: str = "https://forum.lunduke.com"):
|
|
# URL Configuration
|
|
self.page = page
|
|
self.root_url = root_url
|
|
self.latest_url = f"{self.root_url}/latest"
|
|
self.hot_url = f"{self.root_url}/hot"
|
|
self.categories_url = f"{self.root_url}/categories"
|
|
|
|
# Locators (encapsulated here for maintainability)
|
|
self.login_modal = self.page.locator('.login-left-side')
|
|
self.username_field = self.page.locator('#login-account-name')
|
|
self.password_field = self.page.locator('#login-account-password')
|
|
self.login_button = self.page.locator('#login-button')
|
|
self.create_topic_button = self.page.locator('#create-topic')
|
|
self.topic_drafts_button = self.page.locator('[data-identifier="topic-drafts-menu"]')
|
|
self.reply_control = self.page.locator('#reply-control')
|
|
self.category_dropdown = self.page.locator('.category-input')
|
|
self.submit_button = self.page.locator('.save-or-cancel')
|
|
|
|
# Asynchronous Navigation Methods
|
|
async def goto(self, endpoint: str):
|
|
"""Navigate to a specific endpoint on the site."""
|
|
target_url = f"{self.root_url}/{endpoint}"
|
|
await self.page.goto(target_url)
|
|
|
|
async def goto_latest(self):
|
|
"""Navigate to the 'Latest' discussions page."""
|
|
await self.goto("/latest")
|
|
|
|
async def goto_categories(self):
|
|
"""Navigate to the 'Categories' page."""
|
|
await self.goto("/categories")
|
|
|
|
# Login Action
|
|
async def login(self, username: str, password: str):
|
|
"""Perform a login on the forum."""
|
|
# Go to the login page
|
|
await self.page.goto(f"{self.root_url}/login")
|
|
|
|
# Wait for login modal to appear
|
|
await self.login_modal.wait_for(state="visible", timeout=30000)
|
|
|
|
# Type username and password
|
|
await self.username_field.type(username)
|
|
await self.password_field.type(password)
|
|
|
|
# Click the login button
|
|
await self.login_button.click()
|
|
await element_not_visible(self.login_modal)
|
|
|
|
# Confirm login
|
|
await self.wait_for_dom_load()
|
|
|
|
# Create a New Topic
|
|
async def create_new_topic(self, category: str, title: str, body: str, draft: bool = True):
|
|
"""Create a new topic in the forum.
|
|
:param category: the category to place the post under
|
|
:param title: the title of the topic.
|
|
:param body: the body of the topic.
|
|
:param draft: whether to store the post as a draft. (Default: True)
|
|
:return: None
|
|
"""
|
|
# Navigate to Categories
|
|
# await self.goto_categories()
|
|
|
|
# Click 'Create Topic' button and await visibility of reply control
|
|
await self.create_topic_button.click()
|
|
await self.reply_control.wait_for(state="visible", timeout=30000)
|
|
|
|
# Fill out the topic fields
|
|
title_field = self.page.locator('[placeholder="Type title, or paste a link here"]')
|
|
body_field = self.page.locator(
|
|
'[placeholder="Type here. Use Markdown, BBCode, or HTML to format. Drag or paste images."]')
|
|
|
|
await title_field.type(title)
|
|
await body_field.type(body)
|
|
|
|
# Select the desired category
|
|
await self.category_dropdown.click()
|
|
category_locator = self.page.locator(f'[data-name="{category}"]')
|
|
await category_locator.click()
|
|
|
|
if draft:
|
|
close_button = self.page.locator('.d-button-label', has_text='Close')
|
|
await close_button.click()
|
|
save_draft = self.page.locator('.d-button-label', has_text='Save draft for later')
|
|
await save_draft.click()
|
|
await self.wait_for_dom_load()
|
|
else:
|
|
await self.submit_button.click()
|
|
await self.wait_for_dom_load()
|
|
|
|
async def publish_draft_topic(self, title: str):
|
|
# Click 'Submit' to create the topic
|
|
await self.topic_drafts_button.click()
|
|
await self._select_draft(title)
|
|
await self.submit_button.click()
|
|
await self.wait_for_dom_load()
|
|
|
|
async def _select_draft(self, title: str):
|
|
draft_entry = self.page.locator('.d-button-label', has_text=title)
|
|
await draft_entry.wait_for(state="visible", timeout=5000)
|
|
await draft_entry.click()
|
|
|
|
async def wait_for_dom_load(self):
|
|
await self.page.wait_for_load_state("domcontentloaded", timeout=30000)
|
|
|
|
# Additional Utility Methods
|
|
async def element_visible(locator: Locator, timeout: int = 10000):
|
|
"""Wait for a specific element to confirm the page has loaded."""
|
|
await locator.wait_for(state="visible", timeout=timeout)
|
|
|
|
async def element_not_visible(locator: Locator, timeout: int = 10000):
|
|
await locator.wait_for(state="hidden", timeout=timeout)
|
|
|