docs: sync KB — discord-browser-testing-workflow.md
This commit is contained in:
parent
acb8fef084
commit
dd7c68c13a
170
paper-dynasty/discord-browser-testing-workflow.md
Normal file
170
paper-dynasty/discord-browser-testing-workflow.md
Normal file
@ -0,0 +1,170 @@
|
||||
---
|
||||
title: "Discord Bot Browser Testing via Playwright + CDP"
|
||||
description: "Step-by-step workflow for automated Discord bot testing using Playwright connected to Brave browser via Chrome DevTools Protocol. Covers setup, slash command execution, and screenshot capture."
|
||||
type: runbook
|
||||
domain: paper-dynasty
|
||||
tags: [paper-dynasty, discord, testing, playwright, automation]
|
||||
---
|
||||
|
||||
# Discord Bot Browser Testing via Playwright + CDP
|
||||
|
||||
Automated testing of Paper Dynasty Discord bot commands by connecting Playwright to a running Brave browser instance with Discord open.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Brave browser installed (`brave-browser-stable`)
|
||||
- Playwright installed (`pip install playwright && playwright install chromium`)
|
||||
- Discord logged in via browser (not desktop app)
|
||||
- Discord bot running (locally via docker-compose or on remote host)
|
||||
- Bot's `API_TOKEN` must match the target API environment
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Launch Brave with CDP enabled
|
||||
|
||||
Brave must be started with `--remote-debugging-port`. If Brave is already running, **kill it first** — otherwise the flag is ignored and the new process merges into the existing one.
|
||||
|
||||
```bash
|
||||
killall brave && sleep 2 && brave-browser-stable --remote-debugging-port=9222 &
|
||||
```
|
||||
|
||||
### 2. Verify CDP is responding
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:9222/json/version | python3 -m json.tool
|
||||
```
|
||||
|
||||
Should return JSON with `Browser`, `webSocketDebuggerUrl`, etc.
|
||||
|
||||
### 3. Open Discord in browser
|
||||
|
||||
Navigate to `https://discord.com/channels/<server_id>/<channel_id>` in Brave.
|
||||
|
||||
**Paper Dynasty test server:**
|
||||
- Server: Cals Test Server (`669356687294988350`)
|
||||
- Channel: #pd-game-test (`982850262903451658`)
|
||||
- URL: `https://discord.com/channels/669356687294988350/982850262903451658`
|
||||
|
||||
### 4. Verify bot is running with correct API token
|
||||
|
||||
```bash
|
||||
# Check docker-compose.yml has the right API_TOKEN for the target environment
|
||||
grep API_TOKEN /mnt/NV2/Development/paper-dynasty/discord-app/docker-compose.yml
|
||||
|
||||
# Dev API token lives on the dev host:
|
||||
ssh pd-database "docker exec sba_postgres psql -U sba_admin -d paperdynasty_dev -c \"SELECT 1;\""
|
||||
|
||||
# Restart bot if token was changed:
|
||||
cd /mnt/NV2/Development/paper-dynasty/discord-app && docker compose up -d
|
||||
```
|
||||
|
||||
## Running Commands
|
||||
|
||||
### Find the Discord tab
|
||||
|
||||
```python
|
||||
from playwright.sync_api import sync_playwright
|
||||
import time
|
||||
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.connect_over_cdp('http://localhost:9222')
|
||||
for ctx in browser.contexts:
|
||||
for page in ctx.pages:
|
||||
if 'discord' in page.url.lower():
|
||||
print(f'Found: {page.url}')
|
||||
break
|
||||
browser.close()
|
||||
```
|
||||
|
||||
### Execute a slash command and capture result
|
||||
|
||||
```python
|
||||
from playwright.sync_api import sync_playwright
|
||||
import time
|
||||
|
||||
def run_slash_command(command: str, wait_seconds: int = 5, screenshot_path: str = '/tmp/discord_result.png'):
|
||||
"""
|
||||
Type a slash command in Discord, select the top autocomplete option,
|
||||
submit it, wait for the bot response, and take a screenshot.
|
||||
"""
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.connect_over_cdp('http://localhost:9222')
|
||||
for ctx in browser.contexts:
|
||||
for page in ctx.pages:
|
||||
if 'discord' in page.url.lower():
|
||||
msg_box = page.locator('[role="textbox"][data-slate-editor="true"]')
|
||||
msg_box.click()
|
||||
time.sleep(0.3)
|
||||
|
||||
# Type the command (delay simulates human typing for autocomplete)
|
||||
msg_box.type(command, delay=80)
|
||||
time.sleep(2)
|
||||
|
||||
# Tab selects the top autocomplete option
|
||||
page.keyboard.press('Tab')
|
||||
time.sleep(1)
|
||||
|
||||
# Enter submits the command
|
||||
page.keyboard.press('Enter')
|
||||
time.sleep(wait_seconds)
|
||||
|
||||
page.screenshot(path=screenshot_path)
|
||||
print(f'Screenshot saved to {screenshot_path}')
|
||||
break
|
||||
browser.close()
|
||||
|
||||
# Example usage:
|
||||
run_slash_command('/refractor status')
|
||||
```
|
||||
|
||||
### Commands with parameters
|
||||
|
||||
After pressing Tab to select the command, Discord shows an options panel. To fill parameters:
|
||||
|
||||
1. The first parameter input is auto-focused after Tab
|
||||
2. Type the value, then Tab to move to the next parameter
|
||||
3. Press Enter when ready to submit
|
||||
|
||||
```python
|
||||
# Example: /refractor status with tier filter
|
||||
msg_box.type('/refractor status', delay=80)
|
||||
time.sleep(2)
|
||||
page.keyboard.press('Tab') # Select command from autocomplete
|
||||
time.sleep(1)
|
||||
# Now fill parameters if needed, or just submit
|
||||
page.keyboard.press('Enter')
|
||||
```
|
||||
|
||||
## Key Selectors
|
||||
|
||||
| Element | Selector |
|
||||
|---------|----------|
|
||||
| Message input box | `[role="textbox"][data-slate-editor="true"]` |
|
||||
| Autocomplete popup | `[class*="autocomplete"]` |
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Brave must be killed before relaunch** — if an instance is already running, `--remote-debugging-port` is silently ignored
|
||||
- **Bot token mismatch** — the bot's `API_TOKEN` in `docker-compose.yml` must match the target API (dev or prod). Symptoms: `{"detail":"Unauthorized"}` in bot logs
|
||||
- **Viewport is None** — when connecting via CDP, `page.viewport_size` returns None. Use `page.evaluate('() => ({w: window.innerWidth, h: window.innerHeight})')` instead
|
||||
- **Autocomplete timing** — typing too fast may not trigger Discord's autocomplete. The `delay=80` on `msg_box.type()` simulates human speed
|
||||
- **Multiple bots** — if multiple bots register the same slash command (e.g. MantiTestBot and PucklTestBot), Tab selects the top option. Verify the correct bot name in the autocomplete popup before proceeding
|
||||
|
||||
## Test Plan Reference
|
||||
|
||||
The Refractor integration test plan is at:
|
||||
`discord-app/tests/refractor-integration-test-plan.md`
|
||||
|
||||
Key test case groups:
|
||||
- REF-01 to REF-06: Tier badges and display
|
||||
- REF-10 to REF-15: Progress bars and filtering
|
||||
- REF-40 to REF-42: Cross-command badges (card, roster)
|
||||
- REF-70 to REF-72: Cross-command badge propagation (the current priority)
|
||||
|
||||
## Verified On
|
||||
|
||||
- **Date:** 2026-04-06
|
||||
- **Browser:** Brave 146.0.7680.178 (Chromium-based)
|
||||
- **Playwright:** Node.js driver via Python sync API
|
||||
- **Bot:** MantiTestBot on Cals Test Server, #pd-game-test channel
|
||||
- **API:** pddev.manticorum.com (dev environment)
|
||||
Loading…
Reference in New Issue
Block a user