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:
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')
})
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 })
})