@simplepg/repo
Version:
SimplePage repository
1,130 lines (842 loc) • 41.3 kB
JavaScript
import { searchPage } from '../src/search-util.js'
describe('searchPage', () => {
const createMarkdown = (frontmatter = '', content = '') => {
if (frontmatter) {
return `---\n${frontmatter}\n---\n\n${content}`
}
return content
}
describe('basic functionality', () => {
test('should return empty array for no matches', () => {
const markdown = createMarkdown(
'title: Test Page\ndescription: A test page',
'# Introduction\nThis is a test page about nothing.'
)
const results = searchPage('/test/', markdown, ['nonexistent'])
expect(results).toEqual([])
})
test('should return result for title match', () => {
const markdown = createMarkdown(
'title: React Tutorial\ndescription: Learn React',
`# Introduction
This is a comprehensive guide to React development. React is a powerful JavaScript library for building user interfaces.
## What You'll Learn
- Component-based architecture
- State management
- Lifecycle methods
- Hooks and functional components
## Getting Started
To begin with React, you'll need to install Node.js and create a new project.`
)
const results = searchPage('/react/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
// Find the highest priority result (should be title match)
const bestResult = results.reduce((max, r) => r.priority > max.priority ? r : max, results[0])
expect(bestResult).toMatchObject({
path: '/react/',
title: 'React Tutorial',
description: 'Learn React',
heading: '',
match: expect.stringContaining('React'),
priority: expect.any(Number)
})
expect(bestResult.priority).toBeGreaterThan(100) // Should be title match priority
})
test('should return result for description match', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: Learn JavaScript programming',
`# Introduction
This guide covers the fundamentals of web development and modern JavaScript programming techniques.
## Core Concepts
- HTML structure and semantics
- CSS styling and layout
- JavaScript fundamentals
- DOM manipulation
## Advanced Topics
- ES6+ features
- Asynchronous programming
- Module systems
- Build tools and bundlers`
)
const results = searchPage('/js/', markdown, ['javascript'])
expect(results).toHaveLength(3) // Introduction, Core Concepts, and title/description match
// Find the title/description match (highest priority)
const titleMatch = results.find(r => r.heading === '' && r.match === 'Learn JavaScript programming')
expect(titleMatch).toMatchObject({
path: '/js/',
title: 'Web Development',
description: 'Learn JavaScript programming',
heading: '',
match: 'Learn JavaScript programming',
priority: expect.any(Number)
})
})
test('should return result for heading match', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: General web dev guide',
`# Introduction
Welcome to our comprehensive web development guide.
## React Hooks
Advanced state management and lifecycle handling.
## Vue Components
Component-based architecture with Vue.js.
## Angular Services
Dependency injection and service patterns.
## Conclusion
These frameworks each have their strengths and use cases.`
)
const results = searchPage('/web/', markdown, ['react'])
expect(results).toHaveLength(1)
expect(results[0]).toMatchObject({
path: '/web/',
title: 'Web Development',
description: 'General web dev guide',
heading: 'React Hooks',
match: '', // Should be empty since no body content contains "react"
priority: expect.any(Number)
})
})
test('should return result for content match', () => {
const markdown = createMarkdown(
'title: General Guide\ndescription: A general guide',
`# Introduction
Welcome to our comprehensive programming guide.
## Data Science
Statistical analysis and machine learning techniques.
This section covers Python programming and data science methodologies. You'll learn about NumPy, Pandas, and scikit-learn for data manipulation and analysis.
## Machine Learning
Advanced algorithms and model training approaches.`
)
const results = searchPage('/guide/', markdown, ['python'])
expect(results).toHaveLength(1)
expect(results[0]).toMatchObject({
path: '/guide/',
title: 'General Guide',
description: 'A general guide',
heading: 'Data Science', // Should be the closest heading before the match
match: expect.stringContaining('Python'),
priority: expect.any(Number)
})
})
test('should handle both header and body matches', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: Learn web technologies',
`# Introduction
Modern web development involves many different technologies and frameworks.
## React Hooks
React hooks revolutionized how we write functional components. This section covers useState, useEffect, and custom hooks.
This is about React hooks and state management in modern applications. You'll learn how to manage component state effectively using the hooks API.
## Vue Composition API
Similar patterns in Vue.js with the Composition API.`
)
const results = searchPage('/web/', markdown, ['react'])
expect(results).toHaveLength(1)
expect(results[0]).toMatchObject({
path: '/web/',
title: 'Web Development',
description: 'Learn web technologies',
heading: 'React Hooks', // Should return the matching heading
match: expect.stringContaining('React'), // Should also return body match
priority: expect.any(Number)
})
})
})
describe('priority scoring', () => {
test('should prioritize title matches over description', () => {
const markdown = createMarkdown(
'title: React Development\ndescription: Learn React programming',
`# Introduction
This comprehensive guide covers modern web development practices and React frameworks.
## Getting Started
To begin your journey in web development, you'll need to understand the fundamentals of HTML, CSS, and JavaScript.
## Advanced Topics
We'll explore advanced concepts like React state management, component architecture, and performance optimization.`
)
const results = searchPage('/test/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
// Find the title match (highest priority)
const titleMatch = results.find(r => r.heading === '' && r.match === 'React Development')
expect(titleMatch).toMatchObject({
path: '/test/',
title: 'React Development',
description: 'Learn React programming',
heading: '',
match: 'React Development',
priority: expect.any(Number)
})
})
test('should prioritize description over headings', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: Learn React programming',
`# Introduction
This guide covers various web development frameworks and their use cases.
## React Hooks
Advanced state management and lifecycle handling in React applications.
## Vue Components
Component-based architecture patterns with Vue.js framework.
## Angular Services
Dependency injection and service-oriented architecture patterns.`
)
const results = searchPage('/test/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
// Find the highest priority result (should be description match)
const bestResult = results.reduce((max, r) => r.priority > max.priority ? r : max, results[0])
expect(bestResult).toMatchObject({
path: '/test/',
title: 'Web Development',
description: 'Learn React programming',
heading: '', // Description matches now have empty heading
match: 'Learn React programming',
priority: expect.any(Number)
})
expect(bestResult.priority).toBeGreaterThan(30) // Should be heading match priority (description match gets combined with heading match)
})
test('should prioritize headings over content', () => {
const markdown = createMarkdown(
'title: General Guide\ndescription: A general guide',
`# Introduction
This is a comprehensive guide covering various programming topics and technologies.
## React Hooks
Modern state management and lifecycle handling in web applications.
This section is about Python programming and data science methodologies. You'll learn about machine learning algorithms and statistical analysis techniques.
## Conclusion
Each technology has its own strengths and use cases.`
)
const results = searchPage('/test/', markdown, ['react'])
expect(results).toHaveLength(1)
expect(results[0]).toMatchObject({
path: '/test/',
title: 'General Guide',
description: 'A general guide',
heading: 'React Hooks',
match: '', // Should be empty since no body content contains "react"
priority: expect.any(Number)
})
})
test('should give exact matches higher scores', () => {
const markdown1 = createMarkdown(
'title: React\ndescription: Learn React',
'# Introduction'
)
const markdown2 = createMarkdown(
'title: React Development\ndescription: Learn React programming',
'# Introduction'
)
const results1 = searchPage('/test1/', markdown1, ['react'])
const results2 = searchPage('/test2/', markdown2, ['react'])
// Find the highest priority result from each
const result1 = results1.reduce((max, r) => r.priority > max.priority ? r : max, results1[0])
const result2 = results2.reduce((max, r) => r.priority > max.priority ? r : max, results2[0])
expect(result1.priority).toBeGreaterThan(result2.priority)
})
})
describe('multiple keywords', () => {
test('should match multiple keywords', () => {
const markdown = createMarkdown(
'title: React Hooks Tutorial\ndescription: Learn React hooks',
`# Introduction
Welcome to our comprehensive React Hooks tutorial. This guide will teach you everything you need to know about modern React development.
## React Hooks
React hooks revolutionized functional components by providing state and lifecycle features.
## State Management
Understanding how to manage component state effectively using hooks.
## useEffect Hook
Handling side effects and lifecycle events in functional components.
## Custom Hooks
Creating reusable logic with custom hook patterns.`
)
const results = searchPage('/tutorial/', markdown, ['react', 'hooks'])
expect(results.length).toBeGreaterThan(0)
// Find the highest priority result (should be title/description match)
const bestResult = results.reduce((max, r) => r.priority > max.priority ? r : max, results[0])
expect(bestResult).toMatchObject({
path: '/tutorial/',
title: 'React Hooks Tutorial',
description: 'Learn React hooks',
heading: '', // Title matches now have empty heading
match: 'React Hooks Tutorial',
priority: expect.any(Number)
})
// Should also have a React Hooks heading match
const hooksResult = results.find(r => r.heading === 'React Hooks')
expect(hooksResult).toBeDefined()
})
test('should prioritize matches with more keywords', () => {
const markdown = createMarkdown(
'title: React Development\ndescription: Learn programming',
`# Introduction
This guide covers various aspects of modern web development and programming.
## React Hooks Tutorial
Comprehensive guide to React hooks and state management patterns.
## JavaScript Basics
Fundamental concepts of JavaScript programming language.
## React Components Guide
Building reusable and maintainable React components.
## Advanced Patterns
Exploring advanced React patterns and best practices.`
)
const results = searchPage('/test/', markdown, ['react', 'hooks'])
expect(results.length).toBeGreaterThan(0)
// Find the result with the highest priority (should be the one with both keywords)
const bestResult = results.reduce((max, r) => r.priority > max.priority ? r : max, results[0])
expect(bestResult).toMatchObject({
path: '/test/',
title: 'React Development',
description: 'Learn programming',
heading: '', // Title matches now have empty heading
match: 'React Development',
priority: expect.any(Number)
})
})
test('should calculate proximity bonus for close keywords', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: A general guide',
`# Introduction
Modern web development has evolved significantly over the years. This comprehensive guide covers the latest trends and technologies.
This is about React hooks and state management in modern web development. You'll learn how to build scalable applications using these powerful patterns and techniques.
## Advanced Topics
We'll explore advanced concepts like performance optimization and testing strategies.`
)
const results = searchPage('/test/', markdown, ['react', 'hooks'])
expect(results.length).toBeGreaterThan(0)
// Find the result with the highest priority
const bestResult = results.reduce((max, r) => r.priority > max.priority ? r : max, results[0])
expect(bestResult.priority).toBeGreaterThan(20) // Should have proximity bonus
})
})
describe('markdown cleaning', () => {
test('should ignore markdown syntax in search', () => {
const markdown = createMarkdown(
'title: Test\ndescription: Test',
`# Introduction
This is a comprehensive guide to **markdown formatting** and *text styling*.
## Formatting Examples
Here are some examples of [useful links](http://example.com) and \`inline code\` snippets.
### Advanced Features
- **Bold** text for emphasis
- *Italic text* for subtle emphasis
- [External links](https://example.com) for references
- \`code blocks\` for technical content`
)
const results = searchPage('/test/', markdown, ['bold', 'italic', 'links', 'code'])
expect(results.length).toBeGreaterThan(0)
// Find a result that contains the cleaned text
const resultWithBold = results.find(r => r.match.includes('Bold text'))
expect(resultWithBold).toBeDefined()
})
test('should handle code blocks', () => {
const markdown = createMarkdown(
'title: Test\ndescription: Test',
`# Introduction
Welcome to our programming guide. This section covers various coding examples and best practices.
## JavaScript Examples
Here's a simple example of JavaScript code:
\`\`\`javascript
const greeting = 'Hello, World!';
function sayHello(name) {
return \`Hello, \${name}!\`;
}
\`\`\`
This is regular text that should be searchable and match our keywords effectively.
## More Examples
Additional code samples and explanations follow in the next section.`
)
const results = searchPage('/test/', markdown, ['regular'])
expect(results.length).toBeGreaterThan(0)
const resultWithRegular = results.find(r => r.match.includes('regular'))
expect(resultWithRegular).toBeDefined()
// Test that code block content is also searchable
const codeResults = searchPage('/test/', markdown, ['javascript'])
expect(codeResults.length).toBeGreaterThan(0)
const resultWithJS = codeResults.find(r => r.match.includes('JavaScript'))
expect(resultWithJS).toBeDefined()
})
test('should handle images and links', () => {
const markdown = createMarkdown(
'title: Test\ndescription: Test',
'# Introduction\n\n[Link text](http://example.com)\nThis is content.'
)
const results = searchPage('/test/', markdown, ['content'])
expect(results.length).toBeGreaterThan(0)
const resultWithContent = results.find(r => r.match.includes('content'))
expect(resultWithContent).toBeDefined()
})
})
describe('edge cases', () => {
test('should handle empty keywords', () => {
const markdown = createMarkdown(
'title: Test\ndescription: Test',
'# Introduction\nThis is content.'
)
const results = searchPage('/test/', markdown, [])
expect(results).toEqual([])
})
test('should handle empty markdown', () => {
const results = searchPage('/test/', '', ['test'])
expect(results).toEqual([])
})
test('should handle markdown without frontmatter', () => {
const markdown = '# Introduction\nThis is about React development.'
const results = searchPage('/test/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
expect(results[0].title).toBe('')
expect(results[0].description).toBe('')
})
test('should handle case insensitive search', () => {
const markdown = createMarkdown(
'title: React Development\ndescription: Learn React',
'# Introduction\nThis is about REACT programming.'
)
const results = searchPage('/test/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
// Find the highest priority result
const bestResult = results.reduce((max, r) => r.priority > max.priority ? r : max, results[0])
expect(bestResult).toMatchObject({
path: '/test/',
title: 'React Development',
description: 'Learn React',
heading: '',
match: 'React Development',
priority: expect.any(Number)
})
})
test('should handle special characters in keywords', () => {
const markdown = createMarkdown(
'title: C++ Programming\ndescription: Learn C++',
'# Introduction\nThis is about C++ development.'
)
const results = searchPage('/test/', markdown, ['c++'])
expect(results.length).toBeGreaterThan(0)
// Find the highest priority result
const bestResult = results.reduce((max, r) => r.priority > max.priority ? r : max, results[0])
expect(bestResult).toMatchObject({
path: '/test/',
title: 'C++ Programming',
description: 'Learn C++',
heading: '',
match: expect.stringContaining('C++'),
priority: expect.any(Number)
})
})
})
describe('text snippet extraction', () => {
test('should extract relevant text snippet', () => {
const longText = 'This is a very long piece of text that contains the keyword React multiple times. ' +
'React is a popular JavaScript library for building user interfaces. ' +
'Many developers use React to create modern web applications. ' +
'The React ecosystem includes many tools and libraries.'
const markdown = createMarkdown(
'title: Test\ndescription: Test',
`# Introduction\n${longText}`
)
const results = searchPage('/test/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
const resultWithReact = results.find(r => r.match.includes('React'))
expect(resultWithReact).toBeDefined()
expect(resultWithReact.match.length).toBeLessThanOrEqual(200) // Should be truncated
})
test('should prefer title/description over text snippet', () => {
const markdown = createMarkdown(
'title: React Tutorial\ndescription: Learn React programming',
'# Introduction\nThis is a very long piece of text that contains the keyword React multiple times.'
)
const results = searchPage('/test/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
// Find the highest priority result (should be title/description match)
const bestResult = results.reduce((max, r) => r.priority > max.priority ? r : max, results[0])
expect(bestResult).toMatchObject({
path: '/test/',
title: 'React Tutorial',
description: 'Learn React programming',
heading: '',
match: expect.stringContaining('React'),
priority: expect.any(Number)
})
})
})
describe('heading extraction', () => {
test('should extract all heading levels', () => {
const markdown = createMarkdown(
'title: Test\ndescription: Test',
'# H1 Heading\n## H2 Heading\n### H3 Heading\n#### H4 Heading\n\nThis is some content about headings.'
)
const results = searchPage('/test/', markdown, ['heading'])
expect(results.length).toBeGreaterThan(0)
// Should find matches in headings
const headingMatches = results.filter(r => r.heading && r.heading.includes('Heading'))
expect(headingMatches.length).toBeGreaterThan(0)
})
test('should handle headings with special characters', () => {
const markdown = createMarkdown(
'title: Test\ndescription: Test',
'# React & Vue\n## JavaScript (ES6+)\n### Node.js & Express\n\nThis is about React and Vue development.'
)
const results = searchPage('/test/', markdown, ['react', 'vue'])
expect(results.length).toBeGreaterThan(0)
// Check if any result has the React & Vue heading
const resultWithReactVue = results.find(r => r.heading === 'React & Vue')
// If not found, check if we have a result with the Node.js heading (which contains the body match)
const resultWithNode = results.find(r => r.heading === 'Node.js & Express')
expect(resultWithReactVue || resultWithNode).toBeDefined()
})
test('should clean markdown syntax from headings', () => {
const markdown = createMarkdown(
'title: Test\ndescription: Test',
`# Introduction
## [React Tutorial](/tutorials/react)
This is about React development.
### **Bold Heading** with *italic text*
This section covers React hooks.
## [Vue.js Guide](https://vuejs.org) - *Learn Vue*
Advanced Vue concepts and patterns.
### \`Code Heading\` with [links](/links)
This is about JavaScript programming.`
)
const results = searchPage('/test/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
const resultWithReactTutorial = results.find(r => r.heading === 'React Tutorial')
expect(resultWithReactTutorial).toBeDefined()
})
test('should clean various markdown syntax from headings', () => {
const markdown = createMarkdown(
'title: Test\ndescription: Test',
`# Introduction
## **Bold** and *Italic* Heading
This is about web development.
### [Link Text](/path) with **bold** and *italic*
This section covers React development.
## \`Code\` in Heading with [External Link](https://example.com)
Advanced programming concepts.
### Heading with [Reference Link][ref] and **bold text**
[ref]: /reference
This is about JavaScript programming.`
)
const results = searchPage('/test/', markdown, ['heading'])
expect(results.length).toBeGreaterThan(0)
const resultWithBoldItalic = results.find(r => r.heading === 'Bold and Italic Heading')
expect(resultWithBoldItalic).toBeDefined()
})
})
describe('single page multiple matches', () => {
test('searchPage should return multiple results for matches under different headers', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: Learn web technologies',
`# Introduction
This is a comprehensive guide to modern web development.
## React Hooks
React hooks revolutionized functional components. This section covers useState, useEffect, and custom hooks.
Advanced state management and lifecycle handling in React applications.
## Vue Components
Vue.js provides a different approach to component-based architecture. This section covers Vue components and composition API.
Component patterns and best practices for Vue development.
## Angular Services
Angular uses a service-oriented architecture with dependency injection. This section covers Angular services and providers.
Advanced patterns for building scalable Angular applications.`
)
// Test with keyword that appears under multiple different headers
const results = searchPage('/web/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
const reactResult = results.find(r => r.heading === 'React Hooks')
expect(reactResult).toMatchObject({
path: '/web/',
title: 'Web Development',
description: 'Learn web technologies',
heading: 'React Hooks',
match: expect.stringContaining('React'),
priority: expect.any(Number)
})
})
test('searchPage should return multiple results for matches under different headers', () => {
const markdown = createMarkdown(
'title: React Guide\ndescription: Learn React development',
`# Introduction
This is a comprehensive React development guide.
## React Hooks
React hooks are essential for modern React development. This section covers various React hooks including useState, useEffect, useCallback, and useMemo.
React hooks provide a way to use state and other React features in functional components. You'll learn about custom React hooks and how to build reusable logic.
Advanced React hooks patterns and best practices for state management in React applications.`
)
// Test with keyword that appears multiple times under different headers
const results = searchPage('/react/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
// Should have results for both Introduction and React Hooks sections
const headings = results.map(r => r.heading).sort()
expect(headings).toContain('Introduction')
expect(headings).toContain('React Hooks')
})
test('searchPage should return multiple results for matches under different headers with different keywords', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: Learn web technologies',
`# Introduction
This is a comprehensive guide to modern web development.
## React Hooks
React hooks revolutionized functional components. This section covers useState, useEffect, and custom hooks.
Advanced state management and lifecycle handling in React applications.
## Vue Components
Vue.js provides a different approach to component-based architecture. This section covers Vue components and composition API.
Component patterns and best practices for Vue development.
## Angular Services
Angular uses a service-oriented architecture with dependency injection. This section covers Angular services and providers.
Advanced patterns for building scalable Angular applications.`
)
// Test with keywords that appear under different headers
const results = searchPage('/web/', markdown, ['react', 'vue'])
expect(results.length).toBeGreaterThan(0)
const headings = results.map(r => r.heading).sort()
expect(headings).toContain('React Hooks')
expect(headings).toContain('Vue Components')
})
test('searchPage should return multiple results for heading matches', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: Learn web technologies',
`# Introduction
This is a comprehensive guide to modern web development.
## React Hooks
This section covers various React hooks and state management.
## Vue Components
This section covers Vue.js component patterns and best practices.
## Angular Services
This section covers Angular service patterns and dependency injection.`
)
// Test with keywords that match headings directly
const results = searchPage('/web/', markdown, ['react', 'vue', 'angular'])
expect(results.length).toBeGreaterThan(0)
const headings = results.map(r => r.heading).sort()
expect(headings).toContain('React Hooks')
expect(headings).toContain('Vue Components')
expect(headings).toContain('Angular Services')
})
test('searchPage should return multiple results for matches under same header', () => {
const markdown = createMarkdown(
'title: React Guide\ndescription: Learn React development',
`# Introduction
This is a comprehensive React development guide.
## React Hooks
React hooks are essential for modern React development. This section covers various React hooks including useState, useEffect, useCallback, and useMemo.
React hooks provide a way to use state and other React features in functional components. You'll learn about custom React hooks and how to build reusable logic.
Advanced React hooks patterns and best practices for state management in React applications.`
)
// Test with keyword that appears multiple times under different headers
const results = searchPage('/react/', markdown, ['react'])
expect(results.length).toBeGreaterThan(0)
const headings = results.map(r => r.heading).sort()
expect(headings).toContain('Introduction')
expect(headings).toContain('React Hooks')
})
test('searchPage should return multiple results for all heading matches', () => {
const markdown = createMarkdown(
'title: Web Development\ndescription: Learn web technologies',
`# Introduction
This is a comprehensive guide to modern web development.
## React Hooks
This section covers various React hooks and state management.
## Vue Components
This section covers Vue.js component patterns and best practices.
## Angular Services
This section covers Angular service patterns and dependency injection.`
)
// Test with keywords that match headings directly
const results = searchPage('/web/', markdown, ['react', 'vue', 'angular'])
expect(results.length).toBeGreaterThan(0)
const headings = results.map(r => r.heading).sort()
expect(headings).toContain('React Hooks')
expect(headings).toContain('Vue Components')
expect(headings).toContain('Angular Services')
})
})
describe('multi-page score comparison', () => {
test('should prioritize pages with higher scores', () => {
// Page 1: Title match (highest priority)
const markdown1 = createMarkdown(
'title: React Tutorial\ndescription: Learn programming',
`# Introduction
This is a basic guide about React web development.`
)
const results1 = searchPage('/page1/', markdown1, ['react'])
// Page 2: Description match (medium priority)
const markdown2 = createMarkdown(
'title: Web Development\ndescription: Learn React programming',
`# Introduction
This is a comprehensive guide about React web development.`
)
const results2 = searchPage('/page2/', markdown2, ['react'])
// Page 3: Heading match (lower priority)
const markdown3 = createMarkdown(
'title: Programming Guide\ndescription: Learn coding',
`# Introduction
## React Hooks
This section covers React hooks and state management in React applications.`
)
const results3 = searchPage('/page3/', markdown3, ['react'])
// Page 4: Body content match (lowest priority)
const markdown4 = createMarkdown(
'title: General Guide\ndescription: Learn programming',
`# Introduction
This is about React development and modern web frameworks.`
)
const results4 = searchPage('/page4/', markdown4, ['react'])
// All should match
expect(results1.length).toBeGreaterThan(0)
expect(results2.length).toBeGreaterThan(0)
expect(results3.length).toBeGreaterThan(0)
expect(results4.length).toBeGreaterThan(0)
// Find highest priority result from each page
const result1 = results1.reduce((max, r) => r.priority > max.priority ? r : max, results1[0])
const result2 = results2.reduce((max, r) => r.priority > max.priority ? r : max, results2[0])
const result3 = results3.reduce((max, r) => r.priority > max.priority ? r : max, results3[0])
const result4 = results4.reduce((max, r) => r.priority > max.priority ? r : max, results4[0])
// Score comparison: title > description > heading > body
expect(result1.priority).toBeGreaterThan(result2.priority)
expect(result2.priority).toBeGreaterThan(result3.priority)
expect(result3.priority).toBeGreaterThan(result4.priority)
// Verify match content is returned for all
expect(result1.match).toContain('React')
expect(result2.match).toContain('React')
expect(result3.match).toContain('React')
expect(result4.match).toContain('React')
})
test('should prioritize pages with multiple keyword matches', () => {
// Page 1: Only "react" keyword match
const markdown1 = createMarkdown(
'title: Web Guide\ndescription: Learn programming',
`# Introduction
This is about React development and web frameworks.`
)
const results1 = searchPage('/page1/', markdown1, ['react', 'hooks'])
// Page 2: Both "react" and "hooks" keyword matches
const markdown2 = createMarkdown(
'title: Web Development\ndescription: Learn programming',
`# Introduction
This is about React hooks and state management. You'll learn about React components and custom hooks.`
)
const results2 = searchPage('/page2/', markdown2, ['react', 'hooks'])
// Both should match
expect(results1.length).toBeGreaterThan(0)
expect(results2.length).toBeGreaterThan(0)
// Find highest priority result from each page
const result1 = results1.reduce((max, r) => r.priority > max.priority ? r : max, results1[0])
const result2 = results2.reduce((max, r) => r.priority > max.priority ? r : max, results2[0])
// Page with more keyword matches should have higher score
expect(result2.priority).toBeGreaterThan(result1.priority)
})
test('should prioritize multiple keywords over single keyword regardless of match location', () => {
// Page 1: Single keyword in title (highest individual priority)
const markdown1 = createMarkdown(
'title: React Tutorial\ndescription: Learn programming',
`# Introduction
This is about web development and programming frameworks.`
)
const results1 = searchPage('/page1/', markdown1, ['react', 'hooks'])
// Page 2: Both keywords in body text (lower individual priority)
const markdown2 = createMarkdown(
'title: Web Development\ndescription: Learn programming',
`# Introduction
This is about React hooks and state management. You'll learn about React components and custom hooks.`
)
const results2 = searchPage('/page2/', markdown2, ['react', 'hooks'])
// Both should match
expect(results1.length).toBeGreaterThan(0)
expect(results2.length).toBeGreaterThan(0)
// Find highest priority result from each page
const result1 = results1.reduce((max, r) => r.priority > max.priority ? r : max, results1[0])
const result2 = results2.reduce((max, r) => r.priority > max.priority ? r : max, results2[0])
// Page with 2 keyword matches should score higher than page with 1 keyword match
// even though the single match is in the title
expect(result2.priority).toBeGreaterThan(result1.priority)
})
test('should maintain priority order within same number of keywords', () => {
// Page 1: Both keywords in title
const markdown1 = createMarkdown(
'title: React Hooks Tutorial\ndescription: Learn programming',
`# Introduction
This is about web development and programming frameworks.`
)
const results1 = searchPage('/page1/', markdown1, ['react', 'hooks'])
// Page 2: Both keywords in description
const markdown2 = createMarkdown(
'title: Web Development\ndescription: Learn React hooks programming',
`# Introduction
This is about web development and programming frameworks.`
)
const results2 = searchPage('/page2/', markdown2, ['react', 'hooks'])
// Page 3: Both keywords in headings
const markdown3 = createMarkdown(
'title: Programming Guide\ndescription: Learn coding',
`# Introduction
## React Hooks
This section covers React hooks and state management.`
)
const results3 = searchPage('/page3/', markdown3, ['react', 'hooks'])
// Page 4: Both keywords in body text
const markdown4 = createMarkdown(
'title: General Guide\ndescription: Learn programming',
`# Introduction
This is about React hooks and state management. You'll learn about React components and custom hooks.`
)
const results4 = searchPage('/page4/', markdown4, ['react', 'hooks'])
// All should match
expect(results1.length).toBeGreaterThan(0)
expect(results2.length).toBeGreaterThan(0)
expect(results3.length).toBeGreaterThan(0)
expect(results4.length).toBeGreaterThan(0)
// Find highest priority result from each page
const result1 = results1.reduce((max, r) => r.priority > max.priority ? r : max, results1[0])
const result2 = results2.reduce((max, r) => r.priority > max.priority ? r : max, results2[0])
const result3 = results3.reduce((max, r) => r.priority > max.priority ? r : max, results3[0])
const result4 = results4.reduce((max, r) => r.priority > max.priority ? r : max, results4[0])
// Within same number of keywords (2), priority should be: title > description > heading > body
expect(result1.priority).toBeGreaterThan(result2.priority)
expect(result2.priority).toBeGreaterThan(result3.priority)
expect(result3.priority).toBeGreaterThan(result4.priority)
})
test('should ensure multiple keywords always score higher than single keyword', () => {
// Page 1: Single keyword in title
const markdown1 = createMarkdown(
'title: React Tutorial\ndescription: Learn programming',
`# Introduction
This is about web development and programming frameworks.`
)
const results1 = searchPage('/page1/', markdown1, ['react', 'hooks'])
// Page 2: Two keywords in body text
const markdown2 = createMarkdown(
'title: Web Development\ndescription: Learn programming',
`# Introduction
This is about React hooks and state management. You will learn about React components and custom hooks.`
)
const results2 = searchPage('/page2/', markdown2, ['react', 'hooks'])
// Both should match
expect(results1.length).toBeGreaterThan(0)
expect(results2.length).toBeGreaterThan(0)
// Find highest priority result from each page
const result1 = results1.reduce((max, r) => r.priority > max.priority ? r : max, results1[0])
const result2 = results2.reduce((max, r) => r.priority > max.priority ? r : max, results2[0])
// Page with 2 keyword matches should score higher than page with 1 keyword match
expect(result2.priority).toBeGreaterThan(result1.priority)
})
test('should ensure multiple keywords score higher even with exact title match', () => {
// Page 1: Exact keyword match in title (gets exact match bonus)
const markdown1 = createMarkdown(
'title: React\ndescription: Learn programming',
`# Introduction
This is about web development and programming frameworks.`
)
const results1 = searchPage('/page1/', markdown1, ['react', 'hooks'])
// Page 2: Two keywords in body text
const markdown2 = createMarkdown(
'title: Web Development\ndescription: Learn programming',
`# Introduction
This is about React hooks and state management. You will learn about React components and custom hooks.`
)
const results2 = searchPage('/page2/', markdown2, ['react', 'hooks'])
// Both should match
expect(results1.length).toBeGreaterThan(0)
expect(results2.length).toBeGreaterThan(0)
// Find highest priority result from each page
const result1 = results1.reduce((max, r) => r.priority > max.priority ? r : max, results1[0])
const result2 = results2.reduce((max, r) => r.priority > max.priority ? r : max, results2[0])
// Page with 2 keyword matches should score higher than page with 1 keyword match
// even when the single match is an exact title match
expect(result2.priority).toBeGreaterThan(result1.priority)
})
})
})