refactor: 抽离 useBase64Converter hook 并新增 Base64ConverterSection 组件,统一 Base64 转换页面的 shadcn 样式
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import FileMode from '../FileMode';
|
||||
|
||||
// Mock CopyButton
|
||||
@@ -13,6 +13,11 @@ vi.mock('@/components/CopyButton', () => ({
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
// useStorageState's async loadState may overwrite user toggle if we click before the
|
||||
@@ -124,7 +129,7 @@ describe('FileMode', () => {
|
||||
|
||||
it('应该渲染 encode/decode 切换按钮', () => {
|
||||
render(<FileMode />);
|
||||
expect(screen.getAllByText('encode').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('encode')).toBeInTheDocument();
|
||||
expect(screen.getByText('decode')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -143,7 +148,9 @@ describe('FileMode', () => {
|
||||
const input = await screen.findByPlaceholderText('decodeBase64Placeholder');
|
||||
fireEvent.change(input, { target: { value: 'JVBERi0K' } });
|
||||
|
||||
fireEvent.click(screen.getAllByText('decode')[1]);
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('decodedFileOutput')).toBeInTheDocument();
|
||||
@@ -159,7 +166,10 @@ describe('FileMode', () => {
|
||||
|
||||
const input = await screen.findByPlaceholderText('decodeBase64Placeholder');
|
||||
fireEvent.change(input, { target: { value: 'JVBERi0K' } });
|
||||
fireEvent.click(screen.getAllByText('decode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
const filenameInput = (await screen.findByDisplayValue('decoded.pdf')) as HTMLInputElement;
|
||||
fireEvent.change(filenameInput, { target: { value: 'my-report.pdf' } });
|
||||
@@ -173,7 +183,10 @@ describe('FileMode', () => {
|
||||
|
||||
const input = await screen.findByPlaceholderText('decodeBase64Placeholder');
|
||||
fireEvent.change(input, { target: { value: 'JVBERi0K' } });
|
||||
fireEvent.click(screen.getAllByText('decode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
expect(await screen.findByText('download')).toBeInTheDocument();
|
||||
});
|
||||
@@ -185,7 +198,10 @@ describe('FileMode', () => {
|
||||
|
||||
const input = await screen.findByPlaceholderText('decodeBase64Placeholder');
|
||||
fireEvent.change(input, { target: { value: '!!!not base64' } });
|
||||
fireEvent.click(screen.getAllByText('decode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('invalidBase64')).toBeInTheDocument();
|
||||
@@ -199,13 +215,16 @@ describe('FileMode', () => {
|
||||
|
||||
const input = await screen.findByPlaceholderText('decodeBase64Placeholder');
|
||||
fireEvent.change(input, { target: { value: 'JVBERi0K' } });
|
||||
fireEvent.click(screen.getAllByText('decode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('decodedFileOutput')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getAllByText('encode')[0]);
|
||||
fireEvent.click(screen.getByText('encode'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('decodedFileOutput')).not.toBeInTheDocument();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import ImageMode from '../ImageMode';
|
||||
|
||||
// Mock CopyButton
|
||||
@@ -13,6 +13,11 @@ vi.mock('@/components/CopyButton', () => ({
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
const waitForStorageReady = () => act(() => Promise.resolve());
|
||||
@@ -119,7 +124,7 @@ describe('ImageMode', () => {
|
||||
it('应该渲染 encode/decode 切换按钮', async () => {
|
||||
render(<ImageMode />);
|
||||
await waitForStorageReady();
|
||||
expect(screen.getAllByText('encode').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('encode')).toBeInTheDocument();
|
||||
expect(screen.getByText('decode')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -130,7 +135,10 @@ describe('ImageMode', () => {
|
||||
|
||||
const input = await screen.findByPlaceholderText('decodeBase64Placeholder');
|
||||
fireEvent.change(input, { target: { value: 'iVBORw0KGgo=' } });
|
||||
fireEvent.click(screen.getAllByText('decode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('decodedImageOutput')).toBeInTheDocument();
|
||||
@@ -147,7 +155,10 @@ describe('ImageMode', () => {
|
||||
|
||||
const input = await screen.findByPlaceholderText('decodeBase64Placeholder');
|
||||
fireEvent.change(input, { target: { value: 'iVBORw0KGgo=' } });
|
||||
fireEvent.click(screen.getAllByText('decode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
expect(await screen.findByDisplayValue('decoded.png')).toBeInTheDocument();
|
||||
});
|
||||
@@ -159,7 +170,10 @@ describe('ImageMode', () => {
|
||||
|
||||
const input = await screen.findByPlaceholderText('decodeBase64Placeholder');
|
||||
fireEvent.change(input, { target: { value: '!!!not base64' } });
|
||||
fireEvent.click(screen.getAllByText('decode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(250);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('invalidBase64')).toBeInTheDocument();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import TextMode from '../TextMode';
|
||||
|
||||
// Mock CopyButton
|
||||
@@ -8,9 +8,17 @@ vi.mock('@/components/CopyButton', () => ({
|
||||
}));
|
||||
|
||||
describe('TextMode', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('应该渲染编码/解码切换按钮', () => {
|
||||
render(<TextMode />);
|
||||
expect(screen.getAllByText('encode').length).toBeGreaterThanOrEqual(1);
|
||||
expect(screen.getByText('encode')).toBeInTheDocument();
|
||||
expect(screen.getByText('decode')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -24,13 +32,13 @@ describe('TextMode', () => {
|
||||
const input = screen.getByPlaceholderText('textInputPlaceholder');
|
||||
fireEvent.change(input, { target: { value: 'Hello' } });
|
||||
|
||||
const convertBtn = screen.getAllByText('encode')[1];
|
||||
fireEvent.click(convertBtn);
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('base64Output')).toBeInTheDocument();
|
||||
});
|
||||
// 输出内容在 CopyButton 的 data-testid 中
|
||||
expect(screen.getByTestId('copy-button')).toHaveTextContent('SGVsbG8=');
|
||||
});
|
||||
|
||||
@@ -43,8 +51,9 @@ describe('TextMode', () => {
|
||||
const input = screen.getByPlaceholderText('base64InputPlaceholder');
|
||||
fireEvent.change(input, { target: { value: 'SGVsbG8=' } });
|
||||
|
||||
const convertBtn = screen.getAllByText('decode')[1];
|
||||
fireEvent.click(convertBtn);
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('textOutput')).toBeInTheDocument();
|
||||
@@ -61,8 +70,9 @@ describe('TextMode', () => {
|
||||
const input = screen.getByPlaceholderText('base64InputPlaceholder');
|
||||
fireEvent.change(input, { target: { value: 'invalid!!!' } });
|
||||
|
||||
const convertBtn = screen.getAllByText('decode')[1];
|
||||
fireEvent.click(convertBtn);
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('invalidBase64')).toBeInTheDocument();
|
||||
@@ -75,16 +85,17 @@ describe('TextMode', () => {
|
||||
// 先编码
|
||||
const input = screen.getByPlaceholderText('textInputPlaceholder');
|
||||
fireEvent.change(input, { target: { value: 'Hello' } });
|
||||
fireEvent.click(screen.getAllByText('encode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('copy-button')).toHaveTextContent('SGVsbG8=');
|
||||
});
|
||||
|
||||
// 切换方向
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText('decode'));
|
||||
});
|
||||
fireEvent.click(screen.getByText('decode'));
|
||||
|
||||
// 输出应该被清除
|
||||
await waitFor(() => {
|
||||
@@ -97,7 +108,10 @@ describe('TextMode', () => {
|
||||
|
||||
const input = screen.getByPlaceholderText('textInputPlaceholder');
|
||||
fireEvent.change(input, { target: { value: 'Hello' } });
|
||||
fireEvent.click(screen.getAllByText('encode')[1]);
|
||||
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('copy-button')).toHaveTextContent('SGVsbG8=');
|
||||
@@ -111,20 +125,6 @@ describe('TextMode', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('空输入时转换按钮应该禁用', () => {
|
||||
render(<TextMode />);
|
||||
const convertBtn = screen.getAllByText('encode')[1];
|
||||
expect(convertBtn).toBeDisabled();
|
||||
});
|
||||
|
||||
it('输入非空时转换按钮应该启用', () => {
|
||||
render(<TextMode />);
|
||||
const input = screen.getByPlaceholderText('textInputPlaceholder');
|
||||
fireEvent.change(input, { target: { value: 'Hello' } });
|
||||
const convertBtn = screen.getAllByText('encode')[1];
|
||||
expect(convertBtn).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it('解码模式下粘贴图片 data URI 时应该显示切换图像模式的提示', () => {
|
||||
render(<TextMode />);
|
||||
|
||||
@@ -172,8 +172,9 @@ describe('TextMode', () => {
|
||||
const input = screen.getByPlaceholderText('base64InputPlaceholder');
|
||||
fireEvent.change(input, { target: { value: 'iVBORw0KGgo=' } });
|
||||
|
||||
const convertBtn = screen.getAllByText('decode')[1];
|
||||
fireEvent.click(convertBtn);
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('binaryDataDetected')).toBeInTheDocument();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import Base64ConverterPage from '../index';
|
||||
|
||||
// Mock useLazyTranslation
|
||||
@@ -22,46 +22,61 @@ vi.mock('@/config/features', async (importOriginal) => {
|
||||
|
||||
// Mock 子组件
|
||||
vi.mock('../TextMode', () => ({
|
||||
default: () => <div data-testid="text-mode">TextMode</div>,
|
||||
default: ({ onSwitchToImageMode }: { onSwitchToImageMode?: () => void }) => (
|
||||
<div data-testid="text-mode">
|
||||
TextMode
|
||||
{onSwitchToImageMode && <button onClick={onSwitchToImageMode}>switchToImage</button>}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('../FileMode', () => ({
|
||||
default: () => <div data-testid="file-mode">FileMode</div>,
|
||||
vi.mock('../Base64ConverterSection', () => ({
|
||||
default: ({ mode }: { mode: string }) => <div data-testid={`${mode}-mode`}>{mode}</div>,
|
||||
}));
|
||||
|
||||
vi.mock('../ImageMode', () => ({
|
||||
default: () => <div data-testid="image-mode">ImageMode</div>,
|
||||
}));
|
||||
const waitForStorageInit = () =>
|
||||
act(async () => {
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
describe('Base64ConverterPage', () => {
|
||||
it('应该默认渲染文本模式', () => {
|
||||
it('应该默认渲染文本模式', async () => {
|
||||
render(<Base64ConverterPage />);
|
||||
await waitForStorageInit();
|
||||
expect(screen.getByTestId('text-mode')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('应该渲染模式切换按钮', () => {
|
||||
it('应该渲染模式切换按钮', async () => {
|
||||
render(<Base64ConverterPage />);
|
||||
await waitForStorageInit();
|
||||
expect(screen.getByText('base64Converter:textMode')).toBeInTheDocument();
|
||||
expect(screen.getByText('base64Converter:fileMode')).toBeInTheDocument();
|
||||
expect(screen.getByText('base64Converter:imageMode')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('切换到文件模式应该渲染 FileMode', () => {
|
||||
it('切换到文件模式应该渲染 FileMode', async () => {
|
||||
render(<Base64ConverterPage />);
|
||||
await waitForStorageInit();
|
||||
fireEvent.click(screen.getByText('base64Converter:fileMode'));
|
||||
expect(screen.getByTestId('file-mode')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('file-mode')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.queryByTestId('text-mode')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('切换到图像模式应该渲染 ImageMode', () => {
|
||||
it('切换到图像模式应该渲染 ImageMode', async () => {
|
||||
render(<Base64ConverterPage />);
|
||||
await waitForStorageInit();
|
||||
fireEvent.click(screen.getByText('base64Converter:imageMode'));
|
||||
expect(screen.getByTestId('image-mode')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('image-mode')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.queryByTestId('text-mode')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('应该渲染页面标题', () => {
|
||||
it('应该渲染页面标题', async () => {
|
||||
render(<Base64ConverterPage />);
|
||||
await waitForStorageInit();
|
||||
expect(screen.getByText('base64Converter:pageTitle')).toBeInTheDocument();
|
||||
expect(screen.getByText('base64Converter:pageSubtitle')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user