Files
ARR-2.0-0918/tests/javascript/daily_price_review.cjs
T

76 lines
4.0 KiB
JavaScript

// Actual UI price saving against a synthetic API, without live business writes.
const test = require('node:test');
const assert = require('node:assert/strict');
const {harness,response} = require('./helpers/arr_ui_harness.cjs');
const fixture=(price=null)=>({case_id:'fixture-case',case_status:'open',revision:1,
completed_items:price===null?0:1,total_items:1,
items:[{item_id:1,company_key:'Fixture Company',rate_code:'FIXTURE',effective_rate_amount:100,
real_price:price,affected_records:1,affected_rooms:1,affected_room_nights:2,candidate_prices:[]}],
pagination:{offset:0,limit:50,total:1}});
test('zero is a valid explicit price while blank and negative prices remain invalid',()=>{
const h=harness(()=>{throw new Error('unexpected request');});
for(const value of [0,'0',' 0 ','1500']) assert.equal(h.strictReviewPrice(value),true);
for(const value of [null,undefined,'',' ','-1',-1,'0.00','1.5','abc']) assert.equal(h.strictReviewPrice(value),false);
assert.equal(h.reviewPriceInput(0),'0');
assert.equal(h.reviewPriceInput('0'),'0');
assert.equal(h.reviewPriceInput(null),'');
});
test('zero is rendered as a saved value and permits continuation when all items are saved',()=>{
const h=harness(()=>{throw new Error('unexpected request');});
h.renderDailyPriceReview(fixture(0));
const html=h.element('#daily-price-review-body').innerHTML;
assert.match(html,/value="0"/);
assert.match(html,/daily-price-review-row is-complete/);
assert.match(html,/daily_review.saved/);
assert.equal(h.element('#daily-price-review-finalize').disabled,false);
h.renderDailyPriceReview(fixture(null));
assert.match(h.element('#daily-price-review-body').innerHTML,/value=""/);
assert.match(h.element('#daily-price-review-body').innerHTML,/daily_review.price_placeholder/);
assert.equal(h.element('#daily-price-review-body').innerHTML.includes('placeholder="0"'),false);
assert.equal(h.element('#daily-price-review-finalize').disabled,true);
});
test('explicit zero is saved as zero and survives reloading without becoming empty',async()=>{
let current=fixture();
const h=harness((url,options)=>{
if(options.method==='PATCH') {
assert.equal(url,'/api/jobs/fixture-job/review/items/1');
const decision=JSON.parse(options.body);
assert.deepEqual(decision,{case_id:'fixture-case',revision:1,real_price:'0'});
current={...fixture('0'),revision:2};
return response(200,{revision:2});
}
if(url.startsWith('/api/jobs/fixture-job/review?')) return response(200,current);
if(url.startsWith('/api/jobs?')) return response(200,[]);
throw new Error('unexpected request '+url);
});
h.state.dailyPriceReview=fixture();
h.state.dailyPriceReviewJobId='fixture-job';
const input={value:'0',focus(){}};
const row={dataset:{dailyPriceReviewItemId:'1'},querySelector:()=>input};
await h.saveDailyPriceReviewItem({closest:()=>row});
assert.equal(h.calls.filter(c=>c.method==='PATCH').length,1);
assert.equal(h.state.dailyPriceReview.items[0].real_price,'0');
assert.equal(h.state.dailyPriceReview.completed_items,1);
assert.equal(h.dailyPriceReviewCanFinalize(h.state.dailyPriceReview),true);
assert.equal(h.element('#daily-price-review-finalize').disabled,false);
assert.match(h.element('#daily-price-review-body').innerHTML,/value="0"/);
await h.loadDailyPriceReview();
assert.equal(h.state.dailyPriceReview.items[0].real_price,'0');
assert.equal(h.calls.filter(c=>c.method==='PATCH').length,1,'reload must not submit another decision');
});
test('an empty input cannot be silently saved as zero',async()=>{
const h=harness(()=>{throw new Error('unexpected request');});
h.state.dailyPriceReview=fixture();
h.state.dailyPriceReviewJobId='fixture-job';
const input={value:'',focus(){}};
const row={dataset:{dailyPriceReviewItemId:'1'},querySelector:()=>input};
await h.saveDailyPriceReviewItem({closest:()=>row});
assert.equal(h.calls.length,0);
assert.equal(h.state.dailyPriceReview.items[0].real_price,null);
assert.equal(h.dailyPriceReviewCanFinalize(h.state.dailyPriceReview),false);
});