Skip to content

JS coverage medium: core/helpers + dynamic-fragments + device-signals/frontend partial (#170) - #171

Merged
rpgmem merged 4 commits into
mainfrom
claude/js-coverage-medium
May 12, 2026
Merged

JS coverage medium: core/helpers + dynamic-fragments + device-signals/frontend partial (#170)#171
rpgmem merged 4 commits into
mainfrom
claude/js-coverage-medium

Conversation

@rpgmem

@rpgmem rpgmem commented May 12, 2026

Copy link
Copy Markdown
Owner

Closes #170.

Summary

Four sprints (E + F + G + H) from #170. Each commit is independently revertable.

Sprint Commit What
E e7fbdd3 28 tests covering window.FFC.* (core) and window.FFC.Frontend.{Validation, Masks, UI} (frontend-helpers). No extraction needed — those files already publish their helpers on the FFC namespace, so the originally-planned refactor wasn't necessary.
F ca7b293 11 tests for ffc-dynamic-fragments.js (cache-refresh IIFE). Custom MockXHR over window.XMLHttpRequest to drive applyFragments paths.
G f1e89ec 6 load-side tests for device-signals + frontend.js. The deep paths (SubtleCrypto + thumbmarkjs fingerprint pipeline, AJAX form submit) explicitly deferred — they need a dedicated PR with heavier mocking infra.
H 0ef0ec7 JS_COVERAGE_FLOOR_LINES ratchets 15 → 24.

Coverage delta

Before (main) After (this PR)
Total tests 127 172 (+45)
Overall line coverage 16.86% 25.78%
ffc-core.js 0% 59%
ffc-frontend-helpers.js 0% 63%
ffc-dynamic-fragments.js 0% 73%
ffc-device-signals.js 0% 38%
ffc-frontend.js 0% 27%
JS_COVERAGE_FLOOR_LINES 15 24

Notes

  • Sprint E plan revised mid-implementation: the original issue body proposed "extract pure helpers, then test". After reading ffc-core.js / ffc-frontend-helpers.js, I found both already publish their helpers on window.FFC. Extraction would have been pure churn. Tests go straight against the existing public surface; the commit message documents this.

  • Sprint G partial: device-signals and frontend.js both have heavy dependency surfaces (SubtleCrypto, thumbmarkjs, jQuery AJAX, jsdom-incompatible APIs). This commit ships the cheap parts (early-returns, telemetry-off enforcement, load-side namespace registration). The crypto + AJAX paths are deferred to a focused future sprint that builds the necessary mocking layer. Coverage moved from 0 to 38% / 27% respectively without that infrastructure — a real but partial win.

  • window.crypto is non-writable in jsdom: tests that need to mutate it use Object.defineProperty(window, 'crypto', { value, configurable: true }) and restore the original in beforeEach.

  • XHR mocking pattern: tests/js/dynamic-fragments.test.js introduces a MockXHR class that captures method / url / payload and exposes deliver(response, status) to trigger onload. Reusable for any future IIFE script that uses raw XHR.

Test plan

  • npm run test:js172 / 172 OK (was 127).
  • npm run test:js:coverage — line coverage 25.78%, gate (floor 24) passes.
  • npm run lint:js — clean (9 pre-existing unused-vars warnings unchanged).
  • vendor/bin/phpunit — runs unchanged (no PHP touched).

What's deferred

  • Device-fingerprint full pipeline (SubtleCrypto + thumbmarkjs).
  • Frontend.js form-submit AJAX flow + magic-link verification + PDF handoff.
  • ffc-csv-download.js (668 LOC, File API).
  • ffc-reregistration-frontend.js (507 LOC).
  • Admin pages (ffc-admin.js, field-builder, submission-edit, etc.).
  • Calendar subsystem.
  • Profile panel deep coverage (form save + password change + LGPD).

https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx


Generated by Claude Code

claude added 4 commits May 12, 2026 14:17
The original Sprint E plan was "extract pure helpers into new files
then test", on the assumption that both ffc-core.js and
ffc-frontend-helpers.js wrapped useful helpers inside IIFEs with no
public surface. Re-reading the code showed that they ALREADY publish:

  - `window.FFC.*` from ffc-core.js (getString, getAjaxUrl, getNonce,
    isModuleLoaded, log, ajax, toggleFields, ...).
  - `window.FFC.Frontend.{Validation, Masks, UI, RateLimit}` from
    ffc-frontend-helpers.js.

So this is a "test in place" pass instead of a refactor. 28 new tests:

  - FFC.getString (3): localized hit, missing key falls back, missing
    strings object falls back.
  - FFC.getAjaxUrl / getNonce (2): both return the localized values.
  - FFC.isModuleLoaded (2): true for registered (Frontend), false for
    nonexistent.
  - FFC.Frontend.Validation.validateCPF (6): valid formatted/unformatted,
    too-short rejected, all-same-digit rejected (e.g. 111...), invalid
    first check digit, invalid second check digit.
  - FFC.Frontend.Validation.validateRF (5): valid 7 digits, formatting
    stripped, too short, too long, letters reduce digit count.
  - FFC.Frontend.Validation.validateEmail (5): valid forms, missing @,
    missing local part, missing TLD, whitespace.
  - FFC.Frontend.UI.showFormError (2): notice injected, replaces
    previous (no stacking).
  - FFC.Frontend.UI.showFormSuccess (1): replaces form HTML.
  - FFC.Frontend.Masks (2): applyCpfRf masks 11 digits to
    XXX.XXX.XXX-XX; applyAuthCode inserts midpoint dash.

Coverage delta:
  - ffc-core.js:             0%  → 59.13% lines.
  - ffc-frontend-helpers.js: 0%  → 63.47% lines.
  - Overall:                 16.86% → 22.21%.

Floor ratchet deferred to Sprint H so the bump captures E+F+G
combined.

https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
11 tests for the page-cache freshness IIFE in
assets/js/ffc-dynamic-fragments.js. The script has no public API —
it runs on parse, detects whether the page needs refreshed fragments,
fires an XHR to `ffc_get_dynamic_fragments`, and patches captchas /
nonces / user fields / geofence configs from the response.

Testing strategy: install a `MockXHR` over `window.XMLHttpRequest`
before loading the script so the test can inspect the request and
deliver canned responses on demand. The mock captures `method`, `url`,
and `payload`, plus a `deliver(response, status)` helper that fires
`onload` with the test-supplied data.

What's covered:
  - 5 early-return paths: no FFC elements on page → no XHR; no
    ajaxUrl source → no XHR; captcha row + ajaxUrl → XHR fired;
    ffc_ajax fallback when ffcDynamic missing; form IDs collected
    into the payload.
  - 6 applyFragments paths: per-form captcha update (label/hash/blank
    answer), default captcha update, geofence config refresh +
    FFCGeofence.recheck() trigger, response.success=false ignored,
    HTTP 500 ignored, malformed JSON swallowed without throwing.

Coverage:
  - ffc-dynamic-fragments.js: 0% → 72.64% lines.
  - Overall: 22.21% → 23.59%.

https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
…G of #170)

6 tests for the cheap parts of two heavy files. The deep paths (the
crypto-based fingerprint pipeline, AJAX-bound form submission flow)
require expensive mocking of SubtleCrypto + thumbmarkjs + jQuery
AJAX, which is bigger than the rest of the sprint combined. This
commit ships what's cheap and explicitly defers the rest.

  - ffc-device-signals (3 tests): bails silently when window.crypto.subtle
    is unavailable; bails silently when ThumbmarkJS is unavailable;
    calls ThumbmarkJS.setOption('logging', false) when both deps are
    present (proves the telemetry-off enforcement that the PHP unit
    test in DeviceSignalsLoggingOffTest.php greps for).

  - ffc-frontend (3 tests): republishes `window.ffcUtils` as the
    Frontend.Masks namespace after load; loads without throwing on
    a page with no forms; loads without throwing on a page with one
    .ffc-submission-form.

`window.crypto` is non-writable in jsdom — tests swap it via
`Object.defineProperty(window, 'crypto', { value, configurable: true })`
and restore the original in `beforeEach`.

Coverage:
  - ffc-device-signals.js: 0% → 38.27% lines (the early-return guards
    + the setOption call cover ~1/3 of the IIFE; the
    fingerprint/sha256/submit-time injection paths stay uncovered).
  - ffc-frontend.js:       0% → 27% lines (script-init code paths +
    the no-form / one-form load paths).
  - Overall:               23.59% → 25.78%.

Deferred to a future sprint:
  - device-signals full fingerprint pipeline (mapComponents,
    collectSignals, sha256Hex on the 14 documented signal types).
  - frontend.js form-submit AJAX flow + magic-link verification +
    PDF generation handoff.

These warrant a focused PR with dedicated mocking infrastructure
(SubtleCrypto polyfill, thumbmarkjs response fixtures, jQuery AJAX
intercept layer) — bigger lift than the rest of #170 combined.

https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
#170)

Sprints E+F+G of #170 added ~45 new tests covering:
  - FFC core + Frontend.{Validation, Masks, UI} helpers (Sprint E)
  - dynamic-fragments cache-refresh IIFE (Sprint F)
  - device-signals + frontend.js load-side paths (Sprint G)

Overall line coverage went from 16.86% to 25.78%. The floor moves
from 15 → 24, preserving the ~1.8% buffer the previous ratchets used.

https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
@rpgmem
rpgmem marked this pull request as ready for review May 12, 2026 14:50
@rpgmem
rpgmem merged commit ac9a466 into main May 12, 2026
16 checks passed
@rpgmem
rpgmem deleted the claude/js-coverage-medium branch May 12, 2026 14:50
rpgmem pushed a commit that referenced this pull request May 13, 2026
Bumps:
- ffcertificate.php plugin header `Version` → 6.5.3
- FFC_VERSION constant → 6.5.3
- readme.txt Stable tag → 6.5.3

CHANGELOG: collapses the Unreleased block into a 6.5.3 (2026-05-13)
section and adds the items merged since 6.5.2 that the prior diff was
missing:

Changed:
  - thumbmarkjs 1.8.1 → 1.9.0 (this PR)
  - jQuery UI theme 1.14.1 → 1.14.2 (this PR)
  - Recruitment CSV import: CPF/RF normalisation at parse time
    (#172, shipped via #182).

Fixed:
  - Form-editor groups 7/8 toggle-off persistence (already on
    Unreleased; kept).
  - Form-editor public CSV CPF "No" reverting to "Audit" (already on
    Unreleased; kept).
  - Reregistration form: $.trim() TypeError under jQuery 4 (#185).
  - Reregistration form: [name^="fields["] selector rejected under
    jQuery 4, getFields() returned {} (#185).

Internal:
  - JS coverage uplift 7.37% → 72.84% across multiple sprints
    (#162 / #164 / #166 / #168-#171 / #174-#180 / #183-#186),
    floor ratcheted 3 → 70.
  - Coverage job timeout 15 → 20 min (#181).
  - The 9 pre-existing ESLint no-unused-vars warnings cleared (#188).
  - CLAUDE.md added documenting auto-merge convention + CI gates +
    test-infrastructure notes (#187).
rpgmem added a commit that referenced this pull request May 13, 2026
…1 → 1.14.2 (#189)

* chore(deps): bump thumbmarkjs 1.8.1 → 1.9.0 and jQuery UI theme 1.14.1 → 1.14.2

Two upstream patch bumps that keep the API/CSS surface this plugin
relies on:

thumbmarkjs 1.8.1 → 1.9.0:
- New vendored bundle: `libs/js/thumbmark-1.9.0.umd.js` (33 KB).
- Old bundle removed.
- `FFC_THUMBMARK_VERSION` bumped; `Frontend::enqueue_*` resolves the
  filename from the constant so the enqueue call updates automatically.
- Surface used by `assets/js/ffc-device-signals.js`
  (`window.ThumbmarkJS.setOption('logging', false)`,
  `stableStringify`, `getFingerprintData()`) verified present in the
  new bundle and exercised by the existing JS suites
  (device-signals-deep.test.js / device-signals-and-frontend.test.js)
  — both still pass.
- `DeviceSignalsLoggingOffTest::test_vendored_thumbmarkjs_present_at_pinned_path`
  path + redownload-URL hint updated to track the new bundle.

jQuery UI theme 1.14.1 → 1.14.2:
- `libs/css/jquery-ui-smoothness.css` replaced with the 1.14.2 release.
- The CSS payload is byte-identical between the two upstream releases;
  the diff is just the file-header comment moving to
  `v1.14.2 - 2026-01-28`. Visible change for users is the cache-bust
  version string `wp_enqueue_style` emits.
- `FFC_JQUERY_UI_VERSION` bumped to `'1.14.2'`.

Tests: 3893 PHP + 487 JS still green.

* release: 6.5.2 → 6.5.3, consolidate CHANGELOG for the maintenance cut

Bumps:
- ffcertificate.php plugin header `Version` → 6.5.3
- FFC_VERSION constant → 6.5.3
- readme.txt Stable tag → 6.5.3

CHANGELOG: collapses the Unreleased block into a 6.5.3 (2026-05-13)
section and adds the items merged since 6.5.2 that the prior diff was
missing:

Changed:
  - thumbmarkjs 1.8.1 → 1.9.0 (this PR)
  - jQuery UI theme 1.14.1 → 1.14.2 (this PR)
  - Recruitment CSV import: CPF/RF normalisation at parse time
    (#172, shipped via #182).

Fixed:
  - Form-editor groups 7/8 toggle-off persistence (already on
    Unreleased; kept).
  - Form-editor public CSV CPF "No" reverting to "Audit" (already on
    Unreleased; kept).
  - Reregistration form: $.trim() TypeError under jQuery 4 (#185).
  - Reregistration form: [name^="fields["] selector rejected under
    jQuery 4, getFields() returned {} (#185).

Internal:
  - JS coverage uplift 7.37% → 72.84% across multiple sprints
    (#162 / #164 / #166 / #168-#171 / #174-#180 / #183-#186),
    floor ratcheted 3 → 70.
  - Coverage job timeout 15 → 20 min (#181).
  - The 9 pre-existing ESLint no-unused-vars warnings cleared (#188).
  - CLAUDE.md added documenting auto-merge convention + CI gates +
    test-infrastructure notes (#187).

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Coverage] Medium sprints — extract+test core/frontend-helpers + dynamic-fragments + device-signals + frontend

2 participants