feat(PHP Unit): first configuration for PHP Unit tests in ValidForm Builder#154
Merged
flangfeldt merged 72 commits intoJun 12, 2026
Merged
feat(PHP Unit): first configuration for PHP Unit tests in ValidForm Builder#154flangfeldt merged 72 commits into
flangfeldt merged 72 commits into
Conversation
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Instead, we’re going to use their docker image directly to generate the docs in Github Actions. Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
Signed-off-by: Robin van Baalen <robin@ganttify.com>
d75fced to
a245ce5
Compare
Reorganized and added badges to provide more relevant project information. Replaced Travis CI badge with GitHub Actions workflow status badges and added download statistics from Packagist. This improves clarity and highlights key project metrics.
Covers constructor, toHtml wrapper with vf__notes class, conditional h4 header, body auto-wrapped in p vs raw pass-through when p tags present, empty header/body edge cases. Security: documents raw HTML rendering of header and body as intentional design. No new vulnerabilities.
Covers constructor (explicit/auto id, header, class/style/overview meta), addField (direct Fieldset vs auto-wrap, reuse of last fieldset), toHtml (vf__page wrapper, h2 header, overview skip), toJS (addPage call), isValid, hasFields, isDynamic, getShortHeader. Documents broken summaryLabel meta bug (validformbuilder#204) as regression-doc test. Security: header rendered as raw HTML (developer content, intentional). No new vulnerabilities.
Paragraph (11 tests): constructor, toHtml (h3 header, auto-p-wrap body, raw body with existing p tags, empty header/body), placeholders, security audit (raw HTML by design). Password (11 tests): construction, toHtml (password input with autocomplete=off, label, required class, tip), validation (any input passes empty regex, minLength enforcement, matchWith), toJS, security (value htmlspecialchars escaping verified via DOM parse). StaticText (9 tests): constructor, toHtml (raw HTML rendering, metaString placeholder replacement, simple layout span wrapping), placeholders, security (raw HTML by design). No new vulnerabilities found. Filed validformbuilder#205 for a stray double-quote in the simple-layout div tag across File/Group/Password. closes validformbuilder#181, closes validformbuilder#182, closes validformbuilder#185
Same XSS class as GroupField (validformbuilder#202). SelectOption::toHtmlInternal() and SelectGroup::toHtmlInternal() rendered value and label into HTML without htmlspecialchars(). Adds SelectTest with 12 tests covering construction, addField/addGroup, rendering (option values, optgroup labels, selected attribute, label linkage), toJS, and XSS regression tests. closes validformbuilder#183, closes validformbuilder#184, closes validformbuilder#206
Text (11 tests): construction, toHtml (input with name/id, label, submitted value, default value, required class, tip, maxlength attribute), toJS, email CSS class, XSS escape verification. Textarea (10 tests): construction, toHtml (textarea with name/id, default rows/cols, custom rows/cols meta limitation documented, submitted value, label), toJS, HTML type renders as Textarea, XSS escape verification. Documents the custom rows/cols append bug — Textarea constructor sets defaults before parent::__construct so meta values append instead of overwriting. No new vulnerabilities found — both classes escape values with htmlspecialchars. closes validformbuilder#186, closes validformbuilder#187
Keeps both: feat/tests's autoload-dev PSR-4 mapping + phpunit require-dev, and upstream/master's phpdocumentor/shim + config/platform + ext-mbstring.
52 tests exercising every VFORM_* type regex (positive + negative inputs via DataProvider), custom regex dispatch, empty-check passthrough, array input regression for validformbuilder#199, getCheck() lookup, and security probes (HTML tag rejection, SQL injection rejection, invalid-regex error suppression). No new vulnerabilities found. closes validformbuilder#188
The upstream merge brought config.platform.php=8.0.0 which conflicts with phpunit/phpunit ^11.5 (requires php >=8.2). The CI matrix only runs 8.2+ so this is safe.
ValidForm (56 tests): constructor, addField factory for all 20 VFORM_* types via DataProvider, other factory methods (fieldset/hidden/multi/ area/paragraph/button/navigation/html), isSubmitted (dispatch key + CSRF bypass note), isValid (empty/valid/invalid), getFields/getValidField, setDefaults (verified via rendered output), static helpers (get/getIsSet/ getStrippedClassName), toHtml structural checks (form element, dispatch hidden field, child fields), security (dispatch key rejection, dispatch field value rendering). ValidWizard (23 tests): constructor (nav labels from meta + defaults), addPage (page creation, uniqueId hidden field, no duplication), addField/ addFieldset/addMultiField (delegate to last page), getPage (by index, out-of-range quirk documented), isSubmitted (dispatch + uniqueId from request, CSRF bypass documented), isValid (whole wizard + per-page semantics of isValidUntil), confirm page flag, getFields across pages, security (CSRF bypass vs parent documented). closes validformbuilder#189, closes validformbuilder#190
…Ignore Both branches are provably dead code — the guard conditions above them filter out all inputs that could reach them. Annotating them lets pcov report an honest 100% line coverage without requiring contrived mocks.
Replaces the outdated claim that no test suite exists. Adds the vendor/bin/phpunit run command, assertSame preference note, and the Docker pcov image instructions for generating coverage locally.
Adds rendering paths, dynamic-field cloning, condition/comparison logic, multi-step wizard flows, and per-class security audits (XSS, injection). ButtonTest/MultiFieldTest include Linux-libxml fixes (explicit ids instead of anonymous-class NUL-byte ids). Coverage grew from 72.6% to 100%.
Replace the three assertTrue(true) placeholders in BaseTest with a real setConditions test (or deletion where the method doesn't exist on Base), assert the validate() return value in FieldValidatorTest, and move inline $_REQUEST cleanup into tearDown in AreaTest/CheckboxTest.
The unreachable branches in Base::addCondition() and Condition::addComparison() are now documented with inline comments suggesting deletion instead of coverage-tool metadata.
Drop issue URLs and history narration from inline comments; that context lives in the commit history and PR description.
flangfeldt
approved these changes
Jun 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First setup for PHPUnit tests in ValidForm Builder — see #161.
952 tests, 1,751 assertions — every reachable line in
classes/ValidFormBuilder/is covered. The suite passes under randomized test order and runs in ~0.13s.Test suite
tests/ValidFormBuilder/), unified structure:#[Test]attributes,ValidFormBuilder\Testsnamespace,assertSameoverassertEquals.HtmlAssertionsTrait— shared DOM-level assertion helper. Rendered HTML is parsed with DOMXPath and asserted structurally (element counts, hierarchy, attribute values) instead of brittle substring checks. Captures/restores libxml error state per test.$_REQUEST/$_POST/$_FILES/$_SESSIONclean up intearDown(), so a failing test can't leak state into the next one.KNOWN BUGcomments and issue references (e.g. Page constructor does not store $meta — getMeta() and getShortHeader() always return defaults #204 —Pagenever stores constructor meta;Area::getDynamicCount()returns string instead of int). These tests fail loudly when the underlying bug gets fixed, flagging the assertion to flip.Source fixes (driven by the tests)
Security:
GroupField,SelectOption, andSelectGroupnow escape value/label withhtmlspecialchars(..., ENT_QUOTES)before rendering. Regression-tested with script/attribute injection payloads.Validator::validate()returned viaexit()when an array submission contained an invalid element — a crafted request could kill the response mid-render. Now returnsfalse(Validator::validate() calls exit() on invalid array element — terminates entire PHP request #199).Bug fixes:
Validator:VFORM_BOOLEANregex tightened from/^[on]*$/i(a character class acceptingo,nooo,ono, …) to/^(on)?$/i— exactly the two values an HTML checkbox submits (VFORM_BOOLEAN regex/^[on]*$/iaccepts arbitrary sequences of o/n characters #200).Collection::seek(): reversed loop condition meant the internal pointer never advanced past position 0.Collection::inCollection(): with$blnReturnKey, returned stalekey()state instead of the matched key (PHP 7+ foreach no longer advances the internal pointer).Comparison: added the missingVFORM_COMPARISON_DOES_NOT_CONTAINcase; castpreg_match()result toboolas documented.Condition::addComparison():new \ReflectionClass("Comparison")resolved against the global namespace (always fatal) — nowComparison::class; argument extraction usedarray_keys()wherearray_values()was intended.Base::__sanitizeCheckForJs(): keep the/uregex flag when the pattern uses\p{...}Unicode property escapes (required in JavaScript).Unreachable code: two provably unreachable defensive branches (
Base::addCondition()catch,Condition::addComparison()else) carry inline comments explaining why they cannot be reached and flagging them as candidates for deletion. They are the only uncovered lines in the coverage report.Backwards-compatibility notes
VFORM_BOOLEANnow rejects garbage values (o,nooo,true,1) that previously validated. Standard checkbox submissions (""/"on") are unaffected.Validator::validate()no longer terminates the request on invalid array elements; it returnsfalse. Code relying on the oldexit()behavior will now continue executing.Tooling & CI
phpunit.xml— PHPUnit 11.5 config; coverage source is the fullclasses/directory, no exclusions..github/workflows/php-unit.yml— runs the suite on PR, with line/method coverage gates at 70% (pcov on PHP 8.4).composer.json— addsphpunit/phpunit ^11.5+phpdocumentor/shimto require-dev,ext-mbstringrequirement, platform PHP 8.2.AGENTS.md/CLAUDE.md/README.md— document the test commands, Docker pcov coverage workflow, and testing conventions.Known follow-up
ValidWizard::isSubmitted()does not validate the CSRF token, unlike its parentValidForm::isSubmitted()— discovered and documented by the new tests (ValidWizardTest::isSubmittedReturnsTrueWhenDispatchKeyMatches). Tracked in #208.Per-class checklist
The item is checked if a unit test is added.