Browse Source

Improve flash message capturing

Some view implementations (like inertiajs) manipulate flash messages and
make them into view variables during the `View::render()` method. This
breaks flash assertions as the messages are deleted.

By capturing the messages with a early priority listener in the
`Controller.beforeRender` event we can capture flash messages more
reliably. The trade off being that we will not capture any changes made
to flash messages during the controller `beforeRender` or during view
initialization.
Mark Story 5 years ago
parent
commit
6406bdf4ef

+ 7 - 8
src/TestSuite/IntegrationTestTrait.php

@@ -511,19 +511,18 @@ trait IntegrationTestTrait
         }
         $this->_controller = $controller;
         $events = $controller->getEventManager();
-        $events->on('Controller.beforeRedirect', function ($event): void {
-            $controller = $event->getSubject();
-            if ($this->_retainFlashMessages) {
+        $flashCapture = function (EventInterface $event): void {
+            if ($this->_retainFlashMessages && empty($this->_flashMessages)) {
+                $controller = $event->getSubject();
                 $this->_flashMessages = $controller->getRequest()->getSession()->read('Flash');
             }
-        });
-        $events->on('View.beforeRender', function ($event, $viewFile) use ($controller): void {
+        };
+        $events->on('Controller.beforeRedirect', ['priority' => -100], $flashCapture);
+        $events->on('Controller.beforeRender', ['priority' => -100], $flashCapture);
+        $events->on('View.beforeRender', function ($event, $viewFile): void {
             if (!$this->_viewName) {
                 $this->_viewName = $viewFile;
             }
-            if ($this->_retainFlashMessages) {
-                $this->_flashMessages = $controller->getRequest()->getSession()->read('Flash');
-            }
         });
         $events->on('View.beforeLayout', function ($event, $viewFile): void {
             $this->_layoutName = $viewFile;

+ 18 - 1
tests/TestCase/TestSuite/IntegrationTestTraitTest.php

@@ -764,7 +764,24 @@ class IntegrationTestTraitTest extends TestCase
         $this->get('/posts/flashNoRender');
         $this->assertRedirect();
 
-        $this->assertSession('An error message', 'Flash.flash.0.message');
+        $this->assertFlashElement('flash/error');
+        $this->assertFlashMessage('An error message');
+    }
+
+    /**
+     * Test flash assertions stored with enableRememberFlashMessages() even if
+     * the controller clears flash data in `beforeRender`
+     *
+     * @return void
+     */
+    public function testFlashAssertionsRemoveInBeforeRender()
+    {
+        $this->enableRetainFlashMessages();
+        $this->get('/posts/index/with_flash/?clear=true');
+        $this->assertResponseOk();
+
+        $this->assertFlashElement('flash/error');
+        $this->assertFlashMessage('An error message');
     }
 
     /**

+ 7 - 0
tests/test_app/TestApp/Controller/PostsController.php

@@ -47,6 +47,13 @@ class PostsController extends AppController
         $this->FormProtection->setConfig('unlockedFields', ['some_unlocked_field']);
     }
 
+    public function beforeRender(EventInterface $event)
+    {
+        if ($this->request->getQuery('clear')) {
+            $this->set('flash', $this->request->getSession()->consume('Flash'));
+        }
+    }
+
     /**
      * Index method.
      *