import sys, time, json from playwright.sync_api import sync_playwright DEMO = 'file:///D:/code/crm-backend-matt/.scratch/opportunity-e2e/demo/index.html' OUT = 'D:/code/crm-backend-matt/.scratch/opp-customer-dependency' logs = [] with sync_playwright() as p: b = p.chromium.launch(headless=True) pg = b.new_page() pg.on('console', lambda m: logs.append(f'{m.type}: {m.text}')) pg.goto(DEMO) pg.wait_for_load_state('networkidle') # 1) login as first user (switchUser triggers login() which loads dicts+list) pg.evaluate("switchUser(0)") pg.wait_for_timeout(2500) # verify dicts loaded incl customer_type/customer_role dict_counts = pg.evaluate("({customer_type:(S.dict['customer_type']||[]).length, customer_role:(S.dict['customer_role']||[]).length})") print('DICT COUNTS:', dict_counts) # 2) pick first opp id from list, open detail oppId = pg.evaluate("""async () => { const d = await api('POST','/api/opportunity/page',{form:{viewType:'MINE',current:1,size:5}}); const recs = (d && (d.content||d.records))||[]; return recs.length?String(recs[0].id):null; }""") print('OPP ID:', oppId) pg.evaluate(f"loadDetail('{oppId}')") pg.wait_for_timeout(1500) # 3) go to customer subtab pg.evaluate("S.subTab='customer';renderSubTabs();loadSub('customer')") pg.wait_for_timeout(1200) pg.screenshot(path=f'{OUT}/demo04-1-customer-subtab.png', full_page=True) # 4) open add-customer dialog pg.evaluate("dlgAddCustomer()") pg.wait_for_timeout(600) # check customer_type + customer_role options rendered in dialog opt_check = pg.evaluate("""() => { const dlg = document.querySelector('dialog[open]') || document.querySelector('dialog'); const ct = dlg.querySelector('[name=customerType]'); const cr = dlg.querySelector('[name=customerRole]'); return {customerType_opts: ct?ct.options.length:-1, customerRole_opts: cr?cr.options.length:-1}; }""") print('DIALOG OPTS:', opt_check) pg.screenshot(path=f'{OUT}/demo04-2-dialog-open.png', full_page=True) # 5) search customers pg.evaluate("document.querySelector('dialog[open] [name=custKw]').value=''") pg.evaluate("doCustSearch()") pg.wait_for_timeout(1200) search_rows = pg.evaluate("document.querySelectorAll('#custResult .kv').length") print('SEARCH ROWS:', search_rows) pg.screenshot(path=f'{OUT}/demo04-3-search-results.png', full_page=True) # 6) pick first candidate -> triggers contacts cascade picked = pg.evaluate("""async () => { const first = document.querySelector('#custResult .kv'); if(!first) return null; first.click(); return true; }""") pg.wait_for_timeout(1200) cascade = pg.evaluate("""() => { const sel = document.getElementById('custContactSel'); const picked = document.getElementById('custPicked').textContent; return {contact_options: sel?sel.options.length:-1, picked_text: picked}; }""") print('PICKED:', picked, 'CASCADE:', cascade) pg.screenshot(path=f'{OUT}/demo04-4-selected-cascade.png', full_page=True) # 7) choose a role and submit pg.evaluate("""() => { const cr = document.querySelector('dialog[open] [name=customerRole]'); if(cr && cr.options.length>1) cr.value = cr.options[1].value; }""") pg.evaluate("doAddCustomer()") pg.wait_for_timeout(1800) # 8) back on customer subtab, count linked customers linked = pg.evaluate("""async () => { const d = await api('GET','/api/opportunity/customer/list',{params:{oppId: S.oppId}}); return (d||[]).length; }""") print('LINKED CUSTOMERS AFTER ADD:', linked) pg.screenshot(path=f'{OUT}/demo04-5-after-add.png', full_page=True) b.close() print('--- CONSOLE (errors only) ---') for l in logs: if l.startswith('error'): print(l) print('done')