import { describe, expect, it, vi } from 'vitest'; import { JSDOM } from 'jsdom'; import { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors'; import { getRegistry } from '@jackwener/opencli/registry'; import { __test__ } from './delete.js'; describe('twitter delete command', () => { it('targets the matched tweet article instead of the first More button on the page', async () => { const cmd = getRegistry().get('twitter/delete'); expect(cmd?.func).toBeTypeOf('function'); const page = { goto: vi.fn().mockResolvedValue(undefined), wait: vi.fn().mockResolvedValue(undefined), evaluate: vi.fn().mockResolvedValue({ ok: true, message: 'Tweet successfully deleted.' }), }; const result = await cmd.func(page, { url: 'https://x.com/alice/status/2040254679301718161?s=20', }); expect(page.goto).toHaveBeenCalledWith('https://x.com/alice/status/2040254679301718161?s=20'); expect(page.wait).toHaveBeenNthCalledWith(1, { selector: '[data-testid="primaryColumn"]' }); expect(page.wait).toHaveBeenNthCalledWith(2, 2); const script = page.evaluate.mock.calls[0][0]; // Article-scoping must come from the shared helper (not an inline // `pathname.includes('/status/' + tweetId)` substring match — see // codex-mini0 #1400 catch where `/status/123` would match // `/status/1234567`). The helper emits `__twHasLinkToTarget` and // `__twGetStatusIdFromHref` plus the canonical anchored regex. expect(script).toContain('__twHasLinkToTarget'); expect(script).toContain('__twGetStatusIdFromHref'); expect(script).toContain("document.querySelectorAll('article')"); expect(script).toContain("targetArticle.querySelectorAll('button,[role=\"button\"]')"); expect(script).toContain("closest('article') === targetArticle"); expect(script).toContain(".filter(belongsToTargetArticle)"); // Localized "More" caret: prefer the language-agnostic data-testid, fall // back to a multilingual aria-label match (zh-Hans 更多), and poll for the // late-hydrating target article before giving up. expect(script).toContain('[data-testid="caret"]'); expect(script).toContain('/^(More|更多)/'); expect(script).toContain('i < 20'); // Delete menu item is localized (删除) and must exclude the Lists item in // both languages (List / 列表). expect(script).toContain('删除'); expect(script).toContain('列表'); // Substring match must NOT appear — exact-id match only. expect(script).not.toContain("'/status/' + tweetId"); expect(result).toEqual([ { status: 'success', message: 'Tweet successfully deleted.', }, ]); }); it('passes through matched-tweet lookup failures', async () => { const cmd = getRegistry().get('twitter/delete'); expect(cmd?.func).toBeTypeOf('function'); const page = { goto: vi.fn().mockResolvedValue(undefined), wait: vi.fn().mockResolvedValue(undefined), evaluate: vi.fn().mockResolvedValue({ ok: false, message: 'Could not find the tweet card matching the requested URL.', }), }; const result = await cmd.func(page, { url: 'https://x.com/alice/status/2040254679301718161', }); expect(result).toEqual([ { status: 'failed', message: 'Could not find the tweet card matching the requested URL.', }, ]); expect(page.wait).toHaveBeenCalledTimes(1); }); it('unwraps Browser Bridge evaluate envelopes before checking delete success', async () => { const cmd = getRegistry().get('twitter/delete'); expect(cmd?.func).toBeTypeOf('function'); const page = { goto: vi.fn().mockResolvedValue(undefined), wait: vi.fn().mockResolvedValue(undefined), evaluate: vi.fn().mockResolvedValue({ session: 'twitter', data: { ok: true, message: 'Tweet successfully deleted.' }, }), }; const result = await cmd.func(page, { url: 'https://x.com/alice/status/2040254679301718161', }); expect(result).toEqual([ { status: 'success', message: 'Tweet successfully deleted.', }, ]); expect(page.wait).toHaveBeenNthCalledWith(2, 2); }); it('ignores stale non-target delete menu items that existed before opening the matched tweet menu', async () => { const dom = new JSDOM(`