like.test.js
1 import { describe, expect, it, vi } from 'vitest'; 2 import { getRegistry } from '@jackwener/opencli/registry'; 3 import './like.js'; 4 describe('zhihu like', () => { 5 it('rejects article pages where the like control is not uniquely anchored', async () => { 6 const cmd = getRegistry().get('zhihu/like'); 7 expect(cmd?.func).toBeTypeOf('function'); 8 const page = { 9 goto: vi.fn().mockResolvedValue(undefined), 10 evaluate: vi.fn().mockResolvedValue({ state: 'ambiguous_article_like' }), 11 }; 12 await expect(cmd.func(page, { target: 'article:9', execute: true })).rejects.toMatchObject({ 13 code: 'ACTION_NOT_AVAILABLE', 14 }); 15 }); 16 it('returns already_applied for an already-liked article target', async () => { 17 const cmd = getRegistry().get('zhihu/like'); 18 const page = { 19 goto: vi.fn().mockResolvedValue(undefined), 20 evaluate: vi.fn().mockResolvedValue({ state: 'already_liked' }), 21 }; 22 await expect(cmd.func(page, { target: 'article:9', execute: true })).resolves.toEqual([ 23 expect.objectContaining({ outcome: 'already_applied', target_type: 'article', target: 'article:9' }), 24 ]); 25 }); 26 it('anchors to the requested answer block before clicking like', async () => { 27 const cmd = getRegistry().get('zhihu/like'); 28 const page = { 29 goto: vi.fn().mockResolvedValue(undefined), 30 evaluate: vi.fn().mockResolvedValue({ state: 'liked' }), 31 }; 32 await expect(cmd.func(page, { target: 'answer:123:456', execute: true })).resolves.toEqual([ 33 expect.objectContaining({ outcome: 'applied', target_type: 'answer', target: 'answer:123:456' }), 34 ]); 35 expect(page.goto).toHaveBeenCalledWith('https://www.zhihu.com/question/123/answer/456'); 36 expect(page.evaluate).toHaveBeenCalledTimes(1); 37 expect(page.evaluate.mock.calls[0][0]).toContain('targetQuestionId'); 38 expect(page.evaluate.mock.calls[0][0]).toContain('"123"'); 39 expect(page.evaluate.mock.calls[0][0]).toContain('"456"'); 40 expect(page.evaluate.mock.calls[0][0]).toContain("node.getAttribute('data-answerid')"); 41 expect(page.evaluate.mock.calls[0][0]).toContain("node.getAttribute('data-zop-question-answer')"); 42 }); 43 it('rejects answer targets when the answer-level like control is not unique', async () => { 44 const cmd = getRegistry().get('zhihu/like'); 45 const page = { 46 goto: vi.fn().mockResolvedValue(undefined), 47 evaluate: vi.fn().mockResolvedValue({ state: 'ambiguous_answer_like' }), 48 }; 49 await expect(cmd.func(page, { target: 'answer:123:456', execute: true })).rejects.toMatchObject({ 50 code: 'ACTION_NOT_AVAILABLE', 51 }); 52 }); 53 it('maps missing answer blocks to TARGET_NOT_FOUND', async () => { 54 const cmd = getRegistry().get('zhihu/like'); 55 const page = { 56 goto: vi.fn().mockResolvedValue(undefined), 57 evaluate: vi.fn().mockResolvedValue({ state: 'wrong_answer' }), 58 }; 59 await expect(cmd.func(page, { target: 'answer:123:456', execute: true })).rejects.toMatchObject({ 60 code: 'TARGET_NOT_FOUND', 61 }); 62 expect(page.evaluate.mock.calls[0][0]).toContain("if (!block) return { state: 'wrong_answer' }"); 63 }); 64 });