browser-test
Skill2.9k starsChanged 15 days agoMITView on GitHub
Test the built userscript on the real chatgpt.com in a dedicated Chrome over CDP. Inject dist, trigger exports, collect the downloads and inspect them. Use when verifying a change in the live app, reproducing an export or screenshot bug, or measuring why a screenshot differs from the page.
- Sends data out
We split this skill into the rules it gives an agent
5 rules. 0 are common, 1 is distinctive, and 4 are very specific to this project.
0
Common
Most of the 6 edited versions we read keep these.
1
Distinctive
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 8
Run the current dist/chatgpt.user.js on the live chatgpt.com without Tampermonkey.
- Very specific: line 71
Every script answers JavaScript dialogs: alerts and beforeunload are accepted, confirm and prompt are dismissed, so a test never confirms …
- Very specific: line 101
Check the collapsed rail too.
- Very specific: line 109
Run git checkout dist/chatgpt.user.js before committing.
- Distinctive: line 135
Add a language there, in the README language bars and as a README file.
“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Browser test
Run the current dist/chatgpt.user.js on the live chatgpt.com without
Tampermonkey. Everything goes through CDP on a Chrome started only for
testing. The scripts need Node 22+.
The scripts live in scripts/browser-test/. Run every command below from
the repository root.
Setup
scripts/browser-test/start-chrome.shstarts Chrome with CDP on port 9222 and a separate profile in~/.chrome-chatgpt-test, or reuses the one that is running. The default binary path is macOS; setCHROME_BINelsewhere.- The first time, and whenever the session expires or Cloudflare shows a challenge, the maintainer logs in by hand. Everything else is automatic.
Loop
pnpm build
node scripts/browser-test/inject.mjs --reload # reload the tab, inject the new build
node scripts/browser-test/eval.mjs -e "return location.href"
node scripts/browser-test/export.mjs Screenshot # or Markdown, HTML, "Copy Text", ...
node scripts/browser-test/export.mjs JSON "OpenAI Official Format"inject.mjsloads the@requirelibraries, a localStorage GM shim and the build. A reload drops it. Navigating by clicking the sidebar keeps it. It refuses to inject twice; pass--reload.eval.mjsruns an async function body in the page and prints the result. Strings print raw, so large output such as a base64 PNG can be piped to a file.click.mjs "<text>" [--selector css] [--hover]clicks or hovers the first visible element with that exact text through CDP input events. Unlikeel.click(), this counts as a user gesture, so hover cards, menus and ChatGPT's own controls react as they do for a person. It fails when something else covers the element, such as a menu or modal left open.console.mjs [--seconds 5] [-e "<body>"]prints console messages, uncaught exceptions and browser errors such as CSP or failed loads. It replays what Chrome kept from before it started, then listens. With-e, it runs the body after listening starts.export.mjsclicks a menu item, waits for the download and unpacks zips. Output goes to the system temp dir by default. Exports contain real conversations: never commit them. The JSON item opens a dialog, so name the format too:JSON "OpenAI Official Format". It stops early when the page shows an alert instead, such as "Please start a conversation first" on the home page.- Chrome lets a tab download only once without a user gesture. Later
downloads wait on a prompt in the address bar that a page screenshot does
not show, so a script sees no file and no error.
export.mjsavoids it with CDPBrowser.setDownloadBehavior, which saves into its own folder while its connection is open. To download any other way, such as ana.click()througheval.mjs, open the same CDP session first. After that call the check stays off and files go back to~/Downloadsuntil Chrome restarts, so restart it to reproduce the block. measure-text.mjscompares the width of text on the page and inside an SVG image. See below.ui-shot.mjsopens one exporter UI state (page,menu,setting,export,json) and screenshots it. See UI changes below.readme-hero.mjsshoots the README hero images. See README hero below.
To open a conversation, click it in the sidebar, which keeps the injected
build: node scripts/browser-test/click.mjs "Title" --selector "nav a".
Every script answers JavaScript dialogs: alerts and beforeunload are
accepted, confirm and prompt are dismissed, so a test never confirms
Archive or Delete by accident. Set CDP_DIALOG=accept to accept them. Each
dialog is printed to stderr as [dialog] ....
Inspecting output
- Crop with ffmpeg:
ffmpeg -i in.png -vf crop=W:H:X:Y out.png. Do not usesips -c, which ignores the offset and crops from the center. - Screenshots are 2x. A turn at list offset N CSS px sits near 2N in the image.
- macOS ImageIO (Preview,
sips) cannot open PNGs taller than about 65k px.
UI changes
Compare every UI change before and after, in both themes. Capture the baseline before editing, since the old build is gone once you rebuild:
pnpm build && node scripts/browser-test/inject.mjs --reload
for t in light dark; do
for s in menu setting export json; do
node scripts/browser-test/ui-shot.mjs $s --theme $t --out /tmp/ui/before-$s-$t.png
done
node scripts/browser-test/ui-shot.mjs setting --theme $t --scroll bottom --out /tmp/ui/before-setting2-$t.png
done
# edit, rebuild, reinject, then repeat with after-*.png and compare the pairs--themeflips<html>only, not the account setting, and lasts until the tab reloads.- Check the collapsed rail too. Toggle the sidebar with
click.mjs; a syntheticclick()does not toggle it. Hover states also needclick.mjs --hover. - Toggling settings in the dialog writes them to the test profile. Put them back when done.
- To match a ChatGPT control, find the live one and read its computed style instead of guessing. Settings > Security and login has switches in both states; Library cards show checkboxes on hover.
- Run
git checkout dist/chatgpt.user.jsbefore committing. CI builds dist.
README hero
Each README shows docs/readme/hero-<code>-<light|dark>.png through a
<picture>, so GitHub picks the image for the reader's theme. Reshoot them
when the layout or the menu changes:
pnpm build
node scripts/browser-test/readme-hero.mjs # all languages, into docs/readme
node scripts/browser-test/readme-hero.mjs fr ko # only some- Collapse ChatGPT's sidebar first, so no chat titles show. The script stages the rest: it replaces the greeting, which can include the user's name, hides the suggestions under the composer, which come from the user's calendar and apps, and swaps the avatar for the OpenAI logo. Look at every image before committing anyway.
- It switches ChatGPT's UI language in Settings and the exporter's language for each shot, then sets both back to automatic. Check that the account is back on "Auto detect" if a run fails.
- Greetings and ChatGPT's option labels live in
LANGUAGESat the top of the script. A greeting should read like one of ChatGPT's own, short and conversational, not a slogan. The exporter has no Korean locale, so the Korean hero keeps the English menu. - Add a language there, in the README language bars and as a README file.
Screenshot bugs
SnapDOM freezes each box at its page size and rasterizes the copy as an SVG
<foreignObject> image. When text measures differently inside the image,
shrink-to-fit boxes wrap, clip or overlap. Measure before guessing:
node scripts/browser-test/measure-text.mjs "中文字寬測試" "🥒 Hello" --weight 600Known gaps, both fixed in src/exporter/image.ts:
- macOS
system-uitracks its CJK fallback to 15.34px per glyph at 16px on the page, but not in the image (16px). Fixed bymatchCjkMetrics. - Chrome widens emoji below 24px in the image (16px becomes 20px) while the
glyph stays the same size. Fixed by
pinEmojiWidths.
Reproduce in this real Chrome before trusting a headless result. Headless Chrome has different fonts, and a fix found there may not hold here.
Other things that look like bugs but are not:
- Timestamps only show when enabled in settings (
body[data-time-format]). - Empty gray image cards can be ChatGPT's own. Check the conversation JSON
for
"hidden_reason": "insufficient_images". - Third-party images without CORS headers cannot be read from the page. The exporter draws placeholders for them by design.
Gotchas
local()in@font-facematches PostScript names such asPingFangTC-Regular, not family names.- Styles and fonts a test adds to
<head>stay until the tab reloads, and a failedFontFacekeeps rejecting laterdocument.fonts.loadcalls. - Export All hits the conversation API once per chat. Keep test runs small.
- A dialog that opens while no script is connected blocks the page, and a
later connection cannot answer it. Close the tab and open a new one:
curl -s http://127.0.0.1:9222/json/listfor its id, thencurl -s http://127.0.0.1:9222/json/close/<id>andcurl -s -X PUT "http://127.0.0.1:9222/json/new?https://chatgpt.com/".
Trending
Quiet since June.thin dataabout the whole repository
Who's talking about pionxzh/chatgpt-exporter
- @rootc0re.com“might try and save what you can with this maybe: github.com/pionxzh/chat...”Bluesky · Apr 25, 2025 · 1 likes
- TomSchmidtDev/browser-ai-chat-exporter“Doesn't fully capture long ChatGPT conversations”GitHub · Jun 23, 2026
- @tyatsumi.mstdn-jp.site.ap.brid.gy“ChatGPT Exporter とかいうツールはあるみたい。試してみるか…。 https://github.com/pionxzh/chatgpt-exporter”Bluesky · Feb 19, 2025
- obsidianmd/obsidian-importer“I wish Obsidian creates ChatGPT JSON support. I tried Google Keep or Roam Research JSON & zip too, but both …”GitHub · Oct 14, 2023
- awesome-scripts/awesome-userscripts“Suggestion: ChatGPT Exporter”GitHub · Apr 24, 2023
The biggest 5 of 7 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 7
Talked about on GitHub and Bluesky (7 times).
On GitHub · 5
- Jun 23, 2026Doesn't fully capture long ChatGPT conversations — TomSchmidtDev/browser-ai-chat-exporter0 reactions · 1 comment
- Oct 14, 2023I wish Obsidian creates ChatGPT JSON support. I tried Google Keep or Roam Research JSON & zip too, but both gave an error. I am not a coder; it might be too much to ask, but Thank you so much for Notion and all the imports. — obsidianmd/obsidian-importer0 reactions · 2 comments
- Apr 24, 2023Suggestion: ChatGPT Exporter — awesome-scripts/awesome-userscripts0 reactions · 1 comment
- Apr 4, 2023basic import from chatgpt — jepler/chap0 reactions · 5 comments
- Dec 13, 20222 links to add — humanloop/awesome-chatgpt0 reactions
On Bluesky · 2
- Apr 25, 2025might try and save what you can with this maybe: github.com/pionxzh/chat... — @rootc0re.com1 likes and repost
- Feb 19, 2025ChatGPT Exporter とかいうツールはあるみたい。試してみるか…。 https://github.com/pionxzh/chatgpt-exporter — @tyatsumi.mstdn-jp.site.ap.brid.gy0 likes and reposts · 1 reply
Other versions7 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 7
- langwatch/langwatch★ 5ksame name
- alexiolan/craft-skills★ 17same name
- alexjbarnes/cockpit★ 16same name
- leek/agent-skills★ 4same name
- jpoindexter/theft-kit★ 1same name
- TheophilusChinomona/idev★ 0same name
- wzc-miyasaki/agent-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repositoryOne other file it gives its agents.
- browser-testSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

