Fix five real defects in helpers and console commands#434
Merged
Conversation
- BreadcrumbsHelper: a single-crumb trail (key 0) was never marked active because `!$key` treated index 0 as falsy. Split the "no match found" path (key stays null in `lastWithLink` mode when no crumb has a URL) from the "match at index 0" path. - FormHelper::control(): the `$this->templater()->push()` / `pop()` pair was unbalanced under exceptions. Anything thrown by the option mutators or the parent `control()` call leaked the pushed state into subsequent controls. Wrapped the body in `try/finally`. - PaginatorHelper::first()/last(): the docblock advertises `templates` as accepting an array or a file name, but `_templateOptions()` unconditionally did array operations (`+=`, key access) on it and crashed for string values. String templates now pass through unchanged so the templater can `load()` the file. - InstallCommand::_runNPMInstall(): chdir'd into the plugin directory and never restored the previous cwd. Any later relative-path work in the same process resolved against the wrong directory. Captured `getcwd()` and restore it in a `finally` block. - ModifyViewCommand::_modifyView(): re-running the command was non-idempotent. The literal `str_replace` for the `initialize()` body always matched on the second run too, duplicating `parent::initialize();`. Added guards so each transformation is skipped when its target marker is already present; the command now correctly reports an error when no effective change can be made. Adds regression tests for each fix, including a paginator template file fixture under `tests/test_app/config/`.
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.
Summary
Five small, independent defects across helpers and console commands. Each is testable in isolation; combined into one PR because the diff per fix is tiny.
1.
BreadcrumbsHelper::_markActiveCrumb()— single-crumb a11yif (!$key) return;treated index0as falsy, so a one-item breadcrumb trail never got theactiveclass oraria-current="page". The same guard was implicitly relied on by thelastWithLinkmode when no crumb has a URL (the loop key falls back to0), so a naive fix to=== nullwould break that case. Split the two intents:$keyonly gets assigned inside thelastWithLinkloop when a URL crumb is actually found.2.
FormHelper::control()— templater stack leaktemplater()->push()at the top and the matchingpop()at the end were unbalanced under exceptions: anything thrown by_labelOptions,_spacingOptions, …, orparent::control()left the templater in the pushed state and corrupted every subsequent control in the request. Wrapped the body intry/finally.3.
PaginatorHelper::first()/last()— stringtemplatescrashedThe docblock says
templatesaccepts an array or a file name, and the call site explicitly branches onis_string()forloadvsadd. But_templateOptions()did$options['templates'] += [...]and then indexed into it as an array, which crashed for any string value. String templates now pass through_templateOptions()unchanged.(Note: the per-template
{{label}}substitution that happens for inline templates is intentionally not applied to file templates — those control their own markup. The test fixture omits{{label}}accordingly. Cross-cutting label substitution for file templates is a separate question.)4.
InstallCommand::_runNPMInstall()— cwd leakchdir($pluginPath)was never restored. Any later relative-path work in the same process resolved against the plugin directory instead of the original cwd. Capturedgetcwd()before the chdir and restore it in afinally.5.
ModifyViewCommand::_modifyView()— non-idempotentRe-running
bin/cake bootstrap modify_viewon an already-modifiedAppView.phpwould duplicateparent::initialize();because the literalstr_replace(" public function initialize(): void\n {\n", ...)matched the new content too. The command also silently "succeeded" when nothing was changed. Added per-transformation guards (str_containsagainst the target marker) and a final "nothing to do" check that returns false so the command reportsCould not modifyinstead of pretending it patched the file.Tests
BreadcrumbsHelperTest::testSingleCrumbIsMarkedActive— exact-match assertion that one-item trail getsaria-current="page"on the<li>(matches the existing non-URL path semantics).PaginatorHelperTest::testFirstCustomTemplateFile— passestemplates => 'paginator_templates'(a new fixture attests/test_app/config/paginator_templates.php) and asserts the file's markup wins.InstallCommandTest::testRunNPMInstallRestoresCwd— calls_runNPMInstallvia reflection and assertsgetcwd()unchanged afterwards.ModifyViewCommandTest::testAlreadyModifiedReportsError— runs the command against the (already modified)AppView.phpin the test app and asserts_writeFileis never called and the user sees an error.ModifyViewCommandTest::testFileCannotBeWrittenupdated to first copy the skeleton in, since the idempotent path now correctly skips the write for the already-modified file.All gates green locally:
phpunit(751 tests, 1136 assertions),phpstanlevel 8 (clean),phpcs(clean).