← back to Shopify Sample Shipping
engine-logic-test.mjs
53 lines
// Mirrors the theme engine evaluate() exactly, then asserts the TK-11333 policy scenarios.
const CONFIG={designerCap:12,bandCapCents:4500,retailWC:5,retailFabric:5,softCap:20,
designerTags:['trade','sample-freeship','interior designer'],fabricTypes:['fabric','fabrics','textile','textiles']};
const isSample=l=>(l.variant_title||'').toLowerCase()==='sample'||/-sample$/i.test(l.sku||'');
const isFabric=l=>CONFIG.fabricTypes.indexOf((l.product_type||'').toLowerCase())!==-1;
const isDesigner=t=>{const s=String(t||'').toLowerCase().split(',').map(x=>x.trim());return CONFIG.designerTags.some(d=>s.indexOf(d)!==-1);};
function evaluate(d){
const samples=d.lines.filter(isSample);
const total=samples.reduce((n,l)=>n+l.quantity,0);
const wc=samples.filter(l=>!isFabric(l)).reduce((n,l)=>n+l.quantity,0);
const fab=samples.filter(isFabric).reduce((n,l)=>n+l.quantity,0);
const hasNonSample=d.lines.some(l=>!isSample(l));
const designer=!!d.isLoggedIn&&isDesigner(d.tags);
const overSoftCap=total>CONFIG.softCap;
const bandCovers=d.cartSubtotalCents<=CONFIG.bandCapCents;
const codeCovers=designer&&total<=CONFIG.designerCap&&total>0;
const willBeFree=bandCovers||codeCovers;
const willBeCharged=total>0&&!willBeFree;
const shouldApplyCode=designer&&!bandCovers&&total<=CONFIG.designerCap&&!overSoftCap;
return {total,wc,fab,designer,bandCovers,willBeFree,willBeCharged,shouldApplyCode,overSoftCap};
}
const S=(n,type='Wallcovering')=>({product_type:type,variant_title:'Sample',sku:'X-'+n+'-Sample',quantity:n});
const ROLL={product_type:'Wallcovering',variant_title:'Sold Per Roll',sku:'X-123',quantity:1};
const P=n=>Math.round(n*425); // n samples subtotal in cents
let pass=0,fail=0;
function t(name,d,exp){const g=evaluate(d);const ok=Object.keys(exp).every(k=>g[k]===exp[k]);
console.log((ok?'PASS':'FAIL')+' '+name+' => free:'+g.willBeFree+' charged:'+g.willBeCharged+' applyCode:'+g.shouldApplyCode+(ok?'':' EXPECTED '+JSON.stringify(exp)));ok?pass++:fail++;}
// RETAIL (not logged in / no trade tag)
t('retail 6 WC ($25.50) — under band, must NOT claim charged (the old lie)',
{isLoggedIn:false,tags:'',cartSubtotalCents:P(6),lines:[S(6)]},{willBeFree:true,willBeCharged:false,shouldApplyCode:false});
t('retail 5 WC + 5 fabric ($42.50) — free by band, no code',
{isLoggedIn:false,tags:'',cartSubtotalCents:P(10),lines:[S(5,'Wallcovering'),S(5,'Fabric')]},{willBeFree:true,willBeCharged:false,shouldApplyCode:false});
t('retail 11 samples ($46.75) — over band, charged',
{isLoggedIn:false,tags:'',cartSubtotalCents:P(11),lines:[S(11)]},{willBeFree:false,willBeCharged:true,shouldApplyCode:false});
// DESIGNER
t('designer 10 samples ($42.50) — free by band, NO needless redirect',
{isLoggedIn:true,tags:'sample-freeship',cartSubtotalCents:P(10),lines:[S(10)]},{willBeFree:true,willBeCharged:false,shouldApplyCode:false});
t('designer 12 samples ($51) — over band, code extends -> free + applies TRADESHIP',
{isLoggedIn:true,tags:'trade',cartSubtotalCents:P(12),lines:[S(12)]},{willBeFree:true,willBeCharged:false,shouldApplyCode:true});
t('designer 13 samples ($55.25) — over designer cap, charged, no code',
{isLoggedIn:true,tags:'interior designer',cartSubtotalCents:P(13),lines:[S(13)]},{willBeFree:false,willBeCharged:true,shouldApplyCode:false});
// MIXED + SOFT CAP
t('retail 5 samples + 1 roll — over band (roll), charged',
{isLoggedIn:false,tags:'',cartSubtotalCents:P(5)+16840,lines:[S(5),ROLL]},{willBeFree:false,willBeCharged:true,shouldApplyCode:false});
t('any 21 samples — over soft cap (blocks checkout)',
{isLoggedIn:true,tags:'trade',cartSubtotalCents:P(21),lines:[S(21)]},{overSoftCap:true,shouldApplyCode:false});
// Type classifier + Default Title sample edge case
t('Default-Title sample sku (-Sample) still counts as a sample',
{isLoggedIn:false,tags:'',cartSubtotalCents:P(6),lines:[{product_type:'Wallcovering',variant_title:'Default Title',sku:'DWFE-1-2-Sample',quantity:6}]},{willBeFree:true,total:6});
console.log('\n'+pass+' passed, '+fail+' failed');
process.exit(fail?1:0);