diff --git a/_build/test/Tests/Model/Dashboard/modDashboardTest.php b/_build/test/Tests/Model/Dashboard/modDashboardTest.php index 6e93e0b2d8d..c3758fabe7f 100644 --- a/_build/test/Tests/Model/Dashboard/modDashboardTest.php +++ b/_build/test/Tests/Model/Dashboard/modDashboardTest.php @@ -1,4 +1,5 @@ modx); - $this->assertInstanceOf(modDashboard::class,$dashboard); + $this->assertInstanceOf(modDashboard::class, $dashboard); } /** * Ensure the rendering of the dashboard works properly * - * @medium + * @medium */ - public function testRender() { + public function testRender() + { /** @var modManagerController $controller Fake running the welcome controller */ $controller = new \WelcomeManagerController($this->modx, [ 'namespace' => 'core', @@ -67,4 +74,130 @@ public function testRender() { $output = $dashboard->render($controller); $this->assertNotEmpty($output); } + + /** + * Regression test for #15390: when the template dashboard (user=0) is saved with fewer + * widgets, user-specific placements must stay in sync and render() must not show removed widgets. + * #14753 is the original report; core behavior was addressed in #15390 — this test guards that path. + * + * @medium + */ + public function testRemovedWidgetsNotShownAfterUpdate() + { + $dashboardId = 10001; + $widget1Id = 10002; + $widget2Id = 10003; + + /** @var modDashboard $dashboard */ + $dashboard = $this->modx->newObject(modDashboard::class); + if ($dashboard === null) { + $this->markTestSkipped('modDashboard model not available'); + } + $dashboard->fromArray([ + 'id' => $dashboardId, + 'name' => 'Unit Test Dashboard Widget Removal', + 'description' => '', + 'customizable' => true, + ], '', true, true); + $dashboard->save(); + + foreach ([$widget1Id => 'Widget One', $widget2Id => 'Widget Two'] as $id => $name) { + /** @var modDashboardWidget|null $widget */ + $widget = $this->modx->newObject(modDashboardWidget::class); + if ($widget === null) { + $dashboard->remove(); + $this->markTestSkipped('modDashboardWidget model not available'); + } + $widget->fromArray([ + 'id' => $id, + 'name' => 'Unit Test ' . $name, + 'type' => 'html', + 'content' => '
' . $name . '
', + 'namespace' => 'core', + 'lexicon' => 'core:dashboards', + 'size' => 'half', + ], '', true, true); + $widget->save(); + } + + $placement1 = $this->modx->newObject(modDashboardWidgetPlacement::class); + if ($placement1 === null) { + $dashboard->remove(); + $this->modx->removeObject(modDashboardWidget::class, $widget1Id); + $this->modx->removeObject(modDashboardWidget::class, $widget2Id); + $this->markTestSkipped('modDashboardWidgetPlacement model not available'); + } + $placement1->fromArray([ + 'dashboard' => $dashboardId, + 'user' => 0, + 'widget' => $widget1Id, + 'rank' => 0, + ], '', true, true); + $placement1->save(); + + $placement2 = $this->modx->newObject(modDashboardWidgetPlacement::class); + if ($placement2 === null) { + $dashboard->remove(); + $this->modx->removeObject(modDashboardWidget::class, $widget1Id); + $this->modx->removeObject(modDashboardWidget::class, $widget2Id); + $this->markTestSkipped('modDashboardWidgetPlacement model not available'); + } + $placement2->fromArray([ + 'dashboard' => $dashboardId, + 'user' => 0, + 'widget' => $widget2Id, + 'rank' => 1, + ], '', true, true); + $placement2->save(); + + $controller = new \WelcomeManagerController($this->modx, [ + 'namespace' => 'core', + 'namespace_name' => 'core', + 'namespace_path' => MODX_MANAGER_PATH, + 'lang_topics' => 'dashboards', + 'controller' => 'system/dashboards', + ]); + + $dashboard->render($controller); + if ($this->modx->user === null) { + $dashboard->remove(); + $this->modx->removeCollection(modDashboardWidgetPlacement::class, ['dashboard' => $dashboardId]); + $this->modx->removeObject(modDashboardWidget::class, $widget1Id); + $this->modx->removeObject(modDashboardWidget::class, $widget2Id); + $this->markTestSkipped('modx user not available'); + } + $userId = $this->modx->user->get('id'); + $userPlacementsBefore = $this->modx->getCollection(modDashboardWidgetPlacement::class, [ + 'dashboard' => $dashboardId, + 'user' => $userId, + ]); + $this->assertCount(2, $userPlacementsBefore, 'User must have 2 placements after first render (addUserWidgets)'); + + $widgetsPayload = json_encode([ + ['widget' => $widget1Id, 'rank' => 0], + ]); + $result = $this->modx->runProcessor(Update::class, [ + 'id' => $dashboardId, + 'name' => 'Unit Test Dashboard Widget Removal', + 'widgets' => $widgetsPayload, + ]); + $this->assertFalse($result->isError(), 'Dashboard update must succeed: ' . $result->getMessage()); + + $userPlacementsAfter = $this->modx->getCollection(modDashboardWidgetPlacement::class, [ + 'dashboard' => $dashboardId, + 'user' => $userId, + ]); + $this->assertCount(1, $userPlacementsAfter, 'Removed widget must be removed from user placements'); + + $reloaded = $this->modx->getObject(modDashboard::class, $dashboardId); + $this->assertInstanceOf(modDashboard::class, $reloaded); + $output = $reloaded->render($controller); + $this->assertStringContainsString('Widget One', $output); + $this->assertStringNotContainsString('Widget Two', $output); + + $dashboard->remove(); + $this->modx->removeCollection(modDashboardWidgetPlacement::class, ['dashboard' => $dashboardId]); + $this->modx->removeObject(modDashboardWidget::class, $widget1Id); + $this->modx->removeObject(modDashboardWidget::class, $widget2Id); + } }