feat(flyer-import): integrate AI-based flyer parsing with image support
- Add support for PNG, JPEG, and WebP image formats in flyer import - Replace external importer service with internal AI-based parsing pipeline - Add new services: TextExtractorService, AiFlyerParserService, FlyerNormalizerService - Integrate Mistral AI, pdf-parse, and tesseract.js dependencies - Add quality confidence indicators and warning panels in Flutter UI - Update package.json with new dependencies and transform ignore patterns - Add documentation for flyer importer system - Add Kilo AI planning file for Happy Island project BREAKING CHANGE: Flyer import now uses internal AI parsing instead of external importer service
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { FlyerNormalizerService } from './flyer-normalizer.service';
|
||||
|
||||
describe('FlyerNormalizerService', () => {
|
||||
let service: FlyerNormalizerService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [FlyerNormalizerService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<FlyerNormalizerService>(FlyerNormalizerService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe('normalize', () => {
|
||||
it('should normalize a valid item', () => {
|
||||
const items = [
|
||||
{
|
||||
rawName: 'KALLRÖKT LAX, GRAVAD LAX',
|
||||
normalizedName: 'kallrökt lax gravad lax',
|
||||
category: 'Fisk',
|
||||
price: 39.9,
|
||||
comparisonPrice: 266.0,
|
||||
unit: 'kg',
|
||||
offer: ['Max 3 köp/hushåll'],
|
||||
confidence: 0.85,
|
||||
reasonCodes: ['ai_parsed'],
|
||||
},
|
||||
];
|
||||
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].rawName).toBe('KALLRÖKT LAX, GRAVAD LAX');
|
||||
expect(result[0].price).toBe(39.9);
|
||||
expect(result[0].priceUnit).toBe('kg');
|
||||
expect(result[0].categoryHint).toBe('Fisk');
|
||||
});
|
||||
|
||||
it('should handle missing fields gracefully', () => {
|
||||
const items = [
|
||||
{
|
||||
name: 'PRODUKT',
|
||||
// andra fält saknas
|
||||
},
|
||||
];
|
||||
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].rawName).toBe('PRODUKT');
|
||||
expect(result[0].price).toBeNull();
|
||||
expect(result[0].categoryHint).toBeNull();
|
||||
});
|
||||
|
||||
it('should skip items without name', () => {
|
||||
const items = [
|
||||
{ price: 100 }, // no name
|
||||
{ rawName: 'VALID PRODUCT', price: 50 },
|
||||
];
|
||||
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0].rawName).toBe('VALID PRODUCT');
|
||||
});
|
||||
|
||||
it('should normalize units correctly', () => {
|
||||
const items = [
|
||||
{ rawName: 'Mjölk', unit: 'L' },
|
||||
{ rawName: 'Smör', unit: 'styck' },
|
||||
{ rawName: 'Socker', unit: 'KG' },
|
||||
];
|
||||
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0].priceUnit).toBe('l');
|
||||
expect(result[1].priceUnit).toBe('st');
|
||||
expect(result[2].priceUnit).toBe('kg');
|
||||
});
|
||||
|
||||
it('should parse Swedish prices correctly', () => {
|
||||
const items = [
|
||||
{ rawName: 'Produkt1', price: '39,90' },
|
||||
{ rawName: 'Produkt2', price: 39.9 },
|
||||
{ rawName: 'Produkt3', price: '100' },
|
||||
];
|
||||
|
||||
const result = service.normalize(items);
|
||||
|
||||
expect(result[0].price).toBe(39.9);
|
||||
expect(result[1].price).toBe(39.9);
|
||||
expect(result[2].price).toBe(100);
|
||||
});
|
||||
|
||||
it('should return empty list for non-array input', () => {
|
||||
const result = service.normalize(null as any);
|
||||
expect(result).toEqual([]);
|
||||
|
||||
const result2 = service.normalize(undefined as any);
|
||||
expect(result2).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user