- Added migration to create economic_change_requests table for tracking change requests for customers and products. - Introduced new permissions for requesting and approving changes in e-conomic. - Developed frontend JavaScript functionality for managing economic catalog, including handling change requests and displaying their statuses. - Created browser tests to validate UI interactions related to product management and change requests. - Added unit tests for backend logic to ensure proper handling of product numbers, price rules, and economic write policies.
66 lines
4.7 KiB
JavaScript
66 lines
4.7 KiB
JavaScript
/* 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 = '<!doctype html><meta charset="utf-8">' + 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;});
|