diff --git a/src/webfetch/content.ts b/src/webfetch/content.ts index d102379..2dc213a 100644 --- a/src/webfetch/content.ts +++ b/src/webfetch/content.ts @@ -29,6 +29,7 @@ const EXPLICIT_ARTICLE_SELECTORS = [ ".content-article", "#content .contents_style", ]; +const TITLE_SELECTORS = [".tit_post", ".entry-title", ".post-title", ".article-title", "h1"]; const ARTICLE_NOISE_SELECTOR = [ "script", "style", @@ -53,8 +54,6 @@ const ARTICLE_NOISE_SELECTOR = [ ".tagTrail", ".sidebar", ].join(", "); -const TITLE_SELECTOR = "h1, .tit_post, .entry-title, .post-title, .article-title"; - const ENTITIES: Readonly> = { amp: "&", apos: "'", @@ -159,11 +158,8 @@ function extractReadableArticle(html: string, url: string): ReadableArticle | un } const text = normalizePlainText(clonedNode.textContent ?? ""); if (text.length < MIN_EXPLICIT_ARTICLE_TEXT_LENGTH) continue; - const title = normalizePlainText( - dom.window.document.querySelector(TITLE_SELECTOR)?.textContent ?? dom.window.document.title, - ); explicitArticle = { - title, + title: selectPreferredTitle(dom.window.document, dom.window.document.title), content: clonedNode.innerHTML, hasHeading: /`; } +function titlePriorityFixtureHtml(): string { + return ` + + + 관리자 메뉴가 제목을 이기면 안 됨 + + + +
+

블로그 이름

+ 관리자 + 분류 전체보기 +
+ +
+

티스토리 본문을 읽어야 합니다

+
+
+

첫 번째 본문 문장은 짧은 티스토리 글에서도 반드시 남아야 합니다.

+

두 번째 본문 문장은 카테고리나 관련 글보다 우선되어야 합니다.

+
+
+
+ + + `; +} + function newlineFixtureHtml(): string { return ` @@ -280,6 +311,44 @@ describe("webfetch", () => { expect(text).not.toContain("tistoryTracker"); }); + it("#given Tistory title chrome #when fetching markdown #then prefers the article title over site chrome", async () => { + // given + const server = await createFixtureServer((_request, response) => { + response.writeHead(200, { "content-type": "text/html; charset=utf-8" }); + response.end(titlePriorityFixtureHtml()); + }); + + // when + const result = await executeWebfetch({ url: `${server.baseUrl}/tistory-title`, format: "markdown" }); + const text = textContent(result); + + // then + expect(text).toContain("# 티스토리 본문을 읽어야 합니다"); + expect(text).toContain("첫 번째 본문 문장은"); + expect(text).toContain("두 번째 본문 문장은"); + expect(text).not.toContain("블로그 이름"); + expect(text).not.toContain("관련 없는 사이드바 설명"); + }); + + it("#given Tistory title chrome #when fetching text #then prefers the article title over site chrome", async () => { + // given + const server = await createFixtureServer((_request, response) => { + response.writeHead(200, { "content-type": "text/html; charset=utf-8" }); + response.end(titlePriorityFixtureHtml()); + }); + + // when + const result = await executeWebfetch({ url: `${server.baseUrl}/tistory-title-text`, format: "text" }); + const text = textContent(result); + + // then + expect(text.startsWith("티스토리 본문을 읽어야 합니다")).toBe(true); + expect(text).toContain("첫 번째 본문 문장은"); + expect(text).toContain("두 번째 본문 문장은"); + expect(text).not.toContain("블로그 이름"); + expect(text).not.toContain("관련 없는 사이드바 설명"); + }); + it("#given html page #when fetching text #then returns readable text without tags", async () => { // given const server = await createFixtureServer((_request, response) => {