Reusable Flows & Helpers
All high-level actions live under dapp/e2e/playwright/flows and dapp/e2e/playwright/utils. Specs import from ../utils (the barrel file) so they can chain flows without worrying about selector drift or timing issues.
Flow catalog
Mint/burn helpers plus safety guards that ensure we always start from a clean slate.
NFT flowsToken gate unlock retries and gated form submissions.
Gate & form flowsConversational helpers, inline renderer assertions, and secret song playback.
Chat & music flowsConnect/disconnect logic, QR previews, and navigation helpers.
Wallet & nav utilsNFT flows
mintKey(page)
- Clicks the “Mint NFT” button and waits for the burn button to appear (
60stimeout). - Used after
burnIfKeyPresentto guarantee the UI is ready to mint.
burnKey(page)
- Works with both idle (
Burn NFT) and busy (Burning NFT, processing) aria labels. - Waits until the mint button returns, ensuring the UI resets.
burnIfKeyPresent(page)
- Logs the current UI state via
NFTUtils.debugState(). - Waits for the mint UI to settle (loading placeholder → final buttons).
- Calls
canBurn()and burns the NFT if required, then verifies the mint button is back.
These helpers make the real Sepolia specs idempotent—failed runs won’t leave stray NFTs behind.
Gate & form
unlockTokenGateWithRetry(page, options)
Retry envelope
Attempts the unlock up to maxAttempts (default 3). Between attempts it can reload the page and wait pauseBetweenAttemptsMs.
Dual success criteria
Races the appearance of #gatedTextarea against a successful /api/gate-access response. Either condition counts as a success.
Error recovery
Looks for a processing modal/dialog and tries to cancel it before refreshing. Throws only after exhausting all attempts.
submitMsgRito(page, message)
Switches to the “Msg Rito” tab, fills #gatedTextarea, and handles both the explicit submit button and the fallback Sign & Submit variant. It is typically called after the gate unlock flow succeeds.
Chat & music
flows/chatbot-helpers.flow.ts provides granular helpers (sendChatMessage, deleteChatConversation, etc.) while flows/chatbot.flow.ts bundles a legacy chatOnce function for one-off interactions.
Smart chat
smartChat(page, message, expectedResponse?, options)determines whether a conversation is already active.- It clicks the desired mode button (default “Freestyle Rap Mode”), optionally resets the chat, sends the message, and asserts the assistant response.
- Use
options.skipResponseCheckwhen the response is an inline renderer or handled via a separate assertion.
Wallet & navigation
flows/wallet.flow.ts wraps WalletTestUtils for spec-friendly calls:
ensureConnected(page, walletName?)/ensureDisconnected(page)— idempotent connect/disconnect.previewWalletConnectQr(page)— opens the modal, switches to WalletConnect, waits for a QR image (desktop only), and returns to the wallet list.previewQrThenConnect(page)— preview + connect using whatever wallet is currently highlighted.reconnectAfterReload(page)— ensures the connection persists after a hard reload.cancelConnectViaEsc(page)— exercises the ESC-to-close path and asserts the modal disappears.isMobileWidth(page)— utility that specs use totest.skipQR checks on mobile.
Navigation helpers (NavigationUtils) and screenshot helpers (ScreenshotUtils) live in utils/wallet.utils.ts as well, giving you helper.navigation.goto('/gate') or quick before/after captures.
Chat utilities
utils/chat.utils.ts centralises selectors and lifecycle waits:
ChatSelectorsenumerates the CSS/test-id fallbacks.waitForStreamingToFinish()waits for the stop button to appear/disappear (covers streaming completion).scrollChatToBottom()ensures the last assistant bubble is visible before matching text.expectLastAssistantToContain()is the go-to assertion for textual replies.
When you find yourself repeating the same selectors in a new spec, consider moving them into a flow or utility here. Keeping the selectors in one place makes refactors far less painful.
Ready to see how the flows come together? Jump to the spec playbooks page for scenario-by-scenario breakdowns.