fix(captcha): enqueue the ALTCHA widget from the render, not from a page list (#1053) - #1067
Merged
Merged
Conversation
#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
marked this pull request as ready for review
September 5, 2026 19:47
rpgmem
enabled auto-merge (squash)
September 5, 2026 19:47
Coverage Report for CI Build 33987938342Warning No base build found for commit Coverage: 89.927%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
84 tasks
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.
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_scriptdo bundle estava dentro deif ( $has_form || $has_verification )noFrontend::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.mdjá documenta para o handler de export em lote (#783) — "registre num ponto que sempre roda, não dobrado dentro de outroinit*()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 durantethe_content, bem antes dowp_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
languagedo 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
Test plan
composer test— 7587 testes / 21785 asserções, verdecomposer lint(PHPStan) — nível 8 limpophpcs-tests.xml.dist, ESLint e Stylelint limposDuas guardas novas, ambas verificadas falhando contra o código quebrado:
render_fields()enfileiraffc-captchaFailed asserting that an array contains 'ffc-captcha'register_frontend_assets()registra os dois handles, com as dependências certasFailed 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
languagee 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_localenoLoaderTestquebrou 14 testes em duas classes não relacionadas — o mesmo guardafunction_exists( 'get_locale' )doLabelSorter::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()notests/bootstrap.php. Não funciona: o Patchwork não consegue redefinir função declarada em arquivo não instrumentado, então todoFunctions\when( 'get_locale' )passa a estourarInvalidName. Revertido; as duas classes agora stubam explicitamente, e oCLAUDE.mdregistra a armadilha com esse beco sem saída, para ninguém repetir a tentativa.tests/bootstrap.phptambém passou a definirFFC_ALTCHA_VERSION, lido do arquivo principal do plugin pela mesma via de fonte única que oFFC_VERSIONjá usava — a URL do bundle carrega a versão, então um teste que renderiza o widget precisa dela.Checklist
npm run build) and the resulting*.min.*files are committedCHANGELOG.mdupdated —[Unreleased] → Fixed; sem bump deFFC_VERSION🤖 Generated with Claude Code
https://claude.ai/code/session_012XWx9qJdjZdAq8crxM9GCU
Generated by Claude Code