Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clis/douban/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ function extractDoubanPublishYear(value) {
return match?.[0] || '';
}
function splitDoubanTitle(fullTitle) {
// Inline normalizeText so the toString-injected copy inside
// loadDoubanMovieSubject's page.evaluate runs without a closure reference
// to the module-level helper (which does not survive Function.toString()).
const normalizeText = (value) => String(value || '').replace(/\s+/g, ' ').trim();
const normalized = normalizeText(fullTitle);
Comment on lines +71 to 75
if (!normalized)
return { title: '', originalTitle: '' };
Expand Down
38 changes: 38 additions & 0 deletions clis/douban/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ function runMovieHotEvaluate(script, items) {
return vm.runInNewContext(script, { document, URL });
}

function runMovieSubjectEvaluate(script, elementMap) {
const document = {
querySelector(selector) {
return elementMap[selector] || null;
},
querySelectorAll() {
return [];
},
Comment on lines +86 to +88
};
return vm.runInNewContext(script, { document, URL });
}

async function runSearchEvaluate(script, rawItems, domItems) {
const document = {
querySelector(selector) {
Expand Down Expand Up @@ -321,6 +333,32 @@ ISBN: 9787544270871
await expect(loadDoubanMovieHot(page, 20)).rejects.toThrow('douban movie-hot returned no data');
});

it('loads a movie subject without ReferenceError when splitDoubanTitle is toString-injected (regression for #1851)', async () => {
const elementMap = {
'span[property="v:itemreviewed"]': createFakeNode('海贼王:黄金大冒险 One Piece Gold'),
'.year': createFakeNode('(2016)'),
'strong[property="v:average"]': createFakeNode('7.5'),
'span[property="v:votes"]': createFakeNode('1234'),
'#info': createFakeNode('制片国家/地区: 日本\n类型: 动画'),
'span[property="v:runtime"]': createFakeNode('120分钟'),
'span[property="v:summary"]': createFakeNode('summary text'),
};
const page = {
goto: vi.fn().mockResolvedValue(undefined),
wait: vi.fn().mockResolvedValue(undefined),
evaluate: vi.fn()
.mockResolvedValueOnce({ blocked: false, title: '海贼王 (豆瓣)', href: 'https://movie.douban.com/subject/37116446/' })
.mockImplementationOnce((script) => runMovieSubjectEvaluate(script, elementMap)),
};

const detail = await loadDoubanSubjectDetail(page, '37116446', 'movie');

expect(detail.id).toBe('37116446');
expect(detail.type).toBe('movie');
expect(detail.title).toBe('海贼王:黄金大冒险');
expect(detail.originalTitle).toBe('One Piece Gold');
});

it('loads book subject details from book.douban.com when type=book', async () => {
const page = {
goto: vi.fn().mockResolvedValue(undefined),
Expand Down
Loading