"""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"]'))