云
3 天以前 704265308b1227dbada5a36b2027b588c062ded2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
const { chromium } = require('playwright');
const fs = require('node:fs'), path = require('node:path'), assert = require('node:assert/strict');
const base = process.env.MOBILE_TEST_URL || 'http://127.0.0.1:5173';
const out = path.resolve('.export_tmp/code-toolbar'); fs.mkdirSync(out, { recursive: true });
(async () => {
 const browser = await chromium.launch({ headless: true, channel: 'msedge' });
 try {
  const context = await browser.newContext({ viewport: { width: 1920, height: 1080 } });
  const page = await context.newPage(), errors = [], calls = [];
  page.on('pageerror', error => errors.push(error.message));
  const permissions = ['bag-code','pallet','batch'].flatMap(kind => ['query','create','update','replace','delete','export'].map(action => `mes:pro-${kind}:${action}`));
  await context.route('**/*', async route => {
   const url = new URL(route.request().url()), p = url.pathname;
   if (url.origin !== base) return route.abort();
   if (!p.startsWith('/admin-api/')) return route.continue();
   calls.push(p);
   let data = null;
   if (p.endsWith('/auth/login')) data = { accessToken:'fixture-token', refreshToken:'fixture-refresh', userId:7, expiresTime:Date.now()+3600000 };
   else if (p.endsWith('/get-permission-info')) data = { user:{ id:7, nickname:'测试员', username:'fixture' }, roles:['worker'], permissions,
    menus:[{ id:1, parentId:0, name:'一袋一码管理', path:'/bagcode', component:'Layout', visible:true, children:
     [['bag-code','bagCode','赋码管理'],['pallet','pallet','托盘码管理'],['batch','batch','批次生产管理']].map(([key,folder,name],i)=>({id:i+2,parentId:1,path:key,name,component:`mes/pro/${folder}/index`,visible:true})) }] };
   else if (p.endsWith('/bag-code/page')) data = { list:[{id:11,code:'6028472024079516',businessCode:'8888888888888888',itemName:'玉米种子',status:10}], total:1 };
   else if (p.endsWith('/pallet/page')) data = { list:[{id:12,code:'P-TEST',businessCode:'7777777777777777',bagCount:1,status:10}], total:1 };
   else if (p.endsWith('/batch/page')) data = { list:[{id:20,code:'B-TEST',businessCode:'6666666666666666',status:20}], total:1 };
   else if (p.endsWith('/pallet/unbound-list')) data = [
    {id:1,tempCode:'P-ONE',itemId:1,itemName:'塔河4号',itemCode:'ITEM-1',bagCount:25},
    {id:2,tempCode:'P-TWO',itemId:1,itemName:'塔河4号',itemCode:'ITEM-1',bagCount:25},
    {id:3,tempCode:'P-OTHER',itemId:2,itemName:'其他品种',itemCode:'ITEM-2',bagCount:25},
    {id:4,tempCode:'P-EMPTY',bagCount:0},
   ];
   else if (p.includes('export')) return route.fulfill({contentType:'application/zip',body:'fixture',headers:{'content-disposition':'attachment;filename=fixture.zip'}});
   else if (p.endsWith('/generate-business-code')) data = 1;
   else if (p.endsWith('/page')) data = {list:[],total:0};
   else if (p.endsWith('/simple-list') || p.endsWith('/list-all-simple') || p.endsWith('/get-unread-count')) data = [];
   return route.fulfill({json:{code:0,msg:'',data}});
  });
  await page.goto(base+'/auth/login');
  await page.locator('input[autocomplete=username]').fill('fixture');
  await page.locator('input[autocomplete=current-password]').fill('fixture-password');
  await page.getByRole('button',{name:'登录 PC 端',exact:true}).click();
  await page.waitForURL(url => !url.pathname.startsWith('/auth/'));
  for (const [key,title,code] of [['bag-code','袋码列表(一袋一码)','8888888888888888'],['pallet','托盘码列表','7777777777777777'],['batch','生产批次列表','6666666666666666']]) {
   await page.goto(base+'/bagcode/'+key);
   await page.getByText(title,{exact:true}).waitFor();
   await page.getByText(code,{exact:true}).first().waitFor();
   const toolbar = page.locator('.mes-code-toolbar');
   for (const width of [1920,1280]) {
    await page.setViewportSize({width,height:1080});
    const buttons = await toolbar.locator('button').evaluateAll(nodes => nodes.map(node => ({text:node.textContent.trim(),y:node.getBoundingClientRect().top,x:node.getBoundingClientRect().right})));
    assert(buttons.length <= 6, 'Too many visible toolbar operations');
    assert(Math.max(...buttons.map(b=>b.y))-Math.min(...buttons.map(b=>b.y)) < 4, JSON.stringify(buttons));
    assert(buttons.every(b=>b.x <= width), 'Toolbar exceeds screen');
    const heading = await page.getByText(title,{exact:true}).boundingBox();
    assert(heading.height < 30, 'Title wraps');
    await page.screenshot({path:path.join(out,`${key}-${width}.png`),fullPage:true});
   }
   await toolbar.getByRole('button',{name:'导出',exact:true}).click();
   await page.getByRole('menuitem',{name:key==='bag-code'?'业务二维码':key==='pallet'?'业务二维码(选中记录)':'Excel 数据',exact:true}).waitFor();
   await page.getByText(title,{exact:true}).click();
   const qr = page.getByRole('button',{name:'业务二维码',exact:true});
   if (key !== 'batch') {
    await qr.click();
    await page.getByRole('dialog',{name:'业务码二维码'}).waitFor();
    assert(await page.getByRole('dialog').getByText(code,{exact:true}).isVisible());
    await page.screenshot({path:path.join(out,`${key}-business-qr.png`)});
    await page.keyboard.press('Escape');
   }
  }
  await page.getByRole('button',{name:'新增批次',exact:true}).click();
  const dialog=page.getByRole('dialog');await dialog.waitFor();
  const select=dialog.locator('.ant-select').last();
  await select.click();
  const first=page.locator('.ant-select-item-option').filter({hasText:'P-ONE'});
  await first.click();
  const itemFields=dialog.getByPlaceholder('选择托盘后自动带入');
  await page.waitForFunction(()=>[...document.querySelectorAll('input')].some(input=>input.value==='塔河4号'));
  assert.equal(await itemFields.nth(0).inputValue(),'塔河4号');
  assert.equal(await itemFields.nth(1).inputValue(),'ITEM-1');
  const other=page.locator('.ant-select-item-option').filter({hasText:'P-OTHER'});
  assert((await other.getAttribute('class')).includes('ant-select-item-option-disabled'));
  const empty=page.locator('.ant-select-item-option').filter({hasText:'P-EMPTY'});
  assert((await empty.getAttribute('class')).includes('ant-select-item-option-disabled'));
  await page.locator('.ant-select-item-option').filter({hasText:'P-TWO'}).click();
  await dialog.click({position:{x:10,y:10}});
  await page.screenshot({path:path.join(out,'batch-variety-binding.png'),fullPage:true});
  await page.keyboard.press('Escape');
  if(await dialog.isVisible()) await dialog.getByRole('button',{name:/取\s*消/}).click();
  assert.deepEqual(errors,[]);
  console.log('PASS: three code pages, grouped toolbar at 1920/1280, export dropdown, business QR preview');
 } finally { await browser.close(); }
})().catch(error=>{console.error(error);process.exitCode=1;});