import { describe, expect, it, vi } from 'vitest' vi.mock('electron/main', () => ({ app: { getPath: () => '/tmp/geo-rankly-deepseek-test', getVersion: () => '0.0.0-test', isPackaged: false, on: vi.fn(), whenReady: () => Promise.resolve(), }, BrowserWindow: vi.fn(), WebContentsView: vi.fn(), ipcMain: { handle: vi.fn(), on: vi.fn(), removeHandler: vi.fn(), }, Menu: { buildFromTemplate: vi.fn(() => ({ popup: vi.fn() })), setApplicationMenu: vi.fn(), }, Tray: vi.fn(), nativeTheme: { on: vi.fn(), shouldUseDarkColors: false, }, safeStorage: { decryptString: vi.fn(), encryptString: vi.fn(), isEncryptionAvailable: () => false, }, session: { fromPartition: vi.fn(() => ({ cookies: { get: vi.fn(), remove: vi.fn(), set: vi.fn(), }, webRequest: { onBeforeSendHeaders: vi.fn(), onHeadersReceived: vi.fn(), }, })), }, })) import { __deepseekTestUtils } from './deepseek' import { getMonitorAdapter } from './index' const { extractQuestionText, buildSourceItem, buildDeepSeekPayload, detectDeepSeekBusySignals, dedupeSourceItems, classifyDeepseekSources, mergeDeepSeekRawSourceLinks, resolveDeepSeekCitationPanelLinks, isObservationComplete, isDeepSeekToggleSelected, isDeepSeekWebpagesTriggerText, isDeepSeekReasoningSearchTriggerText, } = __deepseekTestUtils describe('deepseek adapter helpers', () => { it('extracts question text from monitoring payload', () => { expect(extractQuestionText({ question_text: 'DeepSeek 怎么看 GEO?' })).toBe( 'DeepSeek 怎么看 GEO?', ) }) it('dedupes sources by normalized url without hash fragments', () => { const items = dedupeSourceItems([ { url: 'https://example.com/a#top', title: 'A', normalized_url: 'https://example.com/a#top', }, { url: 'https://example.com/a#bottom', title: 'A later', normalized_url: 'https://example.com/a#bottom', }, ]) expect(items).toHaveLength(1) expect(items[0]?.normalized_url).toBe('https://example.com/a') expect(items[0]?.title).toBe('A') }) it('treats revealed search cards as citation sources and inline links as content citation candidates', () => { const classified = classifyDeepseekSources({ inlineLinks: [ { url: 'https://example.com/citation', title: 'Citation', normalized_url: 'https://example.com/citation', }, ], sourcePanelLinks: [], searchPanelLinks: [ { url: 'https://example.com/search', title: 'Search', normalized_url: 'https://example.com/search', }, ], }) expect(classified.citations).toHaveLength(1) expect(classified.inlineCitationCandidates).toHaveLength(1) expect(classified.searchResults).toHaveLength(1) expect(classified.citations[0]?.url).toBe('https://example.com/search') expect(classified.inlineCitationCandidates[0]?.url).toBe('https://example.com/citation') expect(classified.searchResults[0]?.url).toBe('https://example.com/search') }) it('uses revealed xx webpages panel links instead of stale snapshot links for citation sources', () => { const citationPanelLinks = resolveDeepSeekCitationPanelLinks( [ { url: 'https://stale.example/from-snapshot', title: 'Stale snapshot result', text: 'Wrong result from the page snapshot', siteName: 'Stale', kind: 'search', }, ], [ { url: 'https://right.example/from-xx-webpages', title: 'Right side panel result', text: 'Correct result from DeepSeek search results', siteName: 'Right', kind: 'search', }, ], ) const classified = classifyDeepseekSources({ inlineLinks: [ { url: 'https://inline.example/body-link', title: 'Inline body link', text: '[1]', siteName: 'Inline', kind: 'inline', }, ], sourcePanelLinks: [], searchPanelLinks: citationPanelLinks, }) expect(citationPanelLinks.map((item) => item.url)).toEqual([ 'https://right.example/from-xx-webpages', ]) expect(classified.citations.map((item) => item.url)).toEqual([ 'https://right.example/from-xx-webpages', ]) expect(classified.searchResults.map((item) => item.url)).toEqual([ 'https://right.example/from-xx-webpages', ]) expect(classified.inlineCitationCandidates.map((item) => item.url)).toEqual([ 'https://inline.example/body-link', ]) }) it('falls back to snapshot search links when the revealed xx webpages panel is unavailable', () => { const citationPanelLinks = resolveDeepSeekCitationPanelLinks( [ { url: 'https://snapshot.example/result', title: 'Snapshot result', text: 'Fallback result', siteName: 'Snapshot', kind: 'search', }, ], [], ) expect(citationPanelLinks.map((item) => item.url)).toEqual(['https://snapshot.example/result']) }) it('enriches numeric inline citations with source metadata from revealed search cards', () => { const classified = classifyDeepseekSources({ inlineLinks: [ { url: 'https://example.com/citation', title: null, text: '-1', siteName: null, kind: 'inline', }, ], sourcePanelLinks: [], searchPanelLinks: [ { url: 'https://example.com/citation', title: 'DeepSeek source article', text: 'Snippet', siteName: 'Example', kind: 'search', }, ], }) expect(classified.inlineCitationCandidates).toHaveLength(1) expect(classified.inlineCitationCandidates[0]?.title).toBe('DeepSeek source article') expect(classified.inlineCitationCandidates[0]?.site_name).toBe('Example') expect(classified.citations).toHaveLength(1) expect(classified.searchResults).toHaveLength(1) }) it('keeps reasoning search pages as citation sources separate from answer content citations', () => { const classified = classifyDeepseekSources({ inlineLinks: [ { url: 'https://example.com/body-citation', title: 'Answer body citation', text: '[15]', siteName: 'Body Site', kind: 'inline', }, ], sourcePanelLinks: [], searchPanelLinks: [ { url: 'https://example.com/reasoning-search', title: 'Reasoning searched page', text: '浏览页面', siteName: 'Search Site', kind: 'search', }, ], }) expect(classified.inlineCitationCandidates.map((item) => item.url)).toEqual([ 'https://example.com/body-citation', ]) expect(classified.citations.map((item) => item.url)).toEqual([ 'https://example.com/reasoning-search', ]) expect(classified.searchResults.map((item) => item.url)).toEqual([ 'https://example.com/reasoning-search', ]) }) it('retains all revealed DeepSeek search results instead of truncating after 32 items', () => { const searchPanelLinks = Array.from({ length: 78 }, (_, index) => ({ url: `https://example.com/search-${index + 1}`, title: `Search ${index + 1}`, text: `Snippet ${index + 1}`, siteName: 'Example', kind: 'search' as const, })) const classified = classifyDeepseekSources({ inlineLinks: [], sourcePanelLinks: [], searchPanelLinks, }) expect(classified.citations).toHaveLength(78) expect(classified.searchResults).toHaveLength(78) }) it('merges revealed sidebar search links with the snapshot search links by normalized url', () => { const merged = mergeDeepSeekRawSourceLinks( [ { url: 'https://example.com/report#top', title: null, text: 'Visible title', siteName: null, kind: 'search', }, ], [ { url: 'https://example.com/report#bottom', title: 'Merged title', text: 'Merged snippet', siteName: 'Example', kind: 'search', }, { url: 'https://example.com/extra', title: 'Extra result', text: 'Extra snippet', siteName: 'Example', kind: 'search', }, ], ) expect(merged).toHaveLength(2) expect(merged[0]).toEqual( expect.objectContaining({ url: 'https://example.com/report', title: 'Merged title', text: 'Visible title', siteName: 'Example', }), ) expect(merged[1]?.url).toBe('https://example.com/extra') }) it('normalizes deepseek raw source links into monitoring source items', () => { const item = buildSourceItem({ url: 'https://example.com/report#cite-1', title: null, text: 'DeepSeek source title', siteName: 'Example', kind: 'source', }) expect(item).toEqual( expect.objectContaining({ url: 'https://example.com/report', normalized_url: 'https://example.com/report', title: 'DeepSeek source title', site_name: 'Example', host: 'example.com', }), ) }) it('unwraps DeepSeek redirect URLs before storing source items', () => { const item = buildSourceItem({ url: 'https://chat.deepseek.com/api/redirect?url=https%3A%2F%2Fexample.com%2Fsource%23ref', title: 'Redirected source', text: null, siteName: null, kind: 'search', }) expect(item).toEqual( expect.objectContaining({ url: 'https://example.com/source', normalized_url: 'https://example.com/source', host: 'example.com', }), ) }) it('does not use numeric citation markers as source titles', () => { const item = buildSourceItem({ url: 'https://example.com/report#cite-1', title: null, text: '-1', siteName: null, kind: 'inline', }) expect(item?.title).toBeNull() expect(item?.site_name).toBe('example.com') }) it('treats a stable answer as complete even without citations', () => { expect( isObservationComplete({ answer: '这是最终答案', busy: false, loginRequired: false, challengeRequired: false, signature: 'sig-1', }), ).toBe(true) }) it('ignores source-logo loading placeholders when computing busy signals', () => { expect( detectDeepSeekBusySignals({ bodyText: '已阅读 10 个网页', descriptors: [ 'site_logo_loading site_logo_fallback', 'site_logo_loading site_logo_fallback', ], }), ).toEqual([]) }) it('recognizes deepseek toggle selected state from class names', () => { expect(isDeepSeekToggleSelected('ds-toggle-button ds-toggle-button--selected')).toBe(true) expect(isDeepSeekToggleSelected('ds-toggle-button')).toBe(false) }) it('only treats the bottom 网页 pill as the DeepSeek source trigger', () => { expect(isDeepSeekWebpagesTriggerText('57 个网页')).toBe(true) expect(isDeepSeekWebpagesTriggerText('已阅读 57 个网页')).toBe(true) expect(isDeepSeekWebpagesTriggerText('搜索到 39 个网页')).toBe(false) }) it('treats reasoning 搜索到 xx 个网页 as a separate citation-source fallback trigger', () => { expect(isDeepSeekReasoningSearchTriggerText('搜索到 30 个网页')).toBe(true) expect(isDeepSeekReasoningSearchTriggerText('57 个网页')).toBe(false) expect(isDeepSeekReasoningSearchTriggerText('已阅读 57 个网页')).toBe(false) }) it('normalizes DeepSeek model labels without mistaking search toggles for models', () => { const snapshot = { url: 'https://chat.deepseek.com/', title: 'DeepSeek', loginRequired: false, loginReason: null, challengeRequired: false, challengeReason: null, busy: false, busySignals: [], composerValue: null, sendDisabled: false, answer: 'Final answer', answerText: 'Final answer', reasoning: null, signature: 'sig-1', currentModelLabel: '深度思考 (R1)', providerRequestID: null, requestID: null, inlineLinks: [], sourcePanelLinks: [], searchPanelLinks: [], } const sources = { citations: [], inlineCitationCandidates: [], searchResults: [], } const r1Payload = buildDeepSeekPayload(snapshot, sources, 'Final answer') const searchPayload = buildDeepSeekPayload( { ...snapshot, currentModelLabel: '联网搜索', }, sources, 'Final answer', ) expect(r1Payload.provider_model).toBe('DeepSeek') expect(searchPayload.provider_model).toBeNull() }) it('builds json-safe payloads with null placeholders instead of undefined', () => { const payload = buildDeepSeekPayload( { url: 'https://chat.deepseek.com/', title: 'DeepSeek', loginRequired: false, loginReason: null, challengeRequired: false, challengeReason: null, busy: false, busySignals: [], composerValue: null, sendDisabled: false, answer: 'Final answer', answerText: 'Final answer', reasoning: null, signature: 'sig-1', currentModelLabel: null, providerRequestID: null, requestID: null, inlineLinks: [], sourcePanelLinks: [], searchPanelLinks: [], }, { citations: [], inlineCitationCandidates: [], searchResults: [], }, 'Final answer', ) expect(payload.provider_model).toBeNull() expect(payload.provider_request_id).toBeNull() expect(payload.request_id).toBeNull() expect((payload.raw_response_json as Record).provider_model).toBeNull() expect((payload.raw_response_json as Record).provider_request_id).toBeNull() expect((payload.raw_response_json as Record).request_id).toBeNull() expect((payload.raw_response_json as Record).signature).toBe('sig-1') expect( (payload.raw_response_json as Record).inline_citation_candidates, ).toEqual([]) }) }) describe('monitor adapter registry', () => { it('registers deepseek as a monitor adapter', () => { expect(getMonitorAdapter('deepseek')?.provider).toBe('deepseek') }) })