97 lines
5.0 KiB
JavaScript
97 lines
5.0 KiB
JavaScript
const {test} = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const vm = require('node:vm');
|
|
const flush = async () => { for(let i=0;i<12;i++) await Promise.resolve(); };
|
|
|
|
function page(){
|
|
class Element {
|
|
constructor(){ this.children=[]; this.attributes={}; this.hidden=false; this.dataset={}; }
|
|
set textContent(value){ this.text=value; this.children=[]; }
|
|
get textContent(){ return (this.text || '')+this.children.map(x=>x.textContent).join(''); }
|
|
set innerHTML(value){ this.children=[]; this.text=value; }
|
|
append(...nodes){ this.children.push(...nodes); }
|
|
appendChild(node){ this.append(node); }
|
|
setAttribute(key,value){ this.attributes[key]=String(value); }
|
|
focus(){ this.focused=true; }
|
|
}
|
|
const ids={}, requests=[];
|
|
const el=id=>ids[id] ||= new Element();
|
|
const context=vm.createContext({document:{getElementById:el,createElement:()=>new Element()}, AbortController, setTimeout, clearTimeout});
|
|
vm.runInContext(fs.readFileSync('static/js/customer_orders.js','utf8'),context);
|
|
const orders=context.CustomerOrders.create({text:key=>key,
|
|
fetch:(url,options)=>new Promise((resolve,reject)=>requests.push({url,options,resolve,reject})),
|
|
renderCard:(element,card)=>{element.textContent=card.no;},
|
|
onUnauthorized:()=>{page.expired=true;}});
|
|
return {el,orders,requests,reply:async (i,data,status=200)=>{
|
|
requests[i].resolve({ok:status===200,status,json:async()=>data}); await flush();
|
|
}};
|
|
}
|
|
const row=(id=1)=>({id,order_no:'ORDER'+id,inbound_code:'INBOUND'+id,status:'PROCESSING'});
|
|
|
|
test('login automatically opens list; collapse/reopen and paging keep drafts outside panel untouched',async()=>{
|
|
const p=page();p.orders.setUser({id:1});
|
|
assert.equal(p.el('customer-orders').hidden,false);
|
|
assert.equal(p.el('customer-orders-toggle').attributes['aria-expanded'],'true');
|
|
await p.reply(0,{orders:[row()],next_before:1});
|
|
assert.match(p.el('customer-orders-list').textContent,/INBOUND1/);
|
|
p.el('customer-orders-toggle').onclick();
|
|
assert.equal(p.el('customer-orders-body').hidden,true);
|
|
p.el('customer-orders-toggle').onclick();
|
|
p.el('customer-orders-more').onclick();
|
|
assert.equal(p.requests[1].url,'/api/customer/orders?before=1');
|
|
await p.reply(1,{orders:[row(2)],next_before:null});
|
|
assert.match(p.el('customer-orders-list').textContent,/INBOUND1.*INBOUND2/);
|
|
assert.equal(p.el('customer-orders-more').hidden,true);
|
|
});
|
|
|
|
test('logout clears list and ignores delayed list/detail responses from a previous account',async()=>{
|
|
const p=page();p.orders.setUser({id:1});p.orders.setUser(null);
|
|
await p.reply(0,{orders:[row()],next_before:null});
|
|
assert.equal(p.el('customer-orders').hidden,true);
|
|
assert.equal(p.el('customer-orders-list').textContent,'');
|
|
p.orders.setUser({id:2});await p.reply(1,{orders:[row(2)],next_before:null});
|
|
p.el('customer-orders-list').children[0].onclick();
|
|
p.orders.setUser(null);p.orders.setUser({id:3});
|
|
await p.reply(2,{card:{no:'private-old-account'}});
|
|
assert.doesNotMatch(p.el('customer-orders-detail').textContent,/private/);
|
|
p.orders.setUser(null);
|
|
await p.reply(3,{orders:[],next_before:null});
|
|
});
|
|
|
|
test('exact order detail and back preserve list without a new AI request',async()=>{
|
|
const p=page();p.orders.setUser({id:1});
|
|
await p.reply(0,{orders:[row(9)],next_before:null});
|
|
p.el('customer-orders-list').children[0].onclick();
|
|
assert.equal(p.requests[1].url,'/api/customer/orders/9');
|
|
await p.reply(1,{card:{no:'ORDER9'}});
|
|
assert.equal(p.el('customer-orders-detail').textContent,'ORDER9');
|
|
p.el('customer-orders-back').onclick();
|
|
assert.equal(p.el('customer-orders-list').hidden,false);
|
|
assert.equal(p.requests.length,2);
|
|
});
|
|
|
|
test('failed request offers retry; expired login immediately clears protected data',async()=>{
|
|
const p=page();p.orders.setUser({id:1});
|
|
p.requests[0].reject(new Error('offline'));await flush();
|
|
assert.equal(p.el('customer-orders-retry').hidden,false);
|
|
p.el('customer-orders-retry').onclick();
|
|
await p.reply(1,{orders:[row()],next_before:null});
|
|
p.el('customer-orders-refresh').onclick();
|
|
await p.reply(2,{error:'customer_login_required'},401);
|
|
assert.equal(p.el('customer-orders').hidden,true);
|
|
assert.equal(p.el('customer-orders-list').textContent,'');
|
|
});
|
|
|
|
test('refresh cancels pagination; stale errors cannot replace the refreshed list',async()=>{
|
|
const p=page();p.orders.setUser({id:1});
|
|
await p.reply(0,{orders:[row()],next_before:1});
|
|
p.el('customer-orders-more').onclick();
|
|
p.el('customer-orders-refresh').onclick();
|
|
await p.reply(2,{orders:[row(3)],next_before:null});
|
|
p.requests[1].reject(new Error('late error'));await flush();
|
|
assert.equal(p.el('customer-orders-retry').hidden,true);
|
|
assert.doesNotMatch(p.el('customer-orders-list').textContent,/INBOUND1/);
|
|
assert.match(p.el('customer-orders-list').textContent,/INBOUND3/);
|
|
});
|