Skip to content

Fix moveBack() history pointer + add symmetric moveForward()#87

Merged
TavoNiievez merged 2 commits into
Codeception:masterfrom
TavoNiievez:fix-moveback-history-pointer
Jun 26, 2026
Merged

Fix moveBack() history pointer + add symmetric moveForward()#87
TavoNiievez merged 2 commits into
Codeception:masterfrom
TavoNiievez:fix-moveback-history-pointer

Conversation

@TavoNiievez

@TavoNiievez TavoNiievez commented Jun 26, 2026

Copy link
Copy Markdown
Member

1. Fix moveBack() to move the history pointer instead of appending

moveBack() replays the previous request via _loadPage(), which called clientRequest() with the default $changeHistory = true. Replaying with changeHistory = true appends the request to the BrowserKit history and discards the forward entries, so moveBack() didn't behave like a real Back button:

  • the history pointer ended back on the last page after going back, and
  • a second moveBack() no longer walked further back.

Demonstration (fails on master):

$I->amOnPage('/iframe');
$I->amOnPage('/info');
$I->amOnPage('/');
$I->moveBack();
$I->seeCurrentUrlEquals('/info'); // ok
$I->moveBack();
$I->seeCurrentUrlEquals('/iframe'); // FAILS on master -> lands on '/info'

Fix: thread a $changeHistory flag through _loadPage() and pass false from moveBack(). Matches Symfony's native AbstractBrowser::back() (requestFromRequest($request, false)).

2. Add symmetric moveForward()

Symfony's AbstractBrowser exposes both back() and forward() with identical semantics ($this->history->forward() + requestFromRequest($request, false)). InnerBrowser only had moveBack(). This adds the mirror moveForward(int $numberOfSteps = 1). It is only meaningful because the fix above keeps the forward stack intact:

$I->amOnPage('/a');
$I->amOnPage('/b');
$I->moveBack();
$I->seeCurrentUrlEquals('/a');
$I->moveForward();
$I->seeCurrentUrlEquals('/b');

Backward compatibility

No BC break:

  • moveForward() — brand-new public method, purely additive.
  • _loadPage() — gains a trailing optional parameter bool $changeHistory = true. Existing callers (incl. external Helpers using the @api method) are unaffected because the default preserves the previous behavior.
  • moveBack() — signature unchanged; the change is a behavioral bugfix (back no longer mutates history). No relied-upon contract is removed.

Why it matters downstream

moveForward() and a correct moveBack() make the framework history assertions reachable through public steps — e.g. assertBrowserHistoryIsNotOnLastPage() (Symfony BrowserKitAssertionsTrait, symfony/browser-kit >= 7.4), which previously had no step to position history off the last page. Unblocks the matching functional tests in Codeception/symfony-module-tests (#58, #59) for codeception/module-symfony#240.

Tests

moveBack: added testMoveBackPreservesForwardHistory (fails on master, passes here).
moveForward: added testMoveForwardOneStep, testMoveForwardTwoSteps, testMoveForwardThrowsExceptionIfNumberOfStepsIsInvalid.
Full unit suite green: 209 tests, 469 assertions.

@TavoNiievez TavoNiievez changed the title Fix moveBack() to move the history pointer instead of appending a new entry Fix moveBack() history pointer + add symmetric moveForward() Jun 26, 2026
Aaron Gustavo Nieves added 2 commits June 26, 2026 17:01
`moveBack()` replayed the previous request through `_loadPage()`, which
called `clientRequest()` with the default `$changeHistory = true`. That
appended a new entry to the BrowserKit history and discarded the forward
entries, so going back behaved like a fresh navigation rather than a real
Back button: the history pointer ended up on the last page again and a
second `moveBack()` no longer walked further back.

Thread a `$changeHistory` flag through `_loadPage()` (default `true`, so it
is backward compatible) and pass `false` from `moveBack()`. The replayed
request no longer mutates the history, matching Symfony's native
`AbstractBrowser::back()` semantics.

This also makes the framework history assertions
(`assertBrowserHistoryIsNotOnLastPage()`) observable through the public
`moveBack()` step.
Mirror of moveBack(): walks the BrowserKit history pointer forward N steps
and replays the request with changeHistory = false, matching Symfony's
native AbstractBrowser::forward(). Symfony exposes both back() and forward()
with identical semantics, so InnerBrowser now offers the symmetric pair.

This is purely additive (new public method) and is only usable now that
moveBack() preserves the forward history entries instead of discarding them.

Tests: testMoveForwardOneStep, testMoveForwardTwoSteps and
testMoveForwardThrowsExceptionIfNumberOfStepsIsInvalid. The forward tests
also assert that moveBack() kept the forward stack intact.
@TavoNiievez TavoNiievez force-pushed the fix-moveback-history-pointer branch from ce89ee4 to da2befb Compare June 26, 2026 22:02
@TavoNiievez TavoNiievez merged commit 0fa80de into Codeception:master Jun 26, 2026
5 checks passed
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.

1 participant