Testing
The monorepo uses Jest with jest-preset-angular for unit testing. Tests run through Nx, which orchestrates builds and test execution per project.
Test Flow
Running Tests
# Test a specific project
npx nx test <project-name>
# Examples
npx nx test upp-data
npx nx test upp-wdgt
npx nx test upp-base
# Test all projects
npx nx run-many --target=test --all
# With coverage
npx nx test upp-data --coverage
Note: In the Docker development environment, run commands inside the
nx-devcontainer:cd docker && docker compose exec nx-dev bash -c "cd /app && npx nx test upp-data"
Jest Configuration
Each library has its own jest.config.ts. Here is the upp-data configuration as a reference:
export default {
displayName: 'upp-data',
preset: '../../jest.preset.js',
setupFilesAfterSetup: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../coverage/libs/upp-data',
collectCoverageFrom: [
'src/modules/sync.ts',
'src/modules/data.ts',
'src/modules/cache.ts',
'src/modules/login.ts',
'src/modules/model/objects/ticket.ts',
'src/modules/model/objects/ticketchange.ts',
'src/modules/model/views/ticket.ts',
],
coverageThreshold: {
global: {
statements: 65,
branches: 50,
functions: 70,
lines: 65,
},
},
testEnvironment: 'jsdom',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: [
'node_modules/(?!@ionic|@stencil/core|@awesome-cordova-plugins|.*\\.mjs$)',
],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};
Key points
preset: References the sharedjest.preset.jsat the monorepo root.setupFilesAfterSetup: Points totest-setup.tswhich initialises the Angular testing environment.transformIgnorePatterns: Ensures Ionic, Stencil, and.mjsmodules are transformed (they ship as ESM).testEnvironment: Usesjsdomfor DOM APIs.
Test File Conventions
- Test files are placed in
src/specs/within each library. - File names follow the pattern
<name>.spec.ts. - Example:
libs/upp-data/src/specs/ticket.spec.ts.
Mocking Patterns
The codebase uses several mocking patterns consistently. Here are the most common ones:
Mock Data Service
The dataService is the central dependency. Create a minimal mock:
import { dataService, dataStorage } from '../modules/data';
function createMockDataService(): dataService {
return {
store: new dataStorage(),
session: null,
serial: 'TESTSERIAL',
clock: {
Enable: true,
OnRefreshTick: { subscribe: jest.fn() },
},
alives: { get: jest.fn().mockReturnValue(null) },
FetchByObjid: jest.fn().mockResolvedValue({ errorcode: 0 }),
} as unknown as dataService;
}
Mock Views with Property Delegation
For testing view logic (e.g. TicketView), create mock objects that delegate to real data objects using Object.create() with property descriptors:
const ticket = new Ticket(null, mockData);
ticket.status = 'AC';
const mockView = Object.create(null, {
status: {
get() { return ticket.status; },
set(v: string) { ticket.status = v; },
enumerable: true,
},
payment: {
get() { return ticket.payment; },
enumerable: true,
},
// ... more delegated properties
});
This pattern allows the test to interact with real Ticket data while mocking the view's surrounding context (place, session, etc.).
Mock Place
const mockPlace = {
NextDeviceTicket: jest.fn().mockImplementation(
(serial: string, invoice: string) => {
return Promise.resolve([
serial + 'TESTSERIAL',
invoice + '00000001',
]);
},
),
SendVFTEnabled: false,
SendBAIEnabled: false,
AddTicket: jest.fn(),
};
jest.fn() for Callbacks
const callback = jest.fn();
myService.onChange(callback);
expect(callback).toHaveBeenCalledWith(expectedValue);
Coverage
Coverage is configured per project via collectCoverageFrom and coverageThreshold in each jest.config.ts. The output goes to coverage/libs/<project-name>/.
To check coverage:
npx nx test upp-data --coverage
Current thresholds for upp-data:
| Metric | Threshold |
|---|---|
| Statements | 65% |
| Branches | 50% |
| Functions | 70% |
| Lines | 65% |
Tips
- Use
describeblocks to group related tests. - Use
beforeEachto set up fresh mocks for each test. - When testing objects that depend on
dataService, always usecreateMockDataService()or a similar helper. - For async operations, use
async/awaitwith Jest's built-in promise support. - Remember that
transformIgnorePatternsmust include Ionic/Stencil modules since they ship as ESM and need transformation.