- Introduced `EmployeesModule` to manage employee data, including read and write controllers. - Created database migrations for the `employees` table, including constraints and unique indexes. - Implemented validation for employee fields such as name, code, and position with corresponding utility functions. - Developed service and repository layers for handling employee data operations. - Added unit tests for the employees service, repository, and controllers to ensure functionality and correctness. - Updated application module to include the new `EmployeesModule` for better organization.
109 lines
2.8 KiB
TypeScript
109 lines
2.8 KiB
TypeScript
import {
|
|
EMPLOYEE_CODE_MAX_LENGTH,
|
|
EMPLOYEE_NAME_MAX_LENGTH,
|
|
isAllowedCsvUpload,
|
|
isValidEmployeeCode,
|
|
isValidEmployeeName,
|
|
isValidEmployeePosition,
|
|
parseCsvRecord,
|
|
} from './employee-fields';
|
|
|
|
describe('employee fields', () => {
|
|
describe('isValidEmployeeName', () => {
|
|
it.each(['Ada', 'Jean Luc', 'A', 'North West Crew'])(
|
|
'accepts %s',
|
|
(name) => {
|
|
expect(isValidEmployeeName(name)).toBe(true);
|
|
},
|
|
);
|
|
|
|
it.each(['', 'Ada1', 'Jean-Luc', 'EMP_01', ' Ada', 'Ada ', 'Jean Luc'])(
|
|
'rejects %s',
|
|
(name) => {
|
|
expect(isValidEmployeeName(name)).toBe(false);
|
|
},
|
|
);
|
|
|
|
it('rejects names longer than 64 characters', () => {
|
|
expect(
|
|
isValidEmployeeName('A'.repeat(EMPLOYEE_NAME_MAX_LENGTH + 1)),
|
|
).toBe(false);
|
|
expect(isValidEmployeeName('A'.repeat(EMPLOYEE_NAME_MAX_LENGTH))).toBe(
|
|
true,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('isValidEmployeeCode', () => {
|
|
it.each(['EMP', 'EMP_01', 'A', 'ops2', 'A_b_1'])('accepts %s', (code) => {
|
|
expect(isValidEmployeeCode(code)).toBe(true);
|
|
});
|
|
|
|
it.each(['', 'EMP 01', 'EMP-01', 'EMP.01', ' EMP', 'EMP '])(
|
|
'rejects %s',
|
|
(code) => {
|
|
expect(isValidEmployeeCode(code)).toBe(false);
|
|
},
|
|
);
|
|
|
|
it('rejects codes longer than 16 characters', () => {
|
|
expect(
|
|
isValidEmployeeCode('A'.repeat(EMPLOYEE_CODE_MAX_LENGTH + 1)),
|
|
).toBe(false);
|
|
expect(isValidEmployeeCode('A'.repeat(EMPLOYEE_CODE_MAX_LENGTH))).toBe(
|
|
true,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('isValidEmployeePosition', () => {
|
|
it.each(['sales', 'driver', 'crew'])('accepts %s', (position) => {
|
|
expect(isValidEmployeePosition(position)).toBe(true);
|
|
});
|
|
|
|
it.each(['', 'Sales', 'pilot', 'crew '])('rejects %s', (position) => {
|
|
expect(isValidEmployeePosition(position)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('parseCsvRecord', () => {
|
|
it('keeps commas inside quoted fields', () => {
|
|
expect(parseCsvRecord('EMP_01,Ada Lovelace,"sales, lead"')).toEqual([
|
|
'EMP_01',
|
|
'Ada Lovelace',
|
|
'sales, lead',
|
|
]);
|
|
});
|
|
|
|
it('unescapes doubled quotes', () => {
|
|
expect(parseCsvRecord('"Say ""hello""",x')).toEqual(['Say "hello"', 'x']);
|
|
});
|
|
});
|
|
|
|
describe('isAllowedCsvUpload', () => {
|
|
it('accepts csv mime or .csv names', () => {
|
|
expect(
|
|
isAllowedCsvUpload({
|
|
mimetype: 'text/csv',
|
|
originalname: 'x.txt',
|
|
}),
|
|
).toBe(true);
|
|
expect(
|
|
isAllowedCsvUpload({
|
|
mimetype: 'application/octet-stream',
|
|
originalname: 'employees.csv',
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it('rejects non-csv files', () => {
|
|
expect(
|
|
isAllowedCsvUpload({
|
|
mimetype: 'application/pdf',
|
|
originalname: 'x.pdf',
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
});
|
|
});
|