AppLayout.unauthorized.test.tsx 2.23 KB
// REQ-USR-004: AppLayout 注册 401 登出处理(壳层接线,BR10)
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { screen, act } from '@testing-library/react';
import { Routes, Route, useLocation } from 'react-router-dom';
import { renderShell } from './renderShell';
import AppLayout from '../../src/layouts/AppLayout/AppLayout';
import {
  registerUnauthorizedHandler,
  TOKEN_STORAGE_KEY,
} from '../../src/api/request';
import { SESSION_EXPIRED_TEXT } from '../../src/layouts/AppLayout/shellMessages';
import type { AuthUser } from '../../src/api/types';

const ADMIN: AuthUser = { id: 1, sUserName: '朱子纯', sUserType: '超级管理员', sLanguage: '中文' };

function LocProbe() {
  const loc = useLocation();
  return <div data-testid="loc">{loc.pathname}</div>;
}

describe('AppLayout 401 登出接线', () => {
  let captured: (() => void) | null = null;

  beforeEach(() => {
    captured = null;
  });

  afterEach(() => {
    registerUnauthorizedHandler(null);
    vi.restoreAllMocks();
  });

  it('registers onUnauthorized on mount; invoking it clears auth + warns + navigates /login', async () => {
    localStorage.setItem(TOKEN_STORAGE_KEY, 't');
    // 拦截真实注册:把回调存进 captured,同时透传给真实单例
    const origRegister = registerUnauthorizedHandler;
    const restore = vi
      .spyOn(await import('../../src/api/request'), 'registerUnauthorizedHandler')
      .mockImplementation((fn) => {
        if (fn) captured = fn as () => void;
        origRegister(fn);
      });

    const { getState } = renderShell(
      <Routes>
        <Route element={<AppLayout />}>
          <Route path="/" element={<LocProbe />} />
        </Route>
        <Route path="/login" element={<LocProbe />} />
      </Routes>,
      { initialEntries: ['/'], preloadedAuth: { token: 't', user: ADMIN } },
    );

    expect(captured).toBeTypeOf('function');

    await act(async () => {
      captured!();
    });

    expect(getState().auth.token).toBeNull();
    expect(localStorage.getItem(TOKEN_STORAGE_KEY)).toBeNull();
    expect(await screen.findByText(SESSION_EXPIRED_TEXT)).toBeInTheDocument();
    expect(screen.getByTestId('loc').textContent).toBe('/login');

    restore.mockRestore();
  });
});