problem-area-cropper.test.js
1 /** 2 * Problem Area Cropper Unit Tests 3 * Tests cropProblemAreas() with real sharp operations on generated test images 4 */ 5 6 import { describe, test } from 'node:test'; 7 import assert from 'node:assert/strict'; 8 import sharp from 'sharp'; 9 import { cropProblemAreas } from '../../src/reports/problem-area-cropper.js'; 10 11 /** 12 * Create a test PNG buffer of specified dimensions 13 */ 14 async function createTestImage(width, height) { 15 return sharp({ 16 create: { 17 width, 18 height, 19 channels: 3, 20 background: { r: 100, g: 150, b: 200 }, 21 }, 22 }) 23 .png() 24 .toBuffer(); 25 } 26 27 describe('cropProblemAreas', () => { 28 test('returns empty array when no problem areas', async () => { 29 const img = await createTestImage(1440, 5000); 30 const result = await cropProblemAreas(img, []); 31 assert.deepStrictEqual(result, []); 32 }); 33 34 test('returns empty array when problemAreas is null', async () => { 35 const img = await createTestImage(1440, 5000); 36 const result = await cropProblemAreas(img, null); 37 assert.deepStrictEqual(result, []); 38 }); 39 40 test('crops single problem area at 50%', async () => { 41 const img = await createTestImage(1440, 5000); 42 const problemAreas = [ 43 { 44 factor: 'call_to_action', 45 description: 'CTA is below fold', 46 approximate_y_position_percent: 50, 47 severity: 'high', 48 recommendation: 'Move CTA above fold', 49 }, 50 ]; 51 52 const crops = await cropProblemAreas(img, problemAreas); 53 54 assert.equal(crops.length, 1); 55 assert.equal(crops[0].factor, 'call_to_action'); 56 assert.equal(crops[0].description, 'CTA is below fold'); 57 assert.equal(crops[0].recommendation, 'Move CTA above fold'); 58 assert.equal(crops[0].severity, 'high'); 59 assert.ok(Buffer.isBuffer(crops[0].imageBuffer)); 60 61 // Verify crop dimensions 62 const meta = await sharp(crops[0].imageBuffer).metadata(); 63 assert.ok(meta.width <= 768); 64 assert.ok(meta.height <= 400); 65 }); 66 67 test('crops problem area at top (0%)', async () => { 68 const img = await createTestImage(1440, 5000); 69 const problemAreas = [ 70 { 71 factor: 'headline_quality', 72 description: 'Weak headline', 73 approximate_y_position_percent: 0, 74 severity: 'medium', 75 recommendation: 'Improve headline', 76 }, 77 ]; 78 79 const crops = await cropProblemAreas(img, problemAreas); 80 81 assert.equal(crops.length, 1); 82 const meta = await sharp(crops[0].imageBuffer).metadata(); 83 assert.ok(meta.height > 0); 84 }); 85 86 test('crops problem area at bottom (100%)', async () => { 87 const img = await createTestImage(1440, 5000); 88 const problemAreas = [ 89 { 90 factor: 'trust_signals', 91 description: 'No footer trust elements', 92 approximate_y_position_percent: 100, 93 severity: 'low', 94 recommendation: 'Add footer badges', 95 }, 96 ]; 97 98 const crops = await cropProblemAreas(img, problemAreas); 99 100 assert.equal(crops.length, 1); 101 assert.ok(Buffer.isBuffer(crops[0].imageBuffer)); 102 }); 103 104 test('handles multiple problem areas', async () => { 105 const img = await createTestImage(1440, 5000); 106 const problemAreas = [ 107 { 108 factor: 'headline_quality', 109 description: 'Weak headline', 110 approximate_y_position_percent: 5, 111 severity: 'high', 112 recommendation: 'Improve headline', 113 }, 114 { 115 factor: 'call_to_action', 116 description: 'CTA buried', 117 approximate_y_position_percent: 45, 118 severity: 'high', 119 recommendation: 'Move CTA up', 120 }, 121 { 122 factor: 'trust_signals', 123 description: 'Missing reviews', 124 approximate_y_position_percent: 80, 125 severity: 'medium', 126 recommendation: 'Add reviews section', 127 }, 128 ]; 129 130 const crops = await cropProblemAreas(img, problemAreas); 131 132 assert.equal(crops.length, 3); 133 assert.equal(crops[0].factor, 'headline_quality'); 134 assert.equal(crops[1].factor, 'call_to_action'); 135 assert.equal(crops[2].factor, 'trust_signals'); 136 }); 137 138 test('defaults severity to medium when not provided', async () => { 139 const img = await createTestImage(1440, 5000); 140 const problemAreas = [ 141 { 142 factor: 'offer_clarity', 143 description: 'Unclear pricing', 144 approximate_y_position_percent: 60, 145 recommendation: 'Clarify pricing structure', 146 }, 147 ]; 148 149 const crops = await cropProblemAreas(img, problemAreas); 150 151 assert.equal(crops.length, 1); 152 assert.equal(crops[0].severity, 'medium'); 153 }); 154 155 test('handles small images gracefully', async () => { 156 const img = await createTestImage(400, 200); 157 const problemAreas = [ 158 { 159 factor: 'imagery_design', 160 description: 'Poor quality hero image', 161 approximate_y_position_percent: 50, 162 severity: 'medium', 163 recommendation: 'Replace hero image', 164 }, 165 ]; 166 167 const crops = await cropProblemAreas(img, problemAreas); 168 169 assert.equal(crops.length, 1); 170 const meta = await sharp(crops[0].imageBuffer).metadata(); 171 assert.ok(meta.width <= 768); 172 }); 173 });