claude-home/paper-dynasty/discord-browser-testing-workflow.md

6.1 KiB

title description type domain tags
Discord Bot Browser Testing via Playwright + CDP 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. runbook paper-dynasty
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.

killall brave && sleep 2 && brave-browser-stable --remote-debugging-port=9222 &

2. Verify CDP is responding

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

# 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

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

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
# 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)