Skip to content

fix(captcha): enqueue the ALTCHA widget from the render, not from a page list (#1053) - #1067

Merged
rpgmem merged 1 commit into
developfrom
claude/altcha-captcha-integration-4vvuer
Sep 5, 2026
Merged

fix(captcha): enqueue the ALTCHA widget from the render, not from a page list (#1053)#1067
rpgmem merged 1 commit into
developfrom
claude/altcha-captcha-integration-4vvuer

Conversation

@rpgmem

@rpgmem rpgmem commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Encontrado no smoke do modo só-ALTCHA: no download público de CSV o widget renderizava um container vazio. Um custom element que não sobe não reclama, então o console ficava limpo.

O defeito

O wp_enqueue_script do bundle estava dentro de if ( $has_form || $has_verification ) no Frontend::enqueue_scripts(). A página do download de CSV é $has_csv_download — nenhum dos dois.

E era maior que a tela mostrava: o bloco de segurança renderiza em quatro superfícies, e o formulário de agendamento é enfileirado por uma classe inteiramente diferente (SelfSchedulingShortcode). Duas das quatro estavam quebradas nos modos que mostram o widget.

É a armadilha que o CLAUDE.md já documenta para o handler de export em lote (#783) — "registre num ponto que sempre roda, não dobrado dentro de outro init*() que retorna cedo naquela página" — em forma de enqueue. Eu a repeti.

A correção

Acrescentar $has_csv_download à lista consertaria a captura e deixaria o defeito de pé: a lista é o que diverge. Quem renderiza o widget passou a enfileirá-lo.

  • Loader::register_frontend_assets() registra os dois scripts. É o lugar cujo próprio docblock diz "apenas registra; o enqueue acontece quando os shortcodes carregam suas dependências" — o padrão já existia, eu é que não o segui.
  • AltchaCaptcha::render_fields() enfileira no momento do render. Shortcodes renderizam durante the_content, bem antes do wp_footer, e os dois são scripts de rodapé — então o enqueue tardio chega a tempo.

Agora o enqueue segue o render por construção, em qualquer superfície, sem lista para manter em dia.

Ganho de brinde: a localização mudou junto, então o atributo language do elemento e a chave registrada no store de i18n saem do mesmo método. Antes eram dois lugares que podiam discordar — e a resposta do widget a uma divergência é cair para inglês em silêncio.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would alter existing behavior)
  • Refactor / chore (no functional change)
  • Documentation only

Test plan

  • composer test7587 testes / 21785 asserções, verde
  • composer lint (PHPStan) — nível 8 limpo
  • WPCS, phpcs-tests.xml.dist, ESLint e Stylelint limpos
  • Vitest — 117 arquivos, 1750 testes
  • Manual smoke test in WordPress admin / frontend — é o que motivou esta PR; refeito após o merge
  • Updated or added unit tests where relevant

Duas guardas novas, ambas verificadas falhando contra o código quebrado:

Guarda Mensagem contra o código antigo
render_fields() enfileira ffc-captcha Failed asserting that an array contains 'ffc-captcha'
register_frontend_assets() registra os dois handles, com as dependências certas Failed asserting that an array contains 'ffc-altcha'

A primeira é a que importa: ela assevera o vínculo entre enqueue e render, não a lista de superfícies. A lista é o que estava errado; asseverá-la teria congelado o defeito.

Uma terceira prova cobre a armadilha adjacente: o atributo language e a chave do store de i18n têm de ser o mesmo valor, senão o widget aparece em inglês sem um erro sequer.

Fallout de isolamento de testes, e um beco sem saída que vale registrar

Stubar get_locale no LoaderTest quebrou 14 testes em duas classes não relacionadas — o mesmo guarda function_exists( 'get_locale' ) do LabelSorter::locale() que já tinha mordido no #1064. O ramo que ele toma depende de qualquer teste anterior no processo ter definido a função, porque o Brain\Monkey a define via Patchwork e ela permanece definida.

Tentei resolver na raiz definindo get_locale() no tests/bootstrap.php. Não funciona: o Patchwork não consegue redefinir função declarada em arquivo não instrumentado, então todo Functions\when( 'get_locale' ) passa a estourar InvalidName. Revertido; as duas classes agora stubam explicitamente, e o CLAUDE.md registra a armadilha com esse beco sem saída, para ninguém repetir a tentativa.

tests/bootstrap.php também passou a definir FFC_ALTCHA_VERSION, lido do arquivo principal do plugin pela mesma via de fonte única que o FFC_VERSION já usava — a URL do bundle carrega a versão, então um teste que renderiza o widget precisa dela.

Checklist

  • Source CSS/JS changes were re-minified (npm run build) and the resulting *.min.* files are committed
  • CHANGELOG.md updated — [Unreleased] → Fixed; sem bump de FFC_VERSION
  • No new PHPStan baseline entries unless explicitly justified
  • No secrets, tokens, or personally identifiable information in the diff

🤖 Generated with Claude Code

https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU


Generated by Claude Code

#1053)

Smoke-testing the ALTCHA-only mode found the widget's container rendering
empty on the public CSV download. The bundle was never loaded there: the
enqueue sat inside `if ( $has_form || $has_verification )` in
`Frontend::enqueue_scripts()`, and that page is neither. The self-scheduling
booking form would have been the same — a different class enqueues it — so
two of the four surfaces that render the security block were broken. A custom
element that never upgrades reports nothing, so the symptom was an empty box
with a clean console.

This is the trap CLAUDE.md already documents for the batched-export click
handler (#783), in enqueue form.

Adding `$has_csv_download` to the list would fix the screenshot and leave the
defect: the list is what drifts. The provider that renders the widget now
enqueues it. `Loader::register_frontend_assets()` registers both scripts —
the place whose own docblock says "only registers; actual enqueue happens
when shortcodes load their dependencies" — and `AltchaCaptcha::render_fields()`
enqueues at render time. Shortcodes render during `the_content`, well before
`wp_footer`, and both are footer scripts, so a late enqueue lands. The
localisation moved with them, so the `language` attribute and the i18n store
key are now produced by one method instead of two that could disagree —
a disagreement the widget answers by falling back to English in silence.

Both new guards were verified failing against the broken code: the render
test reports `Failed asserting that an array contains 'ffc-captcha'`, the
registration test the same for 'ffc-altcha'.

Test-isolation fallout, and a note for the next person. Stubbing `get_locale`
in `LoaderTest` broke 14 tests in two unrelated classes — the same
`LabelSorter::locale()` `function_exists` guard that bit in #1064, whose
branch depends on whether any earlier test in the process defined that
function. Defining it in `tests/bootstrap.php` is not the fix: Patchwork
cannot redefine a function declared in an uninstrumented file, so every
`Functions\when( 'get_locale' )` starts throwing instead. The two classes now
stub it explicitly, and CLAUDE.md records the trap with that dead end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU
@rpgmem
rpgmem marked this pull request as ready for review September 5, 2026 19:47
@rpgmem
rpgmem enabled auto-merge (squash) September 5, 2026 19:47
@rpgmem
rpgmem merged commit 0041fbc into develop Sep 5, 2026
19 checks passed
@rpgmem
rpgmem deleted the claude/altcha-captcha-integration-4vvuer branch September 5, 2026 19:56
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 33987938342

Warning

No base build found for commit 22f6074 on develop.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 89.927%

Details

  • Patch coverage: 38 of 38 lines across 2 files are fully covered (100%).

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 53856
Covered Lines: 48431
Line Coverage: 89.93%
Coverage Strength: 4.84 hits per line

💛 - Coveralls

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.

3 participants