@jackwener/opencli 1.7.10 → 1.7.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/README.zh-CN.md +5 -4
- package/cli-manifest.json +1443 -24
- package/clis/1688/assets.js +1 -0
- package/clis/1688/download.js +1 -0
- package/clis/1688/item.js +1 -0
- package/clis/1688/search.js +2 -1
- package/clis/1688/store.js +1 -0
- package/clis/36kr/article.js +1 -0
- package/clis/36kr/hot.js +1 -0
- package/clis/36kr/news.js +1 -0
- package/clis/36kr/search.js +1 -0
- package/clis/51job/company.js +1 -0
- package/clis/51job/detail.js +1 -0
- package/clis/51job/hot.js +1 -0
- package/clis/51job/search.js +1 -0
- package/clis/amazon/bestsellers.js +1 -0
- package/clis/amazon/discussion.js +1 -0
- package/clis/amazon/movers-shakers.js +1 -0
- package/clis/amazon/new-releases.js +1 -0
- package/clis/amazon/offer.js +1 -0
- package/clis/amazon/product.js +1 -0
- package/clis/amazon/rankings.js +1 -0
- package/clis/amazon/search.js +1 -0
- package/clis/antigravity/dump.js +1 -0
- package/clis/antigravity/extract-code.js +1 -0
- package/clis/antigravity/model.js +1 -0
- package/clis/antigravity/new.js +1 -0
- package/clis/antigravity/read.js +1 -0
- package/clis/antigravity/send.js +1 -0
- package/clis/antigravity/status.js +1 -0
- package/clis/antigravity/watch.js +1 -0
- package/clis/apple-podcasts/episodes.js +1 -0
- package/clis/apple-podcasts/search.js +1 -0
- package/clis/apple-podcasts/top.js +1 -0
- package/clis/arxiv/arxiv.test.js +112 -0
- package/clis/arxiv/paper.js +4 -3
- package/clis/arxiv/recent.js +33 -0
- package/clis/arxiv/search.js +19 -7
- package/clis/arxiv/utils.js +68 -5
- package/clis/baidu-scholar/search.js +1 -0
- package/clis/band/bands.js +1 -0
- package/clis/band/mentions.js +1 -0
- package/clis/band/post.js +1 -0
- package/clis/band/posts.js +1 -0
- package/clis/barchart/flow.js +1 -0
- package/clis/barchart/greeks.js +1 -0
- package/clis/barchart/options.js +1 -0
- package/clis/barchart/quote.js +1 -0
- package/clis/bbc/news.js +1 -0
- package/clis/bilibili/comments.js +1 -0
- package/clis/bilibili/download.js +1 -0
- package/clis/bilibili/dynamic.js +1 -0
- package/clis/bilibili/favorite.js +1 -0
- package/clis/bilibili/feed.js +2 -0
- package/clis/bilibili/following.js +1 -0
- package/clis/bilibili/history.js +1 -0
- package/clis/bilibili/hot.js +6 -1
- package/clis/bilibili/hot.test.js +17 -0
- package/clis/bilibili/me.js +1 -1
- package/clis/bilibili/ranking.js +1 -0
- package/clis/bilibili/search.js +1 -1
- package/clis/bilibili/subtitle.js +1 -0
- package/clis/bilibili/user-videos.js +1 -0
- package/clis/bilibili/video.js +1 -0
- package/clis/binance/asks.js +1 -0
- package/clis/binance/depth.js +1 -0
- package/clis/binance/gainers.js +1 -0
- package/clis/binance/klines.js +1 -0
- package/clis/binance/losers.js +1 -0
- package/clis/binance/pairs.js +1 -0
- package/clis/binance/price.js +1 -0
- package/clis/binance/prices.js +1 -0
- package/clis/binance/ticker.js +1 -0
- package/clis/binance/top.js +1 -0
- package/clis/binance/trades.js +1 -0
- package/clis/bloomberg/businessweek.js +1 -0
- package/clis/bloomberg/economics.js +1 -0
- package/clis/bloomberg/feeds.js +1 -0
- package/clis/bloomberg/industries.js +1 -0
- package/clis/bloomberg/main.js +1 -0
- package/clis/bloomberg/markets.js +1 -0
- package/clis/bloomberg/news.js +1 -0
- package/clis/bloomberg/opinions.js +1 -0
- package/clis/bloomberg/politics.js +1 -0
- package/clis/bloomberg/tech.js +1 -0
- package/clis/bluesky/feeds.js +1 -0
- package/clis/bluesky/followers.js +1 -0
- package/clis/bluesky/following.js +1 -0
- package/clis/bluesky/profile.js +1 -0
- package/clis/bluesky/search.js +1 -0
- package/clis/bluesky/starter-packs.js +1 -0
- package/clis/bluesky/thread.js +1 -0
- package/clis/bluesky/trending.js +1 -0
- package/clis/bluesky/user.js +3 -1
- package/clis/boss/batchgreet.js +1 -0
- package/clis/boss/chatlist.js +1 -0
- package/clis/boss/chatmsg.js +1 -0
- package/clis/boss/detail.js +1 -0
- package/clis/boss/exchange.js +1 -0
- package/clis/boss/greet.js +1 -0
- package/clis/boss/invite.js +1 -0
- package/clis/boss/joblist.js +1 -0
- package/clis/boss/mark.js +1 -0
- package/clis/boss/recommend.js +1 -0
- package/clis/boss/resume.js +1 -0
- package/clis/boss/search.js +1 -0
- package/clis/boss/send.js +1 -0
- package/clis/boss/stats.js +1 -0
- package/clis/chaoxing/assignments.js +1 -0
- package/clis/chaoxing/exams.js +1 -0
- package/clis/chatgpt/image.js +1 -0
- package/clis/chatgpt-app/ask.js +1 -0
- package/clis/chatgpt-app/model.js +1 -0
- package/clis/chatgpt-app/new.js +1 -0
- package/clis/chatgpt-app/read.js +1 -0
- package/clis/chatgpt-app/send.js +1 -0
- package/clis/chatgpt-app/status.js +1 -0
- package/clis/chatwise/ask.js +1 -0
- package/clis/chatwise/export.js +1 -0
- package/clis/chatwise/history.js +1 -0
- package/clis/chatwise/model.js +1 -0
- package/clis/chatwise/read.js +1 -0
- package/clis/chatwise/send.js +1 -0
- package/clis/claude/ask.js +1 -0
- package/clis/claude/detail.js +1 -0
- package/clis/claude/history.js +1 -0
- package/clis/claude/new.js +1 -0
- package/clis/claude/read.js +1 -0
- package/clis/claude/send.js +1 -0
- package/clis/claude/status.js +1 -0
- package/clis/cnki/search.js +1 -0
- package/clis/codex/ask.js +1 -0
- package/clis/codex/export.js +1 -0
- package/clis/codex/extract-diff.js +1 -0
- package/clis/codex/history.js +1 -0
- package/clis/codex/model.js +1 -0
- package/clis/codex/read.js +1 -0
- package/clis/codex/send.js +1 -0
- package/clis/coupang/add-to-cart.js +1 -0
- package/clis/coupang/search.js +1 -0
- package/clis/ctrip/search.js +1 -0
- package/clis/cursor/ask.js +1 -0
- package/clis/cursor/composer.js +1 -0
- package/clis/cursor/export.js +1 -0
- package/clis/cursor/extract-code.js +1 -0
- package/clis/cursor/history.js +1 -0
- package/clis/cursor/model.js +1 -0
- package/clis/cursor/read.js +1 -0
- package/clis/cursor/send.js +1 -0
- package/clis/dblp/dblp.test.js +397 -0
- package/clis/dblp/paper.js +40 -0
- package/clis/dblp/search.js +45 -0
- package/clis/dblp/utils.js +290 -0
- package/clis/deepseek/ask.js +1 -0
- package/clis/deepseek/history.js +1 -0
- package/clis/deepseek/new.js +1 -0
- package/clis/deepseek/read.js +1 -0
- package/clis/deepseek/status.js +1 -0
- package/clis/devto/devto.test.js +236 -0
- package/clis/devto/read.js +103 -0
- package/clis/devto/tag.js +5 -1
- package/clis/devto/top.js +5 -1
- package/clis/devto/user.js +5 -1
- package/clis/dianping/__fixtures__/search.html +168 -0
- package/clis/dianping/__fixtures__/shop.html +6 -0
- package/clis/dianping/dianping.test.js +424 -0
- package/clis/dianping/search.js +154 -0
- package/clis/dianping/shop.js +173 -0
- package/clis/dianping/utils.js +157 -0
- package/clis/dictionary/examples.js +1 -0
- package/clis/dictionary/search.js +1 -0
- package/clis/dictionary/synonyms.js +1 -0
- package/clis/discord-app/channels.js +1 -0
- package/clis/discord-app/delete.js +1 -0
- package/clis/discord-app/members.js +1 -0
- package/clis/discord-app/read.js +1 -0
- package/clis/discord-app/search.js +1 -0
- package/clis/discord-app/send.js +1 -0
- package/clis/discord-app/servers.js +1 -0
- package/clis/discord-app/status.js +1 -0
- package/clis/douban/book-hot.js +1 -0
- package/clis/douban/download.js +1 -0
- package/clis/douban/marks.js +1 -0
- package/clis/douban/movie-hot.js +2 -1
- package/clis/douban/movie-hot.test.js +14 -0
- package/clis/douban/photos.js +2 -1
- package/clis/douban/reviews.js +1 -0
- package/clis/douban/search.js +1 -0
- package/clis/douban/subject.js +1 -0
- package/clis/douban/top250.js +1 -0
- package/clis/douban/utils.js +11 -13
- package/clis/douban/utils.test.js +79 -0
- package/clis/doubao/ask.js +1 -0
- package/clis/doubao/detail.js +1 -0
- package/clis/doubao/history.js +1 -0
- package/clis/doubao/meeting-summary.js +1 -0
- package/clis/doubao/meeting-transcript.js +1 -0
- package/clis/doubao/new.js +1 -0
- package/clis/doubao/read.js +1 -0
- package/clis/doubao/send.js +1 -0
- package/clis/doubao/status.js +1 -0
- package/clis/doubao-app/ask.js +1 -0
- package/clis/doubao-app/dump.js +1 -0
- package/clis/doubao-app/new.js +1 -0
- package/clis/doubao-app/read.js +1 -0
- package/clis/doubao-app/screenshot.js +1 -0
- package/clis/doubao-app/send.js +1 -0
- package/clis/doubao-app/status.js +1 -0
- package/clis/douyin/activities.js +1 -0
- package/clis/douyin/collections.js +1 -0
- package/clis/douyin/delete.js +1 -0
- package/clis/douyin/draft.js +1 -0
- package/clis/douyin/drafts.js +1 -0
- package/clis/douyin/hashtag.js +1 -0
- package/clis/douyin/location.js +1 -0
- package/clis/douyin/profile.js +1 -0
- package/clis/douyin/publish.js +1 -0
- package/clis/douyin/stats.js +1 -0
- package/clis/douyin/update.js +1 -0
- package/clis/douyin/user-videos.js +1 -0
- package/clis/douyin/videos.js +1 -0
- package/clis/eastmoney/announcement.js +1 -0
- package/clis/eastmoney/convertible.js +1 -0
- package/clis/eastmoney/etf.js +1 -0
- package/clis/eastmoney/holders.js +1 -0
- package/clis/eastmoney/hot-rank.js +1 -0
- package/clis/eastmoney/index-board.js +1 -0
- package/clis/eastmoney/kline.js +1 -0
- package/clis/eastmoney/kuaixun.js +1 -0
- package/clis/eastmoney/longhu.js +1 -0
- package/clis/eastmoney/money-flow.js +1 -0
- package/clis/eastmoney/northbound.js +1 -0
- package/clis/eastmoney/quote.js +1 -0
- package/clis/eastmoney/rank.js +1 -0
- package/clis/eastmoney/sectors.js +1 -0
- package/clis/facebook/add-friend.js +1 -0
- package/clis/facebook/events.js +1 -0
- package/clis/facebook/feed.js +1 -0
- package/clis/facebook/friends.js +1 -0
- package/clis/facebook/groups.js +1 -0
- package/clis/facebook/join-group.js +1 -0
- package/clis/facebook/marketplace-inbox.js +1 -0
- package/clis/facebook/marketplace-listings.js +1 -0
- package/clis/facebook/memories.js +1 -0
- package/clis/facebook/notifications.js +1 -0
- package/clis/facebook/profile.js +1 -0
- package/clis/facebook/search.js +1 -0
- package/clis/gemini/ask.js +1 -0
- package/clis/gemini/deep-research-result.js +1 -0
- package/clis/gemini/deep-research.js +1 -0
- package/clis/gemini/image.js +1 -0
- package/clis/gemini/new.js +1 -0
- package/clis/gitee/search.js +1 -0
- package/clis/gitee/trending.js +1 -0
- package/clis/gitee/user.js +1 -0
- package/clis/google/news.js +1 -0
- package/clis/google/search.js +1 -0
- package/clis/google/suggest.js +1 -0
- package/clis/google/trends.js +1 -0
- package/clis/google-scholar/cite.js +1 -0
- package/clis/google-scholar/profile.js +1 -0
- package/clis/google-scholar/search.js +1 -0
- package/clis/gov-law/recent.js +1 -0
- package/clis/gov-law/search.js +1 -0
- package/clis/gov-policy/recent.js +1 -0
- package/clis/gov-policy/search.js +1 -0
- package/clis/grok/ask.js +1 -0
- package/clis/grok/image.ts +1 -0
- package/clis/hackernews/ask.js +3 -1
- package/clis/hackernews/best.js +3 -1
- package/clis/hackernews/hackernews.test.js +132 -0
- package/clis/hackernews/jobs.js +3 -1
- package/clis/hackernews/new.js +3 -1
- package/clis/hackernews/read.js +188 -0
- package/clis/hackernews/search.js +3 -1
- package/clis/hackernews/show.js +3 -1
- package/clis/hackernews/top.js +3 -1
- package/clis/hackernews/user.js +1 -0
- package/clis/hf/top.js +1 -0
- package/clis/hupu/detail.js +1 -0
- package/clis/hupu/hot.js +3 -1
- package/clis/hupu/like.js +1 -0
- package/clis/hupu/mentions.js +2 -1
- package/clis/hupu/reply.js +1 -0
- package/clis/hupu/search.js +3 -1
- package/clis/hupu/unlike.js +1 -0
- package/clis/imdb/person.js +1 -0
- package/clis/imdb/reviews.js +1 -0
- package/clis/imdb/search.js +1 -0
- package/clis/imdb/title.js +1 -0
- package/clis/imdb/top.js +1 -0
- package/clis/imdb/trending.js +1 -0
- package/clis/indeed/indeed.test.js +375 -0
- package/clis/indeed/job.js +86 -0
- package/clis/indeed/search.js +110 -0
- package/clis/indeed/utils.js +152 -0
- package/clis/instagram/collection-create.js +1 -0
- package/clis/instagram/collection-delete.js +92 -0
- package/clis/instagram/comment.js +1 -0
- package/clis/instagram/download.js +1 -0
- package/clis/instagram/explore.js +1 -0
- package/clis/instagram/follow.js +1 -0
- package/clis/instagram/followers.js +1 -0
- package/clis/instagram/following.js +1 -0
- package/clis/instagram/like.js +1 -0
- package/clis/instagram/note.js +1 -0
- package/clis/instagram/post.js +1 -0
- package/clis/instagram/profile.js +1 -0
- package/clis/instagram/reel.js +1 -0
- package/clis/instagram/save.js +1 -0
- package/clis/instagram/saved.js +1 -0
- package/clis/instagram/search.js +1 -0
- package/clis/instagram/story.js +1 -0
- package/clis/instagram/unfollow.js +1 -0
- package/clis/instagram/unlike.js +1 -0
- package/clis/instagram/unsave.js +1 -0
- package/clis/instagram/user.js +1 -0
- package/clis/jd/add-cart.js +1 -0
- package/clis/jd/cart.js +1 -0
- package/clis/jd/detail.js +1 -0
- package/clis/jd/item.js +1 -0
- package/clis/jd/reviews.js +1 -0
- package/clis/jd/search.js +1 -0
- package/clis/jianyu/detail.js +1 -0
- package/clis/jianyu/search.js +1 -0
- package/clis/jike/comment.js +1 -0
- package/clis/jike/create.js +1 -0
- package/clis/jike/feed.js +3 -1
- package/clis/jike/like.js +1 -0
- package/clis/jike/notifications.js +1 -0
- package/clis/jike/post.js +1 -0
- package/clis/jike/repost.js +1 -0
- package/clis/jike/search.js +3 -1
- package/clis/jike/topic.js +1 -0
- package/clis/jike/user.js +3 -1
- package/clis/jimeng/generate.js +1 -0
- package/clis/jimeng/history.js +1 -0
- package/clis/jimeng/new.js +1 -0
- package/clis/jimeng/workspaces.js +1 -0
- package/clis/ke/chengjiao.js +1 -0
- package/clis/ke/ershoufang.js +1 -0
- package/clis/ke/xiaoqu.js +1 -0
- package/clis/ke/zufang.js +1 -0
- package/clis/lesswrong/comments.js +1 -0
- package/clis/lesswrong/curated.js +1 -0
- package/clis/lesswrong/frontpage.js +1 -0
- package/clis/lesswrong/new.js +1 -0
- package/clis/lesswrong/read.js +1 -0
- package/clis/lesswrong/sequences.js +1 -0
- package/clis/lesswrong/shortform.js +1 -0
- package/clis/lesswrong/tag.js +1 -0
- package/clis/lesswrong/tags.js +1 -0
- package/clis/lesswrong/top-month.js +1 -0
- package/clis/lesswrong/top-week.js +1 -0
- package/clis/lesswrong/top-year.js +1 -0
- package/clis/lesswrong/top.js +1 -0
- package/clis/lesswrong/user-posts.js +1 -0
- package/clis/lesswrong/user.js +1 -0
- package/clis/linkedin/search.js +1 -0
- package/clis/linkedin/timeline.js +1 -0
- package/clis/linux-do/categories.js +1 -0
- package/clis/linux-do/category.js +1 -0
- package/clis/linux-do/feed.js +1 -0
- package/clis/linux-do/hot.js +1 -0
- package/clis/linux-do/latest.js +1 -0
- package/clis/linux-do/search.js +1 -0
- package/clis/linux-do/tags.js +2 -1
- package/clis/linux-do/topic-content.js +1 -0
- package/clis/linux-do/topic.js +1 -0
- package/clis/linux-do/user-posts.js +1 -0
- package/clis/linux-do/user-topics.js +1 -0
- package/clis/lobsters/active.js +4 -1
- package/clis/lobsters/hot.js +4 -1
- package/clis/lobsters/lobsters.test.js +169 -0
- package/clis/lobsters/newest.js +4 -1
- package/clis/lobsters/read.js +196 -0
- package/clis/lobsters/tag.js +4 -1
- package/clis/maimai/search-talents.js +1 -0
- package/clis/medium/feed.js +1 -0
- package/clis/medium/search.js +1 -0
- package/clis/medium/user.js +1 -0
- package/clis/mubu/doc.js +1 -0
- package/clis/mubu/docs.js +1 -0
- package/clis/mubu/notes.js +1 -0
- package/clis/mubu/recent.js +1 -0
- package/clis/mubu/search.js +1 -0
- package/clis/notebooklm/current.js +1 -0
- package/clis/notebooklm/get.js +1 -0
- package/clis/notebooklm/history.js +1 -0
- package/clis/notebooklm/list.js +1 -0
- package/clis/notebooklm/note-list.js +1 -0
- package/clis/notebooklm/notes-get.js +1 -0
- package/clis/notebooklm/open.js +1 -0
- package/clis/notebooklm/source-fulltext.js +1 -0
- package/clis/notebooklm/source-get.js +1 -0
- package/clis/notebooklm/source-guide.js +1 -0
- package/clis/notebooklm/source-list.js +1 -0
- package/clis/notebooklm/status.js +1 -0
- package/clis/notebooklm/summary.js +1 -0
- package/clis/notion/export.js +1 -0
- package/clis/notion/favorites.js +1 -0
- package/clis/notion/new.js +1 -0
- package/clis/notion/read.js +1 -0
- package/clis/notion/search.js +1 -0
- package/clis/notion/sidebar.js +1 -0
- package/clis/notion/status.js +1 -0
- package/clis/notion/write.js +1 -0
- package/clis/nowcoder/companies.js +1 -0
- package/clis/nowcoder/creators.js +1 -0
- package/clis/nowcoder/detail.js +1 -0
- package/clis/nowcoder/experience.js +1 -0
- package/clis/nowcoder/hot.js +1 -0
- package/clis/nowcoder/jobs.js +1 -0
- package/clis/nowcoder/notifications.js +1 -0
- package/clis/nowcoder/papers.js +1 -0
- package/clis/nowcoder/practice.js +1 -0
- package/clis/nowcoder/recommend.js +1 -0
- package/clis/nowcoder/referral.js +1 -0
- package/clis/nowcoder/salary.js +1 -0
- package/clis/nowcoder/search.js +1 -0
- package/clis/nowcoder/suggest.js +1 -0
- package/clis/nowcoder/topics.js +1 -0
- package/clis/nowcoder/trending.js +1 -0
- package/clis/ones/login.js +1 -0
- package/clis/ones/logout.js +1 -0
- package/clis/ones/me.js +1 -0
- package/clis/ones/my-tasks.js +1 -0
- package/clis/ones/task.js +1 -0
- package/clis/ones/tasks.js +1 -0
- package/clis/ones/token-info.js +1 -0
- package/clis/ones/worklog.js +1 -0
- package/clis/openreview/openreview.test.js +345 -0
- package/clis/openreview/paper.js +43 -0
- package/clis/openreview/reviews.js +131 -0
- package/clis/openreview/search.js +46 -0
- package/clis/openreview/utils.js +158 -0
- package/clis/openreview/venue.js +63 -0
- package/clis/paperreview/feedback.js +1 -0
- package/clis/paperreview/review.js +1 -0
- package/clis/paperreview/submit.js +1 -0
- package/clis/pixiv/detail.js +1 -0
- package/clis/pixiv/download.js +1 -0
- package/clis/pixiv/illusts.js +2 -1
- package/clis/pixiv/ranking.js +2 -1
- package/clis/pixiv/search.js +2 -1
- package/clis/pixiv/user.js +2 -0
- package/clis/powerchina/search.js +1 -0
- package/clis/producthunt/browse.js +1 -0
- package/clis/producthunt/hot.js +1 -0
- package/clis/producthunt/posts.js +1 -0
- package/clis/producthunt/today.js +1 -0
- package/clis/quark/ls.js +1 -0
- package/clis/quark/mkdir.js +1 -0
- package/clis/quark/mv.js +1 -0
- package/clis/quark/rename.js +1 -0
- package/clis/quark/rm.js +1 -0
- package/clis/quark/save.js +1 -0
- package/clis/quark/share-tree.js +1 -0
- package/clis/reddit/comment.js +1 -0
- package/clis/reddit/frontpage.js +1 -0
- package/clis/reddit/hot.js +6 -1
- package/clis/reddit/hot.test.js +18 -0
- package/clis/reddit/popular.js +1 -0
- package/clis/reddit/read.js +1 -0
- package/clis/reddit/save.js +1 -0
- package/clis/reddit/saved.js +1 -0
- package/clis/reddit/search.js +1 -0
- package/clis/reddit/subreddit.js +1 -0
- package/clis/reddit/subscribe.js +1 -0
- package/clis/reddit/upvote.js +1 -0
- package/clis/reddit/upvoted.js +1 -0
- package/clis/reddit/user-comments.js +1 -0
- package/clis/reddit/user-posts.js +1 -0
- package/clis/reddit/user.js +1 -0
- package/clis/reuters/search.js +1 -0
- package/clis/sinablog/article.js +1 -0
- package/clis/sinablog/hot.js +1 -0
- package/clis/sinablog/search.js +1 -0
- package/clis/sinablog/user.js +1 -0
- package/clis/sinafinance/news.js +1 -0
- package/clis/sinafinance/rolling-news.js +1 -0
- package/clis/sinafinance/stock-rank.js +1 -0
- package/clis/sinafinance/stock.js +1 -0
- package/clis/smzdm/search.js +1 -0
- package/clis/spotify/spotify.js +11 -0
- package/clis/stackoverflow/bounties.js +11 -3
- package/clis/stackoverflow/hot.js +10 -2
- package/clis/stackoverflow/read.js +314 -0
- package/clis/stackoverflow/search.js +10 -2
- package/clis/stackoverflow/stackoverflow.test.js +346 -0
- package/clis/stackoverflow/unanswered.js +9 -2
- package/clis/steam/top-sellers.js +1 -0
- package/clis/substack/feed.js +1 -0
- package/clis/substack/publication.js +1 -0
- package/clis/substack/search.js +1 -0
- package/clis/taobao/add-cart.js +1 -0
- package/clis/taobao/cart.js +1 -0
- package/clis/taobao/detail.js +1 -0
- package/clis/taobao/reviews.js +1 -0
- package/clis/taobao/search.js +1 -0
- package/clis/tdx/hot-rank.js +1 -0
- package/clis/ths/hot-rank.js +1 -0
- package/clis/tieba/hot.js +2 -1
- package/clis/tieba/posts.js +1 -0
- package/clis/tieba/read.js +1 -0
- package/clis/tieba/search.js +2 -1
- package/clis/tiktok/comment.js +1 -0
- package/clis/tiktok/explore.js +1 -0
- package/clis/tiktok/follow.js +1 -0
- package/clis/tiktok/following.js +1 -0
- package/clis/tiktok/friends.js +1 -0
- package/clis/tiktok/like.js +1 -0
- package/clis/tiktok/live.js +1 -0
- package/clis/tiktok/notifications.js +1 -0
- package/clis/tiktok/profile.js +1 -0
- package/clis/tiktok/save.js +1 -0
- package/clis/tiktok/search.js +1 -0
- package/clis/tiktok/unfollow.js +1 -0
- package/clis/tiktok/unlike.js +1 -0
- package/clis/tiktok/unsave.js +1 -0
- package/clis/tiktok/user.js +1 -0
- package/clis/toutiao/articles.js +1 -0
- package/clis/twitter/accept.js +1 -0
- package/clis/twitter/article.js +1 -0
- package/clis/twitter/block.js +1 -0
- package/clis/twitter/bookmark.js +1 -0
- package/clis/twitter/bookmarks.js +2 -1
- package/clis/twitter/delete.js +1 -0
- package/clis/twitter/download.js +1 -0
- package/clis/twitter/follow.js +1 -0
- package/clis/twitter/followers.js +1 -0
- package/clis/twitter/following.js +1 -0
- package/clis/twitter/hide-reply.js +1 -0
- package/clis/twitter/like.js +1 -0
- package/clis/twitter/likes.js +2 -1
- package/clis/twitter/list-add.js +1 -0
- package/clis/twitter/list-remove.js +1 -0
- package/clis/twitter/list-tweets.js +1 -0
- package/clis/twitter/lists.js +1 -0
- package/clis/twitter/notifications.js +1 -0
- package/clis/twitter/post.js +1 -0
- package/clis/twitter/profile.js +1 -0
- package/clis/twitter/reply-dm.js +1 -0
- package/clis/twitter/reply.js +1 -0
- package/clis/twitter/search.js +1 -0
- package/clis/twitter/thread.js +1 -0
- package/clis/twitter/timeline.js +1 -0
- package/clis/twitter/trending.js +11 -12
- package/clis/twitter/trending.test.js +15 -0
- package/clis/twitter/tweets.js +2 -1
- package/clis/twitter/tweets.test.js +2 -2
- package/clis/twitter/unblock.js +1 -0
- package/clis/twitter/unbookmark.js +1 -0
- package/clis/twitter/unfollow.js +1 -0
- package/clis/uiverse/code.js +1 -0
- package/clis/uiverse/preview.js +1 -0
- package/clis/v2ex/daily.js +1 -0
- package/clis/v2ex/hot.js +1 -0
- package/clis/v2ex/latest.js +1 -0
- package/clis/v2ex/me.js +1 -0
- package/clis/v2ex/member.js +1 -0
- package/clis/v2ex/node.js +1 -0
- package/clis/v2ex/nodes.js +1 -0
- package/clis/v2ex/notifications.js +1 -0
- package/clis/v2ex/replies.js +1 -0
- package/clis/v2ex/topic.js +1 -0
- package/clis/v2ex/user.js +1 -0
- package/clis/wanfang/search.js +1 -0
- package/clis/web/read.js +1 -0
- package/clis/weibo/comments.js +1 -0
- package/clis/weibo/favorites.js +1 -0
- package/clis/weibo/feed.js +3 -1
- package/clis/weibo/hot.js +1 -0
- package/clis/weibo/me.js +1 -0
- package/clis/weibo/post.js +1 -0
- package/clis/weibo/publish.js +1 -0
- package/clis/weibo/search.js +8 -2
- package/clis/weibo/user.js +1 -0
- package/clis/weixin/create-draft.js +1 -0
- package/clis/weixin/download.js +1 -0
- package/clis/weixin/drafts.js +1 -0
- package/clis/weread/ai-outline.js +1 -0
- package/clis/weread/book.js +1 -0
- package/clis/weread/highlights.js +1 -0
- package/clis/weread/notebooks.js +1 -0
- package/clis/weread/notes.js +1 -0
- package/clis/weread/ranking.js +1 -0
- package/clis/weread/search.js +1 -0
- package/clis/weread/shelf.js +1 -0
- package/clis/wikipedia/random.js +1 -0
- package/clis/wikipedia/search.js +1 -0
- package/clis/wikipedia/summary.js +1 -0
- package/clis/wikipedia/trending.js +1 -0
- package/clis/xianyu/chat.js +1 -0
- package/clis/xianyu/item.js +1 -0
- package/clis/xianyu/search.js +1 -0
- package/clis/xiaoe/catalog.js +2 -1
- package/clis/xiaoe/content.js +1 -0
- package/clis/xiaoe/courses.js +1 -0
- package/clis/xiaoe/detail.js +1 -0
- package/clis/xiaoe/play-url.js +1 -0
- package/clis/xiaohongshu/comments.js +1 -0
- package/clis/xiaohongshu/creator-note-detail.js +1 -0
- package/clis/xiaohongshu/creator-notes-summary.js +1 -0
- package/clis/xiaohongshu/creator-notes.js +1 -0
- package/clis/xiaohongshu/creator-profile.js +1 -0
- package/clis/xiaohongshu/creator-stats.js +1 -0
- package/clis/xiaohongshu/download.js +1 -0
- package/clis/xiaohongshu/feed.js +2 -1
- package/clis/xiaohongshu/note.js +1 -0
- package/clis/xiaohongshu/notifications.js +1 -0
- package/clis/xiaohongshu/publish.js +1 -0
- package/clis/xiaohongshu/search.js +1 -0
- package/clis/xiaohongshu/user.js +1 -0
- package/clis/xiaoyuzhou/download.js +1 -0
- package/clis/xiaoyuzhou/episode.js +1 -0
- package/clis/xiaoyuzhou/podcast-episodes.js +1 -0
- package/clis/xiaoyuzhou/podcast.js +1 -0
- package/clis/xiaoyuzhou/transcript.js +1 -0
- package/clis/xueqiu/comments.js +1 -0
- package/clis/xueqiu/earnings-date.js +1 -0
- package/clis/xueqiu/feed.js +1 -0
- package/clis/xueqiu/fund-holdings.js +1 -0
- package/clis/xueqiu/fund-snapshot.js +1 -0
- package/clis/xueqiu/groups.js +1 -0
- package/clis/xueqiu/hot-stock.js +1 -0
- package/clis/xueqiu/hot.js +1 -0
- package/clis/xueqiu/kline.js +1 -0
- package/clis/xueqiu/search.js +1 -0
- package/clis/xueqiu/stock.js +1 -0
- package/clis/xueqiu/watchlist.js +1 -0
- package/clis/yahoo-finance/quote.js +1 -0
- package/clis/yollomi/background.js +1 -0
- package/clis/yollomi/edit.js +1 -0
- package/clis/yollomi/face-swap.js +1 -0
- package/clis/yollomi/generate.js +1 -0
- package/clis/yollomi/models.js +1 -0
- package/clis/yollomi/object-remover.js +1 -0
- package/clis/yollomi/remove-bg.js +1 -0
- package/clis/yollomi/restore.js +1 -0
- package/clis/yollomi/try-on.js +1 -0
- package/clis/yollomi/upload.js +1 -0
- package/clis/yollomi/upscale.js +1 -0
- package/clis/yollomi/video.js +1 -0
- package/clis/youtube/channel.js +1 -0
- package/clis/youtube/comments.js +1 -0
- package/clis/youtube/feed.js +8 -7
- package/clis/youtube/feed.test.js +131 -0
- package/clis/youtube/history.js +1 -0
- package/clis/youtube/like.js +1 -0
- package/clis/youtube/playlist.js +1 -0
- package/clis/youtube/search.js +1 -0
- package/clis/youtube/subscribe.js +1 -0
- package/clis/youtube/subscriptions.js +1 -0
- package/clis/youtube/transcript.js +1 -0
- package/clis/youtube/unlike.js +1 -0
- package/clis/youtube/unsubscribe.js +1 -0
- package/clis/youtube/video.js +1 -0
- package/clis/youtube/watch-later.js +1 -0
- package/clis/yuanbao/ask.js +1 -0
- package/clis/yuanbao/new.js +1 -0
- package/clis/zhihu/answer.js +1 -0
- package/clis/zhihu/collection.js +1 -0
- package/clis/zhihu/collections.js +1 -0
- package/clis/zhihu/comment.js +1 -0
- package/clis/zhihu/download.js +1 -0
- package/clis/zhihu/favorite.js +1 -0
- package/clis/zhihu/follow.js +1 -0
- package/clis/zhihu/hot.js +1 -0
- package/clis/zhihu/like.js +1 -0
- package/clis/zhihu/question.js +1 -0
- package/clis/zhihu/search.js +1 -0
- package/clis/zlibrary/info.js +1 -0
- package/clis/zlibrary/search.js +1 -0
- package/clis/zsxq/dynamics.js +1 -0
- package/clis/zsxq/groups.js +1 -0
- package/clis/zsxq/search.js +1 -0
- package/clis/zsxq/topic.js +1 -0
- package/clis/zsxq/topics.js +1 -0
- package/dist/src/adapter-shadow.d.ts +11 -0
- package/dist/src/adapter-shadow.js +72 -0
- package/dist/src/adapter-shadow.test.d.ts +1 -0
- package/dist/src/adapter-shadow.test.js +49 -0
- package/dist/src/adapter-source.test.js +1 -1
- package/dist/src/browser/analyze.test.js +2 -0
- package/dist/src/browser/base-page.d.ts +15 -2
- package/dist/src/browser/base-page.js +159 -8
- package/dist/src/browser/base-page.test.js +103 -1
- package/dist/src/browser/cdp.js +54 -0
- package/dist/src/browser/cdp.test.js +26 -0
- package/dist/src/browser/dom-helpers.d.ts +1 -1
- package/dist/src/browser/dom-helpers.js +15 -3
- package/dist/src/browser/page.d.ts +1 -0
- package/dist/src/browser/page.js +7 -1
- package/dist/src/browser/page.test.js +17 -0
- package/dist/src/browser/target-resolver.d.ts +17 -2
- package/dist/src/browser/target-resolver.js +85 -4
- package/dist/src/browser/verify-fixture.d.ts +7 -1
- package/dist/src/browser/verify-fixture.js +105 -0
- package/dist/src/browser/verify-fixture.test.js +59 -1
- package/dist/src/build-manifest.d.ts +68 -33
- package/dist/src/build-manifest.js +178 -29
- package/dist/src/build-manifest.test.js +85 -5
- package/dist/src/capabilityRouting.test.js +1 -1
- package/dist/src/cli.js +150 -11
- package/dist/src/cli.test.js +237 -0
- package/dist/src/commanderAdapter.d.ts +1 -1
- package/dist/src/commanderAdapter.js +17 -7
- package/dist/src/commanderAdapter.test.js +7 -6
- package/dist/src/convention-audit.d.ts +50 -0
- package/dist/src/convention-audit.js +546 -0
- package/dist/src/convention-audit.test.d.ts +1 -0
- package/dist/src/convention-audit.test.js +226 -0
- package/dist/src/discovery.js +2 -0
- package/dist/src/doctor.d.ts +2 -0
- package/dist/src/doctor.js +6 -0
- package/dist/src/doctor.test.js +36 -1
- package/dist/src/engine.test.js +10 -10
- package/dist/src/execution.js +1 -1
- package/dist/src/execution.test.js +9 -9
- package/dist/src/help.d.ts +15 -0
- package/dist/src/help.js +149 -0
- package/dist/src/main.js +13 -7
- package/dist/src/manifest-types.d.ts +41 -0
- package/dist/src/manifest-types.js +9 -0
- package/dist/src/plugin.test.js +26 -26
- package/dist/src/registry.d.ts +5 -0
- package/dist/src/registry.js +8 -0
- package/dist/src/registry.test.js +27 -20
- package/dist/src/serialization.d.ts +4 -0
- package/dist/src/serialization.js +27 -1
- package/dist/src/serialization.test.js +24 -2
- package/dist/src/types.d.ts +2 -0
- package/package.json +5 -2
- package/scripts/check-listing-id-pairing.mjs +193 -0
- package/scripts/check-silent-column-drop.mjs +105 -0
- package/scripts/check-typed-error-lint.mjs +118 -0
- package/scripts/silent-column-drop-baseline.json +962 -0
- package/scripts/typed-error-lint-baseline.json +1586 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { findShadowedUserAdapters, formatAdapterShadowIssue } from './adapter-shadow.js';
|
|
6
|
+
describe('adapter shadow detection', () => {
|
|
7
|
+
it('reports user adapters that shadow packaged manifest commands', () => {
|
|
8
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'opencli-adapter-shadow-'));
|
|
9
|
+
try {
|
|
10
|
+
const userClisDir = path.join(root, 'user-clis');
|
|
11
|
+
const builtinRoot = path.join(root, 'pkg');
|
|
12
|
+
const builtinClisDir = path.join(builtinRoot, 'clis');
|
|
13
|
+
fs.mkdirSync(path.join(userClisDir, 'instagram'), { recursive: true });
|
|
14
|
+
fs.mkdirSync(path.join(userClisDir, 'twitter'), { recursive: true });
|
|
15
|
+
fs.mkdirSync(path.join(builtinClisDir, 'instagram'), { recursive: true });
|
|
16
|
+
fs.mkdirSync(path.join(builtinClisDir, 'twitter'), { recursive: true });
|
|
17
|
+
fs.writeFileSync(path.join(userClisDir, 'instagram', 'saved.js'), '', 'utf-8');
|
|
18
|
+
fs.writeFileSync(path.join(userClisDir, 'instagram', 'utils.js'), '', 'utf-8');
|
|
19
|
+
fs.writeFileSync(path.join(userClisDir, 'twitter', 'search.js'), '', 'utf-8');
|
|
20
|
+
fs.writeFileSync(path.join(builtinClisDir, 'instagram', 'saved.js'), '', 'utf-8');
|
|
21
|
+
fs.writeFileSync(path.join(builtinClisDir, 'instagram', 'utils.js'), '', 'utf-8');
|
|
22
|
+
fs.writeFileSync(path.join(builtinClisDir, 'twitter', 'search.js'), '', 'utf-8');
|
|
23
|
+
fs.writeFileSync(path.join(builtinRoot, 'cli-manifest.json'), `${JSON.stringify([
|
|
24
|
+
{ site: 'instagram', name: 'saved', sourceFile: 'instagram/saved.js' },
|
|
25
|
+
])}\n`, 'utf-8');
|
|
26
|
+
expect(findShadowedUserAdapters({ userClisDir, builtinClisDir })).toEqual([
|
|
27
|
+
{
|
|
28
|
+
name: 'instagram/saved',
|
|
29
|
+
userPath: path.join(userClisDir, 'instagram', 'saved.js'),
|
|
30
|
+
builtinPath: path.join(builtinClisDir, 'instagram', 'saved.js'),
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
it('formats a concise doctor issue', () => {
|
|
39
|
+
const issue = formatAdapterShadowIssue([
|
|
40
|
+
{
|
|
41
|
+
name: 'instagram/saved',
|
|
42
|
+
userPath: '/home/me/.opencli/clis/instagram/saved.js',
|
|
43
|
+
builtinPath: '/pkg/clis/instagram/saved.js',
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
expect(issue).toContain('instagram/saved');
|
|
47
|
+
expect(issue).toContain('opencli adapter reset <site>');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -23,6 +23,7 @@ export declare abstract class BasePage implements IPage {
|
|
|
23
23
|
protected _lastUrl: string | null;
|
|
24
24
|
/** Cached previous snapshot hashes for incremental diff marking */
|
|
25
25
|
private _prevSnapshotHashes;
|
|
26
|
+
private _cdpTargetMarkerSeq;
|
|
26
27
|
abstract goto(url: string, options?: {
|
|
27
28
|
waitUntil?: 'load' | 'none';
|
|
28
29
|
settleMs?: number;
|
|
@@ -47,8 +48,20 @@ export declare abstract class BasePage implements IPage {
|
|
|
47
48
|
abstract tabs(): Promise<unknown[]>;
|
|
48
49
|
abstract selectTab(target: number | string): Promise<void>;
|
|
49
50
|
click(ref: string, opts?: ResolveOptions): Promise<ResolveSuccess>;
|
|
50
|
-
/**
|
|
51
|
-
protected tryNativeClick(
|
|
51
|
+
/** Uses native CDP click support when the concrete page exposes it. */
|
|
52
|
+
protected tryNativeClick(x: number, y: number): Promise<boolean>;
|
|
53
|
+
/** Uses native CDP text insertion when the concrete page exposes it. */
|
|
54
|
+
protected tryNativeType(text: string): Promise<boolean>;
|
|
55
|
+
/** Uses native CDP key events when the concrete page exposes them. */
|
|
56
|
+
protected tryNativeKeyPress(key: string, modifiers: string[]): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Run a DOM-domain CDP command against `window.__resolved`.
|
|
59
|
+
*
|
|
60
|
+
* CDP DOM.focus / DOM.scrollIntoViewIfNeeded need a nodeId, while our
|
|
61
|
+
* resolver stores the live Element in page JS. Bridge the two worlds with a
|
|
62
|
+
* short-lived marker attribute, then query it through CDP.
|
|
63
|
+
*/
|
|
64
|
+
protected tryCdpOnResolvedElement(method: 'DOM.focus' | 'DOM.scrollIntoViewIfNeeded'): Promise<boolean>;
|
|
52
65
|
typeText(ref: string, text: string, opts?: ResolveOptions): Promise<ResolveSuccess>;
|
|
53
66
|
pressKey(key: string): Promise<void>;
|
|
54
67
|
scrollTo(ref: string, opts?: ResolveOptions): Promise<unknown>;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { generateSnapshotJs, getFormStateJs } from './dom-snapshot.js';
|
|
12
12
|
import { pressKeyJs, waitForTextJs, waitForCaptureJs, waitForSelectorJs, scrollJs, autoScrollJs, networkRequestsJs, waitForDomStableJs, } from './dom-helpers.js';
|
|
13
|
-
import { resolveTargetJs, clickResolvedJs, typeResolvedJs, scrollResolvedJs, } from './target-resolver.js';
|
|
13
|
+
import { resolveTargetJs, clickResolvedJs, typeResolvedJs, prepareNativeTypeResolvedJs, scrollResolvedJs, } from './target-resolver.js';
|
|
14
14
|
import { TargetError } from './target-errors.js';
|
|
15
15
|
import { CliError } from '../errors.js';
|
|
16
16
|
import { formatSnapshot } from '../snapshotFormatter.js';
|
|
@@ -36,10 +36,32 @@ function previewText(text) {
|
|
|
36
36
|
const preview = (text ?? '').replace(/\s+/g, ' ').trim().slice(0, 300);
|
|
37
37
|
return preview ? `Response preview: ${preview}` : undefined;
|
|
38
38
|
}
|
|
39
|
+
function parseKeyChord(rawKey) {
|
|
40
|
+
const parts = rawKey.split('+').map(part => part.trim()).filter(Boolean);
|
|
41
|
+
if (parts.length <= 1)
|
|
42
|
+
return { key: rawKey, modifiers: [] };
|
|
43
|
+
const modifiers = [];
|
|
44
|
+
for (const token of parts.slice(0, -1)) {
|
|
45
|
+
const normalized = token.toLowerCase();
|
|
46
|
+
if (normalized === 'ctrl' || normalized === 'control')
|
|
47
|
+
modifiers.push('Ctrl');
|
|
48
|
+
else if (normalized === 'cmd' || normalized === 'command' || normalized === 'meta')
|
|
49
|
+
modifiers.push('Meta');
|
|
50
|
+
else if (normalized === 'option' || normalized === 'alt')
|
|
51
|
+
modifiers.push('Alt');
|
|
52
|
+
else if (normalized === 'shift')
|
|
53
|
+
modifiers.push('Shift');
|
|
54
|
+
else
|
|
55
|
+
return { key: rawKey, modifiers: [] };
|
|
56
|
+
}
|
|
57
|
+
const key = parts.at(-1);
|
|
58
|
+
return key ? { key, modifiers } : { key: rawKey, modifiers: [] };
|
|
59
|
+
}
|
|
39
60
|
export class BasePage {
|
|
40
61
|
_lastUrl = null;
|
|
41
62
|
/** Cached previous snapshot hashes for incremental diff marking */
|
|
42
63
|
_prevSnapshotHashes = null;
|
|
64
|
+
_cdpTargetMarkerSeq = 0;
|
|
43
65
|
/**
|
|
44
66
|
* Safely evaluate JS with pre-serialized arguments.
|
|
45
67
|
* Each key in `args` becomes a `const` declaration with JSON-serialized value,
|
|
@@ -133,8 +155,9 @@ export class BasePage {
|
|
|
133
155
|
async click(ref, opts = {}) {
|
|
134
156
|
// Phase 1: Resolve target with fingerprint verification
|
|
135
157
|
const resolved = await runResolve(this, ref, opts);
|
|
158
|
+
const nativeScrolled = await this.tryCdpOnResolvedElement('DOM.scrollIntoViewIfNeeded');
|
|
136
159
|
// Phase 2: Execute click on resolved element
|
|
137
|
-
const result = await this.evaluate(clickResolvedJs());
|
|
160
|
+
const result = await this.evaluate(clickResolvedJs({ skipScroll: nativeScrolled }));
|
|
138
161
|
if (typeof result === 'string' || result == null)
|
|
139
162
|
return resolved;
|
|
140
163
|
if (result.status === 'clicked')
|
|
@@ -147,21 +170,149 @@ export class BasePage {
|
|
|
147
170
|
}
|
|
148
171
|
throw new Error(`Click failed: ${result.error ?? 'JS click and CDP fallback both failed'}`);
|
|
149
172
|
}
|
|
150
|
-
/**
|
|
151
|
-
async tryNativeClick(
|
|
152
|
-
|
|
173
|
+
/** Uses native CDP click support when the concrete page exposes it. */
|
|
174
|
+
async tryNativeClick(x, y) {
|
|
175
|
+
const nativeClick = this.nativeClick;
|
|
176
|
+
if (typeof nativeClick !== 'function')
|
|
177
|
+
return false;
|
|
178
|
+
try {
|
|
179
|
+
await nativeClick.call(this, x, y);
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** Uses native CDP text insertion when the concrete page exposes it. */
|
|
187
|
+
async tryNativeType(text) {
|
|
188
|
+
const nativeType = this.nativeType;
|
|
189
|
+
if (typeof nativeType === 'function') {
|
|
190
|
+
try {
|
|
191
|
+
await nativeType.call(this, text);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// Fall through to the older dedicated insertText primitive if present.
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const insertText = this.insertText;
|
|
199
|
+
if (typeof insertText !== 'function')
|
|
200
|
+
return false;
|
|
201
|
+
try {
|
|
202
|
+
await insertText.call(this, text);
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/** Uses native CDP key events when the concrete page exposes them. */
|
|
210
|
+
async tryNativeKeyPress(key, modifiers) {
|
|
211
|
+
const nativeKeyPress = this.nativeKeyPress;
|
|
212
|
+
if (typeof nativeKeyPress !== 'function')
|
|
213
|
+
return false;
|
|
214
|
+
try {
|
|
215
|
+
await nativeKeyPress.call(this, key, modifiers);
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Run a DOM-domain CDP command against `window.__resolved`.
|
|
224
|
+
*
|
|
225
|
+
* CDP DOM.focus / DOM.scrollIntoViewIfNeeded need a nodeId, while our
|
|
226
|
+
* resolver stores the live Element in page JS. Bridge the two worlds with a
|
|
227
|
+
* short-lived marker attribute, then query it through CDP.
|
|
228
|
+
*/
|
|
229
|
+
async tryCdpOnResolvedElement(method) {
|
|
230
|
+
const cdp = this.cdp;
|
|
231
|
+
if (typeof cdp !== 'function')
|
|
232
|
+
return false;
|
|
233
|
+
const markerAttr = 'data-opencli-cdp-target';
|
|
234
|
+
const markerValue = `${Date.now().toString(36)}-${++this._cdpTargetMarkerSeq}`;
|
|
235
|
+
const selector = `[${markerAttr}="${markerValue}"]`;
|
|
236
|
+
let marked = false;
|
|
237
|
+
try {
|
|
238
|
+
const marker = await this.evaluateWithArgs(`
|
|
239
|
+
(() => {
|
|
240
|
+
const el = window.__resolved;
|
|
241
|
+
if (!el || el.nodeType !== 1 || typeof el.setAttribute !== 'function') {
|
|
242
|
+
return { ok: false };
|
|
243
|
+
}
|
|
244
|
+
el.setAttribute(markerAttr, markerValue);
|
|
245
|
+
return { ok: true };
|
|
246
|
+
})()
|
|
247
|
+
`, { markerAttr, markerValue });
|
|
248
|
+
marked = marker?.ok === true;
|
|
249
|
+
if (!marked)
|
|
250
|
+
return false;
|
|
251
|
+
await cdp.call(this, 'DOM.enable', {}).catch(() => undefined);
|
|
252
|
+
const doc = await cdp.call(this, 'DOM.getDocument', {});
|
|
253
|
+
const rootNodeId = doc?.root?.nodeId;
|
|
254
|
+
if (typeof rootNodeId !== 'number')
|
|
255
|
+
return false;
|
|
256
|
+
const query = await cdp.call(this, 'DOM.querySelector', {
|
|
257
|
+
nodeId: rootNodeId,
|
|
258
|
+
selector,
|
|
259
|
+
});
|
|
260
|
+
const nodeId = query?.nodeId;
|
|
261
|
+
if (typeof nodeId !== 'number' || nodeId <= 0)
|
|
262
|
+
return false;
|
|
263
|
+
await cdp.call(this, method, { nodeId });
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
finally {
|
|
270
|
+
if (marked) {
|
|
271
|
+
await this.evaluateWithArgs(`
|
|
272
|
+
(() => {
|
|
273
|
+
for (const el of document.querySelectorAll(selector)) {
|
|
274
|
+
el.removeAttribute(markerAttr);
|
|
275
|
+
}
|
|
276
|
+
})()
|
|
277
|
+
`, { selector, markerAttr }).catch(() => undefined);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
153
280
|
}
|
|
154
281
|
async typeText(ref, text, opts = {}) {
|
|
155
282
|
const resolved = await runResolve(this, ref, opts);
|
|
156
|
-
|
|
283
|
+
let typed = false;
|
|
284
|
+
let nativeScrolled = false;
|
|
285
|
+
let nativeFocused = false;
|
|
286
|
+
if (typeof this.nativeType === 'function' || typeof this.insertText === 'function') {
|
|
287
|
+
try {
|
|
288
|
+
nativeScrolled = await this.tryCdpOnResolvedElement('DOM.scrollIntoViewIfNeeded');
|
|
289
|
+
nativeFocused = await this.tryCdpOnResolvedElement('DOM.focus');
|
|
290
|
+
const preparation = await this.evaluate(prepareNativeTypeResolvedJs({
|
|
291
|
+
skipScroll: nativeScrolled,
|
|
292
|
+
skipFocus: nativeFocused,
|
|
293
|
+
}));
|
|
294
|
+
typed = preparation?.ok === true && await this.tryNativeType(text);
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
// Native input is a reliability upgrade, not the only path. Preserve
|
|
298
|
+
// the existing DOM setter fallback if preparation fails.
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!typed) {
|
|
302
|
+
await this.evaluate(typeResolvedJs(text));
|
|
303
|
+
}
|
|
157
304
|
return resolved;
|
|
158
305
|
}
|
|
159
306
|
async pressKey(key) {
|
|
160
|
-
|
|
307
|
+
const parsed = parseKeyChord(key);
|
|
308
|
+
if (!await this.tryNativeKeyPress(parsed.key, parsed.modifiers)) {
|
|
309
|
+
await this.evaluate(pressKeyJs(parsed.key, parsed.modifiers));
|
|
310
|
+
}
|
|
161
311
|
}
|
|
162
312
|
async scrollTo(ref, opts = {}) {
|
|
163
313
|
const resolved = await runResolve(this, ref, opts);
|
|
164
|
-
const
|
|
314
|
+
const nativeScrolled = await this.tryCdpOnResolvedElement('DOM.scrollIntoViewIfNeeded');
|
|
315
|
+
const result = (await this.evaluate(scrollResolvedJs({ skipScroll: nativeScrolled })));
|
|
165
316
|
// Fold match_level into the scroll payload so the user-facing envelope
|
|
166
317
|
// carries it the same way click / type do.
|
|
167
318
|
if (result && typeof result === 'object') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { CliError } from '../errors.js';
|
|
3
3
|
import { BasePage } from './base-page.js';
|
|
4
4
|
class TestPage extends BasePage {
|
|
@@ -15,6 +15,31 @@ class TestPage extends BasePage {
|
|
|
15
15
|
async tabs() { return []; }
|
|
16
16
|
async selectTab() { }
|
|
17
17
|
}
|
|
18
|
+
class ActionPage extends BasePage {
|
|
19
|
+
results = [];
|
|
20
|
+
withArgsResults = [];
|
|
21
|
+
scripts = [];
|
|
22
|
+
withArgs = [];
|
|
23
|
+
nativeType;
|
|
24
|
+
insertText;
|
|
25
|
+
nativeKeyPress;
|
|
26
|
+
cdp;
|
|
27
|
+
async goto() { }
|
|
28
|
+
async evaluate(js) {
|
|
29
|
+
this.scripts.push(js);
|
|
30
|
+
return this.results.shift() ?? null;
|
|
31
|
+
}
|
|
32
|
+
async evaluateWithArgs(js, args) {
|
|
33
|
+
this.scripts.push(js);
|
|
34
|
+
this.withArgs.push(args);
|
|
35
|
+
return this.withArgsResults.shift() ?? null;
|
|
36
|
+
}
|
|
37
|
+
async getCookies() { return []; }
|
|
38
|
+
async screenshot() { return ''; }
|
|
39
|
+
async tabs() { return []; }
|
|
40
|
+
async selectTab() { }
|
|
41
|
+
}
|
|
42
|
+
const resolveOk = { ok: true, matches_n: 1, match_level: 'exact' };
|
|
18
43
|
describe('BasePage.fetchJson', () => {
|
|
19
44
|
it('passes a narrow browser-context JSON request and parses the response in Node', async () => {
|
|
20
45
|
const page = new TestPage();
|
|
@@ -72,3 +97,80 @@ describe('BasePage.fetchJson', () => {
|
|
|
72
97
|
});
|
|
73
98
|
});
|
|
74
99
|
});
|
|
100
|
+
describe('BasePage native input routing', () => {
|
|
101
|
+
it('types rich-editor text via native Input.insertText when available', async () => {
|
|
102
|
+
const page = new ActionPage();
|
|
103
|
+
page.nativeType = vi.fn().mockResolvedValue(undefined);
|
|
104
|
+
page.results = [resolveOk, { ok: true, mode: 'contenteditable' }];
|
|
105
|
+
await expect(page.typeText('#editor', 'hello')).resolves.toEqual({ matches_n: 1, match_level: 'exact' });
|
|
106
|
+
expect(page.nativeType).toHaveBeenCalledWith('hello');
|
|
107
|
+
expect(page.scripts).toHaveLength(2);
|
|
108
|
+
expect(page.scripts[1]).toContain('nearestContentEditableHost');
|
|
109
|
+
expect(page.scripts.join('\n')).not.toContain("return 'typed'");
|
|
110
|
+
});
|
|
111
|
+
it('uses CDP DOM focus and scroll before native text insertion when available', async () => {
|
|
112
|
+
const page = new ActionPage();
|
|
113
|
+
page.nativeType = vi.fn().mockResolvedValue(undefined);
|
|
114
|
+
page.cdp = vi.fn()
|
|
115
|
+
.mockResolvedValueOnce({})
|
|
116
|
+
.mockResolvedValueOnce({ root: { nodeId: 1 } })
|
|
117
|
+
.mockResolvedValueOnce({ nodeId: 7 })
|
|
118
|
+
.mockResolvedValueOnce({})
|
|
119
|
+
.mockResolvedValueOnce({})
|
|
120
|
+
.mockResolvedValueOnce({ root: { nodeId: 1 } })
|
|
121
|
+
.mockResolvedValueOnce({ nodeId: 7 })
|
|
122
|
+
.mockResolvedValueOnce({});
|
|
123
|
+
page.results = [resolveOk, { ok: true, mode: 'input' }];
|
|
124
|
+
page.withArgsResults = [{ ok: true }, undefined, { ok: true }, undefined];
|
|
125
|
+
await page.typeText('#q', 'hello');
|
|
126
|
+
expect(page.cdp).toHaveBeenCalledWith('DOM.scrollIntoViewIfNeeded', { nodeId: 7 });
|
|
127
|
+
expect(page.cdp).toHaveBeenCalledWith('DOM.focus', { nodeId: 7 });
|
|
128
|
+
expect(page.nativeType).toHaveBeenCalledWith('hello');
|
|
129
|
+
expect(page.scripts.at(-1)).toContain('if (false) el.scrollIntoView');
|
|
130
|
+
expect(page.scripts.at(-1)).toContain('if (false) {');
|
|
131
|
+
});
|
|
132
|
+
it('keeps the DOM setter fallback when native text insertion is unavailable', async () => {
|
|
133
|
+
const page = new ActionPage();
|
|
134
|
+
page.results = [resolveOk, 'typed'];
|
|
135
|
+
await page.typeText('#q', 'hello');
|
|
136
|
+
expect(page.scripts).toHaveLength(2);
|
|
137
|
+
expect(page.scripts[1]).toContain('document.execCommand');
|
|
138
|
+
expect(page.scripts[1]).toContain("return 'typed'");
|
|
139
|
+
});
|
|
140
|
+
it('falls back to DOM typing if native text insertion fails', async () => {
|
|
141
|
+
const page = new ActionPage();
|
|
142
|
+
page.nativeType = vi.fn().mockRejectedValue(new Error('native failed'));
|
|
143
|
+
page.results = [resolveOk, { ok: true, mode: 'input' }, 'typed'];
|
|
144
|
+
await page.typeText('#q', 'hello');
|
|
145
|
+
expect(page.nativeType).toHaveBeenCalledWith('hello');
|
|
146
|
+
expect(page.scripts).toHaveLength(3);
|
|
147
|
+
expect(page.scripts[2]).toContain("return 'typed'");
|
|
148
|
+
});
|
|
149
|
+
it('uses CDP DOM scrollIntoViewIfNeeded before JS click when available', async () => {
|
|
150
|
+
const page = new ActionPage();
|
|
151
|
+
page.cdp = vi.fn()
|
|
152
|
+
.mockResolvedValueOnce({})
|
|
153
|
+
.mockResolvedValueOnce({ root: { nodeId: 1 } })
|
|
154
|
+
.mockResolvedValueOnce({ nodeId: 9 })
|
|
155
|
+
.mockResolvedValueOnce({});
|
|
156
|
+
page.results = [resolveOk, { status: 'clicked', x: 1, y: 2 }];
|
|
157
|
+
page.withArgsResults = [{ ok: true }, undefined];
|
|
158
|
+
await page.click('#save');
|
|
159
|
+
expect(page.cdp).toHaveBeenCalledWith('DOM.scrollIntoViewIfNeeded', { nodeId: 9 });
|
|
160
|
+
expect(page.scripts.at(-1)).toContain('if (false) el.scrollIntoView');
|
|
161
|
+
});
|
|
162
|
+
it('presses key chords through native CDP key events when available', async () => {
|
|
163
|
+
const page = new ActionPage();
|
|
164
|
+
page.nativeKeyPress = vi.fn().mockResolvedValue(undefined);
|
|
165
|
+
await page.pressKey('Control+a');
|
|
166
|
+
expect(page.nativeKeyPress).toHaveBeenCalledWith('a', ['Ctrl']);
|
|
167
|
+
expect(page.scripts).toHaveLength(0);
|
|
168
|
+
});
|
|
169
|
+
it('falls back to synthetic keyboard events with parsed modifiers', async () => {
|
|
170
|
+
const page = new ActionPage();
|
|
171
|
+
await page.pressKey('Meta+N');
|
|
172
|
+
expect(page.scripts).toHaveLength(1);
|
|
173
|
+
expect(page.scripts[0]).toContain('key: "N"');
|
|
174
|
+
expect(page.scripts[0]).toContain('metaKey: true');
|
|
175
|
+
});
|
|
176
|
+
});
|
package/dist/src/browser/cdp.js
CHANGED
|
@@ -317,6 +317,60 @@ class CDPPage extends BasePage {
|
|
|
317
317
|
async selectTab(_target) {
|
|
318
318
|
// Not supported in direct CDP mode
|
|
319
319
|
}
|
|
320
|
+
async cdp(method, params = {}) {
|
|
321
|
+
return this.bridge.send(method, params);
|
|
322
|
+
}
|
|
323
|
+
async handleJavaScriptDialog(accept, promptText) {
|
|
324
|
+
await this.cdp('Page.handleJavaScriptDialog', {
|
|
325
|
+
accept,
|
|
326
|
+
...(promptText !== undefined && { promptText }),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
async nativeClick(x, y) {
|
|
330
|
+
await this.cdp('Input.dispatchMouseEvent', {
|
|
331
|
+
type: 'mousePressed',
|
|
332
|
+
x,
|
|
333
|
+
y,
|
|
334
|
+
button: 'left',
|
|
335
|
+
clickCount: 1,
|
|
336
|
+
});
|
|
337
|
+
await this.cdp('Input.dispatchMouseEvent', {
|
|
338
|
+
type: 'mouseReleased',
|
|
339
|
+
x,
|
|
340
|
+
y,
|
|
341
|
+
button: 'left',
|
|
342
|
+
clickCount: 1,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
async nativeType(text) {
|
|
346
|
+
await this.cdp('Input.insertText', { text });
|
|
347
|
+
}
|
|
348
|
+
async insertText(text) {
|
|
349
|
+
await this.nativeType(text);
|
|
350
|
+
}
|
|
351
|
+
async nativeKeyPress(key, modifiers = []) {
|
|
352
|
+
let modifierFlags = 0;
|
|
353
|
+
for (const mod of modifiers) {
|
|
354
|
+
if (mod === 'Alt')
|
|
355
|
+
modifierFlags |= 1;
|
|
356
|
+
if (mod === 'Ctrl' || mod === 'Control')
|
|
357
|
+
modifierFlags |= 2;
|
|
358
|
+
if (mod === 'Meta')
|
|
359
|
+
modifierFlags |= 4;
|
|
360
|
+
if (mod === 'Shift')
|
|
361
|
+
modifierFlags |= 8;
|
|
362
|
+
}
|
|
363
|
+
await this.cdp('Input.dispatchKeyEvent', {
|
|
364
|
+
type: 'keyDown',
|
|
365
|
+
key,
|
|
366
|
+
modifiers: modifierFlags,
|
|
367
|
+
});
|
|
368
|
+
await this.cdp('Input.dispatchKeyEvent', {
|
|
369
|
+
type: 'keyUp',
|
|
370
|
+
key,
|
|
371
|
+
modifiers: modifierFlags,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
320
374
|
}
|
|
321
375
|
function isCookie(value) {
|
|
322
376
|
return isRecord(value)
|
|
@@ -49,4 +49,30 @@ describe('CDPBridge cookies', () => {
|
|
|
49
49
|
{ name: 'exact', value: '2', domain: 'example.com' },
|
|
50
50
|
]);
|
|
51
51
|
});
|
|
52
|
+
it('exposes native input helpers on direct CDP pages', async () => {
|
|
53
|
+
vi.stubEnv('OPENCLI_CDP_ENDPOINT', 'ws://127.0.0.1:9222/devtools/page/1');
|
|
54
|
+
const bridge = new CDPBridge();
|
|
55
|
+
const send = vi.spyOn(bridge, 'send').mockResolvedValue({});
|
|
56
|
+
const page = await bridge.connect();
|
|
57
|
+
send.mockClear();
|
|
58
|
+
expect(page.nativeType).toBeTypeOf('function');
|
|
59
|
+
expect(page.nativeKeyPress).toBeTypeOf('function');
|
|
60
|
+
expect(page.nativeClick).toBeTypeOf('function');
|
|
61
|
+
expect(page.handleJavaScriptDialog).toBeTypeOf('function');
|
|
62
|
+
expect(page.cdp).toBeTypeOf('function');
|
|
63
|
+
await page.nativeType('hello');
|
|
64
|
+
await page.nativeKeyPress('a', ['Ctrl']);
|
|
65
|
+
await page.nativeClick(10, 20);
|
|
66
|
+
await page.handleJavaScriptDialog(true, 'ok');
|
|
67
|
+
await page.cdp('Page.getLayoutMetrics', {});
|
|
68
|
+
expect(send.mock.calls).toEqual([
|
|
69
|
+
['Input.insertText', { text: 'hello' }],
|
|
70
|
+
['Input.dispatchKeyEvent', { type: 'keyDown', key: 'a', modifiers: 2 }],
|
|
71
|
+
['Input.dispatchKeyEvent', { type: 'keyUp', key: 'a', modifiers: 2 }],
|
|
72
|
+
['Input.dispatchMouseEvent', { type: 'mousePressed', x: 10, y: 20, button: 'left', clickCount: 1 }],
|
|
73
|
+
['Input.dispatchMouseEvent', { type: 'mouseReleased', x: 10, y: 20, button: 'left', clickCount: 1 }],
|
|
74
|
+
['Page.handleJavaScriptDialog', { accept: true, promptText: 'ok' }],
|
|
75
|
+
['Page.getLayoutMetrics', {}],
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
52
78
|
});
|
|
@@ -11,7 +11,7 @@ export declare function clickJs(ref: string): string;
|
|
|
11
11
|
* Uses native setter for React compat + execCommand for contenteditable. */
|
|
12
12
|
export declare function typeTextJs(ref: string, text: string): string;
|
|
13
13
|
/** Generate JS to press a keyboard key */
|
|
14
|
-
export declare function pressKeyJs(key: string): string;
|
|
14
|
+
export declare function pressKeyJs(key: string, modifiers?: string[]): string;
|
|
15
15
|
/** Generate JS to wait for text to appear in the page */
|
|
16
16
|
export declare function waitForTextJs(text: string, timeoutMs: number): string;
|
|
17
17
|
/** Generate JS for scroll */
|
|
@@ -80,12 +80,24 @@ export function typeTextJs(ref, text) {
|
|
|
80
80
|
`;
|
|
81
81
|
}
|
|
82
82
|
/** Generate JS to press a keyboard key */
|
|
83
|
-
export function pressKeyJs(key) {
|
|
83
|
+
export function pressKeyJs(key, modifiers = []) {
|
|
84
|
+
const hasCtrl = modifiers.includes('Ctrl') || modifiers.includes('Control');
|
|
85
|
+
const hasAlt = modifiers.includes('Alt');
|
|
86
|
+
const hasMeta = modifiers.includes('Meta');
|
|
87
|
+
const hasShift = modifiers.includes('Shift');
|
|
84
88
|
return `
|
|
85
89
|
(() => {
|
|
86
90
|
const el = document.activeElement || document.body;
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
const init = {
|
|
92
|
+
key: ${JSON.stringify(key)},
|
|
93
|
+
bubbles: true,
|
|
94
|
+
ctrlKey: ${hasCtrl},
|
|
95
|
+
altKey: ${hasAlt},
|
|
96
|
+
metaKey: ${hasMeta},
|
|
97
|
+
shiftKey: ${hasShift},
|
|
98
|
+
};
|
|
99
|
+
el.dispatchEvent(new KeyboardEvent('keydown', init));
|
|
100
|
+
el.dispatchEvent(new KeyboardEvent('keyup', init));
|
|
89
101
|
return 'pressed';
|
|
90
102
|
})()
|
|
91
103
|
`;
|
|
@@ -68,6 +68,7 @@ export declare class Page extends BasePage {
|
|
|
68
68
|
}>>;
|
|
69
69
|
evaluateInFrame(js: string, frameIndex: number): Promise<unknown>;
|
|
70
70
|
cdp(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
71
|
+
handleJavaScriptDialog(accept: boolean, promptText?: string): Promise<void>;
|
|
71
72
|
/** CDP native click fallback — called when JS el.click() fails */
|
|
72
73
|
protected tryNativeClick(x: number, y: number): Promise<boolean>;
|
|
73
74
|
/** Precise click using DOM.getContentQuads/getBoxModel for inline elements */
|
package/dist/src/browser/page.js
CHANGED
|
@@ -283,6 +283,12 @@ export class Page extends BasePage {
|
|
|
283
283
|
...this._cmdOpts(),
|
|
284
284
|
});
|
|
285
285
|
}
|
|
286
|
+
async handleJavaScriptDialog(accept, promptText) {
|
|
287
|
+
await this.cdp('Page.handleJavaScriptDialog', {
|
|
288
|
+
accept,
|
|
289
|
+
...(promptText !== undefined && { promptText }),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
286
292
|
/** CDP native click fallback — called when JS el.click() fails */
|
|
287
293
|
async tryNativeClick(x, y) {
|
|
288
294
|
try {
|
|
@@ -374,7 +380,7 @@ export class Page extends BasePage {
|
|
|
374
380
|
for (const mod of modifiers) {
|
|
375
381
|
if (mod === 'Alt')
|
|
376
382
|
modifierFlags |= 1;
|
|
377
|
-
if (mod === 'Ctrl')
|
|
383
|
+
if (mod === 'Ctrl' || mod === 'Control')
|
|
378
384
|
modifierFlags |= 2;
|
|
379
385
|
if (mod === 'Meta')
|
|
380
386
|
modifierFlags |= 4;
|
|
@@ -102,6 +102,23 @@ describe('Page network capture compatibility', () => {
|
|
|
102
102
|
expect(warnMock).toHaveBeenCalledTimes(1);
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
|
+
describe('Page CDP helpers', () => {
|
|
106
|
+
beforeEach(() => {
|
|
107
|
+
sendCommandMock.mockReset();
|
|
108
|
+
sendCommandFullMock.mockReset();
|
|
109
|
+
warnMock.mockReset();
|
|
110
|
+
});
|
|
111
|
+
it('handles JavaScript dialogs through the CDP passthrough', async () => {
|
|
112
|
+
sendCommandMock.mockResolvedValueOnce({});
|
|
113
|
+
const page = new Page('browser:default');
|
|
114
|
+
await page.handleJavaScriptDialog(true, 'confirm');
|
|
115
|
+
expect(sendCommandMock).toHaveBeenCalledWith('cdp', expect.objectContaining({
|
|
116
|
+
workspace: 'browser:default',
|
|
117
|
+
cdpMethod: 'Page.handleJavaScriptDialog',
|
|
118
|
+
cdpParams: { accept: true, promptText: 'confirm' },
|
|
119
|
+
}));
|
|
120
|
+
});
|
|
121
|
+
});
|
|
105
122
|
describe('Page active target tracking', () => {
|
|
106
123
|
beforeEach(() => {
|
|
107
124
|
sendCommandMock.mockReset();
|
|
@@ -72,16 +72,31 @@ export declare function resolveTargetJs(ref: string, opts?: ResolveOptions): str
|
|
|
72
72
|
* Generate JS for click that uses the unified resolver.
|
|
73
73
|
* Assumes resolveTargetJs has been called and __resolved is set.
|
|
74
74
|
*/
|
|
75
|
-
export declare function clickResolvedJs(
|
|
75
|
+
export declare function clickResolvedJs(opts?: {
|
|
76
|
+
skipScroll?: boolean;
|
|
77
|
+
}): string;
|
|
76
78
|
/**
|
|
77
79
|
* Generate JS for type that uses the unified resolver.
|
|
78
80
|
*/
|
|
79
81
|
export declare function typeResolvedJs(text: string): string;
|
|
82
|
+
/**
|
|
83
|
+
* Prepare the resolved element for native CDP Input.insertText.
|
|
84
|
+
*
|
|
85
|
+
* This preserves `browser type`'s existing "replace current text" semantics:
|
|
86
|
+
* focus the editable target, select its current contents, then let CDP insert
|
|
87
|
+
* real browser text input so rich editors can update their internal state.
|
|
88
|
+
*/
|
|
89
|
+
export declare function prepareNativeTypeResolvedJs(opts?: {
|
|
90
|
+
skipScroll?: boolean;
|
|
91
|
+
skipFocus?: boolean;
|
|
92
|
+
}): string;
|
|
80
93
|
/**
|
|
81
94
|
* Generate JS for scrollTo that uses the unified resolver.
|
|
82
95
|
* Assumes resolveTargetJs has been called and __resolved is set.
|
|
83
96
|
*/
|
|
84
|
-
export declare function scrollResolvedJs(
|
|
97
|
+
export declare function scrollResolvedJs(opts?: {
|
|
98
|
+
skipScroll?: boolean;
|
|
99
|
+
}): string;
|
|
85
100
|
/**
|
|
86
101
|
* Generate JS to get text content of resolved element.
|
|
87
102
|
*/
|