更新审核提示流程与工作台版本
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
12 files changed
+296
-268
No files matched your search
@@ -15,7 +15,7 @@ function html({moderation=true,author=true,duplicateTid=false,lazy=false}={}) {
|
||||
<div><table><tr><td id="postmessage_238791">原创正文<img id="aimg_42" aid="42" src="${lazy?'https://www.dww11.com/static/image/common/none.gif':gif}" file="https://www.dww11.com/real.png"></td></tr></table></div></td></tr></table></div>
|
||||
<div id="post_999"><div class="authi"><a href="https://www.dww11.com/home.php?mod=space&uid=456">回帖作者</a></div><div id="postmessage_999">回帖(审核中)</div></div>`;
|
||||
}
|
||||
test('结果页适配:真实主题、首帖范围、状态、图文与延迟加载',async()=>{
|
||||
test.skip('暂时停用详情页核对;结果页适配:真实主题、首帖范围、状态、图文与延迟加载',async()=>{
|
||||
const browser=await chromium.launch({headless:true,executablePath:process.env.CHROME_PATH||'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'});
|
||||
try {
|
||||
const page=await browser.newPage();await page.route('**/*',r=>r.abort());
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
const {test}=require('node:test');
|
||||
const assert=require('node:assert/strict');
|
||||
const {inspect}=require('../browser/results.js');
|
||||
const message='新主题需要审核,您的帖子通过审核后才能显示';
|
||||
function doc(text,selector='#messagetext') {
|
||||
return {querySelector:s=>s==='#messagetext, .messagetext'?{textContent:text}:null};
|
||||
}
|
||||
test('完整审核提示即成功,不依赖详情页字段或跳转链接',()=>{
|
||||
const result=inspect(doc(message+',现在将转入首页'),'https://www.dww11.com/forum.php');
|
||||
assert.equal(result.kind,'accepted_prompt');
|
||||
assert.equal(result.state,'accepted_pending_moderation');
|
||||
assert.equal(result.evidence.message,message);
|
||||
assert.equal(result.thread_id,undefined);
|
||||
});
|
||||
test('只有审核中、缺失提示或相似提示不能判成功',()=>{
|
||||
for(const text of ['', '(审核中)', '新主题需要审核', '您的帖子通过审核后才能显示'])
|
||||
assert.equal(inspect(doc(text),'https://www.dww11.com/').kind,'waiting');
|
||||
});
|
||||
test('明确标题拒绝继续暂停',()=>{
|
||||
assert.equal(inspect(doc('抱歉,您填写的标题超过 80 个字符限制'),'https://www.dww11.com/forum.php').kind,'rejected');
|
||||
});
|
||||
+27
-9
@@ -11,7 +11,7 @@ function fixture({holdImport=false,holdHeartbeat=false,rejectSkip=false,result=n
|
||||
const job={id:'first',state:'preparing',title:'标题',source:'来源',fid:125,account:{uid:'123',name:'账号'}};
|
||||
const run={running:true,epoch:1,active:job.id,owner:'tab-a'};
|
||||
const body={innerHTML:'正文',innerText:'正文',querySelectorAll:()=>[]};
|
||||
const subject={value:''};let tick,finishImport,finishHeartbeat,rolledBack=false;
|
||||
const subject={value:''};let tick,finishImport,finishHeartbeat,mutation,rolledBack=false;
|
||||
class Event {constructor(type,options={}){this.type=type;Object.assign(this,options);}}
|
||||
const doc={
|
||||
body:{append(){}},createElement:()=>({style:{}}),
|
||||
@@ -33,7 +33,7 @@ function fixture({holdImport=false,holdHeartbeat=false,rejectSkip=false,result=n
|
||||
let value={};
|
||||
if(route==='heartbeat')value=run;
|
||||
if(route==='next')value={action:job.state==='preparing'?'prepare':'resume',job,run};
|
||||
if(route==='event')job.state=body.state;
|
||||
if(route==='event'){job.state=body.state;if(body.state==='user_submitting'){job.attempt='attempt-1';job.snapshot=body.snapshot;}}
|
||||
if(route==='skip') {
|
||||
if(rejectSkip){reply(id,null,'状态已改变');return;}
|
||||
job.state='skipped';run.active=null;run.epoch++;value={ok:true,fid:125,run};
|
||||
@@ -44,7 +44,7 @@ function fixture({holdImport=false,holdHeartbeat=false,rejectSkip=false,result=n
|
||||
reply(id,value);
|
||||
});
|
||||
let sequence=0;
|
||||
const context={document:doc,CustomEvent:Event,Node:{TEXT_NODE:3},URL,AbortController,
|
||||
const context={MutationObserver:class{constructor(fn){mutation=fn;}observe(){}},document:doc,CustomEvent:Event,Node:{TEXT_NODE:3},URL,AbortController,
|
||||
crypto:{randomUUID:()=>sequence++===0?'tab-a':`request-${sequence}`},sessionStorage:{getItem:k=>storage.get(k),setItem:(k,v)=>storage.set(k,v),removeItem:k=>storage.delete(k)},
|
||||
location:{href:'https://www.dww11.com/forum.php?mod=post&action=newthread&fid=125',assign:url=>navigations.push(url)},
|
||||
setInterval:fn=>{tick=fn;},setTimeout:(fn,ms)=>{const t=setTimeout(fn,ms);timers.add(t);return t;},clearTimeout:t=>{clearTimeout(t);timers.delete(t);}
|
||||
@@ -56,7 +56,7 @@ function fixture({holdImport=false,holdHeartbeat=false,rejectSkip=false,result=n
|
||||
vm.runInNewContext(fs.readFileSync(path.join(__dirname,'../browser/runner.js'),'utf8'),context);
|
||||
return {calls,navigations,job,run,storage,doc,Event,tick:()=>tick(),skip:()=>doc.dispatchEvent(new Event('dww11:skip-article')),
|
||||
finishImport:()=>finishImport(),finishHeartbeat:()=>finishHeartbeat(),rolledBack:()=>rolledBack,
|
||||
close:()=>{for(const t of timers)clearTimeout(t);}};
|
||||
mutate:()=>mutation(),setResult:value=>{result=value;},close:()=>{for(const t of timers)clearTimeout(t);}};
|
||||
}
|
||||
|
||||
test('跳过待审文章后打开全新编辑器,不再领取或提交旧稿',async()=>{
|
||||
@@ -107,21 +107,28 @@ test('跳过请求失败时暂停且保留当前稿件',async()=>{
|
||||
}finally{f.close();}
|
||||
});
|
||||
|
||||
const acceptedResult={kind:'accepted',thread_id:'236999',first_post_pid:'238791',author_uid:'123',fid:125,title:'标题',state:'accepted_pending_moderation',post_url:'https://www.dww11.com/thread/23699911.html',evidence:{thread_id:'236999',integrity_issues:[]}};
|
||||
const acceptedResult={kind:'accepted_prompt',state:'accepted_pending_moderation',evidence:{adapter:'moderation_prompt',message:'新主题需要审核,您的帖子通过审核后才能显示',observed_text:'新主题需要审核,您的帖子通过审核后才能显示',observed_url:'https://www.dww11.com/forum.php?topicsubmit=yes'}};
|
||||
for(const stopOnAccept of [false,true])test(`受理后先持久记账;停止=${stopOnAccept} 时禁止回首页`,async()=>{
|
||||
const f=fixture({result:acceptedResult,stopOnAccept});try{
|
||||
await flush();f.doc.dispatchEvent(new f.Event('click',{isTrusted:true,target:{closest:()=>true}}));await flush();
|
||||
await f.tick();await flush();
|
||||
assert.equal(f.calls.filter(x=>x.route==='accept').length,1);
|
||||
assert.equal(f.calls.find(x=>x.route==='accept').body.thread_id,'236999');
|
||||
assert.equal(f.calls.find(x=>x.route==='accept').body.attempt,'attempt-1');
|
||||
assert.deepEqual(f.navigations,stopOnAccept?[]:['https://www.dww11.com/']);
|
||||
assert.equal(f.storage.has('auto-news-current'),false);
|
||||
}finally{f.close();}
|
||||
});
|
||||
test('结果首帖作者不匹配时暂停且不记账',async()=>{
|
||||
const f=fixture({result:{...acceptedResult,author_uid:'999'}});try{
|
||||
test('审核提示无需详情页标题、作者、版块和图文信息即可记账',async()=>{
|
||||
const f=fixture({result:{kind:'accepted_prompt',state:'accepted_pending_moderation',evidence:acceptedResult.evidence}});try{
|
||||
await flush();f.doc.dispatchEvent(new f.Event('click',{isTrusted:true,target:{closest:()=>true}}));await flush();await f.tick();
|
||||
assert.equal(f.calls.some(x=>x.route==='accept'),false);assert.equal(f.run.running,false);assert.deepEqual(f.navigations,[]);
|
||||
assert.equal(f.calls.filter(x=>x.route==='accept').length,1);
|
||||
assert.equal(f.run.running,true);assert.deepEqual(f.navigations,['https://www.dww11.com/']);
|
||||
}finally{f.close();}
|
||||
});
|
||||
test('详情页审核中标志不再单独触发记账',async()=>{
|
||||
const f=fixture({result:{kind:'accepted',author_uid:'123',title:'标题'}});try{
|
||||
await flush();f.doc.dispatchEvent(new f.Event('click',{isTrusted:true,target:{closest:()=>true}}));await flush();await f.tick();
|
||||
assert.equal(f.calls.some(x=>x.route==='accept'),false);assert.deepEqual(f.navigations,[]);
|
||||
}finally{f.close();}
|
||||
});
|
||||
|
||||
@@ -131,3 +138,14 @@ test('明确标题拒绝只记录并暂停,不修稿或重试发表',async()=>
|
||||
assert.equal(f.job.state,'rejected');assert.equal(f.calls.some(x=>x.route==='accept'),false);assert.deepEqual(f.navigations,[]);
|
||||
}finally{f.close();}
|
||||
});
|
||||
|
||||
test('短暂审核提示在 DOM 变化时缓存,提示消失后仍可持久记账',async()=>{
|
||||
const f=fixture({result:acceptedResult});try{
|
||||
await flush();f.mutate();await flush();
|
||||
assert.equal(f.calls.some(x=>x.route==='accept'),false,'人工提交前不观察成功');
|
||||
f.doc.dispatchEvent(new f.Event('click',{isTrusted:true,target:{closest:()=>true}}));await flush();
|
||||
f.mutate();f.setResult({kind:'waiting'});await flush();await f.tick();
|
||||
assert.equal(f.calls.filter(x=>x.route==='accept').length,1);
|
||||
assert.deepEqual(f.navigations,['https://www.dww11.com/']);
|
||||
}finally{f.close();}
|
||||
});
|
||||
@@ -44,6 +44,53 @@ class QueueTests(unittest.TestCase):
|
||||
return self.store.accept(dict(owner='tab-a', id=job['id'], post_url='https://www.dww11.com/thread/12345.html',
|
||||
thread_id='123', first_post_pid='456', author_uid='123', fid=125, title=job['title'], state='accepted_pending_moderation', evidence={'thread_id': '123', 'test': 'fixture only'}))
|
||||
|
||||
def prompt_data(self, job):
|
||||
current = self.store.state()['jobs'][0]
|
||||
message = '新主题需要审核,您的帖子通过审核后才能显示'
|
||||
return dict(owner='tab-a', id=job['id'], attempt=current.get('attempt'),
|
||||
state='accepted_pending_moderation', evidence=dict(adapter='moderation_prompt',
|
||||
message=message, observed_text=message + ',现在将转入首页',
|
||||
observed_url='https://www.dww11.com/forum.php?topicsubmit=yes'))
|
||||
|
||||
def test_prompt_accept_counts_once_and_persists_dedup(self):
|
||||
job = self.begin()
|
||||
self.submit(job)
|
||||
data = self.prompt_data(job)
|
||||
self.store.accept(data)
|
||||
self.assertTrue(self.store.accept(data)['duplicate'])
|
||||
state = self.store.state()
|
||||
self.assertEqual(state['run']['batch']['count'], 1)
|
||||
self.assertIsNone(state['run']['active'])
|
||||
self.assertTrue(state['run']['return_home'])
|
||||
self.assertTrue(state['run']['running'])
|
||||
saved = state['jobs'][0]
|
||||
self.assertEqual(saved['state'], 'accepted_pending_moderation')
|
||||
self.assertEqual(saved.get('post_url'), '')
|
||||
self.assertNotIn('thread_id', saved)
|
||||
restarted = Store(self.root / 'queue.sqlite3')
|
||||
self.assertIn('已受理', restarted.enqueue(self.article))
|
||||
|
||||
def test_prompt_requires_manual_submission_and_current_attempt(self):
|
||||
job = self.begin()
|
||||
with self.assertRaisesRegex(ValueError, '人工提交'):
|
||||
self.store.accept(self.prompt_data(job))
|
||||
self.submit(job)
|
||||
data = self.prompt_data(job)
|
||||
for changed in ({'attempt': 'old'}, {'evidence': {**data['evidence'], 'observed_text': '(审核中)'}},
|
||||
{'evidence': {**data['evidence'], 'observed_url': 'https://example.com/'}}):
|
||||
with self.assertRaises(ValueError):
|
||||
self.store.accept({**data, **changed})
|
||||
self.assertEqual(self.store.run['batch']['count'], 0)
|
||||
|
||||
def test_prompt_after_stop_records_without_resuming(self):
|
||||
job = self.begin()
|
||||
self.submit(job)
|
||||
data = self.prompt_data(job)
|
||||
self.store.control('stop')
|
||||
self.store.accept(data)
|
||||
self.assertFalse(self.store.run['running'])
|
||||
self.assertEqual(self.store.run['batch']['count'], 1)
|
||||
|
||||
def test_stable_identity_and_original_preserved(self):
|
||||
before = self.md.read_bytes()
|
||||
item = inspect_article(self.article)
|
||||
|
||||
Reference in new issue
Block a user