|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project |
| 6 | + |
| 7 | +`com.codeborne:pdf-test` — a small Java library for asserting on the contents of PDF files in tests. It exposes both a Hamcrest matcher API (`PDF.containsText(...)` etc.) and an AssertJ API (`Assertions.assertThat(pdf).containsText(...)`), plus AssertJ soft assertions via `PdfSoftAssertions`. PDF parsing is delegated to Apache PDFBox. |
| 8 | + |
| 9 | +## Common commands |
| 10 | + |
| 11 | +Build and run all checks (the `defaultTasks` are `check publishToMavenLocal`): |
| 12 | + |
| 13 | +``` |
| 14 | +./gradlew |
| 15 | +``` |
| 16 | + |
| 17 | +Other useful tasks: |
| 18 | + |
| 19 | +``` |
| 20 | +./gradlew test # run tests only |
| 21 | +./gradlew test --tests <FQCN> # run a single test class |
| 22 | +./gradlew test --tests <FQCN>.<method> # run a single test method |
| 23 | +./gradlew jacocoTestReport # coverage report at build/reports/jacoco/ |
| 24 | +./gradlew publishToMavenLocal # install to ~/.m2 for local consumption |
| 25 | +``` |
| 26 | + |
| 27 | +Java 17 is required (toolchain pinned in `gradle/compilation.gradle`). |
| 28 | + |
| 29 | +## Architecture |
| 30 | + |
| 31 | +The public surface is intentionally tiny — three packages under `src/main/java/com/codeborne/pdftest`: |
| 32 | + |
| 33 | +- **`PDF`** is the central value object. Its constructors eagerly parse a PDF (from `File`, `URL`, `URI`, `byte[]`, or `InputStream`, optionally with a page range) and freeze the extracted state into `public final` fields: `text`, `numberOfPages`, document-info fields (`author`, `title`, `creator`, ...), and signature fields (`signed`, `signerName`, `signatureTime`). All later assertions read these fields — there is no lazy parsing. Any parse failure is rethrown as `IllegalArgumentException("Invalid PDF file: ...")`. URL reads use a 10 s connect/read timeout. |
| 34 | +- **`matchers/`** holds Hamcrest `TypeSafeMatcher<PDF>` implementations, all extending `PDFMatcher`. `PDF` exposes them as static factories (`containsText`, `doesNotContainText`, `containsExactText`, `doesNotContainExactText`, `containsTextCaseInsensitive`, `matchesText`). |
| 35 | +- **`assertj/`** mirrors the same checks as an AssertJ `PdfAssert extends AbstractAssert<PdfAssert, PDF>`, with `Assertions.assertThat(PDF)` as the entry point and `PdfSoftAssertions` for soft assertions (uses AssertJ's `proxy(...)`). |
| 36 | + |
| 37 | +**Whitespace handling is the key invariant.** `Spaces.reduce(text)` collapses any run of whitespace (including ` ` non-breaking space and newlines) to a single space and trims. The `containsText` / `containsTextCaseInsensitive` / `matchesText` / `doesNotContainText` paths apply `Spaces.reduce` to BOTH the PDF text and the expected argument(s); the `*Exact*` variants do not. Both API surfaces (Hamcrest matchers and `PdfAssert`) must keep this behavior consistent — when adding a new matcher, decide explicitly whether it is an "exact" or whitespace-normalized variant and apply `Spaces.reduce` accordingly on both sides. |
| 38 | + |
| 39 | +Adding a new check usually means: a new `PDFMatcher` subclass, a static factory on `PDF`, a method on `PdfAssert`, and tests under both `src/test/java/com/codeborne/pdftest/matchers/` and `src/test/java/com/codeborne/pdftest/assertj/`. |
| 40 | + |
| 41 | +## Tests |
| 42 | + |
| 43 | +JUnit 5 (Jupiter). Test PDFs live in `src/test/resources/` (`minimal.pdf`, `50quickideas.pdf`, `transaction.pdf`, `statement.pdf`, `file-with-images.pdf`, two `PO_*.pdf` purchase orders). Reuse these rather than committing new fixtures unless a new edge case really requires one. |
0 commit comments