Files
ISA-Frontend/libs/shared/barcode/src/lib/barcode.directive.spec.ts
Lorenz Hilpert 7a6a2dc49d Merged PR 2038: feat(shared,crm): add Code 128 barcode generation library
feat(shared,crm): add Code 128 barcode generation library

Implements new @isa/shared/barcode library with directive and component
for generating Code 128 barcodes using JsBarcode.

Features:
- Standalone Angular directive (svg[sharedBarcode])
- Standalone Angular component (<shared-barcode>)
- Signal-based reactive inputs
- SVG-based vector rendering
- Customizable colors, size, margins, fonts
- Comprehensive Vitest test coverage (39 tests)
- Storybook stories for both directive and component
- Integrated into customer loyalty card component

Changes:
- Created @isa/shared/barcode library with directive and component
- Added JsBarcode dependency (v3.12.1)
- Integrated barcode into customer loyalty card display
- Added Storybook stories for interactive documentation
- Fixed ui-switch story component reference
- Updated library reference documentation

Refs #5496

Related work items: #5496
2025-11-21 13:42:32 +00:00

244 lines
6.7 KiB
TypeScript

import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { BarcodeDirective } from './barcode.directive';
// Mock JsBarcode
vi.mock('jsbarcode', () => ({
default: vi.fn((element, value, options) => {
// Simulate JsBarcode by adding a rect element to the SVG
if (element && element.tagName === 'svg') {
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('data-value', value);
rect.setAttribute('data-format', options?.format || 'CODE128');
element.appendChild(rect);
// Call valid callback if provided
if (options?.valid) {
const isValid = value && value.length > 0;
options.valid(isValid);
}
}
}),
}));
@Component({
standalone: true,
imports: [BarcodeDirective],
template: `
<svg
sharedBarcode
[value]="barcodeValue"
[format]="format"
[width]="width"
[height]="height"
[displayValue]="displayValue"
></svg>
`,
})
class TestHostComponent {
barcodeValue = '123456789';
format = 'CODE128';
width = 2;
height = 100;
displayValue = true;
}
describe('BarcodeDirective', () => {
let fixture: ComponentFixture<TestHostComponent>;
let component: TestHostComponent;
let svgElement: SVGElement;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TestHostComponent],
}).compileComponents();
fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
svgElement = fixture.nativeElement.querySelector('svg');
fixture.detectChanges();
});
describe('Initialization', () => {
it('should create the directive', () => {
expect(component).toBeTruthy();
});
it('should apply to SVG element', () => {
expect(svgElement).toBeTruthy();
expect(svgElement.tagName).toBe('svg');
});
it('should render barcode on initialization', () => {
// JsBarcode adds child elements to the SVG
expect(svgElement.children.length).toBeGreaterThan(0);
});
});
describe('Barcode Rendering', () => {
it('should render barcode with default CODE128 format', () => {
const rect = svgElement.querySelector('rect');
expect(rect).toBeTruthy();
});
it('should render barcode with custom value', () => {
component.barcodeValue = '987654321';
fixture.detectChanges();
// SVG should be updated with new value
expect(svgElement.children.length).toBeGreaterThan(0);
});
it('should update barcode when value changes', () => {
component.barcodeValue = 'NEWVALUE123';
fixture.detectChanges();
// Barcode should be re-rendered (may have different number of bars)
expect(svgElement.children.length).toBeGreaterThan(0);
});
it('should render with custom width', () => {
component.width = 4;
fixture.detectChanges();
expect(svgElement.children.length).toBeGreaterThan(0);
});
it('should render with custom height', () => {
component.height = 200;
fixture.detectChanges();
const rect = svgElement.querySelector('rect');
expect(rect).toBeTruthy();
});
it('should render without display value when disabled', () => {
component.displayValue = false;
fixture.detectChanges();
// Check that SVG is still rendered
expect(svgElement.children.length).toBeGreaterThan(0);
});
});
describe('Input Changes', () => {
it('should re-render when format changes', () => {
component.format = 'CODE128';
fixture.detectChanges();
expect(svgElement.children.length).toBeGreaterThan(0);
});
it('should handle multiple rapid value changes', () => {
component.barcodeValue = 'VALUE1';
fixture.detectChanges();
component.barcodeValue = 'VALUE2';
fixture.detectChanges();
component.barcodeValue = 'VALUE3';
fixture.detectChanges();
expect(svgElement.children.length).toBeGreaterThan(0);
});
});
describe('Error Handling', () => {
it('should handle empty value gracefully', () => {
// JsBarcode should handle this or log warning
component.barcodeValue = '';
fixture.detectChanges();
// Directive should not crash
expect(component).toBeTruthy();
});
it('should handle very long values', () => {
component.barcodeValue = '1'.repeat(100);
fixture.detectChanges();
expect(svgElement.children.length).toBeGreaterThan(0);
});
});
describe('Customization Options', () => {
it('should render with custom line color', () => {
@Component({
standalone: true,
imports: [BarcodeDirective],
template: `
<svg
sharedBarcode
[value]="'123456'"
[lineColor]="'#FF0000'"
></svg>
`,
})
class ColorTestComponent {}
const colorFixture = TestBed.createComponent(ColorTestComponent);
colorFixture.detectChanges();
const svg = colorFixture.nativeElement.querySelector('svg');
expect(svg).toBeTruthy();
expect(svg.children.length).toBeGreaterThan(0);
});
it('should render with custom background', () => {
@Component({
standalone: true,
imports: [BarcodeDirective],
template: `
<svg
sharedBarcode
[value]="'123456'"
[background]="'#F0F0F0'"
></svg>
`,
})
class BackgroundTestComponent {}
const bgFixture = TestBed.createComponent(BackgroundTestComponent);
bgFixture.detectChanges();
const svg = bgFixture.nativeElement.querySelector('svg');
expect(svg).toBeTruthy();
});
it('should render with custom font size', () => {
@Component({
standalone: true,
imports: [BarcodeDirective],
template: `
<svg sharedBarcode [value]="'123456'" [fontSize]="30"></svg>
`,
})
class FontSizeTestComponent {}
const fontFixture = TestBed.createComponent(FontSizeTestComponent);
fontFixture.detectChanges();
const svg = fontFixture.nativeElement.querySelector('svg');
expect(svg).toBeTruthy();
});
it('should render with custom margin', () => {
@Component({
standalone: true,
imports: [BarcodeDirective],
template: `
<svg sharedBarcode [value]="'123456'" [margin]="20"></svg>
`,
})
class MarginTestComponent {}
const marginFixture = TestBed.createComponent(MarginTestComponent);
marginFixture.detectChanges();
const svg = marginFixture.nativeElement.querySelector('svg');
expect(svg).toBeTruthy();
});
});
});