import { describe, expect, it } from 'bun:test';
import { renderToStaticMarkup } from 'react-dom/server';
import { InlineNotice } from '../src/appNotice';
import { I18nProvider, supportedLocales, translate } from '../src/i18n';
import {
appNoticeReducer,
initialAppNoticeState,
type AppNotice,
type AppNoticeState,
} from '../src/services/appNotice';
const notice: AppNotice = {
owner: 'api-page',
source: 'app.nav.api',
message: 'Connection enabled',
tone: 'success',
};
const renderNotice = (state: AppNoticeState) => renderToStaticMarkup(
{}} />,
);
describe('统一操作提示', () => {
it('关闭操作反馈的文案覆盖所有支持的语言', () => {
for (const locale of supportedLocales) {
expect(translate(locale, 'app.notice.dismiss').length).toBeGreaterThan(0);
}
});
it('新结果替换旧结果,不叠加悬浮提示', () => {
const first = appNoticeReducer(initialAppNoticeState, { type: 'show', notice });
const next = appNoticeReducer(first, {
type: 'show',
notice: { ...notice, message: 'Connection disabled' },
});
expect(next.notice?.message).toBe('Connection disabled');
expect(next.revision).toBe(2);
expect(first.notice?.message).toBe('Connection enabled');
});
it('重复的操作结果也产生新的可播报版本', () => {
const first = appNoticeReducer(initialAppNoticeState, { type: 'show', notice });
const next = appNoticeReducer(first, { type: 'show', notice });
expect(next.revision).toBe(first.revision + 1);
expect(next.notice).toEqual(notice);
});
it('错误和成功都保留结果,用户关闭后才清空', () => {
for (const tone of ['success', 'error', 'info'] as const) {
const shown = appNoticeReducer(initialAppNoticeState, { type: 'show', notice: { ...notice, tone } });
expect(shown.notice?.tone).toBe(tone);
expect(appNoticeReducer(shown, { type: 'dismiss' }).notice).toBeNull();
}
});
it('一个页面清理自己的状态时不会关闭其他页面的结果', () => {
const shown = appNoticeReducer(initialAppNoticeState, { type: 'show', notice });
expect(appNoticeReducer(shown, { type: 'dismiss', owner: 'oauth-page' })).toBe(shown);
expect(appNoticeReducer(shown, { type: 'dismiss', owner: 'api-page' }).notice).toBeNull();
});
it('空消息只清除当前来源,不显示空白成功提示', () => {
const shown = appNoticeReducer(initialAppNoticeState, { type: 'show', notice });
expect(appNoticeReducer(shown, { type: 'show', notice: { ...notice, message: ' ' } }).notice).toBeNull();
expect(appNoticeReducer(shown, {
type: 'show',
notice: { ...notice, owner: 'another-page', message: '' },
})).toBe(shown);
});
it('无消息时不渲染任何空白栏或占位文字', () => {
expect(renderNotice(initialAppNoticeState)).toBe('');
expect(renderNotice({ notice: { ...notice, message: ' ' }, revision: 1 })).toBe('');
});
it('操作结果包含来源、完整消息和可访问的关闭按钮', () => {
const html = renderNotice(appNoticeReducer(initialAppNoticeState, { type: 'show', notice }));
expect(html).toContain('action-feedback inline-notice success');
expect(html).toContain('API Access');
expect(html).toContain('Connection enabled');
expect(html).toContain('role="status"');
expect(html).toContain('aria-live="polite"');
expect(html).toContain('aria-atomic="true"');
expect(html).toContain('aria-label="Dismiss operation feedback"');
expect(html).not.toContain('config-toast');
expect(html).not.toContain('