Compare commits

...

2 Commits

Author SHA1 Message Date
雨霖铃 10644fae95 fix(hooks): pre-push 无 upstream 时回退到 origin/main 对比 2026-06-05 12:50:55 +08:00
雨霖铃 77f722b575 fix: 修复右键菜单 srcUrl 误判、pre-push 全量扫描、generating 状态不可见问题
- contextMenu: srcUrl 检查限定为 qrCode-image 菜单项,避免其他菜单误返回图片 URL
- pre-push: eslint 改为只检查变更文件(git diff 过滤),tsc 保留全项目检查
- useQrCode: confirmGenerate 用 setTimeout 延迟生成,让 loading 状态先被 React 渲染
- tests: 生成相关测试改为 async + waitFor 适配异步 confirmGenerate
2026-06-05 12:49:43 +08:00
4 changed files with 57 additions and 32 deletions
+8 -3
View File
@@ -1,6 +1,11 @@
# pre-push: 严格检查,阻塞有问题的代码推送到远程 # pre-push: 严格检查,阻塞有问题的代码推送到远程
# 类型检查 # 类型检查(全项目,因为类型错误可能跨文件传播)
npx tsc --noEmit npx tsc --noEmit
# ESLint 严格检查(不允许 warning # ESLint 严格检查(仅检查本次推送的变更文件,不阻塞不相关的旧代码
npx eslint . --max-warnings=0 # 新分支无 upstream 时,回退到与 origin/main 对比
BASE=$(git rev-parse --verify @{upstream} 2>/dev/null && echo "@{upstream}" || echo "origin/main")
CHANGED_FILES=$(git diff --name-only --diff-filter=d "$BASE"...HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx' '*.mjs')
if [ -n "$CHANGED_FILES" ]; then
echo "$CHANGED_FILES" | xargs npx eslint --max-warnings=0
fi
+29 -13
View File
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { beforeEach, describe, expect, it, vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import QrCodePage from '../index'; import QrCodePage from '../index';
vi.mock('lucide-react', async (importOriginal) => { vi.mock('lucide-react', async (importOriginal) => {
@@ -102,7 +102,7 @@ describe('QrCodePage', () => {
expect(generateButton).not.toBeDisabled(); expect(generateButton).not.toBeDisabled();
}); });
it('点击生成按钮应该切换到预览态', () => { it('点击生成按钮应该切换到预览态', async () => {
render(<QrCodePage />); render(<QrCodePage />);
// 输入文本 // 输入文本
@@ -114,14 +114,16 @@ describe('QrCodePage', () => {
fireEvent.click(generateButton); fireEvent.click(generateButton);
// 验证切换到预览态 // 验证切换到预览态
expect(screen.getByText('原始文本')).toBeInTheDocument(); await waitFor(() => {
expect(screen.getByText('编辑')).toBeInTheDocument(); expect(screen.getByText('原始文本')).toBeInTheDocument();
expect(screen.queryByText('生成二维码')).not.toBeInTheDocument(); expect(screen.getByText('编辑')).toBeInTheDocument();
// 预览态应该显示二维码 expect(screen.queryByText('生成二维码')).not.toBeInTheDocument();
expect(screen.getByTestId('qr-code-preview')).toBeInTheDocument(); // 预览态应该显示二维码
expect(screen.getByTestId('qr-code-preview')).toBeInTheDocument();
});
}); });
it('预览态应该显示截断的文本', () => { it('预览态应该显示截断的文本', async () => {
render(<QrCodePage />); render(<QrCodePage />);
// 输入长文本 // 输入长文本
@@ -135,10 +137,12 @@ describe('QrCodePage', () => {
// 验证显示截断的文本(80字符 + "..." // 验证显示截断的文本(80字符 + "..."
const truncatedText = longText.slice(0, 80) + '...'; const truncatedText = longText.slice(0, 80) + '...';
expect(screen.getByText(truncatedText)).toBeInTheDocument(); await waitFor(() => {
expect(screen.getByText(truncatedText)).toBeInTheDocument();
});
}); });
it('预览态应该显示完整的短文本', () => { it('预览态应该显示完整的短文本', async () => {
render(<QrCodePage />); render(<QrCodePage />);
// 输入短文本 // 输入短文本
@@ -150,10 +154,12 @@ describe('QrCodePage', () => {
fireEvent.click(screen.getByText('生成二维码')); fireEvent.click(screen.getByText('生成二维码'));
// 验证显示完整文本 // 验证显示完整文本
expect(screen.getByText(shortText)).toBeInTheDocument(); await waitFor(() => {
expect(screen.getByText(shortText)).toBeInTheDocument();
});
}); });
it('点击编辑按钮应该返回输入态', () => { it('点击编辑按钮应该返回输入态', async () => {
render(<QrCodePage />); render(<QrCodePage />);
// 输入文本并生成 // 输入文本并生成
@@ -161,6 +167,11 @@ describe('QrCodePage', () => {
fireEvent.change(textarea, { target: { value: 'https://example.com' } }); fireEvent.change(textarea, { target: { value: 'https://example.com' } });
fireEvent.click(screen.getByText('生成二维码')); fireEvent.click(screen.getByText('生成二维码'));
// 等待生成完成
await waitFor(() => {
expect(screen.getByText('编辑')).toBeInTheDocument();
});
// 点击编辑按钮 // 点击编辑按钮
fireEvent.click(screen.getByText('编辑')); fireEvent.click(screen.getByText('编辑'));
@@ -172,7 +183,7 @@ describe('QrCodePage', () => {
expect(screen.queryByTestId('qr-code-preview')).not.toBeInTheDocument(); expect(screen.queryByTestId('qr-code-preview')).not.toBeInTheDocument();
}); });
it('返回编辑态应该保留上次输入的内容', () => { it('返回编辑态应该保留上次输入的内容', async () => {
render(<QrCodePage />); render(<QrCodePage />);
// 输入文本并生成 // 输入文本并生成
@@ -180,6 +191,11 @@ describe('QrCodePage', () => {
fireEvent.change(textarea, { target: { value: 'https://example.com' } }); fireEvent.change(textarea, { target: { value: 'https://example.com' } });
fireEvent.click(screen.getByText('生成二维码')); fireEvent.click(screen.getByText('生成二维码'));
// 等待生成完成
await waitFor(() => {
expect(screen.getByText('编辑')).toBeInTheDocument();
});
// 点击编辑按钮 // 点击编辑按钮
fireEvent.click(screen.getByText('编辑')); fireEvent.click(screen.getByText('编辑'));
+18 -14
View File
@@ -165,27 +165,31 @@ export function useQrCode(): QrCodeContextValue {
return; return;
} }
// 先设置 loading 状态,让 React 渲染加载动画
setGeneratorState((prev) => ({ ...prev, generating: true, inputError: '' })); setGeneratorState((prev) => ({ ...prev, generating: true, inputError: '' }));
const qrCodeDataUrl = generateQrCodeDataUrl(text); // 延迟到下一帧生成,确保 loading 状态先被渲染显示
setTimeout(() => {
const qrCodeDataUrl = generateQrCodeDataUrl(text);
if (!qrCodeDataUrl) {
setGeneratorState((prev) => ({
...prev,
generating: false,
inputError: t('qrCode:generateError'),
}));
toast.error(t('qrCode:generateError'));
return;
}
if (!qrCodeDataUrl) {
setGeneratorState((prev) => ({ setGeneratorState((prev) => ({
...prev, ...prev,
step: 'preview',
savedText: text,
qrCodeDataUrl,
generating: false, generating: false,
inputError: t('qrCode:generateError'),
})); }));
toast.error(t('qrCode:generateError')); }, 0);
return;
}
setGeneratorState((prev) => ({
...prev,
step: 'preview',
savedText: text,
qrCodeDataUrl,
generating: false,
}));
}, [generatorState.textToEncode, t]); }, [generatorState.textToEncode, t]);
/** 返回编辑态,保留上次输入内容 */ /** 返回编辑态,保留上次输入内容 */
+2 -2
View File
@@ -103,8 +103,8 @@ export function parseContextMenuClick(
): ParseResult { ): ParseResult {
const featureKey = getMenuPageType(menuItemId); const featureKey = getMenuPageType(menuItemId);
// 处理图片 URL(右键点击图片时) // 处理图片 URL仅 qrCode-image 菜单项,右键点击图片时)
if (info.srcUrl) { if (menuItemId === 'qrCode-image' && info.srcUrl) {
return { return {
success: true, success: true,
data: { featureKey, payload: info.srcUrl }, data: { featureKey, payload: info.srcUrl },