From 0f8fff8daf62a9c8c2d6945c9f375de335633b76 Mon Sep 17 00:00:00 2001 From: fs Date: Thu, 19 Mar 2026 18:34:02 +0100 Subject: [PATCH] Split frontend runtime contracts --- phpunit.xml | 4 +- .../FrontendComponentRuntimeContractTest.php | 227 ------------------ .../FrontendRuntimeComponentContractTest.php | 104 ++++++++ .../FrontendRuntimeContractSupport.php | 40 +++ .../FrontendRuntimeEntrypointContractTest.php | 56 +++++ .../FrontendRuntimeHostContractTest.php | 60 +++++ tests/README.md | 2 +- 7 files changed, 264 insertions(+), 229 deletions(-) delete mode 100644 tests/Architecture/FrontendComponentRuntimeContractTest.php create mode 100644 tests/Architecture/FrontendRuntimeComponentContractTest.php create mode 100644 tests/Architecture/FrontendRuntimeContractSupport.php create mode 100644 tests/Architecture/FrontendRuntimeEntrypointContractTest.php create mode 100644 tests/Architecture/FrontendRuntimeHostContractTest.php diff --git a/phpunit.xml b/phpunit.xml index 1e9232e..c6eba8d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -52,7 +52,9 @@ tests/Architecture/DetailPageContractTest.php tests/Architecture/DetailValidationSummaryContractTest.php tests/Architecture/FilterDrawerContractTest.php - tests/Architecture/FrontendComponentRuntimeContractTest.php + tests/Architecture/FrontendRuntimeComponentContractTest.php + tests/Architecture/FrontendRuntimeEntrypointContractTest.php + tests/Architecture/FrontendRuntimeHostContractTest.php tests/Architecture/ListActionContractTest.php tests/Architecture/ListDataEndpointContractTest.php tests/Architecture/ListTemplateContractTest.php diff --git a/tests/Architecture/FrontendComponentRuntimeContractTest.php b/tests/Architecture/FrontendComponentRuntimeContractTest.php deleted file mode 100644 index b9211ad..0000000 --- a/tests/Architecture/FrontendComponentRuntimeContractTest.php +++ /dev/null @@ -1,227 +0,0 @@ -readProjectFile('web/js/app-init.js'); - $loginEntry = $this->readProjectFile('web/js/app-login-init.js'); - - $this->assertStringContainsString("import { createComponentRuntime } from './core/app-component-runtime.js';", $defaultEntry); - $this->assertStringContainsString("import { createComponentRuntime } from './core/app-component-runtime.js';", $loginEntry); - $this->assertDoesNotMatchRegularExpression('/import\s+[\'"]\.\/components\//', $defaultEntry); - $this->assertDoesNotMatchRegularExpression('/import\s+[\'"]\.\/components\//', $loginEntry); - - $this->assertStringContainsString("runtime.register('sidebar-toggle', initSidebarToggle", $defaultEntry); - $this->assertStringContainsString("runtime.register('global-search', initGlobalSearch", $defaultEntry); - $this->assertStringContainsString("await mountModuleComponentsByPhase('late');", $defaultEntry); - $this->assertStringContainsString('moduleRuntimeComponents', $defaultEntry); - $this->assertStringContainsString("selector: '[data-app-component=\"tabs\"]'", $defaultEntry); - $this->assertStringContainsString('runtime.mountAll();', $defaultEntry); - - $this->assertStringContainsString("runtime.register('password-hints', initPasswordHints", $loginEntry); - $this->assertStringContainsString("runtime.register('password-toggle', initPasswordToggles", $loginEntry); - $this->assertStringContainsString("runtime.register('login-submit-lock', initLoginSubmitLock", $loginEntry); - $this->assertStringContainsString('runtime.mountAll();', $loginEntry); - } - - public function testRuntimeMigratedComponentsDoNotAutoInitialize(): void - { - $componentFiles = [ - 'web/js/components/app-flash-auto-dismiss.js', - 'web/js/components/app-fslightbox-refresh.js', - 'web/js/components/app-password-hints.js', - 'web/js/components/app-copy-badge.js', - 'web/js/components/app-multiselect-init.js', - 'web/js/components/app-nav-history.js', - 'web/js/components/app-toggle-sidebar.js', - 'web/js/components/app-toggle-aside-panels.js', - 'web/js/components/app-toggle-details-aside.js', - 'web/js/components/app-global-search.js', - 'web/js/components/app-theme-toggle.js', - 'web/js/components/app-contrast-toggle.js', - 'web/js/components/app-confirm-actions.js', - 'web/js/components/app-details-state.js', - 'web/js/components/app-tenant-switcher.js', - 'web/js/components/app-color-default-toggle.js', - 'web/js/components/app-custom-field-options-toggle.js', - 'web/js/components/app-tenant-sso-toggle.js', - 'web/js/components/app-settings-telemetry.js', - 'web/js/components/app-standard-detail-page.js', - 'web/js/components/app-auto-submit.js', - 'modules/bookmarks/web/js/components/app-bookmark-save.js', - 'modules/bookmarks/web/js/components/app-bookmark-panel.js', - 'web/js/components/app-password-toggle.js', - 'web/js/components/app-page-editor.js', - ]; - - foreach ($componentFiles as $file) { - $content = $this->readProjectFile($file); - $this->assertStringNotContainsString('DOMContentLoaded', $content, $file); - $this->assertStringNotContainsString("if (document.readyState === 'loading')", $content, $file); - } - } - - public function testMigratedComponentsExposeDestroyCleanupPaths(): void - { - $sidebar = $this->readProjectFile('web/js/components/app-toggle-sidebar.js'); - $aside = $this->readProjectFile('web/js/components/app-toggle-aside-panels.js'); - $search = $this->readProjectFile('web/js/components/app-global-search.js'); - $bookmarkSave = $this->readProjectFile('modules/bookmarks/web/js/components/app-bookmark-save.js'); - $bookmarkPanel = $this->readProjectFile('modules/bookmarks/web/js/components/app-bookmark-panel.js'); - $multiSelect = $this->readProjectFile('web/js/components/app-multiselect-init.js'); - - $this->assertStringContainsString('destroy: () => {', $sidebar); - $this->assertStringContainsString("cleanupFns.push(() => document.removeEventListener('keydown', onShortcut));", $sidebar); - - $this->assertStringContainsString('destroy: () => {', $aside); - $this->assertStringContainsString("cleanupFns.push(() => document.removeEventListener('keydown', onKeyDown));", $aside); - - $this->assertStringContainsString('const destroy = () => {', $search); - $this->assertStringContainsString("document.removeEventListener('keydown', onDocumentKeydown);", $search); - - $this->assertStringContainsString('const destroy = () => {', $bookmarkSave); - $this->assertStringContainsString('abortController.abort();', $bookmarkSave); - - $this->assertStringContainsString('const destroy = () => {', $bookmarkPanel); - $this->assertStringContainsString('abortController.abort();', $bookmarkPanel); - $this->assertStringNotContainsString('export const init =', $bookmarkSave); - $this->assertStringNotContainsString('export const init =', $bookmarkPanel); - - $this->assertStringContainsString('return {', $multiSelect); - $this->assertStringContainsString('destroy: () => {', $multiSelect); - } - - public function testBookmarkLegacyEntrypointFileIsRemoved(): void - { - $this->assertFileDoesNotExist($this->projectRootPath() . '/web/js/components/app-bookmark-init.js'); - } - - public function testRuntimeStorageContractUsesSharedUiStorageHelper(): void - { - $files = [ - 'web/js/components/app-toggle-sidebar.js', - 'web/js/components/app-toggle-aside-panels.js', - 'web/js/components/app-toggle-details-aside.js', - 'web/js/components/app-contrast-toggle.js', - 'web/js/components/app-tabs.js', - 'web/js/core/app-details-open-state.js', - 'web/js/core/app-grid-factory.js', - ]; - - foreach ($files as $file) { - $content = $this->readProjectFile($file); - $this->assertStringNotContainsString('window.localStorage', $content, $file); - $this->assertStringContainsString('createUiStorage', $content, $file); - } - } - - public function testHostBoundComponentsExposeDataAppComponentMarkupHosts(): void - { - $defaultTemplate = $this->readProjectFile('templates/default.phtml'); - $loginTemplate = $this->readProjectFile('templates/login.phtml'); - $flashPartial = $this->readProjectFile('templates/partials/app-flash.phtml'); - $topbarPartial = $this->readProjectFile('templates/partials/app-topbar.phtml'); - $asideIconBar = $this->readProjectFile('templates/partials/app-main-aside-icon-bar.phtml'); - $asidePartial = $this->readProjectFile('templates/partials/app-main-aside.phtml'); - $bookmarkDialog = $this->readProjectFile('modules/bookmarks/templates/bookmark-dialogs.phtml'); - $bookmarkPanelTemplate = $this->readProjectFile('modules/bookmarks/templates/aside-bookmarks-panel.phtml'); - $sessionWarningDialog = $this->readProjectFile('templates/partials/app-session-warning-dialog.phtml'); - - $this->assertStringContainsString('id="page-config-app-components"', $defaultTemplate); - $this->assertStringContainsString('id="page-config-login-components"', $loginTemplate); - $this->assertStringNotContainsString("assetVersion('js/components/app-bookmark-init.js')", $defaultTemplate); - - $this->assertStringContainsString('data-app-component="flash-auto-dismiss"', $flashPartial); - $this->assertStringContainsString('data-app-component="nav-history"', $topbarPartial); - $this->assertStringContainsString('data-app-component="tenant-switcher"', $topbarPartial); - $this->assertStringContainsString('data-app-component="theme-controls"', $topbarPartial); - $this->assertStringContainsString('data-app-component="contrast-toggle"', $topbarPartial); - $this->assertStringContainsString('data-app-component="details-aside-toggle"', $topbarPartial); - $this->assertStringContainsString('data-app-component="aside-panels"', $asideIconBar); - $this->assertStringContainsString('data-app-component="sidebar-toggle"', $asidePartial); - $this->assertStringContainsString('data-app-component="global-search"', $asidePartial); - $this->assertStringContainsString('data-app-component="bookmark-panel"', $bookmarkPanelTemplate); - $this->assertStringContainsString('data-app-component="bookmark-save"', $bookmarkDialog); - $this->assertStringContainsString('data-app-component="session-warning"', $sessionWarningDialog); - } - - public function testTabsHostsUseDataAppComponentContract(): void - { - $files = [ - 'modules/addressbook/pages/address-book/view(default).phtml', - 'pages/admin/departments/_form.phtml', - 'pages/admin/permissions/_form.phtml', - 'pages/admin/roles/_form.phtml', - 'pages/admin/settings/index(default).phtml', - 'pages/admin/stats/index(default).phtml', - 'pages/admin/tenants/_form.phtml', - 'pages/admin/users/_form.phtml', - ]; - - foreach ($files as $file) { - $content = $this->readProjectFile($file); - $this->assertStringContainsString('data-tabs', $content, $file); - $this->assertStringContainsString('data-app-component="tabs"', $content, $file); - } - } - - public function testPageEditorUsesPageModuleEntrypointWithPageConfig(): void - { - $pageEditor = $this->readProjectFile('web/js/components/app-page-editor.js'); - $pageEditorEntry = $this->readProjectFile('web/js/pages/app-page-editor-entry.js'); - $adminPage = $this->readProjectFile('pages/page/index(default).phtml'); - $publicPage = $this->readProjectFile('pages/public-page/index(page).phtml'); - - $this->assertStringContainsString('export function initPageEditor(root = document, config = {})', $pageEditor); - $this->assertStringNotContainsString('const form = document.querySelector', $pageEditor); - $this->assertStringContainsString("readPageConfig('page-editor')", $pageEditorEntry); - $this->assertStringContainsString("assetVersion('js/pages/app-page-editor-entry.js')", $adminPage); - $this->assertStringContainsString("assetVersion('js/pages/app-page-editor-entry.js')", $publicPage); - $this->assertStringNotContainsString("assetVersion('js/components/app-page-editor.js')", $adminPage); - $this->assertStringNotContainsString("assetVersion('js/components/app-page-editor.js')", $publicPage); - $this->assertStringContainsString('id="page-config-page-editor"', $adminPage); - $this->assertStringContainsString('id="page-config-page-editor"', $publicPage); - } - - public function testNoCustomWindowGlobalsRemainInWebJs(): void - { - $violations = $this->findPatternMatchesInFiles( - 'web/js', - '/window\.(AppSidebar|AppAsidePanels|requestFsLightboxRefresh|__fsLightboxRefreshPending|APP_ASSET_BASE|APP_BASE)\b/', - ['js'] - ); - - $this->assertSame([], $violations, 'Custom window globals found in web/js: ' . implode(', ', $violations)); - } - - public function testHostBoundRuntimeComponentsAreRootStrictWithoutDocumentFallbackQuery(): void - { - $files = [ - 'web/js/components/app-global-search.js', - 'modules/bookmarks/web/js/components/app-bookmark-panel.js', - 'web/js/components/app-tabs.js', - ]; - - foreach ($files as $file) { - $content = $this->readProjectFile($file); - $this->assertDoesNotMatchRegularExpression('/\|\|\s*document\.querySelector/', $content, $file); - } - } - - public function testDomBindingUtilitiesExposeDestroyLifecycle(): void - { - $activeFilterChips = $this->readProjectFile('web/js/components/app-active-filter-chips.js'); - $multiselectCascade = $this->readProjectFile('web/js/components/app-multiselect-cascade.js'); - - $this->assertStringContainsString('return { render, destroy };', $activeFilterChips); - $this->assertStringContainsString('return {', $multiselectCascade); - $this->assertStringContainsString('destroy: () => {', $multiselectCascade); - } -} diff --git a/tests/Architecture/FrontendRuntimeComponentContractTest.php b/tests/Architecture/FrontendRuntimeComponentContractTest.php new file mode 100644 index 0000000..2ffba1c --- /dev/null +++ b/tests/Architecture/FrontendRuntimeComponentContractTest.php @@ -0,0 +1,104 @@ +runtimeMigratedComponentFiles() as $file) { + $content = $this->readProjectFile($file); + $this->assertStringNotContainsString('DOMContentLoaded', $content, $file); + $this->assertStringNotContainsString("if (document.readyState === 'loading')", $content, $file); + } + } + + public function testMigratedComponentsExposeDestroyCleanupPaths(): void + { + $sidebar = $this->readProjectFile('web/js/components/app-toggle-sidebar.js'); + $aside = $this->readProjectFile('web/js/components/app-toggle-aside-panels.js'); + $search = $this->readProjectFile('web/js/components/app-global-search.js'); + $bookmarkSave = $this->readProjectFile('modules/bookmarks/web/js/components/app-bookmark-save.js'); + $bookmarkPanel = $this->readProjectFile('modules/bookmarks/web/js/components/app-bookmark-panel.js'); + $multiSelect = $this->readProjectFile('web/js/components/app-multiselect-init.js'); + + $this->assertStringContainsString('destroy: () => {', $sidebar); + $this->assertStringContainsString("cleanupFns.push(() => document.removeEventListener('keydown', onShortcut));", $sidebar); + + $this->assertStringContainsString('destroy: () => {', $aside); + $this->assertStringContainsString("cleanupFns.push(() => document.removeEventListener('keydown', onKeyDown));", $aside); + + $this->assertStringContainsString('const destroy = () => {', $search); + $this->assertStringContainsString("document.removeEventListener('keydown', onDocumentKeydown);", $search); + + $this->assertStringContainsString('const destroy = () => {', $bookmarkSave); + $this->assertStringContainsString('abortController.abort();', $bookmarkSave); + + $this->assertStringContainsString('const destroy = () => {', $bookmarkPanel); + $this->assertStringContainsString('abortController.abort();', $bookmarkPanel); + $this->assertStringNotContainsString('export const init =', $bookmarkSave); + $this->assertStringNotContainsString('export const init =', $bookmarkPanel); + + $this->assertStringContainsString('return {', $multiSelect); + $this->assertStringContainsString('destroy: () => {', $multiSelect); + } + + public function testRuntimeStorageContractUsesSharedUiStorageHelper(): void + { + $files = [ + 'web/js/components/app-toggle-sidebar.js', + 'web/js/components/app-toggle-aside-panels.js', + 'web/js/components/app-toggle-details-aside.js', + 'web/js/components/app-contrast-toggle.js', + 'web/js/components/app-tabs.js', + 'web/js/core/app-details-open-state.js', + 'web/js/core/app-grid-factory.js', + ]; + + foreach ($files as $file) { + $content = $this->readProjectFile($file); + $this->assertStringNotContainsString('window.localStorage', $content, $file); + $this->assertStringContainsString('createUiStorage', $content, $file); + } + } + + public function testNoCustomWindowGlobalsRemainInWebJs(): void + { + $violations = $this->findPatternMatchesInFiles( + 'web/js', + '/window\.(AppSidebar|AppAsidePanels|requestFsLightboxRefresh|__fsLightboxRefreshPending|APP_ASSET_BASE|APP_BASE)\b/', + ['js'] + ); + + $this->assertSame([], $violations, 'Custom window globals found in web/js: ' . implode(', ', $violations)); + } + + public function testHostBoundRuntimeComponentsAreRootStrictWithoutDocumentFallbackQuery(): void + { + $files = [ + 'web/js/components/app-global-search.js', + 'modules/bookmarks/web/js/components/app-bookmark-panel.js', + 'web/js/components/app-tabs.js', + ]; + + foreach ($files as $file) { + $content = $this->readProjectFile($file); + $this->assertDoesNotMatchRegularExpression('/\|\|\s*document\.querySelector/', $content, $file); + } + } + + public function testDomBindingUtilitiesExposeDestroyLifecycle(): void + { + $activeFilterChips = $this->readProjectFile('web/js/components/app-active-filter-chips.js'); + $multiselectCascade = $this->readProjectFile('web/js/components/app-multiselect-cascade.js'); + + $this->assertStringContainsString('return { render, destroy };', $activeFilterChips); + $this->assertStringContainsString('return {', $multiselectCascade); + $this->assertStringContainsString('destroy: () => {', $multiselectCascade); + } +} diff --git a/tests/Architecture/FrontendRuntimeContractSupport.php b/tests/Architecture/FrontendRuntimeContractSupport.php new file mode 100644 index 0000000..e9d4305 --- /dev/null +++ b/tests/Architecture/FrontendRuntimeContractSupport.php @@ -0,0 +1,40 @@ + + */ + private function runtimeMigratedComponentFiles(): array + { + return [ + 'web/js/components/app-flash-auto-dismiss.js', + 'web/js/components/app-fslightbox-refresh.js', + 'web/js/components/app-password-hints.js', + 'web/js/components/app-copy-badge.js', + 'web/js/components/app-multiselect-init.js', + 'web/js/components/app-nav-history.js', + 'web/js/components/app-toggle-sidebar.js', + 'web/js/components/app-toggle-aside-panels.js', + 'web/js/components/app-toggle-details-aside.js', + 'web/js/components/app-global-search.js', + 'web/js/components/app-theme-toggle.js', + 'web/js/components/app-contrast-toggle.js', + 'web/js/components/app-confirm-actions.js', + 'web/js/components/app-details-state.js', + 'web/js/components/app-tenant-switcher.js', + 'web/js/components/app-color-default-toggle.js', + 'web/js/components/app-custom-field-options-toggle.js', + 'web/js/components/app-tenant-sso-toggle.js', + 'web/js/components/app-settings-telemetry.js', + 'web/js/components/app-standard-detail-page.js', + 'web/js/components/app-auto-submit.js', + 'modules/bookmarks/web/js/components/app-bookmark-save.js', + 'modules/bookmarks/web/js/components/app-bookmark-panel.js', + 'web/js/components/app-password-toggle.js', + 'web/js/components/app-page-editor.js', + ]; + } +} diff --git a/tests/Architecture/FrontendRuntimeEntrypointContractTest.php b/tests/Architecture/FrontendRuntimeEntrypointContractTest.php new file mode 100644 index 0000000..a97181a --- /dev/null +++ b/tests/Architecture/FrontendRuntimeEntrypointContractTest.php @@ -0,0 +1,56 @@ +readProjectFile('web/js/app-init.js'); + $loginEntry = $this->readProjectFile('web/js/app-login-init.js'); + + $this->assertStringContainsString("import { createComponentRuntime } from './core/app-component-runtime.js';", $defaultEntry); + $this->assertStringContainsString("import { createComponentRuntime } from './core/app-component-runtime.js';", $loginEntry); + $this->assertDoesNotMatchRegularExpression('/import\s+[\'"]\.\/components\//', $defaultEntry); + $this->assertDoesNotMatchRegularExpression('/import\s+[\'"]\.\/components\//', $loginEntry); + + $this->assertStringContainsString("runtime.register('sidebar-toggle', initSidebarToggle", $defaultEntry); + $this->assertStringContainsString("runtime.register('global-search', initGlobalSearch", $defaultEntry); + $this->assertStringContainsString("await mountModuleComponentsByPhase('late');", $defaultEntry); + $this->assertStringContainsString('moduleRuntimeComponents', $defaultEntry); + $this->assertStringContainsString("selector: '[data-app-component=\"tabs\"]'", $defaultEntry); + $this->assertStringContainsString('runtime.mountAll();', $defaultEntry); + + $this->assertStringContainsString("runtime.register('password-hints', initPasswordHints", $loginEntry); + $this->assertStringContainsString("runtime.register('password-toggle', initPasswordToggles", $loginEntry); + $this->assertStringContainsString("runtime.register('login-submit-lock', initLoginSubmitLock", $loginEntry); + $this->assertStringContainsString('runtime.mountAll();', $loginEntry); + } + + public function testBookmarkLegacyEntrypointFileIsRemoved(): void + { + $this->assertFileDoesNotExist($this->projectRootPath() . '/web/js/components/app-bookmark-init.js'); + } + + public function testPageEditorUsesPageModuleEntrypointWithPageConfig(): void + { + $pageEditor = $this->readProjectFile('web/js/components/app-page-editor.js'); + $pageEditorEntry = $this->readProjectFile('web/js/pages/app-page-editor-entry.js'); + $adminPage = $this->readProjectFile('pages/page/index(default).phtml'); + $publicPage = $this->readProjectFile('pages/public-page/index(page).phtml'); + + $this->assertStringContainsString('export function initPageEditor(root = document, config = {})', $pageEditor); + $this->assertStringNotContainsString('const form = document.querySelector', $pageEditor); + $this->assertStringContainsString("readPageConfig('page-editor')", $pageEditorEntry); + $this->assertStringContainsString("assetVersion('js/pages/app-page-editor-entry.js')", $adminPage); + $this->assertStringContainsString("assetVersion('js/pages/app-page-editor-entry.js')", $publicPage); + $this->assertStringNotContainsString("assetVersion('js/components/app-page-editor.js')", $adminPage); + $this->assertStringNotContainsString("assetVersion('js/components/app-page-editor.js')", $publicPage); + $this->assertStringContainsString('id="page-config-page-editor"', $adminPage); + $this->assertStringContainsString('id="page-config-page-editor"', $publicPage); + } +} diff --git a/tests/Architecture/FrontendRuntimeHostContractTest.php b/tests/Architecture/FrontendRuntimeHostContractTest.php new file mode 100644 index 0000000..69660f9 --- /dev/null +++ b/tests/Architecture/FrontendRuntimeHostContractTest.php @@ -0,0 +1,60 @@ +readProjectFile('templates/default.phtml'); + $loginTemplate = $this->readProjectFile('templates/login.phtml'); + $flashPartial = $this->readProjectFile('templates/partials/app-flash.phtml'); + $topbarPartial = $this->readProjectFile('templates/partials/app-topbar.phtml'); + $asideIconBar = $this->readProjectFile('templates/partials/app-main-aside-icon-bar.phtml'); + $asidePartial = $this->readProjectFile('templates/partials/app-main-aside.phtml'); + $bookmarkDialog = $this->readProjectFile('modules/bookmarks/templates/bookmark-dialogs.phtml'); + $bookmarkPanelTemplate = $this->readProjectFile('modules/bookmarks/templates/aside-bookmarks-panel.phtml'); + $sessionWarningDialog = $this->readProjectFile('templates/partials/app-session-warning-dialog.phtml'); + + $this->assertStringContainsString('id="page-config-app-components"', $defaultTemplate); + $this->assertStringContainsString('id="page-config-login-components"', $loginTemplate); + $this->assertStringNotContainsString("assetVersion('js/components/app-bookmark-init.js')", $defaultTemplate); + + $this->assertStringContainsString('data-app-component="flash-auto-dismiss"', $flashPartial); + $this->assertStringContainsString('data-app-component="nav-history"', $topbarPartial); + $this->assertStringContainsString('data-app-component="tenant-switcher"', $topbarPartial); + $this->assertStringContainsString('data-app-component="theme-controls"', $topbarPartial); + $this->assertStringContainsString('data-app-component="contrast-toggle"', $topbarPartial); + $this->assertStringContainsString('data-app-component="details-aside-toggle"', $topbarPartial); + $this->assertStringContainsString('data-app-component="aside-panels"', $asideIconBar); + $this->assertStringContainsString('data-app-component="sidebar-toggle"', $asidePartial); + $this->assertStringContainsString('data-app-component="global-search"', $asidePartial); + $this->assertStringContainsString('data-app-component="bookmark-panel"', $bookmarkPanelTemplate); + $this->assertStringContainsString('data-app-component="bookmark-save"', $bookmarkDialog); + $this->assertStringContainsString('data-app-component="session-warning"', $sessionWarningDialog); + } + + public function testTabsHostsUseDataAppComponentContract(): void + { + $files = [ + 'modules/addressbook/pages/address-book/view(default).phtml', + 'pages/admin/departments/_form.phtml', + 'pages/admin/permissions/_form.phtml', + 'pages/admin/roles/_form.phtml', + 'pages/admin/settings/index(default).phtml', + 'pages/admin/stats/index(default).phtml', + 'pages/admin/tenants/_form.phtml', + 'pages/admin/users/_form.phtml', + ]; + + foreach ($files as $file) { + $content = $this->readProjectFile($file); + $this->assertStringContainsString('data-tabs', $content, $file); + $this->assertStringContainsString('data-app-component="tabs"', $content, $file); + } + } +} diff --git a/tests/README.md b/tests/README.md index 43c427a..6167adf 100644 --- a/tests/README.md +++ b/tests/README.md @@ -32,8 +32,8 @@ ohne dass jede Aenderung immer die komplette Suite im Kopf behalten muss. ## Naechste Refactor-Kandidaten -- `tests/Architecture/FrontendComponentRuntimeContractTest.php` - `tests/Architecture/FilterDrawerContractTest.php` - `tests/Architecture/PublicPageContractTest.php` - `tests/Architecture/CodexSkillsContractTest.php` - `tests/Architecture/ModuleRegistryContractTest.php` +- `tests/Architecture/AgentSystemContractTest.php`