1
0
forked from boos/BoosAPI

feat: refine BoosAPI console and user workspace

This commit is contained in:
boos
2026-06-09 16:48:18 +08:00
parent aaba6f152e
commit 59106e6a96
284 changed files with 59886 additions and 5594 deletions
+66
View File
@@ -0,0 +1,66 @@
import { describe, expect, it } from 'vitest';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
describe('App route transition feedback', () => {
it('tracks pathname and query changes without remounting page content for search-only navigation', () => {
const source = readFileSync(resolve(process.cwd(), 'src/web/App.tsx'), 'utf8').replace(/\r\n/g, '\n');
expect(source).toContain('const routeTransitionKey = `${location.pathname}${location.search}`;');
expect(source).toContain('const previousRouteTransitionKeyRef = useRef(routeTransitionKey);');
expect(source).toContain('const sidebarRef = useRef<HTMLElement>(null);');
expect(source).toContain('const mainRef = useRef<HTMLElement>(null);');
expect(source).toContain("import { shouldFocusMainOnRouteChange } from './pages/helpers/routeFocus.js';");
expect(source).toContain('previousRouteTransitionKeyRef.current === routeTransitionKey');
expect(source).toContain('className="skip-link"');
expect(source).toContain('href="#main-content"');
expect(source).toContain('mainRef.current?.focus({ preventScroll: true });');
expect(source).toContain("{t('跳到主内容')}");
expect(source).toContain('ref={mainRef}');
expect(source).toContain('id="main-content"');
expect(source).toContain('tabIndex={-1}');
expect(source).toContain("aria-label={t('主内容')}");
expect(source).toContain('const pageMotionKey = `${location.pathname}${location.search}`;');
expect(source).toContain('key={location.pathname} motionKey={pageMotionKey}');
expect(source).toContain('<Suspense fallback={null}>');
expect(source).not.toContain('function RouteLoadingFallback');
expect(source).not.toContain('fallback={<RouteLoadingFallback />}');
expect(source).not.toContain('routeTransitioning');
expect(source).not.toContain('is-route-transitioning');
expect(source).toContain('className="sr-only"');
expect(source).toContain('aria-live="polite"');
expect(source).toContain('aria-atomic="true"');
});
it('moves focus to the main region on shell-driven route changes without stealing field focus', () => {
const source = readFileSync(resolve(process.cwd(), 'src/web/App.tsx'), 'utf8').replace(/\r\n/g, '\n');
expect(source).toContain('const shouldFocusMain = shouldFocusMainOnRouteChange(document.activeElement, {');
expect(source).toContain('body: document.body,');
expect(source).toContain('topbar: topbarRef.current,');
expect(source).toContain('sidebar: sidebarRef.current,');
expect(source).not.toContain('const shouldFocusMain = !activeElement');
expect(source).toContain('let focusFrameId: number | null = null;');
expect(source).toContain("const schedule = typeof window.requestAnimationFrame === 'function'");
expect(source).toContain('focusFrameId = schedule(() => {');
expect(source).toContain('mainRef.current?.focus({ preventScroll: true });');
expect(source).toContain("typeof window.cancelAnimationFrame === 'function'");
expect(source).toContain('window.cancelAnimationFrame(focusFrameId);');
});
it('does not render a blocking shell loading frame while lazy routes resolve', () => {
const css = readFileSync(resolve(process.cwd(), 'src/web/index.css'), 'utf8').replace(/\r\n/g, '\n');
const source = readFileSync(resolve(process.cwd(), 'src/web/App.tsx'), 'utf8').replace(/\r\n/g, '\n');
expect(css).toContain('.skip-link');
expect(css).toContain('.skip-link:focus,\n.skip-link:focus-visible');
expect(css).toContain('.sr-only');
expect(css).toContain('.skip-link,\n .skip-link:focus,\n .skip-link:focus-visible');
expect(source).not.toContain('RouteLoadingFallback');
expect(source).not.toContain('animate-fade-in');
expect(source).not.toContain('aria-busy={routeTransitioning}');
expect(css).not.toContain('.route-progress');
expect(css).not.toContain('@keyframes route-progress-sweep');
expect(css).not.toContain('.main-content.is-route-transitioning');
});
});