171 lines
6.1 KiB
Markdown
171 lines
6.1 KiB
Markdown
---
|
|
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)
|