115 lines
6.9 KiB
Python
115 lines
6.9 KiB
Python
"""Execute confirmation modal interactions without network or real database writes."""
|
|
import json
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
try:
|
|
import quickjs
|
|
except ImportError:
|
|
quickjs=None
|
|
|
|
SOURCE=(Path(__file__).resolve().parents[1]/'static/js/app.js').read_text()
|
|
|
|
|
|
@unittest.skipIf(quickjs is None,'QuickJS required')
|
|
class ConfirmationDetailFrontendTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.ctx=quickjs.Context()
|
|
self.ctx.eval('''
|
|
var window=globalThis, nodes={}, fields={}, alerts=[], requests=[], canEdit=true;
|
|
function node(){return {value:'',style:{},listeners:{},classList:{toggle:function(name,value){this[name]=value;}},
|
|
addEventListener:function(name,fn){this.listeners[name]=fn;},
|
|
querySelectorAll:function(){return [];},setAttribute:function(name,value){this[name]=value;},
|
|
querySelector:function(){return nodes.submit;},remove:function(){this.removed=true;}};}
|
|
nodes.submit=node();
|
|
var document={getElementById:function(id){return nodes[id]||(nodes[id]=node());},
|
|
createElement:node,querySelector:function(){return nodes.table;},
|
|
body:{appendChild:function(el){nodes[el.id]=el;}}};
|
|
nodes.table=node();
|
|
var form=document.getElementById('spotConfirmationDetailForm');
|
|
form.elements={namedItem:function(name){return fields[name];}};
|
|
form.reportValidity=function(){return true;};
|
|
var values={sample_status:'样品已到货',product_unit_price:'12.3456',oa_approval_no:'OA1',
|
|
express_tracking_no:'00123',arrival_date:'2026-10-15',remark:'备注',product_code:'P1',
|
|
final_product_name:'最终名称',platform:'京东',sample_confirmed:'做',
|
|
sample_confirmation_reason:'价格高',other_reason:''};
|
|
Object.keys(values).forEach(function(key){fields[key]=node();fields[key].value=values[key];});
|
|
window._spotConfirmRows={7:Object.assign({application_id:7,brand:'品牌',product_name:'样品',recipient:'申请人'},values)};
|
|
window._hasPagePerm=function(){return canEdit;};
|
|
var alert=function(value){alerts.push(value);};
|
|
var promptValue=null;
|
|
window.prompt=function(){return promptValue;};
|
|
var FormData=function(){this.forEach=function(fn){Object.keys(fields).forEach(function(key){fn(fields[key].value,key);});};};
|
|
var fetch=function(url,options){requests.push({url:url,options:options});return Promise.resolve({json:function(){return Promise.resolve({code:0});}});};
|
|
var reloads=0;
|
|
var loadSpotConfirmationList=function(){reloads++;};
|
|
''')
|
|
self.ctx.eval(SOURCE[SOURCE.index('function escJs('):SOURCE.index('// 成本价、单价')])
|
|
start=SOURCE.index(' var fields = [',SOURCE.index('function loadSpotConfirmationPage('))
|
|
end=SOURCE.index(' var headerHtml',start)
|
|
self.ctx.eval('(function(){'+SOURCE[start:end]+'window._spotConfirmFields=fields;})();')
|
|
self.ctx.eval(SOURCE[SOURCE.index('function applySpotConfirmationActions('):SOURCE.index('function openSpotConfirmationImportModal(')])
|
|
|
|
def run_js(self, code):
|
|
self.ctx.eval(code)
|
|
while self.ctx.execute_pending_job():
|
|
pass
|
|
|
|
def test_modal_fields_and_readonly_permissions(self):
|
|
self.run_js('openSpotConfirmationDetail(7);')
|
|
html=self.ctx.eval('nodes.spotConfirmationDetailModal.innerHTML')
|
|
for label in ('品牌','申请人','领样人','产品单价','样品状态','样品确认原因'):
|
|
self.assertIn(label,html)
|
|
self.assertNotIn('样品位置',html)
|
|
for key in ('brand','recipient','receiving_department','product_name'):
|
|
self.assertNotIn('name="'+key+'"',html)
|
|
for value in ('做','不做','其他','价格高','质感差','资质不全','同质化严重','无',
|
|
'未付款','付款中','样品到货中','样品已到货','不授权','厂家无货','停产'):
|
|
self.assertIn('value="'+value+'"',html)
|
|
self.run_js('canEdit=false;openSpotConfirmationDetail(7);')
|
|
html=self.ctx.eval('nodes.spotConfirmationDetailModal.innerHTML')
|
|
self.assertNotIn('type="submit"',html)
|
|
self.assertNotIn('name="product_unit_price"',html)
|
|
|
|
def test_other_reason_cancel_invalid_and_valid(self):
|
|
self.run_js('openSpotConfirmationDetail(7);')
|
|
for value in (None,'','价格高且尺寸不合适','质感差','资质不全','同质化严重'):
|
|
self.run_js('promptValue='+json.dumps(value)+';fields.sample_confirmation_reason.value="其他";fields.sample_confirmation_reason.listeners.change.call(fields.sample_confirmation_reason);')
|
|
self.assertEqual(self.ctx.eval('fields.sample_confirmation_reason.value'),'价格高')
|
|
self.run_js('promptValue="尺寸不合适";fields.sample_confirmation_reason.value="其他";fields.sample_confirmation_reason.listeners.change.call(fields.sample_confirmation_reason);')
|
|
self.assertEqual(self.ctx.eval('fields.other_reason.value'),'尺寸不合适')
|
|
self.assertEqual(self.ctx.eval('nodes.spotConfirmOtherReason.textContent'),'尺寸不合适')
|
|
|
|
def test_confirmed_do_defaults_empty_reason_to_none(self):
|
|
self.run_js('''
|
|
fields.sample_confirmed.value=''; fields.sample_confirmation_reason.value='价格高';
|
|
window._spotConfirmRows[7].sample_confirmed='';
|
|
window._spotConfirmRows[7].sample_confirmation_reason='价格高';
|
|
openSpotConfirmationDetail(7);
|
|
fields.sample_confirmed.value='做';
|
|
fields.sample_confirmed.listeners.change.call(fields.sample_confirmed);
|
|
''')
|
|
self.assertEqual(self.ctx.eval('fields.sample_confirmation_reason.value'),'无')
|
|
|
|
def test_save_uses_editable_fields_and_prevents_double_submission(self):
|
|
self.run_js('''
|
|
openSpotConfirmationDetail(7);
|
|
form.listeners.submit({preventDefault:function(){}});
|
|
form.listeners.submit({preventDefault:function(){}});
|
|
''')
|
|
self.assertEqual(self.ctx.eval('requests.length'),1)
|
|
request=json.loads(self.ctx.eval('JSON.stringify(requests[0])'))
|
|
payload=json.loads(request['options']['body'])
|
|
self.assertEqual(request['url'],'/api/spot/confirmation/save')
|
|
self.assertEqual(payload['product_unit_price'],'12.3456')
|
|
self.assertEqual(payload['application_id'],7)
|
|
self.assertNotIn('recipient',payload)
|
|
self.assertTrue(self.ctx.eval('nodes.spotConfirmationDetailModal.removed'))
|
|
self.assertEqual(self.ctx.eval('reloads'),1)
|
|
|
|
def test_operations_column_toggle(self):
|
|
self.run_js('toggleSpotConfirmationActions();')
|
|
self.assertTrue(self.ctx.eval('nodes.table.classList["actions-collapsed"]'))
|
|
self.run_js('toggleSpotConfirmationActions();')
|
|
self.assertFalse(self.ctx.eval('nodes.table.classList["actions-collapsed"]'))
|