@sovovs/bycli 1.8.0 → 2.0.0
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/cli-manifest.json +0 -61
- package/dist/src/adapter-shadow.js +2 -2
- package/dist/src/browser/analyze.d.ts +10 -99
- package/dist/src/browser/analyze.js +10 -226
- package/dist/src/browser/cdp.js +64 -7
- package/dist/src/browser/navigation-guard.d.ts +78 -0
- package/dist/src/browser/navigation-guard.js +142 -0
- package/dist/src/browser/navigation-guard.test.d.ts +1 -0
- package/dist/src/browser/url-policy.d.ts +89 -0
- package/dist/src/browser/url-policy.js +311 -0
- package/dist/src/browser/url-policy.test.d.ts +1 -0
- package/dist/src/browser/verify-fixture.js +2 -2
- package/dist/src/cli.js +10 -13
- package/dist/src/config-paths.d.ts +19 -0
- package/dist/src/config-paths.js +31 -0
- package/dist/src/daemon.js +240 -0
- package/dist/src/discovery.d.ts +5 -3
- package/dist/src/discovery.js +8 -6
- package/dist/src/execution.js +8 -3
- package/dist/src/main.js +18 -2
- package/dist/src/plugin.js +11 -8
- package/dist/src/recorder/highlevel/analyze.d.ts +29 -0
- package/dist/src/recorder/highlevel/analyze.js +91 -0
- package/dist/src/recorder/highlevel/init.d.ts +125 -0
- package/dist/src/recorder/highlevel/init.js +279 -0
- package/dist/src/recorder/highlevel/init.test.d.ts +1 -0
- package/dist/src/recorder/highlevel/verify.d.ts +73 -0
- package/dist/src/recorder/highlevel/verify.js +74 -0
- package/dist/src/recorder/http/analyze-runner.d.ts +28 -0
- package/dist/src/recorder/http/analyze-runner.js +78 -0
- package/dist/src/recorder/http/analyze-runner.test.d.ts +1 -0
- package/dist/src/recorder/http/handlers.d.ts +51 -0
- package/dist/src/recorder/http/handlers.js +219 -0
- package/dist/src/recorder/http/wrapper-config.d.ts +20 -0
- package/dist/src/recorder/http/wrapper-config.js +82 -0
- package/dist/src/recorder/http/wrapper-envelope.d.ts +31 -0
- package/dist/src/recorder/http/wrapper-envelope.js +51 -0
- package/dist/src/recorder/http/wrapper-gates.d.ts +16 -0
- package/dist/src/recorder/http/wrapper-gates.js +28 -0
- package/dist/src/recorder/http/wrapper-metrics.d.ts +1 -0
- package/dist/src/recorder/http/wrapper-metrics.js +5 -0
- package/dist/src/recorder/http/wrapper-registry.d.ts +50 -0
- package/dist/src/recorder/http/wrapper-registry.js +98 -0
- package/dist/src/recorder/http/wrapper-server.d.ts +59 -0
- package/dist/src/recorder/http/wrapper-server.js +240 -0
- package/dist/src/recorder/http/wrapper.test.d.ts +1 -0
- package/dist/src/recorder/observability/logger.d.ts +5 -0
- package/dist/src/recorder/observability/logger.js +13 -0
- package/dist/src/recorder/runner/config.d.ts +34 -0
- package/dist/src/recorder/runner/config.js +84 -0
- package/dist/src/recorder/runner/config.test.d.ts +1 -0
- package/dist/src/recorder/runner/reap.d.ts +79 -0
- package/dist/src/recorder/runner/reap.js +230 -0
- package/dist/src/recorder/runner/reap.test.d.ts +1 -0
- package/dist/src/recorder/runner/runner-port.d.ts +95 -0
- package/dist/src/recorder/runner/runner-port.js +518 -0
- package/dist/src/recorder/runner/runner-port.test.d.ts +1 -0
- package/dist/src/recorder/runner/session-keys.d.ts +28 -0
- package/dist/src/recorder/runner/session-keys.js +72 -0
- package/dist/src/recorder/runner/session-keys.test.d.ts +1 -0
- package/dist/src/recorder/runner/verify-runner-main.d.ts +95 -0
- package/dist/src/recorder/runner/verify-runner-main.js +275 -0
- package/dist/src/recorder/runner/verify-runner-main.test.d.ts +1 -0
- package/package.json +8 -5
- package/scripts/m1-daemon-navigate-drive.mjs +138 -0
- package/scripts/m1-extension-spike.mjs +237 -0
- package/scripts/m1-live-spike.mjs +156 -0
- package/scripts/m6b-connect-back-spike.sh +31 -0
- package/scripts/recorder.sh +186 -0
- package/clis/12306/utils.test.js +0 -331
- package/clis/1688/assets.test.js +0 -39
- package/clis/1688/download.test.js +0 -31
- package/clis/1688/item.test.js +0 -67
- package/clis/1688/search.test.js +0 -75
- package/clis/1688/shared.test.js +0 -57
- package/clis/1688/store.test.js +0 -62
- package/clis/36kr/article.test.js +0 -46
- package/clis/36kr/hot.test.js +0 -15
- package/clis/36kr/news.test.js +0 -85
- package/clis/51job/utils.test.js +0 -69
- package/clis/_atlassian/shared.test.js +0 -170
- package/clis/aibase/news.test.js +0 -59
- package/clis/amazon/bestsellers.test.js +0 -29
- package/clis/amazon/discussion.test.js +0 -124
- package/clis/amazon/offer.test.js +0 -29
- package/clis/amazon/product.test.js +0 -24
- package/clis/amazon/rankings.test.js +0 -41
- package/clis/amazon/search.test.js +0 -22
- package/clis/amazon/shared.test.js +0 -44
- package/clis/apple-podcasts/commands.test.js +0 -119
- package/clis/apple-podcasts/utils.test.js +0 -57
- package/clis/arxiv/arxiv.test.js +0 -112
- package/clis/baidu-scholar/search.test.js +0 -23
- package/clis/barchart/greeks.test.js +0 -138
- package/clis/bilibili/comment.test.js +0 -153
- package/clis/bilibili/comments.test.js +0 -142
- package/clis/bilibili/dynamic.test.js +0 -68
- package/clis/bilibili/hot.test.js +0 -17
- package/clis/bilibili/subtitle.test.js +0 -196
- package/clis/bilibili/summary.test.js +0 -210
- package/clis/bilibili/utils.test.js +0 -61
- package/clis/bilibili/video.test.js +0 -132
- package/clis/binance/commands.test.js +0 -70
- package/clis/bloomberg/utils.test.js +0 -129
- package/clis/booking/booking.test.js +0 -356
- package/clis/boss/chatlist.test.js +0 -211
- package/clis/boss/chatmsg.test.js +0 -230
- package/clis/boss/search.test.js +0 -78
- package/clis/boss/utils.test.js +0 -34
- package/clis/brave/search.test.js +0 -76
- package/clis/chaoxing/utils.test.js +0 -45
- package/clis/chatgpt/commands.test.js +0 -45
- package/clis/chatgpt/envelope.test.js +0 -108
- package/clis/chatgpt/image.test.js +0 -188
- package/clis/chatgpt/utils.test.js +0 -330
- package/clis/chatgpt-app/ax.test.js +0 -35
- package/clis/chatwise/composer.test.js +0 -186
- package/clis/chess/analyze.test.js +0 -79
- package/clis/chess/game.test.js +0 -178
- package/clis/chess/games.test.js +0 -164
- package/clis/chess/stats.test.js +0 -79
- package/clis/chess/utils.test.js +0 -230
- package/clis/claude/ask.test.js +0 -338
- package/clis/claude/commands.test.js +0 -118
- package/clis/claude/utils.test.js +0 -148
- package/clis/cnki/search.test.js +0 -18
- package/clis/codex/sidebar.test.js +0 -329
- package/clis/coingecko/coingecko.test.js +0 -109
- package/clis/confluence/commands.test.js +0 -195
- package/clis/coupang/coupang.test.js +0 -159
- package/clis/coupang/utils.test.js +0 -62
- package/clis/ctrip/ctrip.test.js +0 -719
- package/clis/dblp/dblp.test.js +0 -397
- package/clis/deepseek/ask.test.js +0 -312
- package/clis/deepseek/detail.test.js +0 -81
- package/clis/deepseek/send.test.js +0 -107
- package/clis/deepseek/utils.test.js +0 -370
- package/clis/defillama/defillama.test.js +0 -99
- package/clis/devto/devto.test.js +0 -236
- package/clis/dianping/dianping.test.js +0 -578
- package/clis/douban/download.test.js +0 -170
- package/clis/douban/movie-hot.test.js +0 -14
- package/clis/douban/search.test.js +0 -11
- package/clis/douban/subject.test.js +0 -11
- package/clis/douban/utils.test.js +0 -438
- package/clis/doubao/detail.test.js +0 -42
- package/clis/doubao/history.test.js +0 -37
- package/clis/doubao/utils.test.js +0 -344
- package/clis/douyin/_shared/browser-fetch.test.js +0 -64
- package/clis/douyin/_shared/creation-id.test.js +0 -22
- package/clis/douyin/_shared/imagex-upload.test.js +0 -87
- package/clis/douyin/_shared/sts2.test.js +0 -27
- package/clis/douyin/_shared/text-extra.test.js +0 -37
- package/clis/douyin/_shared/timing.test.js +0 -28
- package/clis/douyin/_shared/tos-upload-short-read.test.js +0 -83
- package/clis/douyin/_shared/tos-upload.test.js +0 -229
- package/clis/douyin/_shared/transcode.test.js +0 -91
- package/clis/douyin/_shared/vod-upload.test.js +0 -38
- package/clis/douyin/activities.test.js +0 -55
- package/clis/douyin/collections.test.js +0 -45
- package/clis/douyin/delete.test.js +0 -100
- package/clis/douyin/draft.test.js +0 -335
- package/clis/douyin/drafts.test.js +0 -11
- package/clis/douyin/hashtag.test.js +0 -171
- package/clis/douyin/location.test.js +0 -23
- package/clis/douyin/profile.test.js +0 -46
- package/clis/douyin/publish-upload-id.test.js +0 -170
- package/clis/douyin/publish.test.js +0 -38
- package/clis/douyin/stats.test.js +0 -22
- package/clis/douyin/update.test.js +0 -11
- package/clis/douyin/user-videos.test.js +0 -151
- package/clis/douyin/videos.test.js +0 -54
- package/clis/duckduckgo/search.test.js +0 -128
- package/clis/duckduckgo/suggest.test.js +0 -66
- package/clis/eastmoney/hot-rank.test.js +0 -59
- package/clis/endoflife/endoflife.test.js +0 -51
- package/clis/facebook/feed.test.js +0 -169
- package/clis/facebook/marketplace.test.js +0 -91
- package/clis/facebook/notifications.test.js +0 -458
- package/clis/facebook/search.test.js +0 -55
- package/clis/flathub/flathub.test.js +0 -90
- package/clis/flomo/memos.test.js +0 -144
- package/clis/gemini/ask.test.js +0 -100
- package/clis/gemini/deep-research-result.test.js +0 -155
- package/clis/gemini/deep-research.test.js +0 -181
- package/clis/gemini/reply-state.test.js +0 -671
- package/clis/gemini/utils.test.js +0 -198
- package/clis/geogebra/geogebra.test.js +0 -175
- package/clis/gitee/search.test.js +0 -65
- package/clis/gitee/user.test.js +0 -63
- package/clis/google/utils.test.js +0 -75
- package/clis/google-scholar/cite.test.js +0 -47
- package/clis/google-scholar/profile.test.js +0 -49
- package/clis/google-scholar/search.test.js +0 -71
- package/clis/goproxy/goproxy.test.js +0 -103
- package/clis/gov-law/commands.test.js +0 -39
- package/clis/gov-policy/gov-policy.test.js +0 -224
- package/clis/grok/ask.test.js +0 -29
- package/clis/grok/utils.test.js +0 -103
- package/clis/hackernews/hackernews.test.js +0 -132
- package/clis/hf/hf.test.js +0 -16
- package/clis/hupu/hot.test.js +0 -224
- package/clis/imdb/utils.test.js +0 -88
- package/clis/indeed/indeed.test.js +0 -375
- package/clis/instagram/_shared/private-publish.test.js +0 -705
- package/clis/instagram/_shared/protocol-capture.test.js +0 -114
- package/clis/instagram/download.test.js +0 -118
- package/clis/instagram/note.test.js +0 -53
- package/clis/instagram/post.test.js +0 -1647
- package/clis/instagram/reel.test.js +0 -167
- package/clis/instagram/story.test.js +0 -134
- package/clis/jd/commands.test.js +0 -41
- package/clis/jd/item.test.js +0 -354
- package/clis/jianyu/search.test.js +0 -153
- package/clis/jianyu/shared/procurement-contract.test.js +0 -72
- package/clis/jianyu/shared/procurement-detail.test.js +0 -84
- package/clis/jike/read.test.js +0 -86
- package/clis/jira/commands.test.js +0 -287
- package/clis/juejin/search.js +0 -123
- package/clis/juejin/search.test.js +0 -98
- package/clis/lesswrong/frontpage.test.js +0 -37
- package/clis/lichess/lichess.test.js +0 -85
- package/clis/linkedin/connect.test.js +0 -213
- package/clis/linkedin/inbox.test.js +0 -152
- package/clis/linkedin/job-detail.test.js +0 -38
- package/clis/linkedin/jobs-preferences.test.js +0 -43
- package/clis/linkedin/people-search.test.js +0 -216
- package/clis/linkedin/post-analytics.test.js +0 -40
- package/clis/linkedin/posts.test.js +0 -40
- package/clis/linkedin/profile-analytics.test.js +0 -67
- package/clis/linkedin/profile-experience.test.js +0 -152
- package/clis/linkedin/profile-projects.test.js +0 -111
- package/clis/linkedin/profile-read.test.js +0 -77
- package/clis/linkedin/safe-send.test.js +0 -204
- package/clis/linkedin/salesnav-inbox.test.js +0 -113
- package/clis/linkedin/salesnav-message.test.js +0 -172
- package/clis/linkedin/salesnav-search.test.js +0 -76
- package/clis/linkedin/salesnav-thread.test.js +0 -79
- package/clis/linkedin/search.test.js +0 -222
- package/clis/linkedin/sent-invitations.test.js +0 -62
- package/clis/linkedin/services-read.test.js +0 -105
- package/clis/linkedin/thread-snapshot.test.js +0 -89
- package/clis/linkedin/timeline.test.js +0 -81
- package/clis/linkedin-learning/course.test.js +0 -114
- package/clis/linkedin-learning/search.test.js +0 -144
- package/clis/linkedin-learning/trending.test.js +0 -123
- package/clis/linux-do/feed.test.js +0 -153
- package/clis/linux-do/topic-content.test.js +0 -59
- package/clis/lobsters/lobsters.test.js +0 -169
- package/clis/notebooklm/add-source.test.js +0 -97
- package/clis/notebooklm/compat.test.js +0 -16
- package/clis/notebooklm/create.test.js +0 -58
- package/clis/notebooklm/generate-audio.test.js +0 -63
- package/clis/notebooklm/generate-slides.test.js +0 -75
- package/clis/notebooklm/history.test.js +0 -58
- package/clis/notebooklm/note-list.test.js +0 -56
- package/clis/notebooklm/notes-get.test.js +0 -72
- package/clis/notebooklm/open.test.js +0 -63
- package/clis/notebooklm/rpc.test.js +0 -131
- package/clis/notebooklm/source-fulltext.test.js +0 -106
- package/clis/notebooklm/source-get.test.js +0 -84
- package/clis/notebooklm/source-guide.test.js +0 -104
- package/clis/notebooklm/summary.test.js +0 -78
- package/clis/notebooklm/utils.test.js +0 -449
- package/clis/notebooklm/write-note.test.js +0 -70
- package/clis/nuget/nuget.test.js +0 -111
- package/clis/oeis/oeis.test.js +0 -88
- package/clis/ones/task-helpers.test.js +0 -12
- package/clis/ones/worklog.test.js +0 -20
- package/clis/openfda/openfda.test.js +0 -114
- package/clis/openreview/openreview.test.js +0 -427
- package/clis/osv/osv.test.js +0 -97
- package/clis/paperreview/commands.test.js +0 -243
- package/clis/paperreview/utils.test.js +0 -49
- package/clis/pixiv/download.test.js +0 -87
- package/clis/pixiv/illusts.test.js +0 -99
- package/clis/pixiv/search.test.js +0 -83
- package/clis/powerchina/search.test.js +0 -93
- package/clis/producthunt/utils.test.js +0 -64
- package/clis/pubmed/pubmed.test.js +0 -276
- package/clis/quark/utils.test.js +0 -58
- package/clis/qwen/utils.test.js +0 -45
- package/clis/reddit/extract-media.test.js +0 -149
- package/clis/reddit/frontpage.test.js +0 -34
- package/clis/reddit/home.test.js +0 -170
- package/clis/reddit/hot.test.js +0 -32
- package/clis/reddit/popular.test.js +0 -26
- package/clis/reddit/read.test.js +0 -335
- package/clis/reddit/reply.test.js +0 -89
- package/clis/reddit/search.test.js +0 -26
- package/clis/reddit/subreddit-info.test.js +0 -163
- package/clis/reddit/subreddit.test.js +0 -31
- package/clis/reddit/subscribed.test.js +0 -168
- package/clis/reddit/whoami.test.js +0 -105
- package/clis/rednote/navigation.test.js +0 -26
- package/clis/rednote/rednote.test.js +0 -222
- package/clis/rest-countries/rest-countries.test.js +0 -83
- package/clis/reuters/reuters.test.js +0 -299
- package/clis/rfc/rfc.test.js +0 -74
- package/clis/sinafinance/stock.test.js +0 -59
- package/clis/spotify/utils.test.js +0 -67
- package/clis/stackoverflow/stackoverflow.test.js +0 -404
- package/clis/steam/steam.test.js +0 -46
- package/clis/substack/utils.test.js +0 -44
- package/clis/suno/commands.test.js +0 -188
- package/clis/suno/download.test.js +0 -151
- package/clis/suno/generate.test.js +0 -252
- package/clis/suno/utils.test.js +0 -329
- package/clis/taobao/commands.test.js +0 -41
- package/clis/tdx/hot-rank.test.js +0 -59
- package/clis/ths/hot-rank.test.js +0 -64
- package/clis/tieba/commands.test.js +0 -79
- package/clis/tieba/utils.test.js +0 -290
- package/clis/tiktok/creator-videos.test.js +0 -113
- package/clis/tiktok/refactor.test.js +0 -389
- package/clis/tiktok/write-refactor.test.js +0 -370
- package/clis/toutiao/toutiao.test.js +0 -378
- package/clis/tvmaze/tvmaze.test.js +0 -93
- package/clis/twitter/bookmark-folder.test.js +0 -395
- package/clis/twitter/bookmark-folders.test.js +0 -152
- package/clis/twitter/bookmark.test.js +0 -74
- package/clis/twitter/bookmarks.test.js +0 -205
- package/clis/twitter/delete.test.js +0 -85
- package/clis/twitter/device-follow.test.js +0 -287
- package/clis/twitter/download.test.js +0 -457
- package/clis/twitter/followers.test.js +0 -62
- package/clis/twitter/following.test.js +0 -338
- package/clis/twitter/hide-reply.test.js +0 -76
- package/clis/twitter/like.test.js +0 -73
- package/clis/twitter/likes.test.js +0 -220
- package/clis/twitter/list-add.test.js +0 -133
- package/clis/twitter/list-create.test.js +0 -169
- package/clis/twitter/list-remove.test.js +0 -110
- package/clis/twitter/list-tweets.test.js +0 -196
- package/clis/twitter/lists.test.js +0 -253
- package/clis/twitter/post.test.js +0 -258
- package/clis/twitter/profile.test.js +0 -39
- package/clis/twitter/quote.test.js +0 -194
- package/clis/twitter/reply.test.js +0 -237
- package/clis/twitter/retweet.test.js +0 -73
- package/clis/twitter/search.test.js +0 -349
- package/clis/twitter/shared.test.js +0 -794
- package/clis/twitter/thread.test.js +0 -58
- package/clis/twitter/timeline.test.js +0 -104
- package/clis/twitter/trending.test.js +0 -15
- package/clis/twitter/tweets.test.js +0 -362
- package/clis/twitter/unbookmark.test.js +0 -73
- package/clis/twitter/unlike.test.js +0 -75
- package/clis/twitter/unretweet.test.js +0 -73
- package/clis/twitter/utils.test.js +0 -169
- package/clis/uisdc/news.test.js +0 -66
- package/clis/uiverse/_shared.test.js +0 -62
- package/clis/uiverse/navigation.test.js +0 -12
- package/clis/upwork/upwork.test.js +0 -566
- package/clis/wanfang/search.test.js +0 -23
- package/clis/web/read.test.js +0 -392
- package/clis/weibo/delete.test.js +0 -94
- package/clis/weibo/envelope.test.js +0 -85
- package/clis/weibo/favorites.test.js +0 -114
- package/clis/weibo/publish.test.js +0 -192
- package/clis/weibo/user-posts.test.js +0 -92
- package/clis/weibo/utils.test.js +0 -36
- package/clis/weixin/drafts.test.js +0 -69
- package/clis/weixin/search.test.js +0 -227
- package/clis/weread/ai-outline.test.js +0 -83
- package/clis/weread/book-search.test.js +0 -242
- package/clis/weread/commands.test.js +0 -398
- package/clis/weread/private-api-regression.test.js +0 -369
- package/clis/weread/search-regression.test.js +0 -494
- package/clis/weread/utils.test.js +0 -128
- package/clis/weread-official/commands.test.js +0 -385
- package/clis/weread-official/utils.test.js +0 -242
- package/clis/wikidata/wikidata.test.js +0 -83
- package/clis/wikipedia/trending.test.js +0 -57
- package/clis/wttr/wttr.test.js +0 -84
- package/clis/xianyu/chat.test.js +0 -84
- package/clis/xianyu/im.test.js +0 -253
- package/clis/xianyu/item.test.js +0 -56
- package/clis/xianyu/publish.test.js +0 -220
- package/clis/xianyu/search.test.js +0 -17
- package/clis/xiaoe/xiaoe.test.js +0 -486
- package/clis/xiaohongshu/comments.test.js +0 -258
- package/clis/xiaohongshu/creator-note-detail.test.js +0 -451
- package/clis/xiaohongshu/creator-notes-summary.test.js +0 -94
- package/clis/xiaohongshu/creator-notes.test.js +0 -281
- package/clis/xiaohongshu/creator-stats.test.js +0 -24
- package/clis/xiaohongshu/delete-note.test.js +0 -172
- package/clis/xiaohongshu/download.test.js +0 -316
- package/clis/xiaohongshu/navigation.test.js +0 -34
- package/clis/xiaohongshu/note.test.js +0 -249
- package/clis/xiaohongshu/publish.test.js +0 -608
- package/clis/xiaohongshu/search.test.js +0 -325
- package/clis/xiaohongshu/user-helpers.test.js +0 -160
- package/clis/xiaoyuzhou/auth.test.js +0 -124
- package/clis/xiaoyuzhou/download.test.js +0 -135
- package/clis/xiaoyuzhou/podcast-episodes.test.js +0 -78
- package/clis/xiaoyuzhou/transcript.test.js +0 -195
- package/clis/xiaoyuzhou/utils.test.js +0 -39
- package/clis/xueqiu/comments.test.js +0 -696
- package/clis/xueqiu/danjuan-utils.test.js +0 -41
- package/clis/xueqiu/utils.test.js +0 -26
- package/clis/yahoo/search.test.js +0 -94
- package/clis/youdao/note.test.js +0 -99
- package/clis/youtube/channel.test.js +0 -59
- package/clis/youtube/feed.test.js +0 -131
- package/clis/youtube/transcript-group.test.js +0 -99
- package/clis/youtube/transcript.test.js +0 -296
- package/clis/youtube/utils.test.js +0 -84
- package/clis/yuanbao/ask.test.js +0 -124
- package/clis/yuanbao/new.test.js +0 -30
- package/clis/yuanbao/shared.test.js +0 -80
- package/clis/zhihu/answer-comments.test.js +0 -287
- package/clis/zhihu/answer-detail.test.js +0 -338
- package/clis/zhihu/answer.test.js +0 -54
- package/clis/zhihu/collection.test.js +0 -337
- package/clis/zhihu/collections.test.js +0 -182
- package/clis/zhihu/comment.test.js +0 -50
- package/clis/zhihu/download.test.js +0 -12
- package/clis/zhihu/favorite.test.js +0 -59
- package/clis/zhihu/follow.test.js +0 -42
- package/clis/zhihu/like.test.js +0 -41
- package/clis/zhihu/question.test.js +0 -251
- package/clis/zhihu/recommend.test.js +0 -143
- package/clis/zhihu/search.test.js +0 -198
- package/clis/zhihu/target.test.js +0 -77
- package/clis/zhihu/text.test.js +0 -24
- package/clis/zhihu/write-shared.test.js +0 -176
- package/clis/zlibrary/commands.test.js +0 -65
- package/clis/zsxq/search.test.js +0 -24
- package/clis/zsxq/topic.test.js +0 -30
- package/clis/zsxq/topics.test.js +0 -24
- package/clis/zsxq/utils.test.js +0 -31
- package/dist/src/adapter-shadow.test.js +0 -49
- package/dist/src/adapter-source.test.js +0 -29
- package/dist/src/browser/analyze.test.js +0 -166
- package/dist/src/browser/article-extract.e2e.test.js +0 -105
- package/dist/src/browser/article-extract.test.js +0 -94
- package/dist/src/browser/ax-snapshot.test.js +0 -91
- package/dist/src/browser/base-page.test.js +0 -758
- package/dist/src/browser/cdp-click-fixture.test.js +0 -87
- package/dist/src/browser/cdp.test.js +0 -79
- package/dist/src/browser/compound.test.js +0 -175
- package/dist/src/browser/daemon-client.test.js +0 -199
- package/dist/src/browser/dom-helpers.test.js +0 -106
- package/dist/src/browser/dom-snapshot.test.js +0 -313
- package/dist/src/browser/errors.test.js +0 -60
- package/dist/src/browser/extract.test.js +0 -129
- package/dist/src/browser/find.test.js +0 -180
- package/dist/src/browser/html-tree.test.js +0 -181
- package/dist/src/browser/network-cache.test.js +0 -58
- package/dist/src/browser/network-key.test.js +0 -49
- package/dist/src/browser/page.test.js +0 -378
- package/dist/src/browser/shape-filter.test.js +0 -101
- package/dist/src/browser/shape.test.js +0 -82
- package/dist/src/browser/stealth.test.js +0 -134
- package/dist/src/browser/target-errors.test.js +0 -94
- package/dist/src/browser/target-resolver.test.js +0 -118
- package/dist/src/browser/utils.test.js +0 -29
- package/dist/src/browser/verify-fixture.test.js +0 -219
- package/dist/src/browser.test.js +0 -232
- package/dist/src/build-manifest.test.js +0 -341
- package/dist/src/capabilityRouting.test.js +0 -63
- package/dist/src/cli-argv-preprocess.test.js +0 -209
- package/dist/src/cli.test.js +0 -2803
- package/dist/src/commanderAdapter.test.js +0 -352
- package/dist/src/commands/daemon.test.js +0 -219
- package/dist/src/completion.test.js +0 -24
- package/dist/src/convention-audit.test.js +0 -247
- package/dist/src/daemon.test.js +0 -60
- package/dist/src/doctor.test.js +0 -263
- package/dist/src/download/article-download.test.js +0 -235
- package/dist/src/download/index.test.js +0 -140
- package/dist/src/download/media-download.test.js +0 -112
- package/dist/src/download/progress.test.js +0 -36
- package/dist/src/electron-apps.test.js +0 -49
- package/dist/src/engine.test.js +0 -312
- package/dist/src/errors.test.js +0 -109
- package/dist/src/execution.test.js +0 -623
- package/dist/src/extension-manifest-regression.test.js +0 -12
- package/dist/src/external.test.js +0 -146
- package/dist/src/help.test.js +0 -58
- package/dist/src/hooks.test.js +0 -92
- package/dist/src/interceptor.test.js +0 -81
- package/dist/src/launcher.test.js +0 -138
- package/dist/src/node-network.test.js +0 -55
- package/dist/src/observation/artifact.test.js +0 -121
- package/dist/src/observation/manager.test.js +0 -13
- package/dist/src/observation/redaction.test.js +0 -32
- package/dist/src/observation/retention.test.js +0 -118
- package/dist/src/observation/ring-buffer.test.js +0 -22
- package/dist/src/output.test.js +0 -53
- package/dist/src/package-exports.test.js +0 -112
- package/dist/src/pipeline/executor.test.js +0 -182
- package/dist/src/pipeline/steps/download.test.js +0 -102
- package/dist/src/pipeline/steps/fetch.test.js +0 -117
- package/dist/src/pipeline/template.test.js +0 -175
- package/dist/src/pipeline/transform.test.js +0 -133
- package/dist/src/plugin-manifest.test.js +0 -179
- package/dist/src/plugin-scaffold.test.js +0 -83
- package/dist/src/plugin.test.js +0 -1324
- package/dist/src/registry.test.js +0 -209
- package/dist/src/runtime-detect.test.js +0 -39
- package/dist/src/serialization.test.js +0 -78
- package/dist/src/snapshotFormatter.test.js +0 -521
- package/dist/src/tui.test.js +0 -19
- package/dist/src/update-check.test.js +0 -116
- package/dist/src/utils.test.js +0 -155
- package/dist/src/validate.test.js +0 -90
- package/dist/src/weixin-download.test.js +0 -45
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { __test__, collectGeminiTranscriptAdditions, pickGeminiDeepResearchExportUrl, sanitizeGeminiResponseText, sendGeminiMessage, } from './utils.js';
|
|
3
|
-
function createPageMock() {
|
|
4
|
-
return {
|
|
5
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
6
|
-
evaluate: vi.fn(),
|
|
7
|
-
getCookies: vi.fn().mockResolvedValue([]),
|
|
8
|
-
snapshot: vi.fn().mockResolvedValue(undefined),
|
|
9
|
-
click: vi.fn().mockResolvedValue(undefined),
|
|
10
|
-
typeText: vi.fn().mockResolvedValue(undefined),
|
|
11
|
-
pressKey: vi.fn().mockResolvedValue(undefined),
|
|
12
|
-
scrollTo: vi.fn().mockResolvedValue(undefined),
|
|
13
|
-
getFormState: vi.fn().mockResolvedValue({}),
|
|
14
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
15
|
-
tabs: vi.fn().mockResolvedValue([]),
|
|
16
|
-
selectTab: vi.fn().mockResolvedValue(undefined),
|
|
17
|
-
networkRequests: vi.fn().mockResolvedValue([]),
|
|
18
|
-
consoleMessages: vi.fn().mockResolvedValue([]),
|
|
19
|
-
scroll: vi.fn().mockResolvedValue(undefined),
|
|
20
|
-
autoScroll: vi.fn().mockResolvedValue(undefined),
|
|
21
|
-
installInterceptor: vi.fn().mockResolvedValue(undefined),
|
|
22
|
-
getInterceptedRequests: vi.fn().mockResolvedValue([]),
|
|
23
|
-
waitForCapture: vi.fn().mockResolvedValue(undefined),
|
|
24
|
-
screenshot: vi.fn().mockResolvedValue(''),
|
|
25
|
-
nativeType: vi.fn().mockResolvedValue(undefined),
|
|
26
|
-
nativeKeyPress: vi.fn().mockResolvedValue(undefined),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
describe('sanitizeGeminiResponseText', () => {
|
|
30
|
-
it('strips a prompt echo only when it appears as a prefixed block', () => {
|
|
31
|
-
const prompt = 'Reply with the word bycli';
|
|
32
|
-
const value = `Reply with the word bycli\n\nbycli`;
|
|
33
|
-
expect(sanitizeGeminiResponseText(value, prompt)).toBe('bycli');
|
|
34
|
-
});
|
|
35
|
-
it('does not strip prompt text that appears later in a legitimate answer', () => {
|
|
36
|
-
const prompt = 'bycli';
|
|
37
|
-
const value = 'You asked about bycli, and bycli is the right keyword here.';
|
|
38
|
-
expect(sanitizeGeminiResponseText(value, prompt)).toBe(value);
|
|
39
|
-
});
|
|
40
|
-
it('removes known Gemini footer noise', () => {
|
|
41
|
-
const value = 'Answer body\nGemini can make mistakes.\nGoogle Terms';
|
|
42
|
-
expect(sanitizeGeminiResponseText(value, '')).toBe('Answer body');
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe('collectGeminiTranscriptAdditions', () => {
|
|
46
|
-
it('joins multiple new transcript lines instead of keeping only the last line', () => {
|
|
47
|
-
const before = ['Older answer'];
|
|
48
|
-
const current = ['Older answer', 'First new line', 'Second new line'];
|
|
49
|
-
expect(collectGeminiTranscriptAdditions(before, current, '')).toBe('First new line\nSecond new line');
|
|
50
|
-
});
|
|
51
|
-
it('filters prompt echoes out of transcript additions', () => {
|
|
52
|
-
const prompt = 'Tell me a haiku';
|
|
53
|
-
const before = ['Previous'];
|
|
54
|
-
const current = ['Previous', 'Tell me a haiku', 'Tell me a haiku\n\nSoft spring rain arrives'];
|
|
55
|
-
expect(collectGeminiTranscriptAdditions(before, current, prompt)).toBe('Soft spring rain arrives');
|
|
56
|
-
});
|
|
57
|
-
it('keeps a reply line that quotes the prompt inside the answer body', () => {
|
|
58
|
-
const prompt = '请只回复:OK';
|
|
59
|
-
const before = ['baseline'];
|
|
60
|
-
const current = ['baseline', '关于“请只回复:OK”,这里是解释。'];
|
|
61
|
-
expect(collectGeminiTranscriptAdditions(before, current, prompt)).toBe('关于“请只回复:OK”,这里是解释。');
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
describe('gemini send strategy', () => {
|
|
65
|
-
it('includes structural composer selectors instead of relying only on english aria labels', () => {
|
|
66
|
-
expect(__test__.GEMINI_COMPOSER_SELECTORS).toContain('.ql-editor[contenteditable="true"]');
|
|
67
|
-
expect(__test__.GEMINI_COMPOSER_SELECTORS).toContain('.ql-editor[role="textbox"]');
|
|
68
|
-
});
|
|
69
|
-
it('prefers native text insertion before submitting the composer', async () => {
|
|
70
|
-
const page = createPageMock();
|
|
71
|
-
const evaluate = vi.mocked(page.evaluate);
|
|
72
|
-
const nativeType = vi.mocked(page.nativeType);
|
|
73
|
-
const nativeKeyPress = vi.mocked(page.nativeKeyPress);
|
|
74
|
-
evaluate
|
|
75
|
-
.mockResolvedValueOnce('https://gemini.google.com/app')
|
|
76
|
-
.mockResolvedValueOnce({ ok: true })
|
|
77
|
-
.mockResolvedValueOnce({ hasText: true })
|
|
78
|
-
.mockResolvedValueOnce('button');
|
|
79
|
-
const result = await sendGeminiMessage(page, '你好');
|
|
80
|
-
expect(nativeType).toHaveBeenCalledWith('你好');
|
|
81
|
-
expect(nativeKeyPress).not.toHaveBeenCalled();
|
|
82
|
-
expect(result).toBe('button');
|
|
83
|
-
});
|
|
84
|
-
it('falls back when native insertion does not update the composer', async () => {
|
|
85
|
-
const page = createPageMock();
|
|
86
|
-
const evaluate = vi.mocked(page.evaluate);
|
|
87
|
-
const nativeType = vi.mocked(page.nativeType);
|
|
88
|
-
const nativeKeyPress = vi.mocked(page.nativeKeyPress);
|
|
89
|
-
evaluate
|
|
90
|
-
.mockResolvedValueOnce('https://gemini.google.com/app')
|
|
91
|
-
.mockResolvedValueOnce({ ok: true })
|
|
92
|
-
.mockResolvedValueOnce({ hasText: false })
|
|
93
|
-
.mockResolvedValueOnce({ hasText: true })
|
|
94
|
-
.mockResolvedValueOnce('enter');
|
|
95
|
-
const result = await sendGeminiMessage(page, '你好');
|
|
96
|
-
expect(nativeType).toHaveBeenCalledWith('你好');
|
|
97
|
-
expect(nativeKeyPress).toHaveBeenCalledWith('Enter');
|
|
98
|
-
expect(evaluate).toHaveBeenCalledTimes(5);
|
|
99
|
-
expect(result).toBe('enter');
|
|
100
|
-
});
|
|
101
|
-
it('falls back when native insertion throws', async () => {
|
|
102
|
-
const page = createPageMock();
|
|
103
|
-
const evaluate = vi.mocked(page.evaluate);
|
|
104
|
-
const nativeType = vi.mocked(page.nativeType);
|
|
105
|
-
nativeType.mockRejectedValueOnce(new Error('Unknown action: cdp'));
|
|
106
|
-
evaluate
|
|
107
|
-
.mockResolvedValueOnce('https://gemini.google.com/app')
|
|
108
|
-
.mockResolvedValueOnce({ ok: true })
|
|
109
|
-
.mockResolvedValueOnce({ hasText: true })
|
|
110
|
-
.mockResolvedValueOnce('button');
|
|
111
|
-
const result = await sendGeminiMessage(page, '你好');
|
|
112
|
-
expect(nativeType).toHaveBeenCalledWith('你好');
|
|
113
|
-
expect(result).toBe('button');
|
|
114
|
-
});
|
|
115
|
-
it('retries composer preparation until a slow-loading composer appears', async () => {
|
|
116
|
-
const page = createPageMock();
|
|
117
|
-
const evaluate = vi.mocked(page.evaluate);
|
|
118
|
-
const wait = vi.mocked(page.wait);
|
|
119
|
-
evaluate
|
|
120
|
-
.mockResolvedValueOnce('https://gemini.google.com/app')
|
|
121
|
-
.mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
|
|
122
|
-
.mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
|
|
123
|
-
.mockResolvedValueOnce({ ok: true })
|
|
124
|
-
.mockResolvedValueOnce({ hasText: true })
|
|
125
|
-
.mockResolvedValueOnce('button');
|
|
126
|
-
const result = await sendGeminiMessage(page, '你好');
|
|
127
|
-
expect(result).toBe('button');
|
|
128
|
-
expect(wait.mock.calls.filter(([value]) => value === 1)).toHaveLength(3);
|
|
129
|
-
});
|
|
130
|
-
it('keeps retrying until a composer that appears on the fourth attempt is ready', async () => {
|
|
131
|
-
const page = createPageMock();
|
|
132
|
-
const evaluate = vi.mocked(page.evaluate);
|
|
133
|
-
const wait = vi.mocked(page.wait);
|
|
134
|
-
evaluate
|
|
135
|
-
.mockResolvedValueOnce('https://gemini.google.com/app')
|
|
136
|
-
.mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
|
|
137
|
-
.mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
|
|
138
|
-
.mockResolvedValueOnce({ ok: false, reason: 'Could not find Gemini composer' })
|
|
139
|
-
.mockResolvedValueOnce({ ok: true })
|
|
140
|
-
.mockResolvedValueOnce({ hasText: true })
|
|
141
|
-
.mockResolvedValueOnce('button');
|
|
142
|
-
const result = await sendGeminiMessage(page, '你好');
|
|
143
|
-
expect(result).toBe('button');
|
|
144
|
-
expect(wait.mock.calls.filter(([value]) => value === 1)).toHaveLength(4);
|
|
145
|
-
});
|
|
146
|
-
it('avoids innerHTML in the fallback insertion path for trusted types pages', () => {
|
|
147
|
-
expect(__test__.insertComposerTextFallbackScript('你好')).not.toContain('innerHTML');
|
|
148
|
-
expect(__test__.insertComposerTextFallbackScript('你好')).toContain('replaceChildren');
|
|
149
|
-
});
|
|
150
|
-
it('keeps a button submit path in the generated submit script', () => {
|
|
151
|
-
expect(__test__.submitComposerScript()).toContain('.click()');
|
|
152
|
-
});
|
|
153
|
-
it('supports localized new chat labels in the generated new-chat script', () => {
|
|
154
|
-
expect(__test__.clickNewChatScript()).toContain('发起新对话');
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
describe('gemini turn normalization', () => {
|
|
158
|
-
it('collapses only adjacent duplicate turns so identical replies across rounds remain visible', () => {
|
|
159
|
-
const turns = [
|
|
160
|
-
{ Role: 'User', Text: '你说\n\n请只回复:OK' },
|
|
161
|
-
{ Role: 'User', Text: '请只回复:OK' },
|
|
162
|
-
{ Role: 'Assistant', Text: 'OK' },
|
|
163
|
-
{ Role: 'Assistant', Text: 'OK' },
|
|
164
|
-
{ Role: 'User', Text: '你说\n\n请只回复:OK' },
|
|
165
|
-
{ Role: 'User', Text: '请只回复:OK' },
|
|
166
|
-
{ Role: 'Assistant', Text: 'OK' },
|
|
167
|
-
{ Role: 'Assistant', Text: 'OK' },
|
|
168
|
-
];
|
|
169
|
-
expect(__test__.collapseAdjacentGeminiTurns(turns)).toEqual([
|
|
170
|
-
{ Role: 'User', Text: '你说\n\n请只回复:OK' },
|
|
171
|
-
{ Role: 'User', Text: '请只回复:OK' },
|
|
172
|
-
{ Role: 'Assistant', Text: 'OK' },
|
|
173
|
-
{ Role: 'User', Text: '你说\n\n请只回复:OK' },
|
|
174
|
-
{ Role: 'User', Text: '请只回复:OK' },
|
|
175
|
-
{ Role: 'Assistant', Text: 'OK' },
|
|
176
|
-
]);
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
describe('pickGeminiDeepResearchExportUrl', () => {
|
|
180
|
-
it('prefers docs.google.com document url over sheets and noise endpoints', () => {
|
|
181
|
-
const picked = pickGeminiDeepResearchExportUrl([
|
|
182
|
-
'xhr::https://gemini.google.com/_/BardChatUi/data/batchexecute?rpcids=ESY5D',
|
|
183
|
-
'performance::https://docs.google.com/spreadsheets/d/1abc/edit',
|
|
184
|
-
'open::https://docs.google.com/document/d/1docid/edit',
|
|
185
|
-
], 'https://gemini.google.com/app/abc');
|
|
186
|
-
expect(picked).toEqual({
|
|
187
|
-
url: 'https://docs.google.com/document/d/1docid/edit',
|
|
188
|
-
source: 'window-open',
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
it('returns none when only non-export telemetry urls are present', () => {
|
|
192
|
-
const picked = pickGeminiDeepResearchExportUrl([
|
|
193
|
-
'fetch::https://gemini.google.com/_/BardChatUi/cspreport',
|
|
194
|
-
'performance::https://www.google-analytics.com/g/collect?v=2',
|
|
195
|
-
], 'https://gemini.google.com/app/abc');
|
|
196
|
-
expect(picked).toEqual({ url: '', source: 'none' });
|
|
197
|
-
});
|
|
198
|
-
});
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { ArgumentError, CommandExecutionError, EmptyResultError } from '@sovovs/bycli/errors';
|
|
3
|
-
import { getRegistry } from '@sovovs/bycli/registry';
|
|
4
|
-
import { ensureApplet, ggbEval, ggbGetProperty, ggbListObjects, ggbWaitForObjectCount } from './utils.js';
|
|
5
|
-
import './add-circle.js';
|
|
6
|
-
import './add-line.js';
|
|
7
|
-
import './add-point.js';
|
|
8
|
-
import './add-polygon.js';
|
|
9
|
-
import './eval.js';
|
|
10
|
-
import './hexagon.js';
|
|
11
|
-
import './info.js';
|
|
12
|
-
import './list.js';
|
|
13
|
-
import './triangle.js';
|
|
14
|
-
|
|
15
|
-
function createPageMock(url = 'https://www.geogebra.org/geometry') {
|
|
16
|
-
return {
|
|
17
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
18
|
-
evaluate: vi.fn(),
|
|
19
|
-
getCurrentUrl: vi.fn().mockResolvedValue(url),
|
|
20
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
21
|
-
screenshot: vi.fn().mockResolvedValue(undefined),
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe('ensureApplet', () => {
|
|
26
|
-
it('skips navigation when already on the geometry page', async () => {
|
|
27
|
-
const page = createPageMock('https://www.geogebra.org/geometry');
|
|
28
|
-
page.evaluate.mockResolvedValue(true);
|
|
29
|
-
await ensureApplet(page);
|
|
30
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('unwraps Browser Bridge evaluate envelopes while checking applet readiness', async () => {
|
|
34
|
-
const page = createPageMock('https://www.geogebra.org/geometry');
|
|
35
|
-
page.evaluate.mockResolvedValue({ session: 1, data: true });
|
|
36
|
-
await ensureApplet(page);
|
|
37
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('navigates when not on the geometry page', async () => {
|
|
41
|
-
const page = createPageMock('https://example.com');
|
|
42
|
-
page.evaluate.mockResolvedValue(true);
|
|
43
|
-
await ensureApplet(page);
|
|
44
|
-
expect(page.goto).toHaveBeenCalledWith('https://www.geogebra.org/geometry');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('throws when ggbApplet never becomes available', async () => {
|
|
48
|
-
const page = createPageMock();
|
|
49
|
-
page.evaluate.mockResolvedValue(false);
|
|
50
|
-
await expect(ensureApplet(page)).rejects.toThrow(CommandExecutionError);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe('ggbEval', () => {
|
|
55
|
-
it('calls evalCommandGetLabels and evalCommand', async () => {
|
|
56
|
-
const page = createPageMock();
|
|
57
|
-
page.evaluate.mockResolvedValue({ ok: true, label: 'A', beforeCount: 0, afterCount: 1, error: null });
|
|
58
|
-
const result = await ggbEval(page, 'A=(1,2)');
|
|
59
|
-
expect(result).toEqual({ ok: true, label: 'A', beforeCount: 0, afterCount: 1, error: null });
|
|
60
|
-
expect(page.evaluate).toHaveBeenCalledTimes(1);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('throws typed errors for malformed evaluate results', async () => {
|
|
64
|
-
const page = createPageMock();
|
|
65
|
-
page.evaluate.mockResolvedValue({ nope: true });
|
|
66
|
-
await expect(ggbEval(page, 'A=(1,2)')).rejects.toThrow(CommandExecutionError);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('ggbGetProperty', () => {
|
|
71
|
-
it('requests a property from the applet', async () => {
|
|
72
|
-
const page = createPageMock();
|
|
73
|
-
page.evaluate.mockResolvedValue('point');
|
|
74
|
-
const result = await ggbGetProperty(page, 'A', 'type');
|
|
75
|
-
expect(result).toBe('point');
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe('ggbListObjects', () => {
|
|
80
|
-
it('normalizes object rows from the applet', async () => {
|
|
81
|
-
const page = createPageMock();
|
|
82
|
-
page.evaluate.mockResolvedValue([
|
|
83
|
-
{ name: 'A', type: 'point', value: '(0, 0)', visible: true },
|
|
84
|
-
{ name: 't1', type: 'polygon', value: '', visible: true },
|
|
85
|
-
]);
|
|
86
|
-
const result = await ggbListObjects(page);
|
|
87
|
-
expect(result).toHaveLength(2);
|
|
88
|
-
expect(page.evaluate).toHaveBeenCalledTimes(1);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('unwraps Browser Bridge envelopes for object rows', async () => {
|
|
92
|
-
const page = createPageMock();
|
|
93
|
-
page.evaluate.mockResolvedValue({ session: 1, data: [{ name: 'A', type: 'point', value: '(0, 0)', visible: true }] });
|
|
94
|
-
const result = await ggbListObjects(page);
|
|
95
|
-
expect(result).toEqual([{ name: 'A', type: 'point', value: '(0, 0)', visible: true }]);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('throws typed error when object names exist but property extraction fails', async () => {
|
|
99
|
-
const page = createPageMock();
|
|
100
|
-
page.evaluate.mockResolvedValue({ error: 'getObjectType failed', name: 'A' });
|
|
101
|
-
await expect(ggbListObjects(page)).rejects.toThrow(CommandExecutionError);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe('ggbWaitForObjectCount', () => {
|
|
106
|
-
it('returns the detected object count', async () => {
|
|
107
|
-
const page = createPageMock();
|
|
108
|
-
page.evaluate.mockResolvedValue(4);
|
|
109
|
-
const result = await ggbWaitForObjectCount(page, 4);
|
|
110
|
-
expect(result).toBe(4);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('geogebra command typed boundaries', () => {
|
|
115
|
-
it('validates add-point args before navigating', async () => {
|
|
116
|
-
const command = getRegistry().get('geogebra/add-point');
|
|
117
|
-
const page = createPageMock('https://example.com');
|
|
118
|
-
await expect(command.func(page, { name: 'A', coords: 'bad' })).rejects.toThrow(ArgumentError);
|
|
119
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('validates triangle size before navigating instead of silently defaulting', async () => {
|
|
123
|
-
const command = getRegistry().get('geogebra/triangle');
|
|
124
|
-
const page = createPageMock('https://example.com');
|
|
125
|
-
await expect(command.func(page, { size: 'not-a-number' })).rejects.toThrow(ArgumentError);
|
|
126
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('fails eval command execution instead of returning a success row for failed commands', async () => {
|
|
130
|
-
const command = getRegistry().get('geogebra/eval');
|
|
131
|
-
const page = createPageMock('https://www.geogebra.org/geometry');
|
|
132
|
-
page.evaluate
|
|
133
|
-
.mockResolvedValueOnce(true)
|
|
134
|
-
.mockResolvedValueOnce({ ok: false, label: '', beforeCount: 0, afterCount: 0, error: 'Unknown command' });
|
|
135
|
-
await expect(command.func(page, { command: 'NotACommand(' })).rejects.toThrow(CommandExecutionError);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('creates add-line rows from validated labels', async () => {
|
|
139
|
-
const command = getRegistry().get('geogebra/add-line');
|
|
140
|
-
const page = createPageMock('https://www.geogebra.org/geometry');
|
|
141
|
-
page.evaluate
|
|
142
|
-
.mockResolvedValueOnce(true)
|
|
143
|
-
.mockResolvedValueOnce({ ok: true, label: 'f', beforeCount: 2, afterCount: 3, error: null });
|
|
144
|
-
await expect(command.func(page, { points: 'A,B', type: 'segment' })).resolves.toEqual([
|
|
145
|
-
{ label: 'f', type: 'segment', points: 'A,B' },
|
|
146
|
-
]);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('does not turn object property extraction failures into empty list results', async () => {
|
|
150
|
-
const command = getRegistry().get('geogebra/list');
|
|
151
|
-
const page = createPageMock('https://www.geogebra.org/geometry');
|
|
152
|
-
page.evaluate
|
|
153
|
-
.mockResolvedValueOnce(true)
|
|
154
|
-
.mockResolvedValueOnce({ error: 'getObjectType failed', name: 'A' });
|
|
155
|
-
await expect(command.func(page, {})).rejects.toThrow(CommandExecutionError);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('does not turn malformed info existence probes into not-found results', async () => {
|
|
159
|
-
const command = getRegistry().get('geogebra/info');
|
|
160
|
-
const page = createPageMock('https://www.geogebra.org/geometry');
|
|
161
|
-
page.evaluate
|
|
162
|
-
.mockResolvedValueOnce(true)
|
|
163
|
-
.mockResolvedValueOnce({ nope: true });
|
|
164
|
-
await expect(command.func(page, { name: 'A' })).rejects.toThrow(CommandExecutionError);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('maps explicit false info existence probes to EmptyResultError', async () => {
|
|
168
|
-
const command = getRegistry().get('geogebra/info');
|
|
169
|
-
const page = createPageMock('https://www.geogebra.org/geometry');
|
|
170
|
-
page.evaluate
|
|
171
|
-
.mockResolvedValueOnce(true)
|
|
172
|
-
.mockResolvedValueOnce({ ok: true, exists: false });
|
|
173
|
-
await expect(command.func(page, { name: 'A' })).rejects.toThrow(EmptyResultError);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { getRegistry } from '@sovovs/bycli/registry';
|
|
3
|
-
import './search.js';
|
|
4
|
-
|
|
5
|
-
function mockGiteeResponse(hits) {
|
|
6
|
-
return {
|
|
7
|
-
ok: true,
|
|
8
|
-
json: () => Promise.resolve({ hits: { hits } }),
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function makePage() {
|
|
13
|
-
return {
|
|
14
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
15
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe('gitee search', () => {
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
vi.restoreAllMocks();
|
|
22
|
-
});
|
|
23
|
-
afterEach(() => {
|
|
24
|
-
vi.unstubAllGlobals();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('emits empty-string for missing language / description instead of a sentinel', async () => {
|
|
28
|
-
const cmd = getRegistry().get('gitee/search');
|
|
29
|
-
expect(cmd?.func).toBeTypeOf('function');
|
|
30
|
-
const fetchMock = vi.fn().mockResolvedValue(mockGiteeResponse([
|
|
31
|
-
{
|
|
32
|
-
fields: {
|
|
33
|
-
title: 'someuser/no-meta-repo',
|
|
34
|
-
url: 'https://gitee.com/someuser/no-meta-repo',
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
]));
|
|
38
|
-
vi.stubGlobal('fetch', fetchMock);
|
|
39
|
-
const rows = await cmd.func(makePage(), { keyword: 'test', limit: 10 });
|
|
40
|
-
expect(rows).toHaveLength(1);
|
|
41
|
-
expect(rows[0].name).toBe('someuser/no-meta-repo');
|
|
42
|
-
expect(rows[0].language).toBe('');
|
|
43
|
-
expect(rows[0].description).toBe('');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('passes through populated language / description verbatim', async () => {
|
|
47
|
-
const cmd = getRegistry().get('gitee/search');
|
|
48
|
-
const fetchMock = vi.fn().mockResolvedValue(mockGiteeResponse([
|
|
49
|
-
{
|
|
50
|
-
fields: {
|
|
51
|
-
title: 'org/repo-a',
|
|
52
|
-
url: 'https://gitee.com/org/repo-a',
|
|
53
|
-
langs: 'TypeScript',
|
|
54
|
-
description: 'A test repo',
|
|
55
|
-
'count.star': '42',
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
]));
|
|
59
|
-
vi.stubGlobal('fetch', fetchMock);
|
|
60
|
-
const rows = await cmd.func(makePage(), { keyword: 'test', limit: 10 });
|
|
61
|
-
expect(rows[0].language).toBe('TypeScript');
|
|
62
|
-
expect(rows[0].description).toBe('A test repo');
|
|
63
|
-
expect(rows[0].stars).toBe('42');
|
|
64
|
-
});
|
|
65
|
-
});
|
package/clis/gitee/user.test.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { getRegistry } from '@sovovs/bycli/registry';
|
|
3
|
-
import './user.js';
|
|
4
|
-
const command = getRegistry().get('gitee/user');
|
|
5
|
-
function createPage(snapshot) {
|
|
6
|
-
return {
|
|
7
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
8
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
9
|
-
evaluate: vi.fn().mockResolvedValue(snapshot),
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
describe('gitee user', () => {
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
vi.stubGlobal('fetch', vi.fn());
|
|
15
|
-
});
|
|
16
|
-
afterEach(() => {
|
|
17
|
-
vi.unstubAllGlobals();
|
|
18
|
-
vi.restoreAllMocks();
|
|
19
|
-
});
|
|
20
|
-
it('registers the gitee user command', () => {
|
|
21
|
-
expect(command).toMatchObject({
|
|
22
|
-
site: 'gitee',
|
|
23
|
-
name: 'user',
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
it('does not mislabel contribution totals as Gitee Index when the real index is unavailable', async () => {
|
|
27
|
-
const page = createPage({
|
|
28
|
-
notFound: false,
|
|
29
|
-
blocked: false,
|
|
30
|
-
nickname: 'Alice',
|
|
31
|
-
followers: '12',
|
|
32
|
-
publicRepos: '7',
|
|
33
|
-
giteeIndex: '',
|
|
34
|
-
contributionTotal: 321,
|
|
35
|
-
});
|
|
36
|
-
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(JSON.stringify({
|
|
37
|
-
login: 'alice',
|
|
38
|
-
name: 'Alice',
|
|
39
|
-
followers: 12,
|
|
40
|
-
public_repos: 7,
|
|
41
|
-
}), { status: 200 })));
|
|
42
|
-
const rows = await command.func(page, { username: 'alice' });
|
|
43
|
-
expect(rows).toContainEqual({ field: 'Gitee Index', value: '-' });
|
|
44
|
-
});
|
|
45
|
-
it('uses an API-provided Gitee Index when available', async () => {
|
|
46
|
-
const page = createPage({
|
|
47
|
-
notFound: false,
|
|
48
|
-
blocked: false,
|
|
49
|
-
nickname: '',
|
|
50
|
-
followers: '',
|
|
51
|
-
publicRepos: '',
|
|
52
|
-
giteeIndex: '',
|
|
53
|
-
});
|
|
54
|
-
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(JSON.stringify({
|
|
55
|
-
login: 'alice',
|
|
56
|
-
followers: 9,
|
|
57
|
-
public_repos: 3,
|
|
58
|
-
gitee_index: 88,
|
|
59
|
-
}), { status: 200 })));
|
|
60
|
-
const rows = await command.func(page, { username: 'alice' });
|
|
61
|
-
expect(rows).toContainEqual({ field: 'Gitee Index', value: '88' });
|
|
62
|
-
});
|
|
63
|
-
});
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { parseRssItems } from './utils.js';
|
|
3
|
-
describe('parseRssItems', () => {
|
|
4
|
-
it('extracts plain text fields', () => {
|
|
5
|
-
const xml = `
|
|
6
|
-
<channel>
|
|
7
|
-
<item><title>Hello</title><link>https://example.com</link></item>
|
|
8
|
-
<item><title>World</title><link>https://test.com</link></item>
|
|
9
|
-
</channel>
|
|
10
|
-
`;
|
|
11
|
-
const items = parseRssItems(xml, ['title', 'link']);
|
|
12
|
-
expect(items).toEqual([
|
|
13
|
-
{ title: 'Hello', link: 'https://example.com' },
|
|
14
|
-
{ title: 'World', link: 'https://test.com' },
|
|
15
|
-
]);
|
|
16
|
-
});
|
|
17
|
-
it('handles CDATA-wrapped content', () => {
|
|
18
|
-
const xml = `
|
|
19
|
-
<item><title><![CDATA[Breaking News]]></title><link>https://news.com</link></item>
|
|
20
|
-
`;
|
|
21
|
-
const items = parseRssItems(xml, ['title', 'link']);
|
|
22
|
-
expect(items).toEqual([
|
|
23
|
-
{ title: 'Breaking News', link: 'https://news.com' },
|
|
24
|
-
]);
|
|
25
|
-
});
|
|
26
|
-
it('handles namespaced fields like ht:approx_traffic', () => {
|
|
27
|
-
const xml = `
|
|
28
|
-
<item>
|
|
29
|
-
<title>AI</title>
|
|
30
|
-
<ht:approx_traffic>500,000+</ht:approx_traffic>
|
|
31
|
-
<pubDate>Mon, 20 Mar 2026</pubDate>
|
|
32
|
-
</item>
|
|
33
|
-
`;
|
|
34
|
-
const items = parseRssItems(xml, ['title', 'ht:approx_traffic', 'pubDate']);
|
|
35
|
-
expect(items).toEqual([
|
|
36
|
-
{ title: 'AI', 'ht:approx_traffic': '500,000+', pubDate: 'Mon, 20 Mar 2026' },
|
|
37
|
-
]);
|
|
38
|
-
});
|
|
39
|
-
it('returns empty string for missing fields', () => {
|
|
40
|
-
const xml = `<item><title>Test</title></item>`;
|
|
41
|
-
const items = parseRssItems(xml, ['title', 'missing']);
|
|
42
|
-
expect(items).toEqual([{ title: 'Test', missing: '' }]);
|
|
43
|
-
});
|
|
44
|
-
it('handles tags with attributes (e.g. <source url="...">)', () => {
|
|
45
|
-
const xml = `
|
|
46
|
-
<item>
|
|
47
|
-
<title><![CDATA[AI reshapes everything - Reuters]]></title>
|
|
48
|
-
<source url="https://reuters.com">Reuters</source>
|
|
49
|
-
<link>https://news.google.com/123</link>
|
|
50
|
-
</item>
|
|
51
|
-
`;
|
|
52
|
-
const items = parseRssItems(xml, ['title', 'source', 'link']);
|
|
53
|
-
expect(items).toEqual([
|
|
54
|
-
{ title: 'AI reshapes everything - Reuters', source: 'Reuters', link: 'https://news.google.com/123' },
|
|
55
|
-
]);
|
|
56
|
-
});
|
|
57
|
-
it('handles mixed CDATA and plain text in the same item', () => {
|
|
58
|
-
const xml = `
|
|
59
|
-
<item>
|
|
60
|
-
<title><![CDATA[Breaking: Major event]]></title>
|
|
61
|
-
<link>https://example.com/article</link>
|
|
62
|
-
<pubDate>Fri, 21 Mar 2026</pubDate>
|
|
63
|
-
</item>
|
|
64
|
-
`;
|
|
65
|
-
const items = parseRssItems(xml, ['title', 'link', 'pubDate']);
|
|
66
|
-
expect(items).toEqual([
|
|
67
|
-
{ title: 'Breaking: Major event', link: 'https://example.com/article', pubDate: 'Fri, 21 Mar 2026' },
|
|
68
|
-
]);
|
|
69
|
-
});
|
|
70
|
-
it('returns empty array for no items', () => {
|
|
71
|
-
const xml = `<channel><title>Empty</title></channel>`;
|
|
72
|
-
const items = parseRssItems(xml, ['title']);
|
|
73
|
-
expect(items).toEqual([]);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { CommandExecutionError } from '@sovovs/bycli/errors';
|
|
3
|
-
import { getRegistry } from '@sovovs/bycli/registry';
|
|
4
|
-
import './cite.js';
|
|
5
|
-
|
|
6
|
-
describe('google-scholar cite command', () => {
|
|
7
|
-
const command = getRegistry().get('google-scholar/cite');
|
|
8
|
-
|
|
9
|
-
it('registers as a public browser command', () => {
|
|
10
|
-
expect(command).toBeDefined();
|
|
11
|
-
expect(command.site).toBe('google-scholar');
|
|
12
|
-
expect(command.strategy).toBe('public');
|
|
13
|
-
expect(command.browser).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('rejects empty queries before browser navigation', async () => {
|
|
17
|
-
const page = { goto: vi.fn() };
|
|
18
|
-
await expect(command.func(page, { query: ' ' })).rejects.toMatchObject({
|
|
19
|
-
name: 'ArgumentError',
|
|
20
|
-
code: 'ARGUMENT',
|
|
21
|
-
});
|
|
22
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('throws when the requested search result index does not exist', async () => {
|
|
26
|
-
const page = {
|
|
27
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
28
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
29
|
-
evaluate: vi.fn().mockResolvedValueOnce({ ok: false, reason: 'result not found at index 2' }),
|
|
30
|
-
};
|
|
31
|
-
await expect(command.func(page, { query: 'test', index: 2 })).rejects.toThrow(CommandExecutionError);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('looks up the requested citation style instead of only locking BibTeX', async () => {
|
|
35
|
-
const page = {
|
|
36
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
37
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
38
|
-
evaluate: vi.fn()
|
|
39
|
-
.mockResolvedValueOnce({ ok: true, title: 'Paper Title' })
|
|
40
|
-
.mockResolvedValueOnce('https://example.com/refworks')
|
|
41
|
-
.mockResolvedValueOnce('RefWorks citation body'),
|
|
42
|
-
};
|
|
43
|
-
const result = await command.func(page, { query: 'test', style: 'refworks' });
|
|
44
|
-
expect(result).toEqual([{ title: 'Paper Title', format: 'refworks', citation: 'RefWorks citation body' }]);
|
|
45
|
-
expect(page.evaluate.mock.calls[1][0]).toContain('RefWorks');
|
|
46
|
-
});
|
|
47
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { CommandExecutionError } from '@sovovs/bycli/errors';
|
|
3
|
-
import { getRegistry } from '@sovovs/bycli/registry';
|
|
4
|
-
import './profile.js';
|
|
5
|
-
|
|
6
|
-
describe('google-scholar profile command', () => {
|
|
7
|
-
const command = getRegistry().get('google-scholar/profile');
|
|
8
|
-
|
|
9
|
-
it('registers as a public browser command', () => {
|
|
10
|
-
expect(command).toBeDefined();
|
|
11
|
-
expect(command.site).toBe('google-scholar');
|
|
12
|
-
expect(command.strategy).toBe('public');
|
|
13
|
-
expect(command.browser).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('rejects empty author before browser navigation', async () => {
|
|
17
|
-
const page = { goto: vi.fn() };
|
|
18
|
-
await expect(command.func(page, { author: ' ' })).rejects.toMatchObject({
|
|
19
|
-
name: 'ArgumentError',
|
|
20
|
-
code: 'ARGUMENT',
|
|
21
|
-
});
|
|
22
|
-
expect(page.goto).not.toHaveBeenCalled();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('throws when author search does not resolve to a profile', async () => {
|
|
26
|
-
const page = {
|
|
27
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
28
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
29
|
-
evaluate: vi.fn().mockResolvedValueOnce(false),
|
|
30
|
-
};
|
|
31
|
-
await expect(command.func(page, { author: 'missing author' })).rejects.toThrow(CommandExecutionError);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('throws when the loaded profile has no papers', async () => {
|
|
35
|
-
const page = {
|
|
36
|
-
goto: vi.fn().mockResolvedValue(undefined),
|
|
37
|
-
wait: vi.fn().mockResolvedValue(undefined),
|
|
38
|
-
evaluate: vi.fn().mockResolvedValueOnce({
|
|
39
|
-
name: 'Author Name',
|
|
40
|
-
affiliation: 'Org',
|
|
41
|
-
citations: '0',
|
|
42
|
-
hIndex: '0',
|
|
43
|
-
i10Index: '0',
|
|
44
|
-
papers: [],
|
|
45
|
-
}),
|
|
46
|
-
};
|
|
47
|
-
await expect(command.func(page, { author: 'JicYPdAAAAAJ' })).rejects.toThrow(CommandExecutionError);
|
|
48
|
-
});
|
|
49
|
-
});
|