Docs: BGG auth policy in spec/README, upload-flow recon notes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
# BGG "Add to Collection" flow — recon notes for the upload stage
|
||||
|
||||
Recorded 2026-08-01 by walking the flow manually on boardgamegeek.com (Wingspan,
|
||||
id 266192) while logged in. Dialog opened, inspected, and **cancelled without
|
||||
saving**. These notes are the selector documentation the spec requires before
|
||||
automating Stage 5.
|
||||
|
||||
## Entry point
|
||||
|
||||
- Game page has **two** "Add To" buttons (one in the game header module, one
|
||||
lower on the page) — target the first, but match by accessible name, not
|
||||
position. Button's accessible name is "Add To" with adjacent text
|
||||
"Collection".
|
||||
- Clicking opens a `role="dialog"` containing a `<form>`. Dialog heading shows
|
||||
a "Loading..." span before content settles — **wait for the game-name
|
||||
heading** (e.g. `getByRole('heading', {name: gameName})`) before
|
||||
interacting.
|
||||
|
||||
## Main dialog structure
|
||||
|
||||
- **Status checkboxes**, each wrapped in a `<label>`: Own, Prev. Owned,
|
||||
For Trade, Want to Play, Want in Trade, Want to Buy, Pre-ordered, Wishlist.
|
||||
→ `dialog.getByLabel('Own')` and check it. Nothing is pre-checked.
|
||||
- Rating: a 1–10 slider plus a "Rating" text input. We do not set ratings.
|
||||
- Comment (public) textbox — unused by us.
|
||||
- "Advanced (private info, parts exchange)" expander: Price Paid, Current
|
||||
Price, Quantity, Acquisition Date, Acquired From, Inventory Date/Location,
|
||||
Private Comment, Want/Has Parts. All unused (we don't track provenance).
|
||||
- "Customize Item Info (title, image)" expander: Custom Title, Custom Image
|
||||
Id, plus **manual version-override fields** (Publisher Id, Language select,
|
||||
Year, Other, Barcode). These are for defining a custom version — do NOT use
|
||||
them; always pick a cataloged version instead (or none).
|
||||
- Footer: `Save` (`type="submit"`) and `Cancel` buttons.
|
||||
|
||||
## Version picker ("Set version/edition")
|
||||
|
||||
- Button labeled **"Set version/edition"** near the top of the dialog swaps
|
||||
the dialog content to a "Versions" sub-view (same `role="dialog"`).
|
||||
- The sub-view is a **paginated list with NO search/filter box**. Each
|
||||
listitem's text is the full canonical version name + year, e.g.
|
||||
"Flügelschlag (German fifth edition) (2024)" — these names match the
|
||||
version names returned by `/thing?id=X&versions=1`.
|
||||
- Selection strategy: resolve the target version NAME from the XML API,
|
||||
then page through the list matching listitem text
|
||||
(`getByRole('listitem').filter({hasText: versionName})`). Newest years
|
||||
appear first.
|
||||
- The sub-view has its own **Cancel** that returns to the main dialog — it
|
||||
is a different button from the main dialog's Cancel. Two-level dismissal.
|
||||
|
||||
## Automation gotchas observed
|
||||
|
||||
1. **Element references go stale constantly.** The page re-renders after
|
||||
load and after every dialog transition; clicks on cached handles silently
|
||||
miss. Playwright's auto-waiting role/label locators handle this — never
|
||||
cache element handles across a dialog state change.
|
||||
2. **Dialog persists in the DOM after cancel**, just hidden
|
||||
(`offsetParent === null`). "Is the dialog gone" checks must test
|
||||
visibility, not existence. Same applies when verifying a save completed.
|
||||
3. First click on "Add To" right after page load can no-op (hydration race).
|
||||
Wait for network-idle or the button's stable state before clicking.
|
||||
4. Verify saves via the collection API (`--verify`), not by UI state.
|
||||
|
||||
## Playwright locator sketch
|
||||
|
||||
```python
|
||||
page.get_by_role("button", name="Add To").first.click()
|
||||
dialog = page.get_by_role("dialog")
|
||||
dialog.get_by_role("heading", name=game_name).wait_for()
|
||||
dialog.get_by_label("Own").check()
|
||||
if version_name:
|
||||
dialog.get_by_role("button", name="Set version/edition").click()
|
||||
# page through listitems until version_name matches, then click it
|
||||
dialog.get_by_role("button", name="Save").click()
|
||||
```
|
||||
|
||||
Unverified so far (needs a real, sacrificial save on one game before batch
|
||||
runs): pagination controls in the version sub-view, exact post-save behavior
|
||||
(toast? dialog close? redirect?), and how the dialog differs when the game is
|
||||
ALREADY in the collection (second-copy flow must create a new entry, not edit
|
||||
the existing one).
|
||||
Reference in New Issue
Block a user