package monkeyd import ( "strings" "testing" ) // chapterFixture mirrors the real page shape: words split between markup and // CSS :before rules, HTML entities,   spacer paragraphs and an ad script // inside the content container. const chapterFixture = `

TEN TRUYEN - 1

1

 

Nghe trưởng tử noi.

 

Một di.

` func TestParseWordClassesDecodesEscapes(t *testing.T) { words := ParseWordClasses([]byte(chapterFixture)) for class, want := range map[string]string{ "t-aaa": "vị", "j-bbb": "trồ", "z-ccc": "nàng", } { if got := words[class]; got != want { t.Errorf("words[%q] = %q, want %q", class, got, want) } } if len(words) != 4 { t.Errorf("got %d rules, want 4", len(words)) } } func TestParseChapterRestoresCSSWords(t *testing.T) { ch, err := ParseChapter([]byte(chapterFixture), ChapterRef{Label: "1", URL: "http://x/1.html"}) if err != nil { t.Fatalf("ParseChapter: %v", err) } want := []string{ "Nghe vị trưởng tử noi.", "Một trồ và nàng di.", } if len(ch.Paragraphs) != len(want) { t.Fatalf("got %d paragraphs %q, want %d", len(ch.Paragraphs), ch.Paragraphs, len(want)) } for i, w := range want { if ch.Paragraphs[i] != w { t.Errorf("paragraph %d = %q, want %q", i, ch.Paragraphs[i], w) } } } // The CSS-injected words are the difference between real text and text with // silent holes, so guard against a regression that drops them. func TestParseChapterWithoutCSSWouldLoseWords(t *testing.T) { withoutCSS := strings.Replace(chapterFixture, `.t-aaa:before { content: "v\1ecb "; }`, "", 1) ch, err := ParseChapter([]byte(withoutCSS), ChapterRef{Label: "1", URL: "http://x/1.html"}) if err != nil { t.Fatalf("ParseChapter: %v", err) } if strings.Contains(ch.Paragraphs[0], "vị") { t.Fatal("word appeared without its CSS rule; fixture no longer proves anything") } if want := "Nghe trưởng tử noi."; ch.Paragraphs[0] != want { t.Errorf("paragraph 0 = %q, want %q", ch.Paragraphs[0], want) } } func TestParseChapterDropsScriptsAndSpacers(t *testing.T) { ch, err := ParseChapter([]byte(chapterFixture), ChapterRef{Label: "1", URL: "http://x/1.html"}) if err != nil { t.Fatalf("ParseChapter: %v", err) } for _, p := range ch.Paragraphs { if strings.Contains(p, "ads()") { t.Errorf("script text leaked into paragraph %q", p) } if strings.TrimSpace(p) == "" { t.Error("empty spacer paragraph was kept") } if strings.Contains(p, " ") { t.Errorf("non-breaking space survived in %q", p) } } } // The leading "

1

" repeats the chapter label and would print twice. func TestParseChapterDropsRepeatedTitle(t *testing.T) { ch, err := ParseChapter([]byte(chapterFixture), ChapterRef{Label: "1", URL: "http://x/1.html"}) if err != nil { t.Fatalf("ParseChapter: %v", err) } if ch.Paragraphs[0] == "1" { t.Error("repeated chapter label was kept as a paragraph") } } // gatedChapterFixture mirrors how most chapters are served: a visible sponsor // block (div.actcl) stands in for the body, while the real text sits in a // sibling div.actac hidden with display:none and revealed by the site's // JavaScript. A source watermark and prev/next navigation bracket the prose. const gatedChapterFixture = `

Moi Quy doc gia CLICK vao lien ket

mo ung dung Shopee, sau do quay tro lai de doc!

CLICK

MonkeyD va doi ngu Editor xin chan thanh cam on!

Doan van dau tien co tri.

 

[Truyen duoc dang tai duy nhat tai MonkeyDD.com - https://monkeydd.com/n/10.html.]

Doan van cuoi cung.

` // The gated body is the one thing that must survive: it is hidden with // display:none, so any rule that drops hidden or "act*" containers silently // discards the entire chapter. func TestParseChapterKeepsGatedBody(t *testing.T) { ch, err := ParseChapter([]byte(gatedChapterFixture), ChapterRef{Label: "10", URL: "http://x/10.html"}) if err != nil { t.Fatalf("ParseChapter: %v", err) } want := []string{ "Doan van dau tien co vị tri.", "Doan van cuoi cung.", } if len(ch.Paragraphs) != len(want) { t.Fatalf("got %d paragraphs %q, want %d", len(ch.Paragraphs), ch.Paragraphs, len(want)) } for i, w := range want { if ch.Paragraphs[i] != w { t.Errorf("paragraph %d = %q, want %q", i, ch.Paragraphs[i], w) } } } // Everything the site injects around the prose must be gone. func TestParseChapterDropsInjectedBlocks(t *testing.T) { ch, err := ParseChapter([]byte(gatedChapterFixture), ChapterRef{Label: "10", URL: "http://x/10.html"}) if err != nil { t.Fatalf("ParseChapter: %v", err) } body := strings.Join(ch.Paragraphs, "\n") for _, junk := range []string{ "Shopee", // sponsor copy "CLICK", // sponsor call to action "cam on", // sponsor sign-off "MonkeyDD.com", // source watermark "Chương trước", // navigation "Chương sau", // navigation } { if strings.Contains(body, junk) { t.Errorf("injected text %q survived extraction in %q", junk, body) } } } func TestParseChapterMissingContainer(t *testing.T) { if _, err := ParseChapter([]byte(`

hi

`), ChapterRef{URL: "http://x/1.html"}); err == nil { t.Fatal("want an error when the content container is absent") } } func TestChapterHeading(t *testing.T) { for _, tc := range []struct{ label, want string }{ {"14", "Chương 14"}, {"Chương 12", "Chương 12"}, {"", "Chương"}, } { ch := &Chapter{Label: tc.label} if got := ch.Heading(); got != tc.want { t.Errorf("Heading(%q) = %q, want %q", tc.label, got, tc.want) } } }