Skip to main content

6 posts tagged with "Testing"

View All Tags

Jest - Async

· 2 min read

Open in Notion

import * as user from '../user';

// The assertion for a promise must be returned.
it('works with promises', () => {
expect.assertions(1);
return user.getUserName(4).then(data => expect(data).toEqual('Mark'));
});

it('works with resolves', () => {
expect.assertions(1);
return expect(user.getUserName(5)).resolves.toEqual('Paul');
});

// async/await can be used.
it('works with async/await', async () => {
expect.assertions(1);
const data = await user.getUserName(4);
expect(data).toEqual('Mark');
});

// async/await can also be used with `.resolves`.
it('works with async/await and resolves', async () => {
expect.assertions(1);
await expect(user.getUserName(5)).resolves.toEqual('Paul');
});

Error handling

// Testing for async errors using Promise.catch.
it('tests error with promises', () => {
expect.assertions(1);
return user.getUserName(2).catch(e =>
expect(e).toEqual({
error: 'User with 2 not found.',
}),
);
});

// Or using async/await.
it('tests error with async/await', async () => {
expect.assertions(1);
try {
await user.getUserName(1);
} catch (e) {
expect(e).toEqual({
error: 'User with 1 not found.',
});
}
});

// Testing for async errors using `.rejects`.
it('tests error with rejects', () => {
expect.assertions(1);
return expect(user.getUserName(3)).rejects.toEqual({
error: 'User with 3 not found.',
});
});

// Or using async/await with `.rejects`.
it('tests error with async/await and rejects', async () => {
expect.assertions(1);
await expect(user.getUserName(3)).rejects.toEqual({
error: 'User with 3 not found.',
});
});

Jest - For Angular

· One min read

Open in Notion

npm i --dev jest jest-preset-angular @types/jest

You need to add this entry to package.json

"jest": {
"preset": "jest-preset-angular",
"setupFilesAfterEnv": ["<rootDir>/src/setupJest.ts"]
}

You’re now ready to add this to your npm scripts:

"test": "jest",
"test:watch": "jest --watch",

Oh, one more thing. Forget about installing PhantomJS on your CI:

"test:ci": "jest --runInBand",

Jest - Timer Mocks

· 3 min read

Open in Notion

// timerGame.js
'use strict';

function timerGame(callback) {
console.log('Ready....go!');
setTimeout(() => {
console.log("Time's up -- stop!");
callback && callback();
}, 1000);
}

module.exports = timerGame;
// __tests__/timerGame-test.js
'use strict';

jest.useFakeTimers();

test('waits 1 second before ending the game', () => {
const timerGame = require('../timerGame');
timerGame();

expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
});

While you can call jest.useFakeTimers() or jest.useRealTimers() from anywhere (top level, inside an it block, etc.), it is a global operation and will affect other tests within the same file. Additionally, you need to call jest.useFakeTimers() to reset internal counters before each test. If you plan to not use fake timers in all your tests, you will want to clean up manually, as otherwise the faked timers will leak across tests:

afterEach(() => {
jest.useRealTimers();
});

test('do something with fake timers', () => {
jest.useFakeTimers();
// ...
});

test('do something with real timers', () => {
// ...
});

Run All Timers

test('calls the callback after 1 second', () => {
const timerGame = require('../timerGame');
const callback = jest.fn();

timerGame(callback);

// At this point in time, the callback should not have been called yet
expect(callback).not.toBeCalled();

// Fast-forward until all timers have been executed
jest.runAllTimers();

// Now our callback should have been called!
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
});

Run Pending Timers

// infiniteTimerGame.js
'use strict';

function infiniteTimerGame(callback) {
console.log('Ready....go!');

setTimeout(() => {
console.log("Time's up! 10 seconds before the next game starts...");
callback && callback();

// Schedule the next game in 10 seconds
setTimeout(() => {
infiniteTimerGame(callback);
}, 10000);
}, 1000);
}

module.exports = infiniteTimerGame;
// __tests__/infiniteTimerGame-test.js
'use strict';

jest.useFakeTimers();

describe('infiniteTimerGame', () => {
test('schedules a 10-second timer after 1 second', () => {
const infiniteTimerGame = require('../infiniteTimerGame');
const callback = jest.fn();

infiniteTimerGame(callback);

// At this point in time, there should have been a single call to
// setTimeout to schedule the end of the game in 1 second.
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);

// Fast forward and exhaust only currently pending timers
// (but not any new timers that get created during that process)
jest.runOnlyPendingTimers();

// At this point, our 1-second timer should have fired it's callback
expect(callback).toBeCalled();

// And it should have created a new timer to start the game over in
// 10 seconds
expect(setTimeout).toHaveBeenCalledTimes(2);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
});
});

Advance Timers by Time

// timerGame.js
'use strict';

function timerGame(callback) {
console.log('Ready....go!');
setTimeout(() => {
console.log("Time's up -- stop!");
callback && callback();
}, 1000);
}

module.exports = timerGame;
it('calls the callback after 1 second via advanceTimersByTime', () => {
const timerGame = require('../timerGame');
const callback = jest.fn();

timerGame(callback);

// At this point in time, the callback should not have been called yet
expect(callback).not.toBeCalled();

// Fast-forward until all timers have been executed
jest.advanceTimersByTime(1000);

// Now our callback should have been called!
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
});

Playwright - Automate Chromium, Firefox and WebKit with a single API

· 2 min read

Open in Notion

Playwright is a Node.js library to automate Chromium, Firefox and WebKit with a single API. Playwright is built to enable cross-browser web automation that is ever-green, capable, reliable and fast.

bookmark

Page screenshot

const playwright = require('playwright');

(async () => {
for (const browserType of ['chromium', 'firefox', 'webkit']) {
const browser = await playwright[browserType].launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('http://whatsmyuseragent.org/');
await page.screenshot({ path: `example-${browserType}.png` });
await browser.close();
}
})();

Mobile and geolocation

const { webkit, devices } = require('playwright');
const iPhone11 = devices['iPhone 11 Pro'];

(async () => {
const browser = await webkit.launch();
const context = await browser.newContext({
...iPhone11,
locale: 'en-US',
geolocation: { longitude: 12.492507, latitude: 41.889938 },
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://maps.google.com');
await page.click('text="Your location"');
await page.waitForRequest(/.*preview\/pwa/);
await page.screenshot({ path: 'colosseum-iphone.png' });
await browser.close();
})();

Evaluate in browser context

const { firefox } = require('playwright');

(async () => {
const browser = await firefox.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://www.example.com/');
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
}
});
console.log(dimensions);

await browser.close();
})();

Intercept network requests

const { webkit } = require('playwright');

(async () => {
const browser = await webkit.launch();
const context = await browser.newContext();
const page = await context.newPage();

// Log and continue all network requests
page.route('**', route => {
console.log(route.request().url());
route.continue();
});

await page.goto('http://todomvc.com');
await browser.close();
})();

Jest - Issues

· One min read

Open in Notion

Configurations for DOM Support

Use document object and methods like document.querySelectorAll

_setup.ts

import "jsdom-global/register";

jest.config.js

module.exports = {
verbose: true,
transform: {
".(ts|tsx)": "<rootdir>/node_modules/ts-jest/preprocessor.js",
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub",
},
testEnvironment: "node",
testRegex: "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
setupTestFrameworkScriptFile: "./test/_setup.ts",
moduleFileExtensions: ["ts", "tsx", "js"],
coveragePathIgnorePatterns: ["/node_modules/", "/test/", "src/index.ts"],
coverageThreshold: {
global: {
branches: 0,
functions: 0,
lines: 0,
statements: 0,
},
},
collectCoverageFrom: ["src/**/*.{js,ts,tsx}"],
};

UT Example for Promise

// ut passed requires `done` called
it("should return a promise with callback and title", done => {
confirm("Promise confirm").then(() =&gt; {
done();
});
document.querySelectorAll<htmlelement>(".btn")[1].click();
});

// reject promise
it("test reject promise", async () => {
// do not use `Promise.reject`, because returns Promise immediately
const mockP = jest.fn(() => Promise.reject("err"));
await delay(1, mockP()).catch(() => {
// nothing
});
expect(mockP.mock.calls.length).toBe(1);
});

Install Xvfb on CentOS

· One min read

Open in Notion

Xvfb, or X virtual frame buffer is needed by selenium and chromedriver or gekodriver, so it can run via cron with your PC locked, or without your script taking focus from the user section.

Installing

yum install xorg-x11-server-Xvfb

Copy below to /etc/systemd/system/Xvfb.service

[Unit]
Description=X Virtual Frame Buffer Service
After=network.target

[Service]
ExecStart=/usr/bin/Xvfb :99 -screen 0 1024x768x24

[Install]
WantedBy=multi-user.target
chmod +x /etc/systemd/system/Xvfb.service
systemctl enable Xvfb.service
systemctl start Xvfb.service