invokeArgs($oObject, $aArgs); } private function getMemberToolbar(): BxTemplMenuToolbar { $aObject = BxDolMenuQuery::getMenuObject('sys_toolbar_member'); if (empty($aObject)) $this->markTestSkipped('The member toolbar menu is not installed.'); return new BxTemplMenuToolbar($aObject, BxDolTemplate::getInstance()); } private function getMemberToolbarRows(BxTemplMenuToolbar $oMenu): array { $oQuery = new ReflectionProperty($oMenu, '_oQuery'); $aRows = []; foreach ($oQuery->getValue($oMenu)->getMenuItems() as $aRow) $aRows[$aRow['name']] = $aRow; if (!isset($aRows['main-menu'])) $this->markTestSkipped("This site has no '[Main Menu]' item in the member toolbar (the migration is not applied)."); return $aRows; } private static function isArtificer(BxTemplMenuToolbar $oMenu): bool { return strpos((new ReflectionClass($oMenu))->getFileName(), '/artificer/') !== false; } public function testFreshInstallPutsTheMainMenuInTheMemberToolbar(): void { $sSql = self::readFile('install/sql/system.sql'); $this->assertDoesNotMatchRegularExpression("/\\('sys_toolbar_site', 'system', 'main-menu',/", $sSql, 'The site toolbar no longer has it.'); $bFound = preg_match("/\\('sys_toolbar_member', 'system', 'main-menu', '_sys_menu_item_title_system_main_menu', [^\\n]*?, (\\d+), (\\d+), (\\d+), (\\d+), (\\d+)\\)[,;]\\n/", $sSql, $aMatch); $this->assertSame(1, $bFound, 'system.sql adds it to the member toolbar.'); [, $iVisibleFor, , $iActive, , $iOrder] = array_map('intval', $aMatch); $this->assertSame(2147483647, $iVisibleFor, 'Guests see it too.'); $this->assertSame(1, $iActive); $this->assertSame(9999, $iOrder, 'It stays the last button, also after modules add their own.'); $sAppSql = self::readFile('modules/system/install/sql/enable-app-menus.sql'); $this->assertStringContainsString("SET `active_api`=1 WHERE `set_name`='sys_toolbar_member' AND `module`='system' AND `name`='main-menu';", $sAppSql); $this->assertStringNotContainsString("`set_name`='sys_toolbar_site' AND `module`='system' AND `name`='main-menu'", $sAppSql); } public function testHeaderHasNoLeftToolbar(): void { foreach (['template/_page_toolbar.html', 'modules/boonex/artificer/template/header.html'] as $sPath) { $sHtml = self::readFile($sPath); $this->assertStringNotContainsString('', $sHtml, $sPath . ' renders nothing left of the logo.'); $this->assertStringContainsString('', $sHtml, $sPath); } } public function testMainMenuOpensTheSiteSidebar(): void { $oMenu = $this->getMemberToolbar(); if (!self::isArtificer($oMenu)) $this->markTestSkipped('The site sidebar belongs to the Artificer template.'); $aRows = $this->getMemberToolbarRows($oMenu); $a = $this->callProtected($oMenu, '_getMenuItem', [$aRows['main-menu']]); $this->assertIsArray($a); $this->assertStringContainsString('bx-sidebar-site-trigger', $a['class_add_a'], 'The sidebar script binds to this class.'); $this->assertFalse($a['bx_if:onclick']['condition'], 'No sliding menu opens on top of the sidebar.'); $this->assertContains('lg:hidden', explode(' ', $a['class_add']), 'Wide screens show the main menu inline and have no sidebar.'); $this->assertStringContainsString('aria-controls="bx-sidebar-site"', $a['attrs']); $this->assertStringContainsString('aria-expanded="false"', $a['attrs']); } public function testApiReadsTheToolbarWithoutWarnings(): void { $mixedApiBackup = $_GET['api'] ?? null; $_GET['api'] = 1; $aWarnings = []; set_error_handler(function ($iErrno, $sError, $sFile, $iLine) use (&$aWarnings) { $aWarnings[] = $sError . ' in ' . $sFile . ':' . $iLine; return true; }, E_WARNING | E_NOTICE); try { //--- Show every item, as a signed-in member sees 'add-content', 'account' and the rest. $aObject = BxDolMenuQuery::getMenuObject('sys_toolbar_member'); if (empty($aObject)) $this->markTestSkipped('The member toolbar menu is not installed.'); $oMenu = new class($aObject, BxDolTemplate::getInstance()) extends BxTemplMenuToolbar { protected function _isVisible($a) { return true; } }; $aItems = []; foreach ($this->getMemberToolbarRows($oMenu) as $sName => $aRow) if (($a = $this->callProtected($oMenu, '_getMenuItem', [$aRow])) !== false) $aItems[$sName] = $a; } finally { restore_error_handler(); if ($mixedApiBackup === null) unset($_GET['api']); else $_GET['api'] = $mixedApiBackup; } $this->assertSame([], $aWarnings, 'A PHP warning printed into api.php output breaks the JSON NEO reads.'); $this->assertArrayHasKey('main-menu', $aItems); $this->assertArrayHasKey('add-content', $aItems, 'Members-only items are rendered too.'); $this->assertArrayNotHasKey('class_add', $aItems['main-menu'], 'HTML-only classes stay out of the API item.'); } }