tests / structuredExtraction.test.ts
tests / structuredExtraction.test.ts
import { describe, it, expect } from "vitest";
import { extractStructuredElements } from "../src/services/structuredExtraction";
describe("extractStructuredElements", () => {
it("returns empty array for empty input", () => {
expect(extractStructuredElements("")).toEqual([]);
});
it("returns empty array for null/undefined input", () => {
expect(extractStructuredElements(null as any)).toEqual([]);
expect(extractStructuredElements(undefined as any)).toEqual([]);
});
it("extracts headings and paragraphs", () => {
const html = `
<html><body>
<h2>Introduction</h2>
<p>This is a paragraph about web scraping techniques and best practices for data extraction from various online sources and websites.</p>
<h2>Methods</h2>
<p>Various methods exist for extracting structured data from HTML documents and web pages using different parsing techniques.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.length).toBeGreaterThan(0);
expect(result.some(s => s.includes("Introduction"))).toBe(true);
expect(result.some(s => s.includes("Methods"))).toBe(true);
});
it("extracts tables", () => {
const html = `
<html><body>
<h2>Data</h2>
<p>Here is some introductory text about the data table that follows below in the document.</p>
<table>
<tr><th>Name</th><th>Value</th></tr>
<tr><td>Alpha</td><td>100</td></tr>
<tr><td>Beta</td><td>200</td></tr>
</table>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Name") && s.includes("Value"))).toBe(true);
expect(result.some(s => s.includes("Alpha") && s.includes("100"))).toBe(true);
});
it("extracts metadata from infobox", () => {
const html = `
<html><body>
<table class="infobox">
<tr><th>Developer</th><td>Example Corp</td></tr>
<tr><th>Release Date</th><td>2024-01-15</td></tr>
</table>
<p>Some content about the software product and its features.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Developer") && s.includes("Example Corp"))).toBe(true);
expect(result.some(s => s.includes("Release Date") && s.includes("2024-01-15"))).toBe(true);
});
it("deduplicates identical sections", () => {
const html = `
<html><body>
<h2>Section A</h2>
<p>This is a unique paragraph with enough content to pass the minimum length requirement for extraction.</p>
<h2>Section A</h2>
<p>This is a unique paragraph with enough content to pass the minimum length requirement for extraction.</p>
</body></html>
`;
const result = extractStructuredElements(html);
// Count occurrences of the section heading
const sectionA = result.filter(s => s.includes("## Section A"));
expect(sectionA.length).toBeLessThanOrEqual(1);
});
it("deduplicates identical tables", () => {
const html = `
<html><body>
<table>
<tr><th>Col1</th><th>Col2</th></tr>
<tr><td>A</td><td>B</td></tr>
</table>
<table>
<tr><th>Col1</th><th>Col2</th></tr>
<tr><td>A</td><td>B</td></tr>
</table>
</body></html>
`;
const result = extractStructuredElements(html);
const tableMatches = result.filter(s => s.includes("Col1") && s.includes("Col2"));
expect(tableMatches.length).toBeLessThanOrEqual(1);
});
it("filters out noise headings", () => {
const html = `
<html><body>
<h2>History</h2>
<p>This is some historical information about the topic that is quite long enough to be extracted normally.</p>
<h2>References</h2>
<p>Some references and sources that are listed here for citation purposes in the article content.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("## History"))).toBe(false);
expect(result.some(s => s.includes("## References"))).toBe(false);
});
it("filters out short/empty content", () => {
const html = `
<html><body>
<h2>Valid Section</h2>
<p>Short.</p>
<p>This is a proper paragraph with enough content to pass the minimum length threshold for extraction.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Short."))).toBe(false);
});
it("extracts list items", () => {
const html = `
<html><body>
<h2>Features</h2>
<ul>
<li>First feature description that is long enough to be useful for extraction purposes here.</li>
<li>Second feature description that is also long enough to pass the minimum length requirement.</li>
</ul>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("First feature description"))).toBe(true);
expect(result.some(s => s.includes("Second feature description"))).toBe(true);
});
it("handles h3 and h4 headings", () => {
const html = `
<html><body>
<h2>Main Section</h2>
<p>This is the main section content that is long enough to pass the minimum length requirement for extraction.</p>
<h3>Subsection</h3>
<p>Content in the subsection that is long enough to pass the minimum length requirement for extraction purposes.</p>
<h4>Detail</h4>
<p>More detailed content that is also long enough to pass the minimum length requirement for extraction here.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("## Main Section"))).toBe(true);
expect(result.some(s => s.includes("## Subsection"))).toBe(true);
expect(result.some(s => s.includes("## Detail"))).toBe(true);
});
it("uses article/main content area when available", () => {
const html = `
<html><body>
<nav><p>Navigation link text that should not be extracted as main content area.</p></nav>
<article>
<h2>Article Title</h2>
<p>Main article content that is long enough to pass the minimum length requirement for extraction.</p>
</article>
<footer><p>Footer text that should not be extracted as main content area here.</p></footer>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Article Title"))).toBe(true);
});
it("falls back to body when no main content area", () => {
const html = `
<html><body>
<h2>Page Title</h2>
<p>Content without a main wrapper element that is long enough to pass the minimum length requirement.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Page Title"))).toBe(true);
});
it("preserves links in content as markdown", () => {
const html = `
<html><body>
<h2>Links Section</h2>
<p>Visit <a href="https://example.com">Example Site</a> for more information about the topic and its various features available online.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("[Example Site](https://example.com)"))).toBe(true);
});
it("handles malformed HTML gracefully", () => {
const html = `<p>Unclosed paragraph <div>Nested div content that is long enough to pass minimum length.</p>`;
const result = extractStructuredElements(html);
expect(Array.isArray(result)).toBe(true);
});
});
import { describe, it, expect } from "vitest";
import { extractStructuredElements } from "../src/services/structuredExtraction";
describe("extractStructuredElements", () => {
it("returns empty array for empty input", () => {
expect(extractStructuredElements("")).toEqual([]);
});
it("returns empty array for null/undefined input", () => {
expect(extractStructuredElements(null as any)).toEqual([]);
expect(extractStructuredElements(undefined as any)).toEqual([]);
});
it("extracts headings and paragraphs", () => {
const html = `
<html><body>
<h2>Introduction</h2>
<p>This is a paragraph about web scraping techniques and best practices for data extraction from various online sources and websites.</p>
<h2>Methods</h2>
<p>Various methods exist for extracting structured data from HTML documents and web pages using different parsing techniques.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.length).toBeGreaterThan(0);
expect(result.some(s => s.includes("Introduction"))).toBe(true);
expect(result.some(s => s.includes("Methods"))).toBe(true);
});
it("extracts tables", () => {
const html = `
<html><body>
<h2>Data</h2>
<p>Here is some introductory text about the data table that follows below in the document.</p>
<table>
<tr><th>Name</th><th>Value</th></tr>
<tr><td>Alpha</td><td>100</td></tr>
<tr><td>Beta</td><td>200</td></tr>
</table>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Name") && s.includes("Value"))).toBe(true);
expect(result.some(s => s.includes("Alpha") && s.includes("100"))).toBe(true);
});
it("extracts metadata from infobox", () => {
const html = `
<html><body>
<table class="infobox">
<tr><th>Developer</th><td>Example Corp</td></tr>
<tr><th>Release Date</th><td>2024-01-15</td></tr>
</table>
<p>Some content about the software product and its features.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Developer") && s.includes("Example Corp"))).toBe(true);
expect(result.some(s => s.includes("Release Date") && s.includes("2024-01-15"))).toBe(true);
});
it("deduplicates identical sections", () => {
const html = `
<html><body>
<h2>Section A</h2>
<p>This is a unique paragraph with enough content to pass the minimum length requirement for extraction.</p>
<h2>Section A</h2>
<p>This is a unique paragraph with enough content to pass the minimum length requirement for extraction.</p>
</body></html>
`;
const result = extractStructuredElements(html);
// Count occurrences of the section heading
const sectionA = result.filter(s => s.includes("## Section A"));
expect(sectionA.length).toBeLessThanOrEqual(1);
});
it("deduplicates identical tables", () => {
const html = `
<html><body>
<table>
<tr><th>Col1</th><th>Col2</th></tr>
<tr><td>A</td><td>B</td></tr>
</table>
<table>
<tr><th>Col1</th><th>Col2</th></tr>
<tr><td>A</td><td>B</td></tr>
</table>
</body></html>
`;
const result = extractStructuredElements(html);
const tableMatches = result.filter(s => s.includes("Col1") && s.includes("Col2"));
expect(tableMatches.length).toBeLessThanOrEqual(1);
});
it("filters out noise headings", () => {
const html = `
<html><body>
<h2>History</h2>
<p>This is some historical information about the topic that is quite long enough to be extracted normally.</p>
<h2>References</h2>
<p>Some references and sources that are listed here for citation purposes in the article content.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("## History"))).toBe(false);
expect(result.some(s => s.includes("## References"))).toBe(false);
});
it("filters out short/empty content", () => {
const html = `
<html><body>
<h2>Valid Section</h2>
<p>Short.</p>
<p>This is a proper paragraph with enough content to pass the minimum length threshold for extraction.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Short."))).toBe(false);
});
it("extracts list items", () => {
const html = `
<html><body>
<h2>Features</h2>
<ul>
<li>First feature description that is long enough to be useful for extraction purposes here.</li>
<li>Second feature description that is also long enough to pass the minimum length requirement.</li>
</ul>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("First feature description"))).toBe(true);
expect(result.some(s => s.includes("Second feature description"))).toBe(true);
});
it("handles h3 and h4 headings", () => {
const html = `
<html><body>
<h2>Main Section</h2>
<p>This is the main section content that is long enough to pass the minimum length requirement for extraction.</p>
<h3>Subsection</h3>
<p>Content in the subsection that is long enough to pass the minimum length requirement for extraction purposes.</p>
<h4>Detail</h4>
<p>More detailed content that is also long enough to pass the minimum length requirement for extraction here.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("## Main Section"))).toBe(true);
expect(result.some(s => s.includes("## Subsection"))).toBe(true);
expect(result.some(s => s.includes("## Detail"))).toBe(true);
});
it("uses article/main content area when available", () => {
const html = `
<html><body>
<nav><p>Navigation link text that should not be extracted as main content area.</p></nav>
<article>
<h2>Article Title</h2>
<p>Main article content that is long enough to pass the minimum length requirement for extraction.</p>
</article>
<footer><p>Footer text that should not be extracted as main content area here.</p></footer>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Article Title"))).toBe(true);
});
it("falls back to body when no main content area", () => {
const html = `
<html><body>
<h2>Page Title</h2>
<p>Content without a main wrapper element that is long enough to pass the minimum length requirement.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("Page Title"))).toBe(true);
});
it("preserves links in content as markdown", () => {
const html = `
<html><body>
<h2>Links Section</h2>
<p>Visit <a href="https://example.com">Example Site</a> for more information about the topic and its various features available online.</p>
</body></html>
`;
const result = extractStructuredElements(html);
expect(result.some(s => s.includes("[Example Site](https://example.com)"))).toBe(true);
});
it("handles malformed HTML gracefully", () => {
const html = `<p>Unclosed paragraph <div>Nested div content that is long enough to pass minimum length.</p>`;
const result = extractStructuredElements(html);
expect(Array.isArray(result)).toBe(true);
});
});