Skip to main content

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-dev container:

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 shared jest.preset.js at the monorepo root.
  • setupFilesAfterSetup: Points to test-setup.ts which initialises the Angular testing environment.
  • transformIgnorePatterns: Ensures Ionic, Stencil, and .mjs modules are transformed (they ship as ESM).
  • testEnvironment: Uses jsdom for 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:

MetricThreshold
Statements65%
Branches50%
Functions70%
Lines65%

Tips

  • Use describe blocks to group related tests.
  • Use beforeEach to set up fresh mocks for each test.
  • When testing objects that depend on dataService, always use createMockDataService() or a similar helper.
  • For async operations, use async/await with Jest's built-in promise support.
  • Remember that transformIgnorePatterns must include Ionic/Stencil modules since they ship as ESM and need transformation.