fix: tighten news output and parsing (#39) (thanks @aavetis)

This commit is contained in:
Peter Steinberger
2026-01-12 05:37:44 +00:00
parent fb092f5cec
commit c96ce5238f
5 changed files with 146 additions and 25 deletions
+82 -4
View File
@@ -24,7 +24,7 @@ const makeResponse = (overrides: Partial<ResponseLike> = {}): ResponseLike => ({
});
// Helper to create GenericTimelineById response structure
const makeTimelineResponse = (items: any[]) => ({
const makeTimelineResponse = (items: unknown[]) => ({
data: {
timeline: {
timeline: {
@@ -50,6 +50,21 @@ const makeTimelineResponse = (items: any[]) => ({
},
});
const makeTimelineResponseWithInstruction = (type: string, entries: unknown[]) => ({
data: {
timeline: {
timeline: {
instructions: [
{
type,
entries,
},
],
},
},
},
});
describe('TwitterClient news API coverage', () => {
const originalFetch = global.fetch;
@@ -162,9 +177,7 @@ describe('TwitterClient news API coverage', () => {
});
it('returns error when no news items found', async () => {
const mockFetch = vi
.fn()
.mockResolvedValue(makeResponse({ json: async () => makeTimelineResponse([]) }));
const mockFetch = vi.fn().mockResolvedValue(makeResponse({ json: async () => makeTimelineResponse([]) }));
global.fetch = mockFetch as unknown as typeof fetch;
@@ -244,5 +257,70 @@ describe('TwitterClient news API coverage', () => {
expect(result.success).toBe(true);
expect(result.items?.length).toBe(2);
});
it('parses entries from non-TimelineAddEntries instructions', async () => {
const mockFetch = vi
.fn()
.mockResolvedValueOnce(
makeResponse({
json: async () =>
makeTimelineResponseWithInstruction('TimelineReplaceEntry', [
{
entryId: 'replace-entry-1',
content: {
itemContent: {
is_ai_trend: true,
name: 'Replacement headline',
},
},
},
]),
}),
)
.mockResolvedValue(makeResponse({ json: async () => makeTimelineResponse([]) }));
global.fetch = mockFetch as unknown as typeof fetch;
const client = new TwitterClient({ cookies: validCookies });
const result = await client.getNews(1);
expect(result.success).toBe(true);
expect(result.items?.[0].headline).toBe('Replacement headline');
});
it('assigns unique ids for module entries', async () => {
const mockFetch = vi
.fn()
.mockResolvedValueOnce(
makeResponse({
json: async () =>
makeTimelineResponseWithInstruction('TimelineAddEntries', [
{
entryId: 'module-entry',
content: {
items: [
{
item: { itemContent: { is_ai_trend: true, name: 'Headline A' } },
},
{
item: { itemContent: { is_ai_trend: true, name: 'Headline B' } },
},
],
},
},
]),
}),
)
.mockResolvedValue(makeResponse({ json: async () => makeTimelineResponse([]) }));
global.fetch = mockFetch as unknown as typeof fetch;
const client = new TwitterClient({ cookies: validCookies });
const result = await client.getNews(2);
expect(result.success).toBe(true);
const ids = result.items?.map((item) => item.id) ?? [];
expect(new Set(ids).size).toBe(ids.length);
});
});
});