Skip to main content

Unit Test - Testing Page Component

In next.js (and so does our framework), page components have special properties that makes it different from normal React component. This includes things like getInitialProps and any other configuration. Another thing to note is because pages directory has special meaning in next.js we're going to use different convention to put our page component test file.

Instead of inside pages directory, we create __tests__ directory (like Jest convention) in root package that follows same directory structure with pages. Similar with another jest convetion, we use .test.js extension for our test files.

For example we have /sample/data page that we want to test, here's a visualization of how page relates to its test in file tree

|- __tests__/
| +- pages/
| +- sample/
| +- data.test.js
+- pages/
+- sample/
+- data.tsx

Basic Rendering Test

To help test entire behavior, we provide renderPage function that you can call that will executes entire lifecycle and return render description of your component from React Testing Library render function.

// packages/sample/mobile/__tests__/pages/sample/data.test.js
import { renderPage } from '@traveloka/core/test';

import DataPage from '../../../pages/sample/data';

test('render correctly', async () => {
await renderPage(DataPage);
});

Note that the function signature is renderPage(Page) instead renderPage(<Page />) because we want to be able to execute getInitialProps automatically. Besides, page component doesn't need custom props, usually this comes from custom _app.

If you have getInitialProps on your page component that fetch data using callAPI function, you can mock it by using mockAPI function

// packages/sample/mobile/__tests__/pages/sample/data.test.js
import { renderPage, mockAPI } from '@traveloka/core/test';

import DataPage from '../../../pages/sample/data';

test('render correctly', async () => {
mockAPI('accomContent', {
method: 'post',
path: '/v2/api/path',
response: {
status: 200,
body: {
data: {
geoAreaContent: {
rows: [],
},
},
},
},
});

const { getByText } = await renderPage(DataPage);
expect(getByText('No results found')).toBeDefined();
});

Make sure to call mockAPI before calling renderPage, because we disable network request in test environment. The rest is similar to how you test React component.

Custom Next & App Context

If needed you can override Next & App Context by passing nextContext and appContext respectively in 2nd argument. This is especially useful if your page component depends on specific value based on request.

// packages/sample/mobile/__tests__/pages/sample/data.test.js
import { renderPage, mockAPI } from '@traveloka/core/test';

import DataPage from '../../../pages/sample/data';

test('render correctly', async () => {
mockAPI('accomContent', {
method: 'post',
path: '/v2/api/path',
response: {
status: 200,
body: {
data: {
geoAreaContent: {
rows: [],
},
},
},
},
});

const nextContext = {
req: {},
res: {},
query: {},
};

const appContext = {
languageTag: 'en-SG',
};

const { getByText } = await renderPage(DataPage, {
nextContext,
appContext,
});
expect(getByText('No results found')).toBeDefined();
});

Test getServersideProps

If you have getServerSideProps on your page component, you can test it by passing the page's getServerSideProps function as 2nd argument to renderPage function.

// packages/sample/mobile/__tests__/pages/sample/data.test.js
import { renderPage, mockAPI } from '@traveloka/core/test';

import DataPage from '../../../pages/sample/data';
import { getServerSideProps } from '../../pages/sample/data';

test('render correctly', async () => {
const { getByText } = await renderPage(DataPage, {
getServerSideProps,
});

expect(getByText('No results found')).toBeDefined();
});

Mock Feature Control

You can mock the result of feature control by passing featureControl key in mock object. renderPage will automatically mock the API call with the defined feature control value.

// packages/sample/mobile/__tests__/pages/sample/data.test.js
import { renderPage, mockAPI } from '@traveloka/core/test';

import DataPage from '../../../pages/sample/data';
import { getServerSideProps } from '../../pages/sample/data';

test('render correctly', async () => {
const { getByText } = await renderPage(DataPage, {
featureControl: {
'activity-product': {
enabled: 'true',
properties: {
currency: ['IDR'],
},
},
},
});

expect(getByText('No results found')).toBeDefined();
});

Mock User Context

in SSR, if you use getSharedServerProps, there'll be a function to updateSessionToken, you can mock user context by passing mockUserContext key in mock object.

// packages/sample/mobile/__tests__/pages/sample/data.test.js
import { renderPage, mockAPI } from '@traveloka/core/test';

import DataPage from '../../../pages/sample/data';
import { getServerSideProps } from '../../pages/sample/data';

test('render correctly', async () => {
const { getByText } = await renderPage(DataPage, {
getServerSideProps,
featureControl: {
mockUserContext: true,
},
});

expect(getByText('No results found')).toBeDefined();
});