Files
wehub-resource-sync 9f97f3abbe
CI - Node Bindings / Build darwin-arm64 (push) Waiting to run
CI - Node Bindings / Build darwin-x64 (push) Waiting to run
CI - Node Bindings / Test win32-x64-msvc (push) Blocked by required conditions
CI - Node Bindings / Build linux-arm64-gnu (push) Waiting to run
CI - Node Bindings / Build linux-x64-gnu (push) Waiting to run
CI - Node Bindings / Build linux-x64-musl (push) Waiting to run
CI - Node Bindings / Build win32-arm64-msvc (push) Waiting to run
CI - Node Bindings / Build win32-x64-msvc (push) Waiting to run
CI - Node Bindings / Test darwin-arm64 (push) Blocked by required conditions
CI - Node Bindings / Test darwin-x64 (push) Blocked by required conditions
CI - Node Bindings / Test linux-x64-gnu (push) Blocked by required conditions
CI - Node Bindings / Test linux-x64-musl (push) Blocked by required conditions
CI - Node Bindings / Test win32-arm64-msvc (push) Blocked by required conditions
CI - Python Bindings / Build aarch64-pc-windows-msvc (push) Waiting to run
CI - Python Bindings / Build x86_64-pc-windows-msvc (push) Waiting to run
CI - Python Bindings / Build x86_64-apple-darwin (push) Waiting to run
CI - Python Bindings / Build aarch64-apple-darwin (push) Waiting to run
CI - Python Bindings / Build aarch64-unknown-linux-gnu (push) Waiting to run
CI - Python Bindings / Test x86_64-apple-darwin (push) Blocked by required conditions
CI - Python Bindings / Test aarch64-apple-darwin (push) Blocked by required conditions
CI - Python Bindings / Test x86_64-unknown-linux-gnu (push) Blocked by required conditions
CI - Python Bindings / Test x86_64-unknown-linux-musl (push) Blocked by required conditions
CI - Python Bindings / Test aarch64-pc-windows-msvc (push) Blocked by required conditions
CI - Python Bindings / Test x86_64-pc-windows-msvc (push) Blocked by required conditions
CI / build-and-test (macos-26-intel) (push) Waiting to run
CI / build-and-test (macos-latest) (push) Waiting to run
CI / build-and-test (windows-11-arm) (push) Waiting to run
CI / build-and-test (windows-latest) (push) Waiting to run
CI - Python Bindings / sdist (push) Failing after 1s
CI - Python Bindings / Build x86_64-unknown-linux-musl (push) Failing after 1s
CI / fmt (push) Failing after 1s
E2E Output Validation / compare-outputs (push) Failing after 1s
Sync Docs to Developer Hub / sync-docs (push) Failing after 1s
CI - Python Bindings / Build x86_64-unknown-linux-gnu (push) Failing after 1s
CI - WASM Bindings / Build WASM (push) Failing after 0s
CI / clippy (push) Failing after 1s
CI / build-and-test (ubuntu-latest) (push) Failing after 0s
CI - WASM Bindings / Edge runtime PDF parse test (push) Has been skipped
CI - WASM Bindings / Browser PDF parse test (push) Has been skipped
E2E Output Validation / upload-dataset (push) Waiting to run
Deploy Demo to GitHub Pages / deploy (push) Failing after 1s
CI / build-docker-image (push) Failing after 3s
chore: import upstream snapshot with attribution
2026-07-13 12:23:44 +08:00

177 lines
6.9 KiB
Python

"""E2E tests for LiteParse.parse() — validates Python types match CLI output."""
from pathlib import Path
import pytest
from liteparse import (
BoundingBox,
LiteParse,
ParsedPage,
ParseError,
ParseResult,
TextItem,
)
class TestParseBasic:
"""Basic parse functionality and output structure."""
def test_parse_returns_parse_result(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
assert isinstance(result, ParseResult)
def test_parse_result_has_pages(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
assert len(result.pages) > 0
assert result.num_pages == len(result.pages)
def test_parse_result_has_text(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
assert isinstance(result.text, str)
assert len(result.text) > 0
def test_parse_result_has_json(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
assert result.json is not None
assert "pages" in result.json
def test_parse_bytest_input(self, parser: LiteParse, invoice_pdf: Path):
file_bytes = invoice_pdf.read_bytes()
result = parser.parse(file_bytes)
assert result.json is not None
assert "pages" in result.json
@pytest.mark.asyncio
async def test_parse_async(self, parser: LiteParse, invoice_pdf: Path):
result = await parser.parse_async(invoice_pdf)
assert result.json is not None
assert "pages" in result.json
@pytest.mark.asyncio
async def test_parse_async_bytes_input(self, parser: LiteParse, invoice_pdf: Path):
file_bytes = invoice_pdf.read_bytes()
result = await parser.parse_async(file_bytes)
assert result.json is not None
assert "pages" in result.json
class TestParsedPageStructure:
"""Validate ParsedPage fields match CLI output."""
def test_page_fields(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
page = result.pages[0]
assert isinstance(page, ParsedPage)
assert isinstance(page.pageNum, int)
assert page.pageNum >= 1
assert isinstance(page.width, (int, float))
assert isinstance(page.height, (int, float))
assert page.width > 0
assert page.height > 0
assert isinstance(page.text, str)
def test_page_has_text_items(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
page = result.pages[0]
assert len(page.textItems) > 0
def test_text_item_fields(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
item = result.pages[0].textItems[0]
assert isinstance(item, TextItem)
assert isinstance(item.text, str)
assert isinstance(item.x, (int, float))
assert isinstance(item.y, (int, float))
assert isinstance(item.width, (int, float))
assert isinstance(item.height, (int, float))
# fontName and fontSize may be None or present
if item.fontName is not None:
assert isinstance(item.fontName, str)
if item.fontSize is not None:
assert isinstance(item.fontSize, (int, float))
def test_bounding_box_fields(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False, precise_bounding_box=True)
page = result.pages[0]
assert len(page.boundingBoxes) > 0
bbox = page.boundingBoxes[0]
assert isinstance(bbox, BoundingBox)
assert isinstance(bbox.x1, (int, float))
assert isinstance(bbox.y1, (int, float))
assert isinstance(bbox.x2, (int, float))
assert isinstance(bbox.y2, (int, float))
class TestParseOptions:
"""Test that CLI options are correctly forwarded."""
def test_target_pages(self, parser: LiteParse, invoice_pdf: Path):
# invoice.pdf has 2 pages — parse only page 1
result = parser.parse(invoice_pdf, ocr_enabled=False, target_pages="1")
assert result.num_pages == 1
assert result.pages[0].pageNum == 1
def test_max_pages(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False, max_pages=1)
assert result.num_pages == 1
def test_no_precise_bbox(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(
invoice_pdf, ocr_enabled=False, precise_bounding_box=False
)
assert isinstance(result, ParseResult)
# With no precise bbox, boundingBoxes should be empty
for page in result.pages:
assert len(page.boundingBoxes) == 0
def test_get_page_helper(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
page1 = result.get_page(1)
assert page1 is not None
assert page1.pageNum == 1
assert result.get_page(999) is None
def test_multi_page_text_joined(self, parser: LiteParse, invoice_pdf: Path):
result = parser.parse(invoice_pdf, ocr_enabled=False)
assert result.num_pages == 2
# result.text should be the join of all page texts
expected = "\n\n".join(p.text for p in result.pages)
assert result.text == expected
class TestParseErrors:
"""Test error handling."""
def test_file_not_found(self, parser: LiteParse):
with pytest.raises(FileNotFoundError):
parser.parse("/nonexistent/file.pdf")
def test_cli_not_found(self):
parser = LiteParse(cli_path="/nonexistent/liteparse")
# subprocess raises FileNotFoundError when the binary doesn't exist
with pytest.raises((ParseError, FileNotFoundError, OSError)):
parser.parse(Path(__file__)) # any existing file
def test_timeout(self, parser: LiteParse, invoice_pdf: Path):
# Extremely short timeout should fail
with pytest.raises(TimeoutError):
parser.parse(invoice_pdf, timeout=0.001)
@pytest.mark.asyncio
async def test_file_not_found_async(self, parser: LiteParse):
with pytest.raises(FileNotFoundError):
await parser.parse_async("/nonexistent/file.pdf")
@pytest.mark.asyncio
async def test_cli_not_found_async(self):
parser = LiteParse(cli_path="/nonexistent/liteparse")
# subprocess raises FileNotFoundError when the binary doesn't exist
with pytest.raises((ParseError, FileNotFoundError, OSError)):
parser.parse(Path(__file__)) # any existing file
@pytest.mark.asyncio
async def test_timeout_async(self, parser: LiteParse, invoice_pdf: Path):
with pytest.raises(TimeoutError):
await parser.parse_async(invoice_pdf, timeout=0.001)