From bc97dfb9735880317df2e4a17a944ced3fa2ac55 Mon Sep 17 00:00:00 2001 From: nicocayo Date: Tue, 30 Jun 2026 13:15:30 +0000 Subject: [PATCH] Generated with Hive: Fix Twitter handle regex to support trailing slashes in source detection --- src/lib/__tests__/source-detection.test.ts | 44 +++++++++++++++++++++- src/lib/source-detection.ts | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/lib/__tests__/source-detection.test.ts b/src/lib/__tests__/source-detection.test.ts index 44e87a8..69ea5bc 100644 --- a/src/lib/__tests__/source-detection.test.ts +++ b/src/lib/__tests__/source-detection.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from "vitest" -import { detectSourceType, SOURCE_TYPES } from "../source-detection" +import { detectSourceType, extractNameFromSource, SOURCE_TYPES } from "../source-detection" // Mock fetch for RSS probe (checkIfRSS) - default to non-RSS vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ @@ -106,6 +106,48 @@ describe("detectSourceType", () => { }) }) + describe("Twitter Handle URLs", () => { + it("detects x.com/handle (no slash) as TWITTER_HANDLE", async () => { + expect(await detectSourceType("https://x.com/sama")).toBe(SOURCE_TYPES.TWITTER_HANDLE) + }) + + it("detects x.com/handle/ (trailing slash) as TWITTER_HANDLE", async () => { + expect(await detectSourceType("https://x.com/sama/")).toBe(SOURCE_TYPES.TWITTER_HANDLE) + }) + + it("detects twitter.com/handle (no slash) as TWITTER_HANDLE", async () => { + expect(await detectSourceType("https://twitter.com/sama")).toBe(SOURCE_TYPES.TWITTER_HANDLE) + }) + + it("detects twitter.com/handle/ (trailing slash) as TWITTER_HANDLE", async () => { + expect(await detectSourceType("https://twitter.com/sama/")).toBe(SOURCE_TYPES.TWITTER_HANDLE) + }) + + it("detects x.com/@handle (no slash) as TWITTER_HANDLE", async () => { + expect(await detectSourceType("https://x.com/@sama")).toBe(SOURCE_TYPES.TWITTER_HANDLE) + }) + + it("detects x.com/@handle/ (trailing slash) as TWITTER_HANDLE", async () => { + expect(await detectSourceType("https://x.com/@sama/")).toBe(SOURCE_TYPES.TWITTER_HANDLE) + }) + + it("detects x.com/handle/?ref=x (trailing slash + query) as TWITTER_HANDLE", async () => { + expect(await detectSourceType("https://x.com/sama/?ref=x")).toBe(SOURCE_TYPES.TWITTER_HANDLE) + }) + + it("detects x.com/handle/status/123 as TWEET (regression guard)", async () => { + expect(await detectSourceType("https://x.com/sama/status/123456")).toBe(SOURCE_TYPES.TWEET) + }) + + it("extractNameFromSource returns @sama for no-slash URL", () => { + expect(extractNameFromSource("https://x.com/sama", SOURCE_TYPES.TWITTER_HANDLE)).toBe("@sama") + }) + + it("extractNameFromSource returns @sama for trailing-slash URL", () => { + expect(extractNameFromSource("https://x.com/sama/", SOURCE_TYPES.TWITTER_HANDLE)).toBe("@sama") + }) + }) + describe("arXiv Paper URLs", () => { it("detects arxiv.org/abs/ as ARXIV_PAPER", async () => { expect(await detectSourceType("https://arxiv.org/abs/2301.00001")).toBe(SOURCE_TYPES.ARXIV_PAPER) diff --git a/src/lib/source-detection.ts b/src/lib/source-detection.ts index 4d66cf6..23971f1 100644 --- a/src/lib/source-detection.ts +++ b/src/lib/source-detection.ts @@ -16,7 +16,7 @@ export const SOURCE_TYPES = { export type SourceType = (typeof SOURCE_TYPES)[keyof typeof SOURCE_TYPES] const twitterHandlePattern = - /\b(?:twitter\.com|x\.com)\/(?:@)?([\w_]+)(?:$|\?[^/]*$)/ + /\b(?:twitter\.com|x\.com)\/(?:@)?([\w_]+)\/?(?:$|\?[^/]*$)/ const youtubeRegex = /(https?:\/\/)?(www\.)?youtube\.com\/watch\?v=([A-Za-z0-9_-]+)/ const youtubeLiveRegex =