/ tests / capture / problem-area-cropper.test.js
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  });