7c50fa489d
反馈页在移动壳声明拍摄能力时展示拍摄凭证入口 拍摄图片复用现有凭证校验、预览和提交链路 同步原生壳测试、文档和共享记忆
252 lines
8.8 KiB
TypeScript
252 lines
8.8 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { beforeEach, expect, test, vi } from 'vitest';
|
|
|
|
import * as hostBridgeServices from '../../services/host-bridge/hostBridge';
|
|
import { PlatformFeedbackView } from './PlatformFeedbackView';
|
|
|
|
class MockFileReader {
|
|
result: string | ArrayBuffer | null = null;
|
|
onload: null | (() => void) = null;
|
|
onerror: null | (() => void) = null;
|
|
|
|
readAsDataURL(file: File) {
|
|
this.result = `data:${file.type};base64,ZmVlZGJhY2s=`;
|
|
this.onload?.();
|
|
}
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.stubGlobal('FileReader', MockFileReader);
|
|
});
|
|
|
|
test('PlatformFeedbackView renders reference feedback fields', () => {
|
|
render(<PlatformFeedbackView onBack={vi.fn()} />);
|
|
|
|
expect(screen.getByText('帮助与反馈')).toBeTruthy();
|
|
expect(screen.getByText('反馈问题')).toBeTruthy();
|
|
const descriptionField = screen.getByLabelText('问题描述');
|
|
expect(descriptionField).toBeTruthy();
|
|
expect(descriptionField.tagName).toBe('TEXTAREA');
|
|
expect(descriptionField.className).toContain('platform-text-field');
|
|
expect(descriptionField.className).toContain('!bg-transparent');
|
|
expect(screen.getByText('问题描述').className).toContain(
|
|
'text-[var(--platform-text-strong)]',
|
|
);
|
|
expect(screen.getByText('0/200')).toBeTruthy();
|
|
expect(screen.getByText('上传凭证(提供问题截图)')).toBeTruthy();
|
|
expect(screen.getByText('上传凭证')).toBeTruthy();
|
|
expect(screen.queryByText('拍摄凭证')).toBeNull();
|
|
const contactPhoneField = screen.getByLabelText('联系电话');
|
|
expect(contactPhoneField).toBeTruthy();
|
|
expect(contactPhoneField.tagName).toBe('INPUT');
|
|
expect(contactPhoneField.className).toContain('platform-text-field');
|
|
expect(contactPhoneField.className).toContain('!bg-transparent');
|
|
expect(screen.getByText('联系电话').className).toContain(
|
|
'text-[var(--platform-text-strong)]',
|
|
);
|
|
expect(screen.getByRole('button', { name: '提交' })).toBeTruthy();
|
|
const feedbackHistoryButton = screen.getByRole('button', {
|
|
name: '查看反馈与投诉记录',
|
|
});
|
|
expect(feedbackHistoryButton).toBeTruthy();
|
|
expect(feedbackHistoryButton.className).toContain('platform-button--ghost');
|
|
expect(feedbackHistoryButton.className).toContain('rounded-full');
|
|
fireEvent.click(feedbackHistoryButton);
|
|
expect(screen.getByText('反馈记录暂未开放')).toBeTruthy();
|
|
|
|
const descriptionPanel = screen.getByLabelText('问题描述').closest('section');
|
|
const evidencePanel = screen
|
|
.getByText('上传凭证(提供问题截图)')
|
|
.closest('section');
|
|
const phonePanel = screen.getByLabelText('联系电话').closest('section');
|
|
|
|
for (const panel of [descriptionPanel, evidencePanel, phonePanel]) {
|
|
expect(panel?.className).toContain('platform-subpanel');
|
|
expect(panel?.className).toContain('rounded-[1.25rem]');
|
|
expect(panel?.className).toContain('p-4');
|
|
}
|
|
});
|
|
|
|
test('PlatformFeedbackView validates minimum description length before submit', () => {
|
|
const onSubmit = vi.fn();
|
|
render(<PlatformFeedbackView onBack={vi.fn()} onSubmit={onSubmit} />);
|
|
|
|
fireEvent.change(screen.getByLabelText('问题描述'), {
|
|
target: { value: '太短' },
|
|
});
|
|
fireEvent.click(screen.getByRole('button', { name: '提交' }));
|
|
|
|
expect(screen.getByText('请填写10个字以上的问题描述')).toBeTruthy();
|
|
expect(onSubmit).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test('PlatformFeedbackView submits trimmed payload', async () => {
|
|
const onSubmit = vi.fn();
|
|
render(<PlatformFeedbackView onBack={vi.fn()} onSubmit={onSubmit} />);
|
|
|
|
fireEvent.change(screen.getByLabelText('问题描述'), {
|
|
target: { value: ' 这个反馈页面无法正常上传图片 ' },
|
|
});
|
|
fireEvent.change(screen.getByLabelText('联系电话'), {
|
|
target: { value: ' 13800000000 ' },
|
|
});
|
|
fireEvent.click(screen.getByRole('button', { name: '提交' }));
|
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
|
|
expect(onSubmit).toHaveBeenCalledWith({
|
|
description: '这个反馈页面无法正常上传图片',
|
|
contactPhone: '13800000000',
|
|
evidenceItems: [],
|
|
});
|
|
await waitFor(() => expect(screen.getByText('反馈已提交')).toBeTruthy());
|
|
});
|
|
|
|
test('PlatformFeedbackView previews image data urls and submits evidence items', async () => {
|
|
const onSubmit = vi.fn();
|
|
render(<PlatformFeedbackView onBack={vi.fn()} onSubmit={onSubmit} />);
|
|
|
|
const file = new File(['feedback'], 'preview.png', { type: 'image/png' });
|
|
fireEvent.change(
|
|
document.querySelector('input[type="file"]') as HTMLInputElement,
|
|
{
|
|
target: { files: [file] },
|
|
},
|
|
);
|
|
|
|
const preview = await screen.findByAltText('反馈凭证预览');
|
|
expect(preview.getAttribute('src')).toBe(
|
|
'data:image/png;base64,ZmVlZGJhY2s=',
|
|
);
|
|
|
|
fireEvent.change(screen.getByLabelText('问题描述'), {
|
|
target: { value: '图片上传后现在应该展示预览' },
|
|
});
|
|
fireEvent.click(screen.getByRole('button', { name: '提交' }));
|
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
|
|
expect(onSubmit).toHaveBeenCalledWith({
|
|
description: '图片上传后现在应该展示预览',
|
|
contactPhone: null,
|
|
evidenceItems: [
|
|
{
|
|
fileName: 'preview.png',
|
|
contentType: 'image/png',
|
|
sizeBytes: file.size,
|
|
dataUrl: 'data:image/png;base64,ZmVlZGJhY2s=',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
test('PlatformFeedbackView imports evidence image through native HostBridge', async () => {
|
|
const inputClickSpy = vi
|
|
.spyOn(HTMLInputElement.prototype, 'click')
|
|
.mockImplementation(() => undefined);
|
|
vi.spyOn(hostBridgeServices, 'canUseNativeHostCapability').mockImplementation(
|
|
(capability) => capability === 'file.importImage',
|
|
);
|
|
vi.spyOn(hostBridgeServices, 'importHostImageFile').mockResolvedValue({
|
|
action: 'selected',
|
|
fileName: 'feedback.png',
|
|
base64Data: 'ZmVlZGJhY2s=',
|
|
mimeType: 'image/png',
|
|
bytes: 8,
|
|
});
|
|
const onSubmit = vi.fn();
|
|
|
|
try {
|
|
render(<PlatformFeedbackView onBack={vi.fn()} onSubmit={onSubmit} />);
|
|
|
|
fireEvent.click(screen.getByText('上传凭证'));
|
|
|
|
const preview = await screen.findByAltText('反馈凭证预览');
|
|
expect(preview.getAttribute('src')).toBe(
|
|
'data:image/png;base64,ZmVlZGJhY2s=',
|
|
);
|
|
expect(inputClickSpy).not.toHaveBeenCalled();
|
|
|
|
fireEvent.change(screen.getByLabelText('问题描述'), {
|
|
target: { value: '原生壳导入截图后应该可以提交' },
|
|
});
|
|
fireEvent.click(screen.getByRole('button', { name: '提交' }));
|
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
|
|
expect(onSubmit).toHaveBeenCalledWith({
|
|
description: '原生壳导入截图后应该可以提交',
|
|
contactPhone: null,
|
|
evidenceItems: [
|
|
{
|
|
fileName: 'feedback.png',
|
|
contentType: 'image/png',
|
|
sizeBytes: 8,
|
|
dataUrl: 'data:image/png;base64,ZmVlZGJhY2s=',
|
|
},
|
|
],
|
|
});
|
|
} finally {
|
|
inputClickSpy.mockRestore();
|
|
}
|
|
});
|
|
|
|
test('PlatformFeedbackView captures evidence image through native HostBridge', async () => {
|
|
const inputClickSpy = vi
|
|
.spyOn(HTMLInputElement.prototype, 'click')
|
|
.mockImplementation(() => undefined);
|
|
vi.spyOn(hostBridgeServices, 'canUseNativeHostCapability').mockImplementation(
|
|
(capability) => capability === 'file.captureImage',
|
|
);
|
|
vi.spyOn(hostBridgeServices, 'captureHostImageFile').mockResolvedValue({
|
|
action: 'captured',
|
|
fileName: 'feedback-camera.png',
|
|
base64Data: 'Y2FtZXJhLWZlZWRiYWNr',
|
|
mimeType: 'image/png',
|
|
bytes: 15,
|
|
});
|
|
const onSubmit = vi.fn();
|
|
|
|
try {
|
|
render(<PlatformFeedbackView onBack={vi.fn()} onSubmit={onSubmit} />);
|
|
|
|
expect(screen.getByText('拍摄凭证')).toBeTruthy();
|
|
fireEvent.click(screen.getByText('拍摄凭证'));
|
|
|
|
const preview = await screen.findByAltText('反馈凭证预览');
|
|
expect(preview.getAttribute('src')).toBe(
|
|
'data:image/png;base64,ZmVlZGJhY2s=',
|
|
);
|
|
expect(inputClickSpy).not.toHaveBeenCalled();
|
|
|
|
fireEvent.change(screen.getByLabelText('问题描述'), {
|
|
target: { value: '拍摄凭证后应该可以直接提交' },
|
|
});
|
|
fireEvent.click(screen.getByRole('button', { name: '提交' }));
|
|
|
|
await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1));
|
|
expect(onSubmit).toHaveBeenCalledWith({
|
|
description: '拍摄凭证后应该可以直接提交',
|
|
contactPhone: null,
|
|
evidenceItems: [
|
|
{
|
|
fileName: 'feedback-camera.png',
|
|
contentType: 'image/png',
|
|
sizeBytes: 15,
|
|
dataUrl: 'data:image/png;base64,ZmVlZGJhY2s=',
|
|
},
|
|
],
|
|
});
|
|
} finally {
|
|
inputClickSpy.mockRestore();
|
|
}
|
|
});
|
|
|
|
test('PlatformFeedbackView calls back from header home button', () => {
|
|
const onBack = vi.fn();
|
|
render(<PlatformFeedbackView onBack={onBack} />);
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: '返回我的页签' }));
|
|
|
|
expect(onBack).toHaveBeenCalledTimes(1);
|
|
});
|