feat(e2e): add Grok (x.com/i/grok) to the dictation sweep target list#509
Merged
Conversation
Grok is X's own AI assistant embedded in the X web app — not a chat host SayPi has a ChatbotIdentifier/Chatbot.ts adapter for, so it only gets generic universal dictation (no call button, auto-submit, or TTS). Verified live: the extension decorates the composer, the dictation button appears, and a synthetic utterance lands correctly in the field. Two live-verified gotchas fixed along the way, both about selector choice for the field config (not product bugs): - `:visible` is a Playwright-only pseudo-class; it works through page.click()/page.waitForSelector() but throws a SyntaxError under the native document.querySelector() the transcript-landed check uses inside page.waitForFunction(). - The field selector must not pin the placeholder's *value* either: UniversalDictationModule rewrites it while dictating (e.g. to "Recording..."), and the sweep never explicitly stops dictation, so a value-pinned selector stops matching once recording starts and never matches again — a false "transcript didn't land" even though it did. Landed on a presence-only `textarea[placeholder]` selector, which still disambiguates from x.com's second, placeholder-less mirror textarea. Grok also requires the seeded CDP profile to be signed into an X account (unlike login-free Mistral) — documented in doc/e2e-dictation-sweep.md, including that Google's FedCM account-chooser can't be automated (it's a native browser dialog outside the page DOM), so it's a one-time manual sign-in. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01T4uuEsKVySbRV9XRQa2ebc
- Fix "both targets" -> "all targets" now that there are three. - Note that a grok failure on an unauthenticated profile is expected (it's the only target requiring manual X sign-in), not a fresh defect. - Update the Boundaries headed-mode note for two real-site targets. - Mention Grok in AGENTS.md's dictation-sweep summary so other agents scoping "what does this sweep cover" aren't misled. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01T4uuEsKVySbRV9XRQa2ebc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
x.com/i/grok) as a thirde2e-dictation-sweeptarget, per founder request to sweep it going forward.ChatbotIdentifier/Chatbot.tsadapter for (that's a separate, much larger feature the founder deferred; see e2e-host-sweep: synthetic click in selectClaudeModel leaves Claude's model-selector backdrop stuck, blocking the page #501/Universal dictation: no button appears on Mistral Le Chat's composer (regression vs #163's confirmed-working list) #502/Dictation contextual refinement silently fails on any id-less field (target key includes mutable placeholder text) #507 investigation trail). Today it only gets generic universal dictation, and this PR confirms/locks that in as working.Notable gotchas fixed along the way (selector choice, not product bugs)
:visibleis a Playwright-only pseudo-class — works viapage.click()/page.waitForSelector()but throws aSyntaxErrorunder the nativedocument.querySelector()the harness's transcript-landed check uses insidepage.waitForFunction().UniversalDictationModulerewrites it while dictating (e.g. to "Recording..."), and the sweep never explicitly stops dictation — so a value-pinned selector ([placeholder="Ask anything"]) stops matching the instant recording starts and never matches again, producing a false "transcript didn't land" even though it visibly did (confirmed via screenshot). Landed on a presence-onlytextarea[placeholder]selector — stable all session, and still disambiguates from x.com's second, placeholder-less mirror<textarea>.Preconditions note
Grok requires the seeded CDP profile to be signed into an X/Twitter account (unlike login-free Mistral) — documented in
doc/e2e-dictation-sweep.md. Google's FedCM account-chooser can't be automated (native browser dialog outside the page DOM), so it's a one-time manual sign-in, same pattern as the SayPi-account seed step in the host-sweep doc.Test plan
test/scripts/e2e-dictation-sweep-lib.spec.ts(target registry shape, default target list, selector-portability assertions) — fail-first, confirmed RED before each fix.x.com/i/grok(Layer 4 CDP, headed): first run caught both gotchas above via real evidence (pageErrors, screenshot showing the transcript present despitetranscriptLanded=false); final run after both fixes:decorated=true button=true transcriptLanded=true, 0 page errors.npm testgreen:tsc --noEmit, Jest (2/2), Vitest (205 files / 1795 tests, 1 pre-existing skip).🤖 Generated with Claude Code
https://claude.ai/code/session_01T4uuEsKVySbRV9XRQa2ebc