dy-dashboard/tests/test_spot_application_frontend.py
2026-10-28 11:14:17 +08:00

221 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Execute sampling list/save JavaScript with a small DOM and fetch substitute."""
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 is required for frontend execution')
class SpotApplicationFrontendTest(unittest.TestCase):
def setUp(self):
self.ctx = quickjs.Context()
self.ctx.eval('''
var window = globalThis, nodes = {}, requests = [], alerts = [];
var _spotApplicationRowsCache = {};
function node() {
return {value:'', innerHTML:'', style:{}, listeners:{},
setCustomValidity:function(message){this.error=message;},
classList:{toggle:function(){}},
remove:function(){this.removed=true;},
querySelector:function(){return document.getElementById('submitButton');},
addEventListener:function(name, fn){this.listeners[name]=fn;}};
}
var document = {
getElementById:function(id){return nodes[id] || (nodes[id]=node());},
querySelectorAll:function(){return [];},
createElement:node,
body:{appendChild:function(el){nodes[el.id]=el;}}
};
window._hasPagePerm = function(){return true;};
var FormData = function(){};
var alert = function(message){alerts.push(message);};
var fields = {};
['product_name','barcode','sample_quantity','recipient_info','brand','group_name','department'].forEach(function(name){fields[name]=node();});
var form = document.getElementById('spotApplicationForm');
form.elements = {namedItem:function(name){return fields[name];}};
form.checkValidity = function(){return ['product_name','barcode','sample_quantity','recipient_info'].every(function(name){
return fields[name].value.trim() && !fields[name].error;
}) && Object.keys(fields).every(function(name){return !fields[name].error;});};
form.reportValidity = function(){};
var listResponse = {code:0,data:{rows:[{
id:7,product_name:'刚保存的商品',barcode:'00123',sample_quantity:2,
status:'待处理',image_path:'/static/uploads/spot_application/test.png'
}],total:1,size:20,page:1}};
var fetch = function(url) {
requests.push(url);
return Promise.resolve({json:function(){
return Promise.resolve(url.indexOf('/create') >= 0 ?
{code:0,id:7} : listResponse);
}});
};
''')
self.ctx.eval(SOURCE[SOURCE.index('function escJs('):SOURCE.index('// 成本价、单价')])
self.ctx.eval(SOURCE[SOURCE.index('window.switchSpotApplicationTab='):
SOURCE.index('function decideSpotApplication(')])
def execute(self, code):
self.ctx.eval(code)
while self.ctx.execute_pending_job():
pass
def test_successful_list_is_visible_in_every_tab(self):
for tab in ('application', 'arrived', 'rejected'):
self.execute('window.switchSpotApplicationTab(' + json.dumps(tab) + ')')
html = self.ctx.eval('nodes.spotAppRows.innerHTML')
self.assertIn('刚保存的商品', html)
self.assertNotIn('网络异常', html)
self.assertEqual(self.ctx.eval('nodes.spotAppTotal.textContent'), '共 1 条')
def test_empty_list_remains_empty_instead_of_network_error(self):
self.execute('listResponse.data.rows=[];listResponse.data.total=0;loadSpotApplicationList()')
self.assertIn('暂无记录', self.ctx.eval('nodes.spotAppRows.innerHTML'))
self.assertEqual(self.ctx.eval('nodes.spotAppPagination.innerHTML'), '')
def test_pagination_changes_request_page_and_keeps_search_and_tab(self):
self.execute('''
nodes.spotAppSearch=document.getElementById('spotAppSearch');
nodes.spotAppSearch.value='品牌';
window._spotAppTab='rejected';
renderSpotApplicationPagination(45,20,1);
''')
html = self.ctx.eval('nodes.spotAppPagination.innerHTML')
self.assertIn('disabled', html)
self.assertIn('window._spotAppPage=2;loadSpotApplicationList()', html)
self.execute('window._spotAppPage=2;loadSpotApplicationList()')
url = self.ctx.eval('requests[requests.length-1]')
self.assertIn('page=2', url)
self.assertIn('tab=rejected', url)
self.assertIn('search=%E5%93%81%E7%89%8C', url)
self.execute('renderSpotApplicationPagination(45,20,3)')
self.assertNotIn('window._spotAppPage=4', self.ctx.eval('nodes.spotAppPagination.innerHTML'))
def test_create_success_resets_filters_and_shows_saved_row(self):
self.execute('''
document.getElementById('spotAppSearch').value='不匹配的旧搜索';
window._spotAppPage=4;window._spotAppTab='rejected';
openSpotApplicationModal();
fields.product_name.value='刚保存的商品';fields.barcode.value='0012345678901';
fields.sample_quantity.value='2';fields.recipient_info.value='收件信息';
nodes.spotApplicationForm.listeners.input.call(nodes.spotApplicationForm);
nodes.spotApplicationForm.listeners.submit.call(
nodes.spotApplicationForm,{preventDefault:function(){}});
''')
self.assertTrue(self.ctx.eval('nodes.spotApplicationModal.removed'))
self.assertEqual(self.ctx.eval('nodes.spotAppSearch.value'), '')
self.assertEqual(self.ctx.eval('window._spotAppPage'), 1)
self.assertEqual(self.ctx.eval('window._spotAppTab'), 'application')
self.assertIn('刚保存的商品', self.ctx.eval('nodes.spotAppRows.innerHTML'))
self.assertEqual(json.loads(self.ctx.eval('JSON.stringify(alerts)')), ['申请已添加'])
def test_recipient_templates_lock_fields_and_preserve_guangzhou_edits(self):
self.execute('''
['recipient_template','recipient_address','recipient_name','recipient_phone'].forEach(
function(name){fields[name]=node();});
openSpotApplicationModal();
fields.product_name.value='样品';fields.barcode.value='0012345678901';
fields.sample_quantity.value='2';
fields.recipient_template.value='shenzhen';
fields.recipient_template.listeners.change();
''')
self.assertFalse(self.ctx.eval('nodes.submitButton.disabled'))
self.assertTrue(self.ctx.eval('fields.recipient_address.readOnly && fields.recipient_name.readOnly && fields.recipient_phone.readOnly'))
self.assertIn('收件人:一部样品\n电话:17771977816', self.ctx.eval('fields.recipient_info.value'))
self.execute('''
fields.recipient_template.value='guangzhou';
fields.recipient_template.listeners.change();
''')
self.assertTrue(self.ctx.eval('fields.recipient_address.readOnly'))
self.assertFalse(self.ctx.eval('fields.recipient_name.readOnly || fields.recipient_phone.readOnly'))
self.assertEqual(self.ctx.eval('fields.recipient_name.value'), 'XXX')
self.assertEqual(self.ctx.eval('fields.recipient_phone.value'), '111111111111')
self.execute('''
fields.recipient_name.value='张三';fields.recipient_phone.value='13800138000';
nodes.spotApplicationForm.listeners.input.call(nodes.spotApplicationForm);
''')
self.assertIn('收件人:张三\n电话:13800138000', self.ctx.eval('fields.recipient_info.value'))
self.execute('''
fields.recipient_phone.value=' ';
nodes.spotApplicationForm.listeners.input.call(nodes.spotApplicationForm);
''')
self.assertTrue(self.ctx.eval('nodes.submitButton.disabled'))
self.execute('''
fields.recipient_template.value='shenzhen';fields.recipient_template.listeners.change();
''')
self.assertFalse(self.ctx.eval('nodes.submitButton.disabled'))
self.assertEqual(self.ctx.eval('fields.recipient_name.value'), '一部样品')
self.execute('''
fields.recipient_template.value='';fields.recipient_template.listeners.change();
''')
self.assertTrue(self.ctx.eval('nodes.submitButton.disabled'))
def test_brand_uses_fixed_select_options(self):
self.execute('openSpotApplicationModal()')
html = self.ctx.eval('nodes.spotApplicationModal.innerHTML')
self.assertIn('<select name="brand">', html)
self.assertIn('<select name="department">', html)
self.assertNotIn('<option value="阿里">', html)
for name in ('泊紫汀兰', '洁维塔'):
self.assertIn(f'<option value="{name}">{name}</option>', html)
def test_brand_changes_update_group_requirement_and_save_button(self):
self.execute('''
openSpotApplicationModal();
fields.product_name.value='样品';fields.barcode.value='0012345678901';
fields.sample_quantity.value='2';fields.recipient_info.value='收件信息';
fields.brand.value='洁维塔';
nodes.spotApplicationForm.listeners.change.call(nodes.spotApplicationForm);
''')
self.assertTrue(self.ctx.eval('fields.group_name.required && nodes.submitButton.disabled'))
self.assertEqual(self.ctx.eval('nodes.spotApplicationGroupRequired.style.display'), '')
self.execute('''
fields.group_name.value='京东1组';
nodes.spotApplicationForm.listeners.change.call(nodes.spotApplicationForm);
''')
self.assertFalse(self.ctx.eval('nodes.submitButton.disabled'))
self.execute('''
fields.group_name.value='';fields.brand.value='泊紫汀兰';
nodes.spotApplicationForm.listeners.change.call(nodes.spotApplicationForm);
''')
self.assertFalse(self.ctx.eval('fields.group_name.required || nodes.submitButton.disabled'))
self.assertEqual(self.ctx.eval('fields.group_name.error'), '')
self.assertEqual(self.ctx.eval('nodes.spotApplicationGroupRequired.style.display'), 'none')
def test_result_headers_and_rows_follow_requested_order(self):
import re
for tab, expected in (
('arrived', ['序号','品牌','产品名称','图片','条形码','平台','运营','到货时间']),
('rejected', ['序号','品牌','产品名称','平台','运营','是否驳回','驳回原因']),
):
self.execute('renderSpotApplicationHead(' + json.dumps(tab) + ')')
self.assertEqual(re.findall(r'<th>(.*?)</th>', self.ctx.eval('nodes.spotAppHead.innerHTML')), expected)
self.execute('''
window._spotAppTab=''' + json.dumps(tab) + ''';
listResponse.data.page=2;
Object.assign(listResponse.data.rows[0],{
brand:'测试品牌', image_path:'/static/uploads/spot_application/test.png',
platform:'京东', operator:'测试运营', arrival_time:'2026-10-15',
rejected:'是', reject_reason:'申请驳回原因'
});
loadSpotApplicationList();
''')
html = self.ctx.eval('nodes.spotAppRows.innerHTML')
cells = re.findall(r'<td[^>]*>(.*?)</td>', html)
self.assertEqual(len(cells), len(expected))
self.assertEqual(cells[:3], ['21','测试品牌','刚保存的商品'])
if tab == 'arrived':
self.assertIn('<img', cells[3])
self.assertEqual(cells[4:], ['00123','京东','测试运营','2026-10-15'])
else:
self.assertEqual(cells[3:], ['京东','测试运营','是','申请驳回原因'])
if __name__ == '__main__':
unittest.main()