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