Lesson 14: Testing - strvcom/frontend-academy-2022 GitHub Wiki
Speaker: Michal Jarnot
- Pull Request
- Recording (Google Drive)
- Slides (Google Slides)
- Slides (PDF)
yarn add jest ts-jest ts-node -D
https://jestjs.io/docs/getting-started
Create jest.config.ts
in the root of your project:
import nextJest from 'next/jest'
import { join } from 'path'
import { pathsToModuleNameMapper } from 'ts-jest'
import { compilerOptions } from './tsconfig.json'
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
})
//jestjs.io/docs/configuration
const customJestConfig = {
// Add more setup options before each test is run
// https://jestjs.io/docs/configuration#testenvironment-string
testEnvironment: 'jest-environment-jsdom',
modulePaths: ['<rootDir>/src'], // https://jestjs.io/docs/configuration#modulepaths-arraystring
// https://jestjs.io/docs/configuration#modulenamemapper-objectstring-string--arraystring
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {
prefix: join('<rootDir>', compilerOptions.baseUrl),
}),
}
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)
Install Storybook
npx sb init --builder webpack5
Add webpack plugin to support absolute imports:
yarn add tsconfig-paths-webpack-plugin -D
Update main.js
in .storybook/
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
module.exports = {
webpackFinal: async (config) => {
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve.extensions,
}),
]
return config
},
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
framework: '@storybook/react',
core: {
builder: '@storybook/builder-webpack5',
},
}
Update preview.js
in .storybook/
import { GlobalStyle } from '../src/features/ui/theme/global'
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}
const withStyles = (Story, context) => (
<>
<GlobalStyle />
<Story {...context} />
</>
)
export const decorators = [withStyles]
Install Cypress:
yarn add cypress -D
Run Cypress and force it to create basic configuration:
npx cypress open
https://docs.cypress.io/guides/tooling/typescript-support#Install-TypeScript
Update cypress.config.js
in root of your project:
/* eslint-disable import/no-default-export,@typescript-eslint/no-unused-vars,@typescript-eslint/no-misused-promises */
import { defineConfig } from 'cypress'
import http from 'http'
import next from 'next'
// TODO: .env
const BASE_URL = 'http://localhost:3000'
const NEXT_PUBLIC_API_URL = 'https://testproject-api-v2.strv.com/'
export default defineConfig({
e2e: {
baseUrl: BASE_URL,
setupNodeEvents: async (on, config) => {
config.env.api_url = NEXT_PUBLIC_API_URL
const app = next({ dev: true })
const handleNextRequests = app.getRequestHandler()
await app.prepare()
const customServer = new http.Server(async (req, res) => {
return await handleNextRequests(req, res)
})
await new Promise<void>((resolve) => {
customServer.listen(3000, () => {
console.log(`> Ready on ${BASE_URL}`)
resolve()
})
})
// We are not actually fetching events on server. But you can intercept requests with nock
// https://glebbahmutov.com/blog/mock-network-from-server/
// on('task', {
// clearNock() {
// nock.restore()
// nock.cleanAll()
// },
//
// nock(hostname: string, method: 'get' | 'post', path: string, statusCode: number, body: any) {
// nock.activate()
// nock(hostname)[method](path).reply(statusCode, body)
// },
// })
return config
},
},
})
Update tsconfig.ts
in the root of the project:
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"baseUrl": ".",
"paths": {
"~/*": [
"src/*"
]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules",
"cypress",
"cypress.config.ts"
]
}
Create tsconfig.ts
in cypress/
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"],
"baseUrl": ".",
"paths": {
"~/*": [
"../src/*"
]
}
},
"include": ["**/*.ts"]
}