/ clis / bilibili / comments.test.js
comments.test.js
 1  import { beforeEach, describe, expect, it, vi } from 'vitest';
 2  const { mockApiGet } = vi.hoisted(() => ({
 3      mockApiGet: vi.fn(),
 4  }));
 5  vi.mock('./utils.js', async (importOriginal) => ({
 6      ...(await importOriginal()),
 7      apiGet: mockApiGet,
 8  }));
 9  import { getRegistry } from '@jackwener/opencli/registry';
10  import './comments.js';
11  describe('bilibili comments', () => {
12      const command = getRegistry().get('bilibili/comments');
13      beforeEach(() => {
14          mockApiGet.mockReset();
15      });
16      it('resolves bvid to aid and fetches replies', async () => {
17          mockApiGet
18              .mockResolvedValueOnce({ data: { aid: 12345 } }) // view endpoint
19              .mockResolvedValueOnce({
20              data: {
21                  replies: [
22                      {
23                          member: { uname: 'Alice' },
24                          content: { message: 'Great video!' },
25                          like: 42,
26                          rcount: 3,
27                          ctime: 1700000000,
28                      },
29                  ],
30              },
31          });
32          const result = await command.func({}, { bvid: 'BV1WtAGzYEBm', limit: 5 });
33          expect(mockApiGet).toHaveBeenNthCalledWith(1, {}, '/x/web-interface/view', { params: { bvid: 'BV1WtAGzYEBm' } });
34          expect(mockApiGet).toHaveBeenNthCalledWith(2, {}, '/x/v2/reply/main', {
35              params: { oid: 12345, type: 1, mode: 3, ps: 5 },
36              signed: true,
37          });
38          expect(result).toEqual([
39              {
40                  rank: 1,
41                  author: 'Alice',
42                  text: 'Great video!',
43                  likes: 42,
44                  replies: 3,
45                  time: new Date(1700000000 * 1000).toISOString().slice(0, 16).replace('T', ' '),
46              },
47          ]);
48      });
49      it('throws when aid cannot be resolved', async () => {
50          mockApiGet.mockResolvedValueOnce({ data: {} }); // no aid
51          await expect(command.func({}, { bvid: 'BVinvalid123', limit: 5 })).rejects.toThrow('Cannot resolve aid for bvid: BVinvalid123');
52      });
53      it('returns empty array when replies is missing', async () => {
54          mockApiGet
55              .mockResolvedValueOnce({ data: { aid: 99 } })
56              .mockResolvedValueOnce({ data: {} }); // no replies key
57          const result = await command.func({}, { bvid: 'BV1xxx', limit: 5 });
58          expect(result).toEqual([]);
59      });
60      it('caps limit at 50', async () => {
61          mockApiGet
62              .mockResolvedValueOnce({ data: { aid: 1 } })
63              .mockResolvedValueOnce({ data: { replies: [] } });
64          await command.func({}, { bvid: 'BV1xxx', limit: 999 });
65          expect(mockApiGet).toHaveBeenNthCalledWith(2, {}, '/x/v2/reply/main', {
66              params: { oid: 1, type: 1, mode: 3, ps: 50 },
67              signed: true,
68          });
69      });
70      it('collapses newlines in comment text', async () => {
71          mockApiGet
72              .mockResolvedValueOnce({ data: { aid: 1 } })
73              .mockResolvedValueOnce({
74              data: {
75                  replies: [
76                      { member: { uname: 'Bob' }, content: { message: 'line1\nline2\nline3' }, like: 0, rcount: 0, ctime: 0 },
77                  ],
78              },
79          });
80          const result = (await command.func({}, { bvid: 'BV1xxx', limit: 5 }));
81          expect(result[0].text).toBe('line1 line2 line3');
82      });
83  });