test: cover bookmarks
This commit is contained in:
@@ -2,8 +2,12 @@
|
||||
|
||||
## 0.4.1 — Unreleased
|
||||
|
||||
### Added
|
||||
- `bookmarks` command to list your bookmarked tweets.
|
||||
|
||||
### Changed
|
||||
- Cookie extraction now uses `@steipete/sweet-cookie` (drops `sqlite3` CLI + custom browser readers in `bird`).
|
||||
- Query ID updater now tracks the Bookmarks GraphQL operation.
|
||||
|
||||
## 0.4.0 — 2025-12-26
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ bird search "from:steipete" -n 5
|
||||
bird mentions -n 5
|
||||
bird mentions --user @steipete -n 5
|
||||
|
||||
# Bookmarks
|
||||
bird bookmarks -n 5
|
||||
|
||||
# Refresh GraphQL query IDs cache (no rebuild)
|
||||
bird query-ids --fresh
|
||||
```
|
||||
@@ -64,6 +67,7 @@ bird query-ids --fresh
|
||||
- `bird thread <tweet-id-or-url> [--json]` — show the full conversation thread.
|
||||
- `bird search "<query>" [-n count] [--json]` — search for tweets matching a query.
|
||||
- `bird mentions [-n count] [--user @handle] [--json]` — find tweets mentioning a user (defaults to the authenticated user).
|
||||
- `bird bookmarks [-n count] [--json]` — list your bookmarked tweets.
|
||||
- `bird whoami` — print which Twitter account your cookies belong to.
|
||||
- `bird check` — show which credentials are available and where they were sourced from.
|
||||
|
||||
@@ -117,7 +121,7 @@ Environment shortcuts:
|
||||
|
||||
## Output
|
||||
|
||||
- `--json` prints raw tweet objects for read/replies/thread/search/mentions.
|
||||
- `--json` prints raw tweet objects for read/replies/thread/search/mentions/bookmarks.
|
||||
- `read` returns full text for Notes and Articles when present.
|
||||
- Use `--plain` for stable, script-friendly output (no emoji, no color).
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ const TARGET_OPERATIONS = [
|
||||
'FavoriteTweet',
|
||||
'TweetDetail',
|
||||
'SearchTimeline',
|
||||
'Bookmarks',
|
||||
] as const;
|
||||
|
||||
type OperationName = (typeof TARGET_OPERATIONS)[number];
|
||||
|
||||
@@ -1072,6 +1072,78 @@ describe('TwitterClient', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('bookmarks', () => {
|
||||
let mockFetch: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetch = vi.fn();
|
||||
global.fetch = mockFetch as unknown as typeof fetch;
|
||||
});
|
||||
|
||||
it('fetches bookmarks and parses tweet results', async () => {
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => ({
|
||||
data: {
|
||||
bookmark_timeline_v2: {
|
||||
timeline: {
|
||||
instructions: [
|
||||
{
|
||||
entries: [
|
||||
{
|
||||
content: {
|
||||
itemContent: {
|
||||
tweet_results: {
|
||||
result: {
|
||||
rest_id: '1',
|
||||
legacy: {
|
||||
full_text: 'saved',
|
||||
created_at: '2024-01-01T00:00:00Z',
|
||||
reply_count: 0,
|
||||
retweet_count: 0,
|
||||
favorite_count: 0,
|
||||
conversation_id_str: '1',
|
||||
},
|
||||
core: {
|
||||
user_results: {
|
||||
result: {
|
||||
rest_id: 'u1',
|
||||
legacy: { screen_name: 'root', name: 'Root' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const client = new TwitterClient({ cookies: validCookies });
|
||||
const result = await client.getBookmarks(2);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.tweets?.[0].id).toBe('1');
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
const [url, options] = mockFetch.mock.calls[0];
|
||||
expect(options.method).toBe('GET');
|
||||
expect(String(url)).toContain('/Bookmarks?');
|
||||
const parsedVars = JSON.parse(new URL(url as string).searchParams.get('variables') as string);
|
||||
expect(parsedVars.count).toBe(2);
|
||||
const parsedFeatures = JSON.parse(new URL(url as string).searchParams.get('features') as string);
|
||||
expect(parsedFeatures.graphql_timeline_v2_bookmark_timeline).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('conversation helpers', () => {
|
||||
let mockFetch: ReturnType<typeof vi.fn>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user