원클릭으로
playwright-tool
Browser automation library with reusable functions and test scripts
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Browser automation library with reusable functions and test scripts
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | playwright-tool |
| description | Browser automation library with reusable functions and test scripts |
Library Location:
~/.config/opencode/tools/playwright_tool/Project Entry Points:
{project_root}/.opencode/scripts/grant_perm.py,{project_root}/.opencode/scripts/playwright/
The Playwright Tool provides a browser automation library for testing web applications. It consists of reusable library functions and a structured folder for disposable test scripts.
scripts/ folder with descriptive namesLibrary (~/.config/opencode/tools/playwright_tool/lib/):
lib/
├── __init__.py # Exports all functions
├── browser.py # Browser management (start, close, login)
├── actions.py # Action functions (goto, click, fill, etc.)
├── utils.py # Utilities (screenshot, inspect_page, etc.)
├── permissions.py # Permission management
└── config.py # Configuration
Project-Specific ({project_root}/.opencode/):
.opencode/
├── scripts/playwright/ # Test scripts (scratchpad)
│ ├── login.py
│ └── test_sales_order.py
└── screenshots/ # Screenshot output
Browser Management (lib/browser.py):
start_browser(headless=False) - Start browser, returns (p, browser, page)close_browser(browser, p) - Close browser and stop Playwrightlogin(page, user_type=None) - Login to Django application (user_type="test" or user_type="super")Actions (lib/actions.py):
goto(page, url) - Navigate to URL (relative or absolute)click(page, selector) - Click elementfill(page, selector, value) - Fill input fieldcheck(page, selector) - Check checkbox/radiouncheck(page, selector) - Uncheck checkboxselect(page, selector, value) - Select dropdown optionhover(page, selector) - Hover over elementpress(page, selector, key) - Press key on elementwait_for_load_state(page, state) - Wait for load statewait_for_selector(page, selector, timeout) - Wait for elementUtilities (lib/utils.py):
take_screenshot(page, filename) - Take screenshotinspect_page(page) - List all clickable elementsdebug_page_state(page, step_name) - Debug page stateget_page_html(page, max_length) - Get page HTMLcapture_console_errors(page) - Set up console error captureThe permissions module (lib/permissions.py) manages Django user permissions for testing.
Child apps inherit parent app permissions through MODEL_PERMISSIONS. Grant parent app permissions to access all child apps:
company.change_company cascades to company_location.change_company, company_contact.change_company, etc.can_* permissions defined on the UserProfile modelview, add, change, delete permissions on modelslib/permissions.py)from lib.permissions import (
get_user_permissions, # Get all UserProfile boolean fields
get_user_permission, # Get specific UserProfile field
set_user_permission, # Set a UserProfile boolean field
reset_user_permissions, # Set multiple UserProfile fields
list_permission_fields, # List available UserProfile fields
get_test_user_permissions, # Convenience for test user
)
from lib.permissions import (
get_django_permissions, # Get all Django permissions for user
grant_app_permission, # Grant single permission (view/add/change/delete)
grant_full_app_access, # Grant full CRUD access to an app
)
Configure Credentials (lib/config.py):
SUPER_USER = "stratus"
SUPER_PASSWORD = "stratus"
TEST_USER = "test"
TEST_PASSWORD = "test"
Login as Test User:
from lib.browser import start_browser, close_browser, login
p, browser, page = start_browser(headless=False)
try:
login(page, user_type="test") # Uses TEST_USER/TEST_PASSWORD
finally:
close_browser(browser, p)
Grant Full App Access (includes all CRUD + cascading to child apps):
from lib.permissions import grant_full_app_access, get_django_permissions
# Grant full access to company app
grant_full_app_access("test", "company")
# Now has: company.view_company, company.add_company,
# company.change_company, company.delete_company
# Plus cascades to company_location, company_contact, etc.
print(get_django_permissions("test"))
Grant Single Permission:
from lib.permissions import grant_app_permission
grant_app_permission("test", "view", "company")
# Adds: company.view_company only
Check User Permissions:
from lib.permissions import get_django_permissions, get_user_permissions
# Django standard permissions
print(get_django_permissions("test"))
# ['add_salespurchaseorder', 'change_salespurchaseorder', ...]
# UserProfile boolean fields
print(get_user_permissions("test"))
# {'can_approve_procurement_purchase_orders': True}
Set UserProfile Permission:
from lib.permissions import set_user_permission
set_user_permission("test", "can_approve_procurement_purchase_orders", True)
Run from project root (where manage.py is). Uses Path.home() to dynamically locate the tool:
# Grant view permission to an app
python .opencode/scripts/grant_perm.py grant -u test -a asset --action view
# Grant full CRUD access to an app
python .opencode/scripts/grant_perm.py full -u test -a company
# List user's permissions
python .opencode/scripts/grant_perm.py list -u test
# Filter by app
python .opencode/scripts/grant_perm.py list -u test --app asset
# Check specific permission
python .opencode/scripts/grant_perm.py check -u test -p view_asset
# List available UserProfile permission fields
python .opencode/scripts/grant_perm.py fields
# Set UserProfile boolean permission
python .opencode/scripts/grant_perm.py set -u test --perm can_approve -v True
# Run a playwright script from project root
python .opencode/scripts/playwright/__main__.py login.py
# Run another script
python .opencode/scripts/playwright/__main__.py check_sidebar.py
Scripts are disposable test code. Write them in scripts/, run them, modify as needed.
DON'T: Create reusable modules or import scripts into other code
# ❌ DON'T: Importing scripts as modules
from scripts.login import main
main() # Scripts are not meant to be imported
DO: Write self-contained scripts that use library functions
# ✅ DO: Self-contained script using library
from __future__ import annotations
import sys
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(BASE_DIR))
from lib.browser import start_browser, close_browser, login
from lib.actions import goto, click
from lib.utils import inspect_page, take_screenshot
def main():
p, browser, page = start_browser(headless=False)
try:
# Login and let redirect happen naturally
login(page)
page.wait_for_timeout(2000) # Allow redirect to complete
# Now inspect the home page or navigate from there
inspect_page(page)
# To click sidebar links, use text selectors
# click(page, "text=Sales Orders")
take_screenshot(page, "home.png")
finally:
close_browser(browser, p)
if __name__ == "__main__":
main()
Execute scripts directly with Python:
python .config/opencode/tools/playwright_tool/scripts/login.py
Edit lib/config.py to customize settings:
BASE_URL = "http://localhost:8000"
LOGIN_URL = "/django_spire/auth/login/"
DEFAULT_TIMEOUT = 10000
SCREENSHOT_DIR = ".opencode/screenshots"
SUPER_USER = "stratus"
SUPER_PASSWORD = "stratus"
TEST_USER = "test"
TEST_PASSWORD = "test"
# .config/opencode/tools/playwright_tool/scripts/check_user_form.py
from __future__ import annotations
import sys
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(BASE_DIR))
from lib.browser import start_browser, close_browser, login
from lib.actions import goto
from lib.utils import (
inspect_page,
debug_page_state,
take_screenshot,
capture_console_errors,
)
from lib.permissions import grant_full_app_access, get_django_permissions
def main():
p, browser, page = start_browser(headless=False)
get_errors = capture_console_errors(page)
try:
# Login
login(page)
# Navigate to company page
goto(page, "/django_spire/companies/")
# Inspect page
inspect_page(page)
# Debug state
debug_page_state(page, "company_list")
# Take screenshot
take_screenshot(page, "company_page.png")
# Check console errors
errors = get_errors()
print(f"Console errors: {len(errors)}")
for err in errors:
print(f" - {err['text']}")
finally:
close_browser(browser, p)
if __name__ == "__main__":
main()
After login, always let the redirect happen naturally. The login function should:
domcontentloaded state (not networkidle)def main():
p, browser, page = start_browser(headless=False)
try:
# Login - let redirect happen naturally
login(page)
page.wait_for_timeout(2000) # Allow redirect to complete
# Now navigate from the home page
# DO NOT manually goto() the home page - the redirect handles it
inspect_page(page)
finally:
close_browser(browser, p)
networkidle?networkidle waits for all network requests to finish, which can timeout on pages with:
__reload__, __debug__)domcontentloaded + timeout is faster and more reliable for SPA navigationAfter login redirect lands on the home page (/), you can navigate using:
inspect_page(page) to see available navigation linksclick(page, "text=Link Text") to click sidebar linksgoto()When reviewing Playwright scripts, verify the following:
lib/, not relative importsclose_browser() is called in finally blockcapture_console_errors() is used for debuggingtest.py, temp.py)BASE_URL from config, not hardcoded URLsheadless=False for debuggingwait_for_load_state() is called after navigationtext=, #id, [name='field']) over generic onesBest practices for writing Django tests (updated template)
GlueFetchHelper patterns for making AJAX calls in templates
Advanced seeding patterns for linking specific foreign keys
Apply DjangoSpire app CSS variables in templates instead of inline styles
Django URL configuration patterns, namespaces, and nested structure conventions
Best practices for AI bots, prompts, and intel in scope_of_work