fix(review): persist uploaded .tex file in reformat panel
- File now stored in edu.js store (reformatFile) - Switching target journal no longer requires re-upload - Explicit reset button clears file - Vitest: 4 cases
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
// tests/frontend/unit/edu/reformat.file.test.js —— ReformatPanel 文件持久化(P3b 修复)
|
||||
// bug:上传 .tex → 重排版 → 点"重新排版"换期刊 → 本地 file 被清空 → 要求重传。
|
||||
// 修复:File 进 store(reformatFile,同 presubmitFile 模式);reset() 不清 file;
|
||||
// 仅"×" removeFile 清空。
|
||||
// 1) store 有 reformatFile → 挂载即回显文件名
|
||||
// 2) 选择文件 → 切换目标期刊 → 文件仍在(store + DOM)
|
||||
// 3) 有结果时点"重新排版"(reset)→ 回上传区且文件保留
|
||||
// 4) "×" 移除 → store + 本地都清空
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { createPinia, setActivePinia } from 'pinia';
|
||||
import ReformatPanel from '../../../../ui/src/components/edu/panels/ReformatPanel.vue';
|
||||
|
||||
vi.mock('../../../../ui/src/api/review.js', async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
getJournals: vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
items: [{ id: 'gb', name: 'Genome Biology' }, { id: 'mp', name: 'Molecular Plant' }],
|
||||
}),
|
||||
getDirectory: vi.fn(),
|
||||
reformatTex: vi.fn(),
|
||||
compileTex: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
import { useEduStore } from '../../../../ui/src/stores/edu.js';
|
||||
|
||||
const TEX = new File(['\\documentclass{article}\\begin{document}Hi\\end{document}'], 'paper.tex', {
|
||||
type: 'application/x-tex',
|
||||
});
|
||||
|
||||
/** 先建 pinia → seed store → 再挂载(保证 mount 时读到 store 初值) */
|
||||
function setup(seedFn) {
|
||||
const pinia = createPinia();
|
||||
setActivePinia(pinia);
|
||||
const edu = useEduStore();
|
||||
if (seedFn) seedFn(edu);
|
||||
const wrapper = mount(ReformatPanel, { global: { plugins: [pinia] } });
|
||||
return { wrapper, edu };
|
||||
}
|
||||
|
||||
describe('ReformatPanel 文件持久化(换期刊不丢文件)', () => {
|
||||
beforeEach(() => {
|
||||
global.fetch = vi.fn().mockRejectedValue(new Error('no network'));
|
||||
if (!URL.createObjectURL) URL.createObjectURL = vi.fn(() => 'blob:mock');
|
||||
if (!URL.revokeObjectURL) URL.revokeObjectURL = vi.fn();
|
||||
});
|
||||
|
||||
it('1) store 有 reformatFile → 挂载即回显文件名', async () => {
|
||||
const { wrapper } = setup((edu) => { edu.reformatFile = TEX; });
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.find('.rf-file-name').text()).toContain('paper.tex');
|
||||
});
|
||||
|
||||
it('2) 选择文件后切目标期刊 → 文件仍在(store + DOM)', async () => {
|
||||
const { wrapper, edu } = setup();
|
||||
const input = wrapper.find('input.rf-file-input');
|
||||
Object.defineProperty(input.element, 'files', { value: [TEX], configurable: true });
|
||||
await input.trigger('change');
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(edu.reformatFile).toBe(TEX);
|
||||
expect(wrapper.find('.rf-file-name').text()).toContain('paper.tex');
|
||||
// 切换目标期刊(watch 只写 drafts,不清 file)
|
||||
await wrapper.find('#rf-target').setValue('mp');
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(edu.reformatFile).toBe(TEX);
|
||||
expect(wrapper.find('.rf-file-name').text()).toContain('paper.tex');
|
||||
expect(edu.drafts.reformatTarget).toBe('mp');
|
||||
});
|
||||
|
||||
it('3) 有结果时点"重新排版" → 回上传区且文件保留', async () => {
|
||||
const { wrapper, edu } = setup((e) => {
|
||||
e.reformatFile = TEX;
|
||||
e.reformatResult = {
|
||||
ok: true, job_id: 'abc123def456', modified_tex: 'x', changes: [], warnings: [],
|
||||
meta: { filename: 'paper.tex' },
|
||||
};
|
||||
e.reformatStatus = 'ready';
|
||||
});
|
||||
await wrapper.vm.$nextTick();
|
||||
const again = wrapper.findAll('button').find((b) => b.text() === '重新排版');
|
||||
expect(again).toBeTruthy();
|
||||
await again.trigger('click');
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(edu.reformatResult).toBeNull();
|
||||
expect(edu.reformatFile).toBe(TEX); // store 保留
|
||||
expect(wrapper.find('.rf-file-name').text()).toContain('paper.tex'); // DOM 回显
|
||||
});
|
||||
|
||||
it('4) "×" 移除 → store + 本地都清空', async () => {
|
||||
const { wrapper, edu } = setup((e) => { e.reformatFile = TEX; });
|
||||
await wrapper.vm.$nextTick();
|
||||
await wrapper.find('.rf-remove').trigger('click');
|
||||
expect(edu.reformatFile).toBeNull();
|
||||
expect(wrapper.find('.rf-file-name').exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -29,7 +29,7 @@ const SOURCE_LABEL = {
|
||||
};
|
||||
|
||||
const fileInput = ref(null);
|
||||
const file = ref(null);
|
||||
const file = ref(edu.reformatFile || null); // File 存 store(换期刊重排不丢;× 移除才清空)
|
||||
const fileError = ref('');
|
||||
const dragging = ref(false);
|
||||
const tab = ref('source'); // 'source' | 'diff' | 'pdf'
|
||||
@@ -202,6 +202,7 @@ function acceptFile(f) {
|
||||
return;
|
||||
}
|
||||
file.value = f;
|
||||
edu.reformatFile = f; // 进 store:换期刊/切菜单重排不丢
|
||||
}
|
||||
function onPick(e) {
|
||||
acceptFile(e.target.files?.[0]);
|
||||
@@ -213,6 +214,7 @@ function onDrop(e) {
|
||||
}
|
||||
function removeFile() {
|
||||
file.value = null;
|
||||
edu.reformatFile = null; // 唯一清空入口:"×" 移除
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@@ -225,7 +227,7 @@ async function submit() {
|
||||
}
|
||||
function reset() {
|
||||
edu.clearReformat();
|
||||
file.value = null;
|
||||
// 不清 file——换期刊重排时保留已上传文件(仅"×"移除才清)
|
||||
fileError.value = '';
|
||||
tab.value = 'source';
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ export const useEduStore = defineStore('edu', {
|
||||
// ③ presubmit 已选 PDF(File 对象,仅内存:面板卸载/切菜单不丢,error 态可直接重试;
|
||||
// 不可序列化 → 不进 localStorage;clearReport 一并清空)
|
||||
presubmitFile: null,
|
||||
// ⑤' reformat 已选 .tex(同 presubmitFile 模式:换期刊重排不丢文件;
|
||||
// 面板内"×"移除才清空。File 不可序列化 → 不进 localStorage)
|
||||
reformatFile: null,
|
||||
recommend: null, // ② 推荐响应 {ok, tiers, meta}
|
||||
recommendStatus: 'idle',
|
||||
recommendError: null,
|
||||
|
||||
Reference in New Issue
Block a user