JS coverage deep: profile + device-signals + frontend (#173) - #174
Merged
Conversation
…ws (Sprint I of #173) 15 tests for the action-handler paths that #168 S4 deliberately skipped. The read-view render is already covered by dashboard-profile.test.js; this file drives the document-level event delegates installed by `panel.bindEvents()` and mocks `$.ajax` to exercise the success / error branches. Coverage: - ffc-user-dashboard-profile.js: 40.21% → 86.50% lines. - Overall: 25.78% → 27.35%. What's covered: showEditForm (3): - replaces read view with edit form, populates fields from state - no-op when state is null (defensive) - cancel button restores the read view from preserved state saveProfile (3): - sends PUT user/profile with form values serialised as JSON - on success: state replaced, read view re-rendered - on error: server message shown, save button re-enabled changePassword (5): - rejects blank fields (no AJAX fired) - rejects mismatched new+confirm - rejects <8-char passwords - on success: POST user/change-password, password fields cleared - on error: server message surfaced privacyRequest LGPD (3): - Export button POSTs without confirmation prompt - Delete button asks confirm; declines cancel the AJAX - Delete button proceeds when confirmation accepted saveNotificationPreferences (1): - .ffc-notif-toggle change → AJAX, response merged into state Pattern: - beforeAll() installs fixtures + invokes `panel.bindEvents()` so the document-level delegates land before the first test triggers a click. (init() isn't called by the harness; bindEvents is the moral equivalent for the panel's local handlers.) - Each test calls `panel.render(PROFILE_FIXTURE)` to materialise the DOM, then triggers user input via `$(selector).trigger(...)` or direct DOM property writes. - $.ajax is replaced via vi.spyOn with the canned success/error payload. - vi.spyOn(window, 'confirm') drives the LGPD-delete branch. The LGPD + notification sections are emitted conditionally by `render()`; tests inject the buttons inline when the rendered template omits them, so the click handler is the unit under test regardless of the template's preference shape. https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
…t J of #173) 10 tests for the SubtleCrypto + ThumbmarkJS-bound paths that Sprint G of #170 deferred. End-to-end exercise: mock SubtleCrypto.digest with a deterministic 32-byte fake, mock ThumbmarkJS with a fixture fingerprint payload, render a submission form, load the script, flush microtasks, and assert against the JSON written into `<input name="ffc_device_signals">`. Coverage: - ffc-device-signals.js: 38.27% → 95.88% lines (100% functions). - Overall: 27.35% → 28.61%. What's covered: - No-form page: pipeline runs but never injects an input. - Multi-form page: each form gets its own hidden input populated. - Hash output: each enabled signal lands as 64-char hex. - Allowlist gate: signals absent from ffc_device_config.signals are filtered before hashing. - Cookie signal + localStorage roundtrip: fresh UUID written on first load, reused on second load (validated against the script's `/^[0-9a-f-]{30,40}$/i` shape check). - getRandomValues fallback when crypto.randomUUID is unavailable. - ThumbmarkJS rejection: collectSignals falls back to cookie-only payload (graceful degradation). - stableStringify failure: falls back to JSON.stringify on the `screen` signal path. - UA coarse-graining: two UAs differing only in 3-part patch version hash identically once the regex strips them. Mocking infrastructure introduced (reusable for future sprints): - `installCryptoMock()` — replaces window.crypto with a stub SubtleCrypto + randomUUID + getRandomValues. - `installThumbmarkMock(fixture)` — replaces window.ThumbmarkJS with a vi-tracked setOption/stableStringify/getFingerprintData triple. - `fakeDigest(algo, data)` — deterministic 32-byte hash that varies with input so different signals produce different hex strings but stable across runs. Timing: - `flush()` awaits two `setTimeout(_, 0)` ticks to settle the chained Promises inside collectSignals (getFingerprintData → forEach → Promise.all → input.value =). https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
…173) 11 tests for `handleFormSubmission` in assets/js/ffc-frontend.js — the form-submit AJAX pipeline that Sprint G of #170 deferred. Pattern: load FFC core + frontend-helpers + frontend.js in beforeAll (with an async flush so $(document).ready fires before the first test), then dispatch synthetic submit events and mock $.ajax to drive each response branch. Coverage: - ffc-frontend.js: 27% → 55.8% lines (60% functions). - Overall: 28.61% → 30.09%. What's covered: Required-field validation (2): - blocks submission when a required input is empty; marks the input with `.ffc-field-error` + `aria-invalid="true"`. - injects an accessible alert with the i18n string above the form (role=alert). AJAX payload shape (1): - sends POST with serialised form data + action=ffc_submit_form + nonce=<localized> to the ajax_url. Success branch (3): - replaces form HTML with response.data.html. - adds data-pdf-data attribute on the download button when pdf_data is in the response. - triggers window.ffcGeneratePDF after 500ms via setTimeout (uses vi.useFakeTimers to fast-forward). Failure branch (2): - response.success=false → FFC.Frontend.UI.showFormError shows the server message. - refresh_captcha → FFC.Frontend.UI.refreshCaptcha updates the captcha label + hash in place. Error branches (2): - rate_limit responseJSON → FFC.Frontend.RateLimit.show called. - generic error → connection-error alert injected. showAccessibleAlert helper (1): - second alert replaces (not stacks) the first. Magic-link verification deferred: it runs once via `$(document).ready` + setTimeout(100), reading window.location state. Testing different URL configurations would need re-loading the script per scenario (which duplicates the submit delegate) or exposing the function for direct call. Both options have downsides larger than the test value; left for a follow-up that decides on either an exposure pattern or a Playwright-level integration test. https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
#173) Sprints I+J+K of #173 added 36 new tests covering: - profile panel save/password/LGPD/notif handlers (Sprint I) - device-signals fingerprint pipeline end-to-end (Sprint J) - frontend.js form-submission AJAX flow (Sprint K) Overall line coverage went from 25.78% to 30.09%. The floor moves from 24 → 28, preserving the ~2% buffer the previous ratchets used. https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
rpgmem
marked this pull request as ready for review
May 12, 2026 15:40
This was referenced May 12, 2026
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>
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.
Closes #173.
Summary
Four sprints (I + J + K + L) from #173. Each commit independently revertable.
fb243ea824b014239d17747a9886Coverage delta
ffc-user-dashboard-profile.jsffc-device-signals.jsffc-frontend.jsJS_COVERAGE_FLOOR_LINESMocking infrastructure introduced (reusable)
fakeDigest(algo, data)— deterministic 32-byte hash returning Promise; varies with input so different signals produce different hex strings.installCryptoMock()— replaceswindow.cryptowith a stub{ subtle: { digest: fakeDigest }, randomUUID, getRandomValues }.installThumbmarkMock(fingerprintData)— replaceswindow.ThumbmarkJSwith vi-tracked{ setOption, stableStringify, getFingerprintData }.mockAjaxSuccess(response)/mockAjaxError(xhr)—vi.spyOn(window.$, 'ajax')patterns now used across 4 test files.Notes
$(document).ready(cb)deferral — frontend.js and several panels register their event delegates inside the ready callback. Tests withbeforeAll(async () => { ...; await flush(); })ensure delegates are wired before the first test triggers an event.window.cryptois non-writable in jsdom — tests useObject.defineProperty(window, 'crypto', { value, configurable: true })to swap and restore.$(document).ready+setTimeout(100), readswindow.location. Testing different URL configurations would need either re-loading the script (which duplicates the submit delegate) or exposing the function for direct call. Both have downsides bigger than the test value; tracked as a follow-up.render(). Tests inject the buttons inline when the template omits them, so the click handler is the unit under test regardless of the preference shape.Test plan
npm run test:js— 208 / 208 OK (was 172).npm run test:js:coverage— line coverage 30.09%, gate (floor 28) passes.npm run lint:js— clean (9 pre-existing unused-vars warnings unchanged).vendor/bin/phpunit— runs unchanged (no PHP touched).What's deferred
ffc-csv-download.js(668 LOC, File API).ffc-reregistration-frontend.js(507 LOC).ffc-admin.js, field-builder, submission-edit, etc.) — next sprint per the roadmap.https://claude.ai/code/session_01HiExaniSqvNBLpVTszCLNx
Generated by Claude Code