From 364d3e28520c5179760c9bd1d8dcfb958e6f8011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Amor=C3=B3s?= <39102625+ElMaxter99@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:40:16 +0100 Subject: [PATCH 1/2] test: cover import normalization and add CI --- .github/workflows/ci.yml | 29 +++ src/app/app-import-normalization.spec.ts | 285 +++++++++++++++++++++++ 2 files changed, 314 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 src/app/app-import-normalization.spec.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..dbc3fef --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + pull_request: + branches: + - '**' + push: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run unit tests + run: npm run test -- --watch=false --browsers=ChromeHeadless diff --git a/src/app/app-import-normalization.spec.ts b/src/app/app-import-normalization.spec.ts new file mode 100644 index 0000000..3b5622b --- /dev/null +++ b/src/app/app-import-normalization.spec.ts @@ -0,0 +1,285 @@ +import { TestBed } from '@angular/core/testing'; +import { App } from './app'; +import { AnnotationTemplatesService, AnnotationTemplate } from './annotation-templates.service'; +import { PageAnnotations } from './models/annotation.model'; +import { Language, TranslationService } from './i18n/translation.service'; + +describe('App import normalization', () => { + class AnnotationTemplatesServiceStub { + readonly defaultTemplateId = 'stub-default-template'; + private readonly templates: AnnotationTemplate[] = [ + { + id: this.defaultTemplateId, + name: 'Default', + createdAt: 0, + pages: [], + }, + ]; + + readonly storeLastCoordsSpy = jasmine.createSpy<(pages: readonly PageAnnotations[]) => void>( + 'storeLastCoords' + ); + + getTemplates(): AnnotationTemplate[] { + return this.templates.map((template) => ({ + ...template, + pages: template.pages.map((page) => ({ + num: page.num, + fields: page.fields.map((field) => ({ ...field })), + })), + })); + } + + saveTemplate(): AnnotationTemplate | null { + return null; + } + + deleteTemplate(): void {} + + storeLastCoords(pages: readonly PageAnnotations[]): void { + this.storeLastCoordsSpy(pages); + } + + loadLastCoords(): PageAnnotations[] | null { + return null; + } + + } + + class TranslationServiceStub { + readonly supportedLanguages: readonly Language[] = ['es-ES']; + + translate(key: string): string { + return key; + } + + setLanguage(): void {} + + getCurrentLanguage(): Language { + return this.supportedLanguages[0]; + } + + language: any; + } + + let alertSpy: jasmine.Spy<(message?: string) => void>; + let consoleErrorSpy: jasmine.Spy; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [App], + providers: [ + { provide: AnnotationTemplatesService, useClass: AnnotationTemplatesServiceStub }, + { provide: TranslationService, useClass: TranslationServiceStub }, + ], + }).compileComponents(); + + alertSpy = spyOn(window, 'alert').and.stub(); + consoleErrorSpy = spyOn(console, 'error').and.stub(); + }); + + it('normalizes imported coordinates with inconsistent values', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + + app.coordsTextModel = JSON.stringify( + { + pages: [ + { + num: '2', + fields: [ + { + x: '10.234', + y: '20.789', + mapField: ' customer . address [ 0 ] ', + fontSize: '12.3456', + color: ' #FF00AA ', + type: 'TEXT', + value: [' Hello ', '', null], + }, + { + x: '30', + y: 'NaN', + mapField: ' ', + fontSize: '-5', + color: '', + type: 'number', + value: ' 123.456 ', + decimals: '2', + appender: ' kg ', + }, + { + x: '50.555', + y: '60.444', + mapField: '', + fontSize: '0', + color: 'rgba(255, 128, 0, 0.5)', + type: 'NUMBER', + value: ' 987.654 ', + decimals: '3.7', + appender: ' kg ', + }, + ], + }, + { + num: '1', + fields: [ + { + x: '5', + y: '15', + mapField: ' ', + fontSize: 0, + color: '#abc', + type: 'radio', + value: false, + }, + { + x: '15.999', + y: '25.111', + mapField: ['', ' order . total '], + fontSize: '16', + color: 'rgb(34, 51, 68)', + type: 'NUMBER', + value: '', + decimals: '2', + appender: '\tUSD ', + }, + ], + }, + { + num: '0', + fields: [ + { + x: 1, + y: 1, + mapField: 'should be skipped', + }, + ], + }, + 'not-an-object', + null, + ], + }, + null, + 2 + ); + + app.applyCoordsText(); + + expect(alertSpy).not.toHaveBeenCalled(); + expect(consoleErrorSpy).not.toHaveBeenCalled(); + expect(app.coords()).toEqual([ + { + num: 1, + fields: [ + { + x: 5, + y: 15, + mapField: 'false', + fontSize: 14, + color: '#aabbcc', + type: 'radio', + value: 'false', + }, + { + x: 16, + y: 25.11, + mapField: 'order.total', + fontSize: 16, + color: '#223344', + type: 'number', + decimals: 2, + appender: '\tUSD ', + }, + ], + }, + { + num: 2, + fields: [ + { + x: 10.23, + y: 20.79, + mapField: 'customer.address[0]', + fontSize: 12.35, + color: '#ff00aa', + type: 'text', + value: 'Hello', + }, + { + x: 50.56, + y: 60.44, + mapField: '987.654', + fontSize: 14, + color: '#ff8000', + type: 'number', + value: '987.654', + decimals: 4, + appender: ' kg ', + }, + ], + }, + ]); + }); + + it('keeps the current coordinates and alerts when JSON is malformed', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + + const initialCoords: PageAnnotations[] = [ + { + num: 3, + fields: [ + { + x: 1, + y: 2, + mapField: 'field', + fontSize: 14, + color: '#000000', + type: 'text', + value: 'value', + }, + ], + }, + ]; + + app.coords.set(initialCoords); + app.coordsTextModel = '{"pages": [invalid]'; + + app.applyCoordsText(); + + expect(alertSpy).toHaveBeenCalledTimes(1); + expect(consoleErrorSpy).toHaveBeenCalled(); + expect(app.coords()).toEqual(initialCoords); + }); + + it('keeps the current coordinates and alerts when pages collection is invalid', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + + const initialCoords: PageAnnotations[] = [ + { + num: 1, + fields: [ + { + x: 10, + y: 20, + mapField: 'existing', + fontSize: 14, + color: '#000000', + type: 'text', + value: 'initial', + }, + ], + }, + ]; + + app.coords.set(initialCoords); + app.coordsTextModel = '{"pages": {"1": {"num": 1, "fields": []}}}'; + + app.applyCoordsText(); + + expect(alertSpy).toHaveBeenCalledTimes(1); + expect(consoleErrorSpy).toHaveBeenCalled(); + expect(app.coords()).toEqual(initialCoords); + }); +}); From 25185bfe9b8a8fe15459db75e10924547ea4b2a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alvaro=20Amor=C3=B3s?= <39102625+ElMaxter99@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:45:26 +0100 Subject: [PATCH 2/2] Update CI workflow for Node 22 --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbc3fef..b9aedee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,11 +19,10 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '20' - cache: 'npm' + node-version: '22.12.0' - name: Install dependencies - run: npm ci + run: npm install - name: Run unit tests run: npm run test -- --watch=false --browsers=ChromeHeadless