mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-17 20:34:19 +08:00
- 延迟隐藏资源树内容,使侧栏宽度收起动画保持连续 - 稳定 Sidebar 属性并保留已加载节点,避免大量对象触发重复渲染 - 覆盖 V2、旧版与减少动态效果场景,补充折叠回归测试 - 关联问题:#697
128 lines
9.6 KiB
TypeScript
128 lines
9.6 KiB
TypeScript
import { readFileSync } from 'node:fs';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
const appSource = readFileSync(new URL('./App.tsx', import.meta.url), 'utf8');
|
|
const appCssSource = readFileSync(new URL('./App.css', import.meta.url), 'utf8');
|
|
const sidebarSource = readFileSync(new URL('./components/Sidebar.tsx', import.meta.url), 'utf8');
|
|
const connectionRailSource = readFileSync(new URL('./components/sidebar/SidebarConnectionRail.tsx', import.meta.url), 'utf8');
|
|
|
|
describe('app sidebar tree panel collapse', () => {
|
|
it('collapses v2 to the scaled fixed rail while preserving the saved expanded width', () => {
|
|
expect(appSource).toContain('const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);');
|
|
expect(appSource).toContain('const sidebarCollapsedWidth = isV2Ui ? 38 * effectiveUiScale * effectiveSidebarRailScale : 0;');
|
|
expect(appSource).toContain('const renderedSidebarWidth = isSidebarCollapsed ? sidebarCollapsedWidth : sidebarWidth;');
|
|
expect(appSource).toContain('width={sidebarWidth}');
|
|
expect(appSource).toContain('collapsed={isSidebarCollapsed}');
|
|
expect(appSource).toContain('collapsedWidth={sidebarCollapsedWidth}');
|
|
expect(appSource).toContain("['--gonavi-sidebar-collapsed-width' as any]: `${sidebarCollapsedWidth}px`");
|
|
expect(appSource).toContain('trigger={null}');
|
|
expect(appSource).not.toContain('setSidebarWidth(0)');
|
|
});
|
|
|
|
it('keeps loaded tree nodes mounted and Sidebar props stable during the width transition', () => {
|
|
expect(appSource).toContain("const sidebarPanelCollapseLabel = t('app.sidebar.collapse');");
|
|
expect(appSource).toContain("const sidebarPanelExpandLabel = t('app.sidebar.expand');");
|
|
expect(appSource).toContain('onCollapseSidebar={isV2Ui ? handleCollapseSidebarPanel : undefined}');
|
|
expect(appSource).toContain('onExpandSidebar={isV2Ui ? handleExpandSidebarPanel : undefined}');
|
|
expect(appSource).not.toContain('isTreePanelCollapsed={isV2Ui && isSidebarCollapsed}');
|
|
expect(sidebarSource).not.toContain('isTreePanelCollapsed?: boolean;');
|
|
expect(sidebarSource).not.toContain("display: isV2Ui && isTreePanelCollapsed ? 'none' : 'flex'");
|
|
expect(appSource).toContain('data-sidebar-content="true"');
|
|
expect(appCssSource).toContain('--gonavi-sidebar-collapse-duration: 200ms;');
|
|
expect(appCssSource).toMatch(
|
|
/\.ant-layout-sider\[data-sidebar-collapsed='true'\]\s+\[data-sidebar-tree-panel='true'\][^{]*\{[^}]*visibility:\s*hidden;[^}]*transition:\s*visibility 0s linear var\(--gonavi-sidebar-collapse-duration\);/s,
|
|
);
|
|
expect(appCssSource).toMatch(
|
|
/\.ant-layout-sider\[data-sidebar-collapsed='false'\]\s+\.gn-v2-rail-sidebar-toggle-slot\s*\{[^}]*display:\s*none;/s,
|
|
);
|
|
});
|
|
|
|
it('keeps the fixed rail visible and hides only the v2 explorer tree panel', () => {
|
|
expect(sidebarSource).toContain("id={isV2Ui ? 'gonavi-sidebar-tree-panel' : undefined}");
|
|
expect(sidebarSource).toContain("data-sidebar-tree-panel={isV2Ui ? 'true' : undefined}");
|
|
expect(sidebarSource).toContain("style={{ display: 'flex', flexDirection: 'column'");
|
|
expect(connectionRailSource).toContain('data-sidebar-fixed-rail="true"');
|
|
expect(appSource).not.toContain('isTreePanelCollapsed={isV2Ui && isSidebarCollapsed}');
|
|
expect(appSource).not.toContain("visibility: !isV2Ui && isSidebarCollapsed ? 'hidden' : 'visible'");
|
|
expect(appSource).toContain('data-sidebar-collapse-trigger="true"');
|
|
expect(appSource).toContain('data-titlebar-brand-region="true"');
|
|
expect(appSource).toContain('data-sidebar-toggle-placement="titlebar"');
|
|
expect(connectionRailSource).toContain('data-sidebar-toggle-placement="fixed-rail"');
|
|
expect(appSource).toContain('data-no-titlebar-toggle="true"');
|
|
expect(appSource).toContain('aria-controls="gonavi-sidebar-tree-panel"');
|
|
expect(appSource).toContain('aria-expanded={!isSidebarCollapsed}');
|
|
expect(appSource).toContain('<MenuFoldOutlined />');
|
|
expect(appSource).toContain('<MenuUnfoldOutlined />');
|
|
expect(appSource).toContain("'app.sidebar.collapse'");
|
|
expect(appSource).toContain("'app.sidebar.expand'");
|
|
expect(appSource).toContain("case 'focusSidebarSearch':");
|
|
expect(appSource).toContain('handleFocusSidebarSearch();');
|
|
expect(appSource).toContain('<TabManager onFocusSidebarSearch={handleFocusSidebarSearch} />');
|
|
expect(appSource).toContain('{!isV2Ui && (');
|
|
expect(appSource).toContain('onCollapseSidebar={isV2Ui ? handleCollapseSidebarPanel : undefined}');
|
|
expect(appSource).toContain('onExpandSidebar={isV2Ui ? handleExpandSidebarPanel : undefined}');
|
|
expect(appSource).toContain('collapseSidebarButtonRef={sidebarExplorerToggleRef}');
|
|
expect(appSource).toContain('expandSidebarButtonRef={sidebarCollapsedToggleRef}');
|
|
expect(appSource).toContain('ref={sidebarCollapsedToggleRef}');
|
|
expect(appSource).toContain("pendingSidebarToggleFocusRef.current = 'collapsed'");
|
|
expect(appSource).toContain("pendingSidebarToggleFocusRef.current = 'explorer'");
|
|
expect(appSource).toContain("(target === 'collapsed' ? sidebarCollapsedToggleRef : sidebarExplorerToggleRef).current?.focus()");
|
|
expect(sidebarSource).toContain('data-sidebar-toggle-placement="explorer-header"');
|
|
|
|
const titlebarToggleIndex = appSource.indexOf('data-sidebar-collapse-trigger="true"');
|
|
const siderIndex = appSource.indexOf('<Sider');
|
|
const siderEndIndex = appSource.indexOf('</Sider>', siderIndex);
|
|
const triggerStartIndex = appSource.lastIndexOf('<Button', titlebarToggleIndex);
|
|
const triggerEndIndex = appSource.indexOf('</Tooltip>', titlebarToggleIndex);
|
|
const triggerSource = appSource.slice(triggerStartIndex, triggerEndIndex);
|
|
const siderSource = appSource.slice(siderIndex, siderEndIndex);
|
|
const explorerActionsIndex = sidebarSource.indexOf('<div className="gn-v2-active-connection-actions">');
|
|
const fixedRailIndex = sidebarSource.indexOf('<SidebarConnectionRail');
|
|
const explorerPanelIndex = sidebarSource.indexOf("id={isV2Ui ? 'gonavi-sidebar-tree-panel' : undefined}");
|
|
const connectionMenuIndex = sidebarSource.indexOf('<Tooltip title={v2ConnectionActionsLabel}>', explorerActionsIndex);
|
|
const explorerToggleIndex = sidebarSource.indexOf('data-sidebar-toggle-placement="explorer-header"', explorerActionsIndex);
|
|
const explorerToggleEndIndex = sidebarSource.indexOf('</Tooltip>', explorerToggleIndex);
|
|
const explorerToggleStartIndex = sidebarSource.lastIndexOf('<Button', explorerToggleIndex);
|
|
const explorerToggleSource = sidebarSource.slice(explorerToggleStartIndex, explorerToggleEndIndex);
|
|
const fixedRailToggleIndex = connectionRailSource.indexOf('data-sidebar-toggle-placement="fixed-rail"');
|
|
const railItemsIndex = connectionRailSource.indexOf('<div className="gn-v2-rail-items">');
|
|
const railPrimaryActionsIndex = connectionRailSource.indexOf('<div className="gn-v2-rail-primary-actions"');
|
|
const firstRailObjectActionIndex = connectionRailSource.indexOf('data-sidebar-create-group-action="true"');
|
|
expect(titlebarToggleIndex).toBeGreaterThan(appSource.indexOf('data-titlebar-brand-region="true"'));
|
|
expect(titlebarToggleIndex).toBeLessThan(siderIndex);
|
|
expect(triggerSource).toContain('type="text"');
|
|
expect(triggerSource).toContain("WebkitAppRegion: 'no-drag'");
|
|
expect(triggerSource).toContain("'--wails-draggable': 'no-drag'");
|
|
expect(siderSource).toContain('onCollapseSidebar={isV2Ui ? handleCollapseSidebarPanel : undefined}');
|
|
expect(fixedRailIndex).toBeGreaterThan(-1);
|
|
expect(explorerPanelIndex).toBeGreaterThan(fixedRailIndex);
|
|
expect(explorerToggleIndex).toBeGreaterThan(connectionMenuIndex);
|
|
expect(explorerToggleSource).toContain('ref={collapseSidebarButtonRef}');
|
|
expect(explorerToggleSource).not.toContain('disabled=');
|
|
expect(fixedRailToggleIndex).toBeGreaterThan(-1);
|
|
expect(fixedRailToggleIndex).toBeGreaterThan(railItemsIndex);
|
|
expect(fixedRailToggleIndex).toBeGreaterThan(railPrimaryActionsIndex);
|
|
expect(fixedRailToggleIndex).toBeLessThan(firstRailObjectActionIndex);
|
|
});
|
|
|
|
it('overrides normal resize bounds with the retained rail width and removes the collapsed resize target', () => {
|
|
expect(appCssSource).toMatch(
|
|
/body\[data-ui-version\]\s+\.ant-layout-sider\[data-sidebar-collapsed='true'\]\s*\{[^}]*min-width:\s*var\(--gonavi-sidebar-collapsed-width, 0px\)\s*!important;[^}]*max-width:\s*var\(--gonavi-sidebar-collapsed-width, 0px\)\s*!important;[^}]*width:\s*var\(--gonavi-sidebar-collapsed-width, 0px\)\s*!important;[^}]*flex:\s*0 0 var\(--gonavi-sidebar-collapsed-width, 0px\)\s*!important;/s,
|
|
);
|
|
expect(appSource).toContain('paddingRight: isSidebarCollapsed ? 0 : sidebarResizeHandleWidth');
|
|
expect(appSource).toContain('{!isSidebarCollapsed && <div');
|
|
expect(appCssSource).toMatch(
|
|
/body\[data-ui-version\]\s+\.gonavi-sidebar-collapse-trigger\.ant-btn\s*\{[^}]*width:\s*26px;[^}]*height:\s*26px(?:\s*!important)?;[^}]*border:\s*0\s*!important;/s,
|
|
);
|
|
expect(appCssSource).toMatch(
|
|
/body\[data-ui-version='v2'\]\s+\.gonavi-sidebar-collapse-trigger\.ant-btn\[data-sidebar-toggle-placement='explorer-header'\]\s*\{[^}]*width:\s*24px;[^}]*height:\s*24px(?:\s*!important)?;[^}]*border:\s*1px solid var\(--gn-br-2\)\s*!important;[^}]*border-radius:\s*7px\s*!important;/s,
|
|
);
|
|
expect(appCssSource).not.toMatch(
|
|
/\.ant-layout-sider\[data-sidebar-panel='true'\]\s*\{[^}]*(?:min-width|max-width|\bwidth\s*:|\bflex\s*:)/s,
|
|
);
|
|
expect(appCssSource).not.toMatch(/\.gonavi-sidebar-collapse-trigger[^}]*position:\s*absolute/s);
|
|
expect(appCssSource).not.toMatch(/\.gonavi-sidebar-collapse-trigger[^}]*right:\s*-\d+px/s);
|
|
expect(appCssSource).not.toMatch(/\.gonavi-sidebar-collapse-trigger[^}]*translateY/s);
|
|
});
|
|
});
|