diff --git a/src/Codeception/Lib/InnerBrowser.php b/src/Codeception/Lib/InnerBrowser.php index 22dd243..bd45f14 100644 --- a/src/Codeception/Lib/InnerBrowser.php +++ b/src/Codeception/Lib/InnerBrowser.php @@ -280,9 +280,10 @@ public function _loadPage( array $parameters = [], array $files = [], array $server = [], - ?string $content = null + ?string $content = null, + bool $changeHistory = true ): void { - $this->crawler = $this->clientRequest($method, $uri, $parameters, $files, $server, $content); + $this->crawler = $this->clientRequest($method, $uri, $parameters, $files, $server, $content, $changeHistory); $this->baseUrl = $this->retrieveBaseUrl(); $this->forms = []; } @@ -2006,7 +2007,45 @@ public function moveBack(int $numberOfSteps = 1): void $request->getParameters(), $request->getFiles(), $request->getServer(), - $request->getContent() + $request->getContent(), + false + ); + } + + /** + * Moves forward in history. + * + * @param int $numberOfSteps (default value 1) + */ + public function moveForward(int $numberOfSteps = 1): void + { + $request = null; + if (!is_int($numberOfSteps) || $numberOfSteps < 1) { + throw new InvalidArgumentException('numberOfSteps must be positive integer'); + } + + try { + $history = $this->getRunningClient()->getHistory(); + for ($i = $numberOfSteps; $i > 0; --$i) { + $request = $history->forward(); + } + } catch (LogicException $exception) { + throw new InvalidArgumentException( + sprintf( + 'numberOfSteps is set to %d, but there are only %d forward steps in the history', + $numberOfSteps, + $numberOfSteps - $i + ), $exception->getCode(), $exception); + } + + $this->_loadPage( + $request->getMethod(), + $request->getUri(), + $request->getParameters(), + $request->getFiles(), + $request->getServer(), + $request->getContent(), + false ); } diff --git a/tests/unit/Codeception/Module/FrameworksTest.php b/tests/unit/Codeception/Module/FrameworksTest.php index a1e0ec1..ac708f9 100644 --- a/tests/unit/Codeception/Module/FrameworksTest.php +++ b/tests/unit/Codeception/Module/FrameworksTest.php @@ -68,6 +68,17 @@ public function testMoveBackTwoSteps() $this->module->seeCurrentUrlEquals('/iframe'); } + public function testMoveBackPreservesForwardHistory() + { + $this->module->amOnPage('/iframe'); + $this->module->amOnPage('/info'); + $this->module->amOnPage('/'); + $this->module->moveBack(); + $this->module->seeCurrentUrlEquals('/info'); + $this->module->moveBack(); + $this->module->seeCurrentUrlEquals('/iframe'); + } + public function testMoveBackThrowsExceptionIfNumberOfStepsIsInvalid() { $this->module->amOnPage('/iframe'); @@ -89,6 +100,50 @@ public function testMoveBackThrowsExceptionIfNumberOfStepsIsInvalid() } } + public function testMoveForwardOneStep() + { + $this->module->amOnPage('/iframe'); + $this->module->amOnPage('/info'); + $this->module->amOnPage('/'); + $this->module->moveBack(2); + $this->module->seeCurrentUrlEquals('/iframe'); + $this->module->moveForward(); + $this->module->seeCurrentUrlEquals('/info'); + $this->module->moveForward(); + $this->module->seeCurrentUrlEquals('/'); + } + + public function testMoveForwardTwoSteps() + { + $this->module->amOnPage('/iframe'); + $this->module->amOnPage('/info'); + $this->module->amOnPage('/'); + $this->module->moveBack(2); + $this->module->seeCurrentUrlEquals('/iframe'); + $this->module->moveForward(2); + $this->module->seeCurrentUrlEquals('/'); + } + + public function testMoveForwardThrowsExceptionIfNumberOfStepsIsInvalid() + { + $this->module->amOnPage('/iframe'); + $this->module->amOnPage('/'); + $this->module->moveBack(); + $this->module->seeCurrentUrlEquals('/iframe'); + + $invalidValues = [0, -5, 1.5, 'a', 3]; + foreach ($invalidValues as $invalidValue) { + try { + $this->module->moveForward($invalidValue); + $this->fail('Expected to get exception here'); + } catch (InvalidArgumentException $exception) { + codecept_debug('Exception: ' . $exception->getMessage()); + } catch (TypeError $error) { + codecept_debug('Error: ' . $error->getMessage()); + } + } + } + public function testCreateSnapshotOnFail() { $container = Stub::make(ModuleContainer::class);