备份现场结果补录、图片修复与跳过文章功能

This commit is contained in:
Longtail committed 2026-10-31 17:08:49 +08:00
1 parent 1e6504b4a2
commit d157c179c8
20 files changed
+597 -47

No files matched your search

+41 -2
View File
@@ -73,7 +73,7 @@ test('服务器返回错误、登录 HTML 或混入脚本时不猜测附件编
});
// Small DOM harness exercises the public API and site adapter without installing browser software.
function harness({ failUpload = false, loseImages = false, existing = false } = {}) {
function harness({ failUpload = false, loseImages = false, brokenImages = false, existing = false } = {}) {
const ids = new Map(), callbacks = new Map(), requests = [];
class Element {
constructor(id='') { this.id=id; this.style={}; this.value=''; this.disabled=false; this.checked=false; this.hidden=false; this.listeners=new Map(); this.children=[]; }
@@ -87,7 +87,14 @@ function harness({ failUpload = false, loseImages = false, existing = false } =
removeEventListener(name) { this.listeners.delete(name); }
dispatchEvent() {}
click() { this.onclick?.(); }
querySelectorAll() { return []; }
querySelectorAll(selector) {
if (selector !== 'img') return [];
return [...this.innerHTML.matchAll(/<img aid="([^"]+)"[^>]*>/g)].map(m => ({
id:'', complete:true, naturalWidth:brokenImages ? 0 : 100,
getAttribute:name=>name==='aid'?m[1]:null,
addEventListener(){},removeEventListener(){}
}));
}
}
for (const id of ['subject', 'e_textarea', 'e_switchercheck', 'postform']) ids.set(id, new Element(id));
ids.get('subject').value=existing ? '原标题' : '';
@@ -170,3 +177,35 @@ test('自动导入:上传中终止不会填写正文或继续下一张', async
await assert.rejects(h.api.importPrepared({automatic:true,signal:c.signal}),/stopped/);
assert.equal(h.requests.length,1);assert.equal(h.ids.get('subject').value,'');
});
test('真实站点 aid 属性:上传成功但图片响应失败不得报告完成', async () => {
const h=harness({brokenImages:true});await h.api.stageFiles(articleFiles());
await assert.rejects(h.api.importPrepared({automatic:true}),/网站附件图片无法显示/);
assert.equal(h.ids.get('subject').value,'');assert.equal(h.api.getState().result,null);
});
test('链接内图片、多行图文链接均参与上传并按原位置渲染', () => {
const md='# 标题\n\n[![宣传](image004.jpg)\n\n### 宣传标题\n\n说明](https://example.com/promo)\n\n![](image001.jpg)';
const article=core.compile(md,'a.md',new Map([['image004.jpg',new Blob(['x'])],['image001.jpg',new Blob(['y'])]]));
assert.deepEqual([...article.images.keys()],['image004.jpg','image001.jpg']);
const rendered=article.render(new Map([['image004.jpg','104'],['image001.jpg','101']]));
assert.match(rendered,/\[url=https:\/\/example.com\/promo\]\[attachimg\]104\[\/attachimg\]/);
assert.deepEqual([...rendered.matchAll(/\[attachimg\](\d+)\[\/attachimg\]/g)].map(x=>x[1]),['104','101']);
assert.equal(rendered.includes('!['),false);
});
test('图片重复引用必须保留每个位置,不能只校验去重后的编号', async () => {
const img={id:'',getAttribute:n=>n==='aid'?'attachimg_12':null,complete:true,naturalWidth:100,addEventListener(){},removeEventListener(){}};
await core.verifyEditorImages({querySelectorAll:()=>[img,img]},['12','12']);
await assert.rejects(core.verifyEditorImages({querySelectorAll:()=>[img]},['12','12']),/数量或顺序/);
});
test('正文图片尚在加载时等待 load;终止后释放等待', async () => {
const make=()=>{const events=new Map();return {events,id:'',complete:false,naturalWidth:0,getAttribute:n=>n==='aid'?'attachimg_8':null,addEventListener:(n,f)=>events.set(n,f),removeEventListener:n=>events.delete(n)};};
const image=make();let finished=false;
const loaded=core.verifyEditorImages({querySelectorAll:()=>[image]},['8']).then(()=>finished=true);
await Promise.resolve();assert.equal(finished,false);
image.complete=true;image.naturalWidth=100;image.events.get('load')();await loaded;
assert.equal(image.events.size,0);
const pending=make(),controller=new AbortController();
const cancelled=core.verifyEditorImages({querySelectorAll:()=>[pending]},['8'],controller.signal);
controller.abort();await assert.rejects(cancelled,/终止/);assert.equal(pending.events.size,0);
});