diff --git a/src/components/Inspector.test.tsx b/src/components/Inspector.test.tsx index 515b6674..330c0b24 100644 --- a/src/components/Inspector.test.tsx +++ b/src/components/Inspector.test.tsx @@ -129,10 +129,10 @@ describe('Inspector', () => { expect(pill).toHaveStyle({ borderRadius: '16px' }) }) - it('computes word count from content minus frontmatter', () => { + it('computes word count from content minus frontmatter and title', () => { render() - // "Test Project" (# stripped) + "This is a test note with some words to count." = 12 words - expect(screen.getByText('12')).toBeInTheDocument() + // Title "# Test Project" excluded; body: "This is a test note with some words to count." = 10 words + expect(screen.getByText('10')).toBeInTheDocument() }) it('shows "Add property" button as disabled placeholder', () => { diff --git a/src/utils/wikilinks.test.ts b/src/utils/wikilinks.test.ts index 33161dcf..7eac7bf0 100644 --- a/src/utils/wikilinks.test.ts +++ b/src/utils/wikilinks.test.ts @@ -222,10 +222,9 @@ describe('countWords', () => { '', 'Only these five words count.', ].join('\n') - // Body: "# My Note\n\nOnly these five words count." - // After stripping '#': " My Note\n\nOnly these five words count." - // Words: My, Note, Only, these, five, words, count. = 7 - expect(countWords(content)).toBe(7) + // Title "# My Note" is excluded from word count + // Body: "Only these five words count." = 5 words + expect(countWords(content)).toBe(5) }) it('ignores frontmatter values containing dashes', () => { @@ -254,15 +253,47 @@ describe('countWords', () => { '', 'After the rule.', ].join('\n') - // Body includes: Title, Before, the, rule., After, the, rule. - // The --- horizontal rule is stripped by the markdown char filter - expect(countWords(content)).toBe(7) + // Title "# Title" is excluded; body: Before, the, rule., After, the, rule. = 6 + expect(countWords(content)).toBe(6) }) it('returns 0 when content is only frontmatter with no trailing body', () => { const content = '---\ntitle: Hello\nstatus: Active\ntags: [a, b, c]\n---' expect(countWords(content)).toBe(0) }) + + it('excludes title heading from word count', () => { + const content = '---\ntitle: Hello World\n---\n\n# Hello World\n\nThree body words.' + expect(countWords(content)).toBe(3) + }) + + it('counts correctly when no title heading is present', () => { + const content = '---\ntitle: Test\n---\n\nFour words in body.' + expect(countWords(content)).toBe(4) + }) + + it('excludes wikilinks from word count', () => { + const content = '---\ntitle: Test\n---\n\n# Test\n\nSee [[My Note]] for details.' + // Title excluded, wikilink excluded: See, for, details. = 3 + expect(countWords(content)).toBe(3) + }) + + it('excludes multiple wikilinks from word count', () => { + const content = 'Check [[note-a]] and [[note-b]] here.' + // wikilinks excluded: Check, and, here. = 3 + expect(countWords(content)).toBe(3) + }) + + it('returns 0 for note with only title and no body', () => { + const content = '---\ntitle: Empty\n---\n\n# Empty\n' + expect(countWords(content)).toBe(0) + }) + + it('does not strip ## or ### subheadings as title', () => { + const content = '---\ntitle: Test\n---\n\n## Subheading\n\nBody text here.' + // ## is not a title heading; "Subheading" counts: Subheading, Body, text, here. = 4 + expect(countWords(content)).toBe(4) + }) }) describe('restoreWikilinksInBlocks', () => { diff --git a/src/utils/wikilinks.ts b/src/utils/wikilinks.ts index dbbc25fc..0bbfe242 100644 --- a/src/utils/wikilinks.ts +++ b/src/utils/wikilinks.ts @@ -106,7 +106,9 @@ export function splitFrontmatter(content: string): [string, string] { export function countWords(content: string): number { const [, body] = splitFrontmatter(content) - const text = body.replace(/[#*_[\]`>~\-|]/g, '').trim() + const withoutTitle = body.replace(/^\s*# [^\n]+\n?/, '') + const withoutWikilinks = withoutTitle.replace(/\[\[[^\]]*\]\]/g, '') + const text = withoutWikilinks.replace(/[#*_[\]`>~\-|]/g, '').trim() if (!text) return 0 return text.split(/\s+/).filter(Boolean).length }