/* Isolated UI test: every request is intercepted; no Hub/e-conomic data is changed. * BMC_PLAYWRIGHT_MODULE=/path/to/playwright-core node tests/browser/economic_catalog.cjs */ const assert = require('node:assert/strict'); const fs = require('node:fs'); const {chromium} = require(process.env.BMC_PLAYWRIGHT_MODULE || 'playwright-core'); (async () => { const browser = await chromium.launch({headless:true, executablePath:process.env.BMC_CHROME_EXECUTABLE || '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'}); const page = await browser.newPage({viewport:{width:1440,height:1000}}); const errors = [], writes = []; page.on('pageerror', e=>errors.push(e.message)); const html = '' + fs.readFileSync('app/products/frontend/economic.html','utf8') .replace(/{%[^%]*%}/g,''); const script = fs.readFileSync('static/js/economic-catalog.js','utf8'); const permissions = Object.fromEntries(['economic.catalog.manage','economic.catalog.rebind','economic.pricing.manage','economic.documents.export','products.update'].map(k=>[k,true])); let products = [{id:1,name:'Microsoft 365',economic_product_number:null,economic_sync_status:'unlinked',sales_price:100,sales_currency:'DKK',lifecycle_status:'active'}]; const connection = {id:1,name:'Testaftale',agreement_number:'123',currency:'DKK',enabled:false,auto_sync:false,number_prefix:'TEST-',sync_interval_minutes:15}; await page.route('**/*', async route=>{ const url = new URL(route.request().url()), path=url.pathname; if (path === '/') return route.fulfill({contentType:'text/html',body:html}); if (path === '/static/js/economic-catalog.js') return route.fulfill({contentType:'application/javascript',body:script}); if (route.request().method() !== 'GET') writes.push({path,body:route.request().postDataJSON()}); let data = {}; if(path.endsWith('/status'))data={connections:[connection],permissions,read_only:true,dry_run:true,jobs:[],exports:[]}; else if(path.endsWith('/references'))data=[{kind:'product-groups',number:1,name:'Software',allow_new:true},{kind:'units',number:1,name:'stk'}]; else if(path === '/api/v1/economic/catalog/products')data=products; else if(path.endsWith('/economic-link')) { products=[{...products[0],economic_product_number:'00123',economic_sync_status:'synced'}];data={linked:true}; } else if(path.endsWith('/price-rules') && route.request().method()==='GET')data=[]; return route.fulfill({contentType:'application/json',body:JSON.stringify(data)}); }); try { await page.goto('http://catalog.test/'); await page.getByRole('button',{name:'Åbn',exact:true}).click(); await page.locator('input[name=number]').fill('00123'); // Linking must work without selecting group/unit (only needed for creation). await page.getByRole('button',{name:'Gem',exact:true}).click(); await page.locator('#ecoDialog').waitFor({state:'hidden'}); assert(writes.some(w=>w.path.endsWith('/economic-link')&&w.body.number==='00123')); await page.getByRole('button',{name:'Pris',exact:true}).click(); await page.locator('input[name=sales_price]').fill('123.45'); await page.getByRole('button',{name:'Gem',exact:true}).click(); await page.locator('#ecoDialog').waitFor({state:'hidden'}); assert(writes.some(w=>w.path.endsWith('/economic-pricing')&&w.body.sales_price==='123.45')); await page.getByRole('button',{name:'Status',exact:true}).click(); await page.locator('select[name=lifecycle_status]').selectOption('phasing_out'); await page.getByRole('button',{name:'Gem',exact:true}).click(); await page.locator('#ecoDialog').waitFor({state:'hidden'}); assert(writes.some(w=>w.path.endsWith('/economic-state')&&w.body.lifecycle_status==='phasing_out')); await page.getByRole('button',{name:'Prisregler',exact:true}).click(); await page.getByRole('button',{name:'Ny prisregel',exact:true}).click(); await page.locator('input[name=name]').fill('Test rabat'); await page.locator('input[name=value]').fill('12.5'); await page.getByRole('button',{name:'Gem',exact:true}).click(); await page.locator('#ecoDialog').waitFor({state:'hidden'}); assert(writes.some(w=>w.path.endsWith('/price-rules')&&w.body.value===12.5)); await page.getByRole('button',{name:'Sync og fejl',exact:true}).click(); assert(await page.getByRole('button',{name:'Eksportér gemt ordre som kladde'}).isVisible()); assert.deepEqual(errors, []); console.log('PASS: link without group/unit, decimal price, lifecycle, price rule, exports tab; zero JS errors'); } catch (error) { console.error('UI errors:', errors, 'Feedback:', await page.locator('#ecoFeedback').textContent().catch(()=>'')); throw error; } finally { await browser.close(); } })().catch(error=>{console.error(error);process.exitCode=1;});