This example demonstrates how to integrate nut.js with Jest to create robust end-to-end tests for desktop applications.
Project Setup
First, install the required dependencies:
bash
npm install --save-dev jest @types/jest ts-jest @nut-tree/nut-js @nut-tree/bolt @nut-tree/nl-matcherJest Configuration
Create a jest.config.js file:
javascript
/** @type {import('jest').Config} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testTimeout: 30000, // Desktop automation tests may take longer
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
}Setup File
Create jest.setup.ts to configure nut.js globally:
typescript
import { keyboard, mouse, screen } from '@nut-tree/nut-js'
// Configure nut.js for testing
beforeAll(() => {
// Set reasonable delays for test stability
keyboard.config.autoDelayMs = 100
mouse.config.autoDelayMs = 100
mouse.config.mouseSpeed = 1000
})
// Optional: helper for ad-hoc screenshot debugging
export async function captureDebugScreenshot(name: string) {
await screen.capture(`./screenshots/${name}-${Date.now()}.png`)
}Example Test Suite
Here's a complete test file (calculator.test.ts):
typescript
import {
keyboard,
screen,
Key,
imageResource,
sleep,
windowWithTitle,
} from '@nut-tree/nut-js';
import {useBoltWindowFinder} from '@nut-tree/bolt';
import { useNlMatcher } from '@nut-tree/nl-matcher';
useNlMatcher();
describe('Calculator Application', () => {
beforeAll(async () => {
useBoltWindowFinder();
// Launch the calculator (platform-specific)
if (process.platform === 'darwin') {
await keyboard.pressKey(Key.LeftSuper, Key.Space)
await keyboard.releaseKey(Key.LeftSuper, Key.Space)
await sleep(500)
await keyboard.type('Calculator')
await keyboard.type(Key.Enter)
}
// Wait for app to launch
await screen.waitFor(windowWithTitle('Calculator'));
})
afterAll(async () => {
// Close the calculator
await keyboard.type(Key.LeftSuper, Key.Q)
})
it('should perform basic addition', async () => {
// Type the calculation
await keyboard.type('5+3')
await keyboard.type(Key.Enter)
// Wait for result
await sleep(500)
// Verify result is displayed (using image matching)
const result = await screen.waitFor(imageResource('result-8.png'))
expect(result).toBeDefined()
})
it('should clear the display', async () => {
await keyboard.type('C')
// Verify display is cleared
const clearDisplay = await screen.waitFor(imageResource('clear-display.png'))
expect(clearDisplay).toBeDefined()
})
})Using Jest Matchers
nut.js provides custom Jest matchers for more readable assertions:
typescript
import { screen, imageResource, jestMatchers } from '@nut-tree/nut-js'
expect.extend(jestMatchers);
describe('Visual Assertions', () => {
it('should show the login button', async () => {
await expect(screen).toShow(imageResource('login-button.png'))
})
it('should show element within timeout', async () => {
await expect(screen).toWaitFor(imageResource('loading-complete.png'), 5000)
})
})Running Tests
Add a script to your package.json:
json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --ci --reporters=default --reporters=jest-junit"
}
}Run your tests:
bash
npm testTips for Stable Tests
- Use appropriate delays: Desktop applications need time to render, so either use static delays via
sleep, or usescreen.waitFor(...)to wait for elements to appear - Use image resources: Store reference images in a dedicated folder
- Handle platform differences: Use
process.platformfor OS-specific logic - Set reasonable timeouts: Default Jest timeout may be too short
- Clean up after tests: Always close applications in
afterAll