@jackwener/opencli 1.5.4 → 1.5.6
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 +27 -2
- package/README.zh-CN.md +36 -4
- package/dist/browser/daemon-client.d.ts +5 -1
- package/dist/browser/page.d.ts +6 -0
- package/dist/browser/page.js +15 -0
- package/dist/cli-manifest.json +1284 -67
- package/dist/cli.js +14 -14
- package/dist/clis/antigravity/serve.js +2 -2
- package/dist/clis/band/bands.d.ts +1 -0
- package/dist/clis/band/bands.js +72 -0
- package/dist/clis/band/mentions.d.ts +1 -0
- package/dist/clis/band/mentions.js +127 -0
- package/dist/clis/band/post.d.ts +1 -0
- package/dist/clis/band/post.js +175 -0
- package/dist/clis/band/posts.d.ts +1 -0
- package/dist/clis/band/posts.js +94 -0
- package/dist/clis/doubao/detail.d.ts +1 -0
- package/dist/clis/doubao/detail.js +33 -0
- package/dist/clis/doubao/detail.test.d.ts +1 -0
- package/dist/clis/doubao/detail.test.js +42 -0
- package/dist/clis/doubao/history.d.ts +1 -0
- package/dist/clis/doubao/history.js +28 -0
- package/dist/clis/doubao/history.test.d.ts +1 -0
- package/dist/clis/doubao/history.test.js +37 -0
- package/dist/clis/doubao/meeting-summary.d.ts +1 -0
- package/dist/clis/doubao/meeting-summary.js +39 -0
- package/dist/clis/doubao/meeting-transcript.d.ts +1 -0
- package/dist/clis/doubao/meeting-transcript.js +36 -0
- package/dist/clis/doubao/utils.d.ts +27 -0
- package/dist/clis/doubao/utils.js +317 -0
- package/dist/clis/doubao/utils.test.d.ts +1 -0
- package/dist/clis/doubao/utils.test.js +24 -0
- package/dist/clis/douyin/_shared/public-api.d.ts +33 -0
- package/dist/clis/douyin/_shared/public-api.js +29 -0
- package/dist/clis/douyin/user-videos.d.ts +5 -0
- package/dist/clis/douyin/user-videos.js +74 -0
- package/dist/clis/douyin/user-videos.test.d.ts +1 -0
- package/dist/clis/douyin/user-videos.test.js +108 -0
- package/dist/clis/ones/common.d.ts +32 -0
- package/dist/clis/ones/common.js +144 -0
- package/dist/clis/ones/enrich-tasks.d.ts +5 -0
- package/dist/clis/ones/enrich-tasks.js +37 -0
- package/dist/clis/ones/login.d.ts +1 -0
- package/dist/clis/ones/login.js +80 -0
- package/dist/clis/ones/logout.d.ts +1 -0
- package/dist/clis/ones/logout.js +17 -0
- package/dist/clis/ones/me.d.ts +1 -0
- package/dist/clis/ones/me.js +30 -0
- package/dist/clis/ones/my-tasks.d.ts +1 -0
- package/dist/clis/ones/my-tasks.js +120 -0
- package/dist/clis/ones/resolve-labels.d.ts +10 -0
- package/dist/clis/ones/resolve-labels.js +64 -0
- package/dist/clis/ones/task-helpers.d.ts +29 -0
- package/dist/clis/ones/task-helpers.js +212 -0
- package/dist/clis/ones/task-helpers.test.d.ts +1 -0
- package/dist/clis/ones/task-helpers.test.js +12 -0
- package/dist/clis/ones/task.d.ts +1 -0
- package/dist/clis/ones/task.js +66 -0
- package/dist/clis/ones/tasks.d.ts +1 -0
- package/dist/clis/ones/tasks.js +79 -0
- package/dist/clis/ones/token-info.d.ts +1 -0
- package/dist/clis/ones/token-info.js +42 -0
- package/dist/clis/ones/worklog.d.ts +11 -0
- package/dist/clis/ones/worklog.js +267 -0
- package/dist/clis/ones/worklog.test.d.ts +1 -0
- package/dist/clis/ones/worklog.test.js +20 -0
- package/dist/clis/sinafinance/rolling-news.d.ts +4 -0
- package/dist/clis/sinafinance/rolling-news.js +40 -0
- package/dist/clis/sinafinance/stock.d.ts +8 -0
- package/dist/clis/sinafinance/stock.js +117 -0
- package/dist/clis/spotify/spotify.d.ts +1 -0
- package/dist/clis/spotify/spotify.js +316 -0
- package/dist/clis/spotify/utils.d.ts +21 -0
- package/dist/clis/spotify/utils.js +66 -0
- package/dist/clis/spotify/utils.test.d.ts +1 -0
- package/dist/clis/spotify/utils.test.js +67 -0
- package/dist/clis/tieba/commands.test.d.ts +4 -0
- package/dist/clis/tieba/commands.test.js +79 -0
- package/dist/clis/tieba/hot.d.ts +1 -0
- package/dist/clis/tieba/hot.js +48 -0
- package/dist/clis/tieba/posts.d.ts +1 -0
- package/dist/clis/tieba/posts.js +85 -0
- package/dist/clis/tieba/read.d.ts +1 -0
- package/dist/clis/tieba/read.js +140 -0
- package/dist/clis/tieba/search.d.ts +1 -0
- package/dist/clis/tieba/search.js +108 -0
- package/dist/clis/tieba/utils.d.ts +101 -0
- package/dist/clis/tieba/utils.js +240 -0
- package/dist/clis/tieba/utils.test.d.ts +1 -0
- package/dist/clis/tieba/utils.test.js +290 -0
- package/dist/clis/weread/book.js +100 -13
- package/dist/clis/weread/commands.test.js +221 -0
- package/dist/clis/weread/private-api-regression.test.d.ts +1 -0
- package/dist/{weread-private-api-regression.test.js → clis/weread/private-api-regression.test.js} +92 -30
- package/dist/clis/weread/search-regression.test.d.ts +1 -0
- package/dist/clis/weread/search-regression.test.js +407 -0
- package/dist/clis/weread/search.js +143 -7
- package/dist/clis/weread/shelf.js +13 -95
- package/dist/clis/weread/utils.d.ts +46 -0
- package/dist/clis/weread/utils.js +214 -7
- package/dist/clis/weread/utils.test.js +71 -1
- package/dist/clis/xiaohongshu/publish.d.ts +1 -1
- package/dist/clis/xiaohongshu/publish.js +78 -31
- package/dist/clis/xiaohongshu/publish.test.js +66 -1
- package/dist/clis/xiaohongshu/user-helpers.d.ts +1 -0
- package/dist/clis/xiaohongshu/user-helpers.js +2 -0
- package/dist/clis/xiaohongshu/user-helpers.test.js +18 -0
- package/dist/clis/xueqiu/comments.d.ts +118 -0
- package/dist/clis/xueqiu/comments.js +354 -0
- package/dist/clis/xueqiu/comments.test.d.ts +1 -0
- package/dist/clis/xueqiu/comments.test.js +696 -0
- package/dist/clis/youtube/transcript.js +2 -4
- package/dist/clis/youtube/utils.d.ts +9 -0
- package/dist/clis/youtube/utils.js +67 -3
- package/dist/clis/youtube/utils.test.d.ts +1 -0
- package/dist/clis/youtube/utils.test.js +37 -0
- package/dist/clis/youtube/video.js +16 -15
- package/dist/clis/zsxq/dynamics.d.ts +1 -0
- package/dist/clis/zsxq/dynamics.js +47 -0
- package/dist/clis/zsxq/groups.d.ts +1 -0
- package/dist/clis/zsxq/groups.js +32 -0
- package/dist/clis/zsxq/search.d.ts +1 -0
- package/dist/clis/zsxq/search.js +43 -0
- package/dist/clis/zsxq/search.test.d.ts +1 -0
- package/dist/clis/zsxq/search.test.js +24 -0
- package/dist/clis/zsxq/topic.d.ts +1 -0
- package/dist/clis/zsxq/topic.js +47 -0
- package/dist/clis/zsxq/topic.test.d.ts +1 -0
- package/dist/clis/zsxq/topic.test.js +29 -0
- package/dist/clis/zsxq/topics.d.ts +1 -0
- package/dist/clis/zsxq/topics.js +25 -0
- package/dist/clis/zsxq/topics.test.d.ts +1 -0
- package/dist/clis/zsxq/topics.test.js +24 -0
- package/dist/clis/zsxq/utils.d.ts +97 -0
- package/dist/clis/zsxq/utils.js +230 -0
- package/dist/commanderAdapter.js +27 -4
- package/dist/commanderAdapter.test.js +39 -0
- package/dist/daemon.js +5 -4
- package/dist/errors.d.ts +29 -1
- package/dist/errors.js +49 -11
- package/dist/external-clis.yaml +17 -0
- package/dist/external.js +3 -3
- package/dist/main.js +2 -1
- package/dist/tui.js +2 -1
- package/dist/types.d.ts +5 -0
- package/docs/.vitepress/config.mts +3 -0
- package/docs/adapters/browser/band.md +63 -0
- package/docs/adapters/browser/ones.md +59 -0
- package/docs/adapters/browser/sinafinance.md +56 -6
- package/docs/adapters/browser/spotify.md +62 -0
- package/docs/adapters/browser/tieba.md +45 -0
- package/docs/adapters/browser/xueqiu.md +5 -0
- package/docs/adapters/browser/zsxq.md +49 -0
- package/docs/adapters/index.md +5 -2
- package/docs/adapters-doc/ones.md +32 -0
- package/extension/dist/background.js +1 -2
- package/extension/manifest.json +1 -1
- package/extension/package.json +1 -1
- package/extension/src/background.ts +17 -1
- package/extension/src/cdp.ts +42 -0
- package/extension/src/protocol.ts +5 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +16 -0
- package/src/browser/daemon-client.ts +5 -1
- package/src/browser/page.ts +16 -0
- package/src/cli.ts +14 -14
- package/src/clis/antigravity/serve.ts +2 -2
- package/src/clis/band/bands.ts +76 -0
- package/src/clis/band/mentions.ts +134 -0
- package/src/clis/band/post.ts +187 -0
- package/src/clis/band/posts.ts +106 -0
- package/src/clis/doubao/detail.test.ts +53 -0
- package/src/clis/doubao/detail.ts +41 -0
- package/src/clis/doubao/history.test.ts +45 -0
- package/src/clis/doubao/history.ts +32 -0
- package/src/clis/doubao/meeting-summary.ts +53 -0
- package/src/clis/doubao/meeting-transcript.ts +48 -0
- package/src/clis/doubao/utils.test.ts +45 -0
- package/src/clis/doubao/utils.ts +371 -0
- package/src/clis/douyin/_shared/public-api.ts +84 -0
- package/src/clis/douyin/user-videos.test.ts +122 -0
- package/src/clis/douyin/user-videos.ts +101 -0
- package/src/clis/ones/common.ts +187 -0
- package/src/clis/ones/enrich-tasks.ts +47 -0
- package/src/clis/ones/login.ts +103 -0
- package/src/clis/ones/logout.ts +19 -0
- package/src/clis/ones/me.ts +34 -0
- package/src/clis/ones/my-tasks.ts +148 -0
- package/src/clis/ones/resolve-labels.ts +80 -0
- package/src/clis/ones/task-helpers.test.ts +14 -0
- package/src/clis/ones/task-helpers.ts +214 -0
- package/src/clis/ones/task.ts +79 -0
- package/src/clis/ones/tasks.ts +92 -0
- package/src/clis/ones/token-info.ts +46 -0
- package/src/clis/ones/worklog.test.ts +24 -0
- package/src/clis/ones/worklog.ts +306 -0
- package/src/clis/sinafinance/rolling-news.ts +42 -0
- package/src/clis/sinafinance/stock.ts +127 -0
- package/src/clis/spotify/spotify.ts +328 -0
- package/src/clis/spotify/utils.test.ts +87 -0
- package/src/clis/spotify/utils.ts +92 -0
- package/src/clis/tieba/commands.test.ts +86 -0
- package/src/clis/tieba/hot.ts +52 -0
- package/src/clis/tieba/posts.ts +108 -0
- package/src/clis/tieba/read.ts +158 -0
- package/src/clis/tieba/search.ts +119 -0
- package/src/clis/tieba/utils.test.ts +322 -0
- package/src/clis/tieba/utils.ts +348 -0
- package/src/clis/weread/book.ts +116 -13
- package/src/clis/weread/commands.test.ts +249 -0
- package/src/{weread-private-api-regression.test.ts → clis/weread/private-api-regression.test.ts} +108 -30
- package/src/clis/weread/search-regression.test.ts +440 -0
- package/src/clis/weread/search.ts +189 -9
- package/src/clis/weread/shelf.ts +20 -122
- package/src/clis/weread/utils.test.ts +81 -1
- package/src/clis/weread/utils.ts +264 -7
- package/src/clis/xiaohongshu/publish.test.ts +79 -1
- package/src/clis/xiaohongshu/publish.ts +84 -30
- package/src/clis/xiaohongshu/user-helpers.test.ts +23 -0
- package/src/clis/xiaohongshu/user-helpers.ts +4 -0
- package/src/clis/xueqiu/comments.test.ts +823 -0
- package/src/clis/xueqiu/comments.ts +461 -0
- package/src/clis/youtube/transcript.ts +2 -4
- package/src/clis/youtube/utils.test.ts +43 -0
- package/src/clis/youtube/utils.ts +69 -0
- package/src/clis/youtube/video.ts +16 -15
- package/src/clis/zsxq/dynamics.ts +60 -0
- package/src/clis/zsxq/groups.ts +41 -0
- package/src/clis/zsxq/search.test.ts +29 -0
- package/src/clis/zsxq/search.ts +54 -0
- package/src/clis/zsxq/topic.test.ts +34 -0
- package/src/clis/zsxq/topic.ts +68 -0
- package/src/clis/zsxq/topics.test.ts +29 -0
- package/src/clis/zsxq/topics.ts +36 -0
- package/src/clis/zsxq/utils.ts +351 -0
- package/src/commanderAdapter.test.ts +47 -0
- package/src/commanderAdapter.ts +26 -3
- package/src/daemon.ts +5 -4
- package/src/errors.ts +71 -10
- package/src/external-clis.yaml +17 -0
- package/src/external.ts +3 -3
- package/src/main.ts +2 -1
- package/src/tui.ts +2 -1
- package/src/types.ts +5 -0
- package/tests/e2e/band-auth.test.ts +20 -0
- package/tests/e2e/browser-auth-helpers.ts +18 -0
- package/tests/e2e/browser-auth.test.ts +35 -47
- package/tests/e2e/browser-public.test.ts +288 -0
- package/tests/e2e/management.test.ts +1 -1
- package/tests/e2e/plugin-management.test.ts +1 -1
- package/vitest.config.ts +1 -0
- package/SKILL.md +0 -879
- package/dist/weread-private-api-regression.test.d.ts +0 -1
- package/dist/weread-search-regression.test.d.ts +0 -1
- package/dist/weread-search-regression.test.js +0 -39
- package/src/weread-search-regression.test.ts +0 -44
package/src/clis/weread/book.ts
CHANGED
|
@@ -1,6 +1,82 @@
|
|
|
1
1
|
import { cli, Strategy } from '../../registry.js';
|
|
2
|
+
import { CliError } from '../../errors.js';
|
|
2
3
|
import type { IPage } from '../../types.js';
|
|
3
|
-
import { fetchPrivateApi } from './utils.js';
|
|
4
|
+
import { fetchPrivateApi, resolveShelfReaderUrl } from './utils.js';
|
|
5
|
+
|
|
6
|
+
interface ReaderFallbackResult {
|
|
7
|
+
title: string;
|
|
8
|
+
author: string;
|
|
9
|
+
publisher: string;
|
|
10
|
+
intro: string;
|
|
11
|
+
category: string;
|
|
12
|
+
rating: string;
|
|
13
|
+
metadataReady: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Read visible book metadata from the web reader cover/flyleaf page.
|
|
18
|
+
* This path is used as a fallback when the private API session has expired.
|
|
19
|
+
*/
|
|
20
|
+
async function loadReaderFallbackResult(page: IPage, readerUrl: string): Promise<ReaderFallbackResult> {
|
|
21
|
+
await page.goto(readerUrl);
|
|
22
|
+
await page.wait({ selector: '.horizontalReaderCoverPage_content_bookTitle, .wr_flyleaf_page_bookInfo_bookTitle', timeout: 10 });
|
|
23
|
+
|
|
24
|
+
const result = await page.evaluate(`
|
|
25
|
+
(() => {
|
|
26
|
+
const text = (node) => node?.textContent?.trim() || '';
|
|
27
|
+
const bodyText = document.body?.innerText?.replace(/\\s+/g, ' ').trim() || '';
|
|
28
|
+
const titleSelector = '.horizontalReaderCoverPage_content_bookTitle, .wr_flyleaf_page_bookInfo_bookTitle';
|
|
29
|
+
const authorSelector = '.horizontalReaderCoverPage_content_author, .wr_flyleaf_page_bookInfo_author';
|
|
30
|
+
const extractRating = () => {
|
|
31
|
+
const match = bodyText.match(/微信读书推荐值\\s*([0-9.]+%)/);
|
|
32
|
+
return match ? match[1] : '';
|
|
33
|
+
};
|
|
34
|
+
const extractPublisher = () => {
|
|
35
|
+
const direct = text(document.querySelector('.introDialog_content_pub_line'));
|
|
36
|
+
return direct.startsWith('出版社') ? direct.replace(/^出版社\\s*/, '').trim() : '';
|
|
37
|
+
};
|
|
38
|
+
const extractIntro = () => {
|
|
39
|
+
const selectors = [
|
|
40
|
+
'.horizontalReaderCoverPage_content_bookInfo_intro',
|
|
41
|
+
'.wr_flyleaf_page_bookIntro_content',
|
|
42
|
+
'.introDialog_content_intro_para',
|
|
43
|
+
];
|
|
44
|
+
for (const selector of selectors) {
|
|
45
|
+
const value = text(document.querySelector(selector));
|
|
46
|
+
if (value) return value;
|
|
47
|
+
}
|
|
48
|
+
return '';
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const categorySource = Array.from(document.scripts)
|
|
52
|
+
.map((script) => script.textContent || '')
|
|
53
|
+
.find((scriptText) => scriptText.includes('"category"')) || '';
|
|
54
|
+
const categoryMatch = categorySource.match(/"category"\\s*:\\s*"([^"]+)"/);
|
|
55
|
+
const title = text(document.querySelector(titleSelector));
|
|
56
|
+
const author = text(document.querySelector(authorSelector));
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
title,
|
|
60
|
+
author,
|
|
61
|
+
publisher: extractPublisher(),
|
|
62
|
+
intro: extractIntro(),
|
|
63
|
+
category: categoryMatch ? categoryMatch[1].trim() : '',
|
|
64
|
+
rating: extractRating(),
|
|
65
|
+
metadataReady: Boolean(title || author),
|
|
66
|
+
};
|
|
67
|
+
})()
|
|
68
|
+
`) as Partial<ReaderFallbackResult>;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
title: String(result?.title || '').trim(),
|
|
72
|
+
author: String(result?.author || '').trim(),
|
|
73
|
+
publisher: String(result?.publisher || '').trim(),
|
|
74
|
+
intro: String(result?.intro || '').trim(),
|
|
75
|
+
category: String(result?.category || '').trim(),
|
|
76
|
+
rating: String(result?.rating || '').trim(),
|
|
77
|
+
metadataReady: result?.metadataReady === true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
4
80
|
|
|
5
81
|
cli({
|
|
6
82
|
site: 'weread',
|
|
@@ -9,20 +85,47 @@ cli({
|
|
|
9
85
|
domain: 'weread.qq.com',
|
|
10
86
|
strategy: Strategy.COOKIE,
|
|
11
87
|
args: [
|
|
12
|
-
{ name: 'book-id', positional: true, required: true, help: 'Book ID
|
|
88
|
+
{ name: 'book-id', positional: true, required: true, help: 'Book ID from search or shelf results' },
|
|
13
89
|
],
|
|
14
90
|
columns: ['title', 'author', 'publisher', 'intro', 'category', 'rating'],
|
|
15
91
|
func: async (page: IPage, args) => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
92
|
+
const bookId = String(args['book-id'] || '').trim();
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const data = await fetchPrivateApi(page, '/book/info', { bookId });
|
|
96
|
+
// newRating is 0-1000 scale per community docs; needs runtime verification
|
|
97
|
+
const rating = data.newRating ? `${(data.newRating / 10).toFixed(1)}%` : '-';
|
|
98
|
+
return [{
|
|
99
|
+
title: data.title ?? '',
|
|
100
|
+
author: data.author ?? '',
|
|
101
|
+
publisher: data.publisher ?? '',
|
|
102
|
+
intro: data.intro ?? '',
|
|
103
|
+
category: data.category ?? '',
|
|
104
|
+
rating,
|
|
105
|
+
}];
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (!(error instanceof CliError) || error.code !== 'AUTH_REQUIRED') {
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const readerUrl = await resolveShelfReaderUrl(page, bookId);
|
|
112
|
+
if (!readerUrl) {
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const data = await loadReaderFallbackResult(page, readerUrl);
|
|
117
|
+
if (!data.metadataReady || !data.title) {
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return [{
|
|
122
|
+
title: data.title,
|
|
123
|
+
author: data.author,
|
|
124
|
+
publisher: data.publisher,
|
|
125
|
+
intro: data.intro,
|
|
126
|
+
category: data.category,
|
|
127
|
+
rating: data.rating,
|
|
128
|
+
}];
|
|
129
|
+
}
|
|
27
130
|
},
|
|
28
131
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { CliError } from '../../errors.js';
|
|
2
3
|
|
|
3
4
|
const { mockFetchPrivateApi } = vi.hoisted(() => ({
|
|
4
5
|
mockFetchPrivateApi: vi.fn(),
|
|
@@ -34,6 +35,254 @@ describe('weread book-id positional args', () => {
|
|
|
34
35
|
expect(mockFetchPrivateApi).toHaveBeenCalledWith({}, '/book/info', { bookId: '12345' });
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
it('falls back to the shelf reader page when private API auth has expired', async () => {
|
|
39
|
+
mockFetchPrivateApi.mockRejectedValue(
|
|
40
|
+
new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first'),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const page = {
|
|
44
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
45
|
+
evaluate: vi.fn()
|
|
46
|
+
.mockResolvedValueOnce({
|
|
47
|
+
cacheFound: true,
|
|
48
|
+
rawBooks: [
|
|
49
|
+
{ bookId: 'MP_WXS_3634777637', title: '文明、现代化、价值投资与中国', author: '李录' },
|
|
50
|
+
],
|
|
51
|
+
shelfIndexes: [
|
|
52
|
+
{ bookId: 'MP_WXS_3634777637', idx: 0, role: 'book' },
|
|
53
|
+
],
|
|
54
|
+
})
|
|
55
|
+
.mockResolvedValueOnce(['https://weread.qq.com/web/reader/6f5323f071bd7f7b6f521e8'])
|
|
56
|
+
.mockResolvedValueOnce({
|
|
57
|
+
title: '文明、现代化、价值投资与中国',
|
|
58
|
+
author: '李录',
|
|
59
|
+
publisher: '中信出版集团',
|
|
60
|
+
intro: '对中国未来几十年的预测。',
|
|
61
|
+
category: '',
|
|
62
|
+
rating: '84.1%',
|
|
63
|
+
metadataReady: true,
|
|
64
|
+
}),
|
|
65
|
+
getCookies: vi.fn().mockResolvedValue([
|
|
66
|
+
{ name: 'wr_vid', value: '70486028', domain: '.weread.qq.com' },
|
|
67
|
+
]),
|
|
68
|
+
wait: vi.fn().mockResolvedValue(undefined),
|
|
69
|
+
} as any;
|
|
70
|
+
|
|
71
|
+
const result = await book!.func!(page, { 'book-id': 'MP_WXS_3634777637' });
|
|
72
|
+
|
|
73
|
+
expect(page.goto).toHaveBeenNthCalledWith(1, 'https://weread.qq.com/web/shelf');
|
|
74
|
+
expect(page.goto).toHaveBeenNthCalledWith(2, 'https://weread.qq.com/web/reader/6f5323f071bd7f7b6f521e8');
|
|
75
|
+
expect(page.evaluate).toHaveBeenCalledTimes(3);
|
|
76
|
+
expect(result).toEqual([
|
|
77
|
+
{
|
|
78
|
+
title: '文明、现代化、价值投资与中国',
|
|
79
|
+
author: '李录',
|
|
80
|
+
publisher: '中信出版集团',
|
|
81
|
+
intro: '对中国未来几十年的预测。',
|
|
82
|
+
category: '',
|
|
83
|
+
rating: '84.1%',
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('keeps mixed shelf entries aligned when resolving MP_WXS reader urls', async () => {
|
|
89
|
+
mockFetchPrivateApi.mockRejectedValue(
|
|
90
|
+
new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first'),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const page = {
|
|
94
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
95
|
+
evaluate: vi.fn()
|
|
96
|
+
.mockResolvedValueOnce({
|
|
97
|
+
cacheFound: true,
|
|
98
|
+
rawBooks: [
|
|
99
|
+
{ bookId: 'MP_WXS_1', title: '公众号文章一', author: '作者甲' },
|
|
100
|
+
{ bookId: 'BOOK_2', title: '普通书二', author: '作者乙' },
|
|
101
|
+
{ bookId: 'MP_WXS_3', title: '公众号文章三', author: '作者丙' },
|
|
102
|
+
],
|
|
103
|
+
shelfIndexes: [
|
|
104
|
+
{ bookId: 'MP_WXS_1', idx: 0, role: 'mp' },
|
|
105
|
+
{ bookId: 'BOOK_2', idx: 1, role: 'book' },
|
|
106
|
+
{ bookId: 'MP_WXS_3', idx: 2, role: 'mp' },
|
|
107
|
+
],
|
|
108
|
+
})
|
|
109
|
+
.mockResolvedValueOnce([
|
|
110
|
+
'https://weread.qq.com/web/reader/mp1',
|
|
111
|
+
'https://weread.qq.com/web/reader/book2',
|
|
112
|
+
'https://weread.qq.com/web/reader/mp3',
|
|
113
|
+
])
|
|
114
|
+
.mockResolvedValueOnce({
|
|
115
|
+
title: '公众号文章一',
|
|
116
|
+
author: '作者甲',
|
|
117
|
+
publisher: '微信读书',
|
|
118
|
+
intro: '第一篇文章。',
|
|
119
|
+
category: '',
|
|
120
|
+
rating: '',
|
|
121
|
+
metadataReady: true,
|
|
122
|
+
}),
|
|
123
|
+
getCookies: vi.fn().mockResolvedValue([
|
|
124
|
+
{ name: 'wr_vid', value: '70486028', domain: '.weread.qq.com' },
|
|
125
|
+
]),
|
|
126
|
+
wait: vi.fn().mockResolvedValue(undefined),
|
|
127
|
+
} as any;
|
|
128
|
+
|
|
129
|
+
const result = await book!.func!(page, { 'book-id': 'MP_WXS_1' });
|
|
130
|
+
|
|
131
|
+
expect(page.goto).toHaveBeenNthCalledWith(1, 'https://weread.qq.com/web/shelf');
|
|
132
|
+
expect(page.goto).toHaveBeenNthCalledWith(2, 'https://weread.qq.com/web/reader/mp1');
|
|
133
|
+
expect(result).toEqual([
|
|
134
|
+
{
|
|
135
|
+
title: '公众号文章一',
|
|
136
|
+
author: '作者甲',
|
|
137
|
+
publisher: '微信读书',
|
|
138
|
+
intro: '第一篇文章。',
|
|
139
|
+
category: '',
|
|
140
|
+
rating: '',
|
|
141
|
+
},
|
|
142
|
+
]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('rethrows AUTH_REQUIRED when shelf ordering is incomplete and reader urls cannot be trusted', async () => {
|
|
146
|
+
mockFetchPrivateApi.mockRejectedValue(
|
|
147
|
+
new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first'),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const page = {
|
|
151
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
152
|
+
evaluate: vi.fn()
|
|
153
|
+
.mockResolvedValueOnce({
|
|
154
|
+
cacheFound: true,
|
|
155
|
+
rawBooks: [
|
|
156
|
+
{ bookId: 'BOOK_1', title: '第一本', author: '作者甲' },
|
|
157
|
+
{ bookId: 'BOOK_2', title: '第二本', author: '作者乙' },
|
|
158
|
+
],
|
|
159
|
+
shelfIndexes: [
|
|
160
|
+
{ bookId: 'BOOK_2', idx: 0, role: 'book' },
|
|
161
|
+
],
|
|
162
|
+
})
|
|
163
|
+
.mockResolvedValueOnce([
|
|
164
|
+
'https://weread.qq.com/web/reader/book2',
|
|
165
|
+
'https://weread.qq.com/web/reader/book1',
|
|
166
|
+
]),
|
|
167
|
+
getCookies: vi.fn().mockResolvedValue([
|
|
168
|
+
{ name: 'wr_vid', value: '70486028', domain: '.weread.qq.com' },
|
|
169
|
+
]),
|
|
170
|
+
wait: vi.fn().mockResolvedValue(undefined),
|
|
171
|
+
} as any;
|
|
172
|
+
|
|
173
|
+
await expect(book!.func!(page, { 'book-id': 'BOOK_1' })).rejects.toMatchObject({
|
|
174
|
+
code: 'AUTH_REQUIRED',
|
|
175
|
+
message: 'Not logged in to WeRead',
|
|
176
|
+
});
|
|
177
|
+
expect(page.goto).toHaveBeenCalledTimes(1);
|
|
178
|
+
expect(page.goto).toHaveBeenCalledWith('https://weread.qq.com/web/shelf');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('waits for shelf indexes to hydrate before resolving a trusted reader url', async () => {
|
|
182
|
+
mockFetchPrivateApi.mockRejectedValue(
|
|
183
|
+
new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first'),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const page = {
|
|
187
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
188
|
+
evaluate: vi.fn()
|
|
189
|
+
.mockResolvedValueOnce({
|
|
190
|
+
cacheFound: true,
|
|
191
|
+
rawBooks: [
|
|
192
|
+
{ bookId: 'BOOK_1', title: '第一本', author: '作者甲' },
|
|
193
|
+
{ bookId: 'BOOK_2', title: '第二本', author: '作者乙' },
|
|
194
|
+
],
|
|
195
|
+
shelfIndexes: [
|
|
196
|
+
{ bookId: 'BOOK_2', idx: 0, role: 'book' },
|
|
197
|
+
],
|
|
198
|
+
})
|
|
199
|
+
.mockResolvedValueOnce({
|
|
200
|
+
cacheFound: true,
|
|
201
|
+
rawBooks: [
|
|
202
|
+
{ bookId: 'BOOK_1', title: '第一本', author: '作者甲' },
|
|
203
|
+
{ bookId: 'BOOK_2', title: '第二本', author: '作者乙' },
|
|
204
|
+
],
|
|
205
|
+
shelfIndexes: [
|
|
206
|
+
{ bookId: 'BOOK_2', idx: 0, role: 'book' },
|
|
207
|
+
{ bookId: 'BOOK_1', idx: 1, role: 'book' },
|
|
208
|
+
],
|
|
209
|
+
})
|
|
210
|
+
.mockResolvedValueOnce([
|
|
211
|
+
'https://weread.qq.com/web/reader/book2',
|
|
212
|
+
'https://weread.qq.com/web/reader/book1',
|
|
213
|
+
])
|
|
214
|
+
.mockResolvedValueOnce({
|
|
215
|
+
title: '第一本',
|
|
216
|
+
author: '作者甲',
|
|
217
|
+
publisher: '出版社甲',
|
|
218
|
+
intro: '简介甲',
|
|
219
|
+
category: '',
|
|
220
|
+
rating: '',
|
|
221
|
+
metadataReady: true,
|
|
222
|
+
}),
|
|
223
|
+
getCookies: vi.fn().mockResolvedValue([
|
|
224
|
+
{ name: 'wr_vid', value: '70486028', domain: '.weread.qq.com' },
|
|
225
|
+
]),
|
|
226
|
+
wait: vi.fn().mockResolvedValue(undefined),
|
|
227
|
+
} as any;
|
|
228
|
+
|
|
229
|
+
const result = await book!.func!(page, { 'book-id': 'BOOK_1' });
|
|
230
|
+
|
|
231
|
+
expect(page.goto).toHaveBeenNthCalledWith(1, 'https://weread.qq.com/web/shelf');
|
|
232
|
+
expect(page.goto).toHaveBeenNthCalledWith(2, 'https://weread.qq.com/web/reader/book1');
|
|
233
|
+
expect(result).toEqual([
|
|
234
|
+
{
|
|
235
|
+
title: '第一本',
|
|
236
|
+
author: '作者甲',
|
|
237
|
+
publisher: '出版社甲',
|
|
238
|
+
intro: '简介甲',
|
|
239
|
+
category: '',
|
|
240
|
+
rating: '',
|
|
241
|
+
},
|
|
242
|
+
]);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('rethrows AUTH_REQUIRED when the reader page lacks stable cover metadata', async () => {
|
|
246
|
+
mockFetchPrivateApi.mockRejectedValue(
|
|
247
|
+
new CliError('AUTH_REQUIRED', 'Not logged in to WeRead', 'Please log in to weread.qq.com in Chrome first'),
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
const page = {
|
|
251
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
252
|
+
evaluate: vi.fn()
|
|
253
|
+
.mockResolvedValueOnce({
|
|
254
|
+
cacheFound: true,
|
|
255
|
+
rawBooks: [
|
|
256
|
+
{ bookId: 'BOOK_1', title: '第一本', author: '作者甲' },
|
|
257
|
+
],
|
|
258
|
+
shelfIndexes: [
|
|
259
|
+
{ bookId: 'BOOK_1', idx: 0, role: 'book' },
|
|
260
|
+
],
|
|
261
|
+
})
|
|
262
|
+
.mockResolvedValueOnce([
|
|
263
|
+
'https://weread.qq.com/web/reader/book1',
|
|
264
|
+
])
|
|
265
|
+
.mockResolvedValueOnce({
|
|
266
|
+
title: '',
|
|
267
|
+
author: '',
|
|
268
|
+
publisher: '',
|
|
269
|
+
intro: '这是正文第一段,不应该被当成简介。',
|
|
270
|
+
category: '',
|
|
271
|
+
rating: '',
|
|
272
|
+
metadataReady: false,
|
|
273
|
+
}),
|
|
274
|
+
getCookies: vi.fn().mockResolvedValue([
|
|
275
|
+
{ name: 'wr_vid', value: '70486028', domain: '.weread.qq.com' },
|
|
276
|
+
]),
|
|
277
|
+
wait: vi.fn().mockResolvedValue(undefined),
|
|
278
|
+
} as any;
|
|
279
|
+
|
|
280
|
+
await expect(book!.func!(page, { 'book-id': 'BOOK_1' })).rejects.toMatchObject({
|
|
281
|
+
code: 'AUTH_REQUIRED',
|
|
282
|
+
message: 'Not logged in to WeRead',
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
37
286
|
it('passes the positional book-id to highlights', async () => {
|
|
38
287
|
mockFetchPrivateApi.mockResolvedValue({ updated: [] });
|
|
39
288
|
|
package/src/{weread-private-api-regression.test.ts → clis/weread/private-api-regression.test.ts}
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { getRegistry } from '
|
|
3
|
-
import {
|
|
4
|
-
import './
|
|
2
|
+
import { getRegistry } from '../../registry.js';
|
|
3
|
+
import { log } from '../../logger.js';
|
|
4
|
+
import { fetchPrivateApi } from './utils.js';
|
|
5
|
+
import './shelf.js';
|
|
5
6
|
|
|
6
7
|
describe('weread private API regression', () => {
|
|
7
8
|
beforeEach(() => {
|
|
@@ -12,8 +13,10 @@ describe('weread private API regression', () => {
|
|
|
12
13
|
const mockPage = {
|
|
13
14
|
getCookies: vi.fn()
|
|
14
15
|
.mockResolvedValueOnce([
|
|
15
|
-
{ name: 'wr_name', value: 'alice', domain: 'weread.qq.com' },
|
|
16
16
|
{ name: 'wr_vid', value: 'vid123', domain: 'i.weread.qq.com' },
|
|
17
|
+
])
|
|
18
|
+
.mockResolvedValueOnce([
|
|
19
|
+
{ name: 'wr_name', value: 'alice', domain: 'weread.qq.com' },
|
|
17
20
|
]),
|
|
18
21
|
evaluate: vi.fn(),
|
|
19
22
|
} as any;
|
|
@@ -28,8 +31,9 @@ describe('weread private API regression', () => {
|
|
|
28
31
|
const result = await fetchPrivateApi(mockPage, '/book/info', { bookId: '123' });
|
|
29
32
|
|
|
30
33
|
expect(result.title).toBe('Test Book');
|
|
31
|
-
expect(mockPage.getCookies).toHaveBeenCalledTimes(
|
|
34
|
+
expect(mockPage.getCookies).toHaveBeenCalledTimes(2);
|
|
32
35
|
expect(mockPage.getCookies).toHaveBeenCalledWith({ url: 'https://i.weread.qq.com/book/info?bookId=123' });
|
|
36
|
+
expect(mockPage.getCookies).toHaveBeenCalledWith({ domain: 'weread.qq.com' });
|
|
33
37
|
expect(mockPage.evaluate).not.toHaveBeenCalled();
|
|
34
38
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
35
39
|
'https://i.weread.qq.com/book/info?bookId=123',
|
|
@@ -41,6 +45,72 @@ describe('weread private API regression', () => {
|
|
|
41
45
|
);
|
|
42
46
|
});
|
|
43
47
|
|
|
48
|
+
it('merges host-only main-domain cookies into private API requests', async () => {
|
|
49
|
+
// Simulates host-only cookies on weread.qq.com that don't match i.weread.qq.com by URL
|
|
50
|
+
const mockPage = {
|
|
51
|
+
getCookies: vi.fn()
|
|
52
|
+
.mockResolvedValueOnce([]) // URL lookup returns nothing for i.weread.qq.com
|
|
53
|
+
.mockResolvedValueOnce([
|
|
54
|
+
{ name: 'wr_skey', value: 'skey-host', domain: 'weread.qq.com' },
|
|
55
|
+
{ name: 'wr_vid', value: 'vid-host', domain: 'weread.qq.com' },
|
|
56
|
+
]),
|
|
57
|
+
evaluate: vi.fn(),
|
|
58
|
+
} as any;
|
|
59
|
+
|
|
60
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
61
|
+
ok: true,
|
|
62
|
+
status: 200,
|
|
63
|
+
json: () => Promise.resolve({ title: 'Book', errcode: 0 }),
|
|
64
|
+
});
|
|
65
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
66
|
+
|
|
67
|
+
await fetchPrivateApi(mockPage, '/book/info', { bookId: '42' });
|
|
68
|
+
|
|
69
|
+
expect(mockPage.getCookies).toHaveBeenCalledTimes(2);
|
|
70
|
+
expect(mockPage.getCookies).toHaveBeenCalledWith({ url: 'https://i.weread.qq.com/book/info?bookId=42' });
|
|
71
|
+
expect(mockPage.getCookies).toHaveBeenCalledWith({ domain: 'weread.qq.com' });
|
|
72
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
73
|
+
'https://i.weread.qq.com/book/info?bookId=42',
|
|
74
|
+
expect.objectContaining({
|
|
75
|
+
headers: expect.objectContaining({
|
|
76
|
+
Cookie: 'wr_skey=skey-host; wr_vid=vid-host',
|
|
77
|
+
}),
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('prefers API-subdomain cookies over main-domain cookies on name collision', async () => {
|
|
83
|
+
const mockPage = {
|
|
84
|
+
getCookies: vi.fn()
|
|
85
|
+
.mockResolvedValueOnce([
|
|
86
|
+
{ name: 'wr_skey', value: 'from-api', domain: 'i.weread.qq.com' },
|
|
87
|
+
])
|
|
88
|
+
.mockResolvedValueOnce([
|
|
89
|
+
{ name: 'wr_skey', value: 'from-main', domain: 'weread.qq.com' },
|
|
90
|
+
{ name: 'wr_vid', value: 'vid-main', domain: 'weread.qq.com' },
|
|
91
|
+
]),
|
|
92
|
+
evaluate: vi.fn(),
|
|
93
|
+
} as any;
|
|
94
|
+
|
|
95
|
+
const fetchMock = vi.fn().mockResolvedValue({
|
|
96
|
+
ok: true,
|
|
97
|
+
status: 200,
|
|
98
|
+
json: () => Promise.resolve({ title: 'Book', errcode: 0 }),
|
|
99
|
+
});
|
|
100
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
101
|
+
|
|
102
|
+
await fetchPrivateApi(mockPage, '/book/info', { bookId: '99' });
|
|
103
|
+
|
|
104
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
105
|
+
'https://i.weread.qq.com/book/info?bookId=99',
|
|
106
|
+
expect.objectContaining({
|
|
107
|
+
headers: expect.objectContaining({
|
|
108
|
+
Cookie: 'wr_skey=from-api; wr_vid=vid-main',
|
|
109
|
+
}),
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
44
114
|
it('maps unauthenticated private API responses to AUTH_REQUIRED', async () => {
|
|
45
115
|
const mockPage = {
|
|
46
116
|
getCookies: vi.fn().mockResolvedValue([]),
|
|
@@ -126,8 +196,10 @@ describe('weread private API regression', () => {
|
|
|
126
196
|
const mockPage = {
|
|
127
197
|
getCookies: vi.fn()
|
|
128
198
|
.mockResolvedValueOnce([
|
|
129
|
-
{ name: 'wr_name', value: 'alice', domain: 'weread.qq.com' },
|
|
130
199
|
{ name: 'wr_vid', value: 'vid123', domain: 'i.weread.qq.com' },
|
|
200
|
+
])
|
|
201
|
+
.mockResolvedValueOnce([
|
|
202
|
+
{ name: 'wr_name', value: 'alice', domain: 'weread.qq.com' },
|
|
131
203
|
]),
|
|
132
204
|
evaluate: vi.fn(),
|
|
133
205
|
} as any;
|
|
@@ -153,9 +225,11 @@ describe('weread private API regression', () => {
|
|
|
153
225
|
'https://i.weread.qq.com/shelf/sync?synckey=0&lectureSynckey=0',
|
|
154
226
|
expect.any(Object),
|
|
155
227
|
);
|
|
228
|
+
expect(mockPage.getCookies).toHaveBeenCalledTimes(2);
|
|
156
229
|
expect(mockPage.getCookies).toHaveBeenCalledWith({
|
|
157
230
|
url: 'https://i.weread.qq.com/shelf/sync?synckey=0&lectureSynckey=0',
|
|
158
231
|
});
|
|
232
|
+
expect(mockPage.getCookies).toHaveBeenCalledWith({ domain: 'weread.qq.com' });
|
|
159
233
|
expect(result).toEqual([
|
|
160
234
|
{
|
|
161
235
|
title: 'Deep Work',
|
|
@@ -169,15 +243,16 @@ describe('weread private API regression', () => {
|
|
|
169
243
|
it('falls back to structured shelf cache when the private API reports AUTH_REQUIRED', async () => {
|
|
170
244
|
const command = getRegistry().get('weread/shelf');
|
|
171
245
|
expect(command?.func).toBeTypeOf('function');
|
|
246
|
+
const warnSpy = vi.spyOn(log, 'warn').mockImplementation(() => {});
|
|
172
247
|
|
|
173
248
|
const mockPage = {
|
|
174
249
|
getCookies: vi.fn()
|
|
175
|
-
.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
.mockResolvedValueOnce([
|
|
179
|
-
|
|
180
|
-
]),
|
|
250
|
+
// fetchPrivateApi: URL lookup (i.weread.qq.com)
|
|
251
|
+
.mockResolvedValueOnce([{ name: 'wr_skey', value: 'skey123', domain: '.weread.qq.com' }])
|
|
252
|
+
// fetchPrivateApi: domain lookup (weread.qq.com)
|
|
253
|
+
.mockResolvedValueOnce([{ name: 'wr_skey', value: 'skey123', domain: '.weread.qq.com' }])
|
|
254
|
+
// loadWebShelfSnapshot: domain lookup for wr_vid
|
|
255
|
+
.mockResolvedValueOnce([{ name: 'wr_vid', value: 'vid-current', domain: '.weread.qq.com' }]),
|
|
181
256
|
goto: vi.fn().mockResolvedValue(undefined),
|
|
182
257
|
evaluate: vi.fn().mockImplementation(async (source: string) => {
|
|
183
258
|
expect(source).toContain('shelf:rawBooks:vid-current');
|
|
@@ -219,6 +294,9 @@ describe('weread private API regression', () => {
|
|
|
219
294
|
expect(mockPage.goto).toHaveBeenCalledWith('https://weread.qq.com/web/shelf');
|
|
220
295
|
expect(mockPage.getCookies).toHaveBeenCalledWith({ domain: 'weread.qq.com' });
|
|
221
296
|
expect(mockPage.evaluate).toHaveBeenCalledTimes(1);
|
|
297
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
298
|
+
'WeRead private API auth expired; showing cached shelf data from localStorage. Results may be stale, and detail commands may still require re-login.',
|
|
299
|
+
);
|
|
222
300
|
expect(result).toEqual([
|
|
223
301
|
{
|
|
224
302
|
title: '文明、现代化、价值投资与中国',
|
|
@@ -235,12 +313,12 @@ describe('weread private API regression', () => {
|
|
|
235
313
|
|
|
236
314
|
const mockPage = {
|
|
237
315
|
getCookies: vi.fn()
|
|
238
|
-
.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.mockResolvedValueOnce([
|
|
242
|
-
|
|
243
|
-
]),
|
|
316
|
+
// fetchPrivateApi: URL lookup (i.weread.qq.com)
|
|
317
|
+
.mockResolvedValueOnce([{ name: 'wr_skey', value: 'skey123', domain: '.weread.qq.com' }])
|
|
318
|
+
// fetchPrivateApi: domain lookup (weread.qq.com)
|
|
319
|
+
.mockResolvedValueOnce([{ name: 'wr_skey', value: 'skey123', domain: '.weread.qq.com' }])
|
|
320
|
+
// loadWebShelfSnapshot: domain lookup for wr_vid
|
|
321
|
+
.mockResolvedValueOnce([{ name: 'wr_vid', value: 'vid-current', domain: '.weread.qq.com' }]),
|
|
244
322
|
goto: vi.fn().mockResolvedValue(undefined),
|
|
245
323
|
evaluate: vi.fn().mockResolvedValue({
|
|
246
324
|
cacheFound: false,
|
|
@@ -270,12 +348,12 @@ describe('weread private API regression', () => {
|
|
|
270
348
|
|
|
271
349
|
const mockPage = {
|
|
272
350
|
getCookies: vi.fn()
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
.mockResolvedValueOnce([
|
|
277
|
-
|
|
278
|
-
]),
|
|
351
|
+
// fetchPrivateApi: URL lookup (i.weread.qq.com)
|
|
352
|
+
.mockResolvedValueOnce([{ name: 'wr_skey', value: 'skey123', domain: '.weread.qq.com' }])
|
|
353
|
+
// fetchPrivateApi: domain lookup (weread.qq.com)
|
|
354
|
+
.mockResolvedValueOnce([{ name: 'wr_skey', value: 'skey123', domain: '.weread.qq.com' }])
|
|
355
|
+
// loadWebShelfSnapshot: domain lookup for wr_vid
|
|
356
|
+
.mockResolvedValueOnce([{ name: 'wr_vid', value: 'vid-current', domain: '.weread.qq.com' }]),
|
|
279
357
|
goto: vi.fn().mockResolvedValue(undefined),
|
|
280
358
|
evaluate: vi.fn().mockResolvedValue({
|
|
281
359
|
cacheFound: true,
|
|
@@ -304,12 +382,12 @@ describe('weread private API regression', () => {
|
|
|
304
382
|
|
|
305
383
|
const mockPage = {
|
|
306
384
|
getCookies: vi.fn()
|
|
307
|
-
.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
.mockResolvedValueOnce([
|
|
311
|
-
|
|
312
|
-
]),
|
|
385
|
+
// fetchPrivateApi: URL lookup (i.weread.qq.com)
|
|
386
|
+
.mockResolvedValueOnce([{ name: 'wr_skey', value: 'skey123', domain: '.weread.qq.com' }])
|
|
387
|
+
// fetchPrivateApi: domain lookup (weread.qq.com)
|
|
388
|
+
.mockResolvedValueOnce([{ name: 'wr_skey', value: 'skey123', domain: '.weread.qq.com' }])
|
|
389
|
+
// loadWebShelfSnapshot: domain lookup for wr_vid
|
|
390
|
+
.mockResolvedValueOnce([{ name: 'wr_vid', value: 'vid-current', domain: '.weread.qq.com' }]),
|
|
313
391
|
goto: vi.fn().mockResolvedValue(undefined),
|
|
314
392
|
evaluate: vi.fn().mockResolvedValue({
|
|
315
393
|
cacheFound: true,
|