Testing

We currently use Jest and Playwright to test different aspects of the Dyff Frontend

Jest

Unit testing using Jest excels in simple test cases such as checking specific components. More complex test cases requiring complex page interactions and navigations are better suited for Playwright

Running Tests

Run jest tests with:

npm run jest

You should get a response along the lines of:

PASS  __tests__/index.test.tsx
Homepage
     renders correctly (71 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.256 s

Creating Tests

Jest tests are stored in tests-jest

Tests should contain 3 directives - describe, it, and expect. A basic test can look like:

describe('Homepage', () => {
    it('renders correctly', () => {
        const { container } = render(<HomePage />)
        expect(container).toBeInTheDocument()
    })
})

Mock Functions

For concepts such as React Context and Providers, we must mock that logic within our tests to isolate any external dependencies.

In most pages, we fetch the user’s identity / authentication status using the useAuth hook. However, our Jest tests should not actually reach out to the Dyff API when authenticating.

Thus, we must “mock” our useAuth hook to return a predictable and fake value in order to test.

const fakeAuth = {
    token: 'asd',
    decodedToken: {},
    isExpired: false,
    loading: false
}

// Return fake auth hook
jest.mock('@/components/auth/AuthProvider', () => ({
    useAuth() {
        console.log('mock use auth')
        return fakeAuth
    }
}))

This “mock” functionality is also used with the Mantine provider, which is implemented within the render function in ./tests-jest-utils/render.tsx

This function layers on top of Jest’s render function to provide all tests’ components with the Mantine theme provider.

Playwright

Playwright’s excels in End to End (E2E) testing, running tests within most major browser runtimes (chromium, firefox, webkit). It works different than Jest, as it spins up browser instances (headless or headed) and hits a dev hosted instance of the frontend and exposes a page handle to simulate human interactions and listen to various page events.

Running Tests

An instance of the front end must be running for playwright to attach to and test on. This is configured in playwright.config.ts:

webServer: {
    command: 'npm run dev',
    url: 'http://127.0.0.1:3000',
    reuseExistingServer: !process.env.CI
}

Additionally, you can specify the base URL of the service to run tests again in the same config:

use: {
    /* Base URL to use in actions like `await page.goto('/')`. */
    baseURL: 'http://127.0.0.1:3000',
},

Run tests with npm run playwright or npm run playwright:headed

Headed mode will open the actual browsers, as opposed to implicit headless mode which will run the tests without any visual browser opening.

Note

Webkit tests fail unless they are run in headed mode. This seems to be an issue with using playwright within Pop OS (Playwright installation falls back to Ubuntu 22 dependencies)

Creating Tests

Tests look similar to Jest, with a basic test looking like:

test('homepage redirects to login', async ({ page }) => {
    await page.goto('/')
    await expect(page).toHaveURL('/auth/signin', { timeout: 10000 })
})

Compared to jest, it’s a bit easier to test redirects given the different methods available on the page object.

Simulating Authentication

Like Jest, we need a way to simulate authentication, as tests shouln’t actually authenticate with the Dyff API. We can do this in two ways:

  1. Manually calling the auth callback URL with a token set in .env within the test:

test('simulated token to allow dashboard view', async ({ page }) => {
    await page.goto(`/auth/callback?token=${process.env.NEXT_PUBLIC_DYFF_API_KEY}`)
    // Do stuff while logged in...
    await page.goto('/dashboard')
    await expect(page).toHaveURL('/dashboard')
})
  1. Using NEXT_PUBLIC_FORCE_DYFF_API_KEY in .env

Note

Setting this environment variable to 1 will automatically set the API Key token in the frontend upon launch.

Subsequently, this disables the ability to test site logic requiring the user to be logged out (auth redirects).

Given the current state of the auth workflow, it’s best to call the callback with your .env token.

test('homepage redirects to login', async ({ page }) => {
    // Already logged in with env variable
    await page.goto('/')
    await expect(page).toHaveURL('/auth/signin', { timeout: 10000 })
})