Merged PR 1904: feat(utils-ean-validation, remission-list): add EAN validation library and im...

feat(utils-ean-validation, remission-list): add EAN validation library and implement exact search

Create new EAN validation utility library with validator function and isEan helper.
Implement exact search functionality for remission lists that bypasses filters
when scanning EAN codes or performing exact searches.

Changes:
- Add new utils/ean-validation library with EAN regex validation
- Export eanValidator for Angular reactive forms integration
- Export isEan utility function for EAN validation checks
- Configure library with Vitest for testing
- Update remission list resource to support exact search mode
- Clear filters and orderBy when performing EAN-based searches
- Add data attributes to product info component for E2E testing

Ref: #5128
This commit is contained in:
Nino Righi
2025-08-01 13:22:41 +00:00
committed by Patrick Brix
parent d7d535c10d
commit 14a5a67a1e
19 changed files with 34964 additions and 34729 deletions

View File

@@ -0,0 +1,7 @@
# ean-validation
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test ean-validation` to execute the unit tests.

View File

@@ -0,0 +1,34 @@
const nx = require('@nx/eslint-plugin');
const baseConfig = require('../../../eslint.config.js');
module.exports = [
...baseConfig,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'lib',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'lib',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
},
];

View File

@@ -0,0 +1,20 @@
{
"name": "ean-validation",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/utils/ean-validation/src",
"prefix": "lib",
"projectType": "library",
"tags": [],
"targets": {
"test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"reportsDirectory": "../../../coverage/libs/utils/ean-validation"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1 @@
export * from './lib/ean-validation/ean-validation';

View File

@@ -0,0 +1,5 @@
/**
* Constants for EAN validation.
* EAN (European Article Number) is a 13-digit number used to identify products.
*/
export const EAN_REGEX = /^[0-9]{13}$/;

View File

@@ -0,0 +1,51 @@
import { describe, it, expect } from 'vitest';
import { FormControl } from '@angular/forms';
import { eanValidator, isEan } from './ean-validation';
describe('eanValidator', () => {
it('should return null for valid EAN', () => {
// Arrange
const control = new FormControl('1234567890123');
// Act
const result = eanValidator(control);
// Assert
expect(result).toBeNull();
});
it('should return null for empty value', () => {
// Arrange
const control = new FormControl('');
// Act
const result = eanValidator(control);
// Assert
expect(result).toBeNull();
});
});
describe('isEan', () => {
it('should return true for valid EAN', () => {
// Arrange
const validEan = '1234567890123';
// Act
const result = isEan(validEan);
// Assert
expect(result).toBe(true);
});
it('should return false for undefined value', () => {
// Arrange
const undefinedValue = undefined;
// Act
const result = isEan(undefinedValue);
// Assert
expect(result).toBe(false);
});
});

View File

@@ -0,0 +1,33 @@
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
import { EAN_REGEX } from './constants';
/**
* Validator function to check if a control's value is a valid EAN (European Article Number).
* A valid EAN is a 13-digit number.
*
* @param {AbstractControl} control - The form control to validate
* @returns {ValidationErrors | null} Returns an error object if the value is invalid, otherwise null
*/
export const eanValidator: ValidatorFn = (
control: AbstractControl,
): ValidationErrors | null => {
const value = control.value;
if (value && !EAN_REGEX.test(value)) {
return { invalidEan: true };
}
return null;
};
/**
* Checks if a value is a valid EAN (European Article Number).
* A valid EAN is a 13-digit number.
*
* @param {string | undefined} value - The value to check
* @returns {boolean} True if the value is a valid EAN, false otherwise
*/
export const isEan = (value: string | undefined): boolean => {
if (!value) {
return false;
}
return EAN_REGEX.test(value);
};

View File

@@ -0,0 +1,13 @@
import '@angular/compiler';
import '@analogjs/vitest-angular/setup-zone';
import {
BrowserTestingModule,
platformBrowserTesting,
} from '@angular/platform-browser/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserTestingModule,
platformBrowserTesting(),
);

View File

@@ -0,0 +1,30 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"importHelpers": true,
"moduleResolution": "bundler",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "preserve"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"typeCheckHostBindings": true,
"strictTemplates": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@@ -0,0 +1,27 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"src/**/*.spec.ts",
"src/test-setup.ts",
"jest.config.ts",
"src/**/*.test.ts",
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx"
],
"include": ["src/**/*.ts"]
}

View File

@@ -0,0 +1,29 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
],
"files": ["src/test-setup.ts"]
}

View File

@@ -0,0 +1,27 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import angular from '@analogjs/vite-plugin-angular';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../../node_modules/.vite/libs/utils/ean-validation',
plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
setupFiles: ['src/test-setup.ts'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../../coverage/libs/utils/ean-validation',
provider: 'v8' as const,
},
},
}));