feat: Setup Unit Test Environment and Refactor Typescript Config (#365)

* modify tsconfig and set up unit tests

* generate .d.ts files

* setup project dependencies and configuration for unit tests

* Add test setup and layout-test-utils along with first spec

* Add paths back to tsconfig

* remove type=module from package.json

* Add typescript definition for .env

* update package-lock
This commit is contained in:
Dan Orlando 2023-05-22 17:49:48 -07:00 committed by GitHub
parent dbfef342e2
commit 4eda4542b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 8805 additions and 14010 deletions

View file

@ -10,6 +10,7 @@ module.exports = {
'eslint:recommended', 'eslint:recommended',
'plugin:react/recommended', 'plugin:react/recommended',
'plugin:react-hooks/recommended', 'plugin:react-hooks/recommended',
"plugin:jest/recommended",
'prettier' 'prettier'
], ],
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
@ -64,7 +65,7 @@ module.exports = {
{ {
files: ['rollup.config.js', '.eslintrc.js', 'jest.config.js'], files: ['rollup.config.js', '.eslintrc.js', 'jest.config.js'],
env: { env: {
node: true node: true,
} }
}, },
{ {
@ -95,7 +96,7 @@ module.exports = {
parserOptions: { parserOptions: {
project: './client/tsconfig.json' project: './client/tsconfig.json'
}, },
plugins: ['@typescript-eslint/eslint-plugin'], plugins: ['@typescript-eslint/eslint-plugin', 'jest'],
extends: [ extends: [
'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended' 'plugin:@typescript-eslint/recommended'

View file

@ -1,23 +0,0 @@
{
/*
a preset is a set of plugins used to support particular language features.
The two presets Babel uses by default: es2015, react
*/
"presets": [
"@babel/preset-env", //compiling ES2015+ syntax
"@babel/preset-react" //for react
],
/*
Babel's code transformations are enabled by applying plugins (or presets) to your configuration file.
*/
"plugins": [
"@babel/plugin-transform-runtime",
[
"babel-plugin-root-import",
{
"rootPathPrefix": "~/",
"rootPathSuffix": "./src"
}
]
]
}

23
client/babel.config.cjs Normal file
View file

@ -0,0 +1,23 @@
module.exports = {
presets: [
["@babel/preset-env", { "targets": { "node": "current" } }], //compiling ES2015+ syntax
['@babel/preset-react', {runtime: 'automatic'}],
"@babel/preset-typescript"
],
/*
Babel's code transformations are enabled by applying plugins (or presets) to your configuration file.
*/
plugins: [
"@babel/plugin-transform-runtime",
'babel-plugin-transform-import-meta',
'babel-plugin-transform-vite-meta-env',
'babel-plugin-replace-ts-export-assignment',
[
"babel-plugin-root-import",
{
"rootPathPrefix": "~/",
"rootPathSuffix": "./src"
}
]
]
}

12
client/env.d.ts vendored Normal file
View file

@ -0,0 +1,12 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_SERVER_URL_DEV: string;
readonly VITE_SERVER_URL_PROD: string;
readonly VITE_SHOW_GOOGLE_LOGIN_OPTION: string;
readonly VITE_CLIENT_URL_DEV: string;
readonly VITE_CLIENT_URL_PROD: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

47
client/jest.config.cjs Normal file
View file

@ -0,0 +1,47 @@
module.exports = {
roots: ['<rootDir>/src'],
testEnvironment: 'jsdom',
testEnvironmentOptions: {
url: 'http://localhost:3080',
},
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!<rootDir>/node_modules/',
'!src/**/*.css.d.ts',
'!src/**/*.d.ts',
],
coveragePathIgnorePatterns: [
'<rootDir>/node_modules/',
'<rootDir>/test/setupTests.js',
],
// Todo: Add coverageThreshold once we have enough coverage
// Note: eventually we want to have these values set to 80%
// coverageThreshold: {
// global: {
// functions: 9,
// lines: 40,
// statements: 40,
// branches: 12,
// },
// },
moduleNameMapper: {
'\\.(css)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'jest-file-loader',
'layout-test-utils': '<rootDir>/test/layout-test-utils',
'^@src/(.*)$': '<rootDir>/src/$1',
'^modules/(.*)$': '<rootDir>/src/modules/$1',
},
restoreMocks: true,
testResultsProcessor: 'jest-junit',
coverageReporters: ['text', 'cobertura', 'lcov'],
transform: {
'\\.[jt]sx?$': 'babel-jest',
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'jest-file-loader',
},
preset: 'ts-jest',
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect', '<rootDir>/test/setupTests.js'],
clearMocks: true,
};

7
client/junit.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="jest tests" tests="1" failures="0" errors="0" time="0.704">
<testsuite name="undefined" errors="0" failures="0" skipped="0" timestamp="2023-05-22T20:54:42" time="0.541" tests="1">
<testcase classname=" renders login form" name=" renders login form" time="0.035">
</testcase>
</testsuite>
</testsuites>

View file

@ -2,11 +2,12 @@
"name": "chat-frontend", "name": "chat-frontend",
"version": "0.4.6", "version": "0.4.6",
"description": "", "description": "",
"type": "module",
"scripts": { "scripts": {
"build": "vite build", "build": "vite build",
"dev": "vite", "dev": "vite",
"preview-prod": "vite preview" "preview-prod": "vite preview",
"test": "jest --watch",
"test:ci": "jest --ci"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -80,24 +81,38 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.20.7", "@babel/cli": "^7.20.7",
"@babel/core": "^7.20.12", "@babel/core": "^7.21.8",
"@babel/eslint-parser": "^7.19.1", "@babel/eslint-parser": "^7.19.1",
"@babel/plugin-transform-runtime": "^7.19.6", "@babel/plugin-transform-runtime": "^7.21.4",
"@babel/preset-env": "^7.20.2", "@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6", "@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.0", "@babel/preset-typescript": "^7.21.0",
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@tanstack/react-query-devtools": "^4.29.0", "@tanstack/react-query-devtools": "^4.29.0",
"@types/jest": "^29.5.0", "@testing-library/dom": "^9.3.0",
"@types/node": "^20.2.3", "@testing-library/jest-dom": "^5.16.5",
"@types/react": "^18.0.30", "@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^29.5.1",
"@types/node": "^18.16.13",
"@types/react": "^18.2.6",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^4.0.0", "@vitejs/plugin-react": "^4.0.0",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"babel-jest": "^29.5.0",
"babel-loader": "^9.1.2", "babel-loader": "^9.1.2",
"babel-plugin-replace-ts-export-assignment": "^0.0.2",
"babel-plugin-root-import": "^6.6.0", "babel-plugin-root-import": "^6.6.0",
"babel-plugin-transform-import-meta": "^2.2.0",
"babel-plugin-transform-vite-meta-env": "^1.0.3",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",
"eslint-plugin-jest": "^27.2.1",
"jest": "^29.5.0",
"jest-canvas-mock": "^2.5.1",
"jest-environment-jsdom": "^29.5.0",
"jest-file-loader": "^1.0.3",
"jest-junit": "^16.0.0",
"path": "^0.12.7", "path": "^0.12.7",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"postcss-loader": "^7.1.0", "postcss-loader": "^7.1.0",
@ -105,6 +120,7 @@
"source-map-loader": "^4.0.1", "source-map-loader": "^4.0.1",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"tailwindcss": "^3.2.6", "tailwindcss": "^3.2.6",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2", "ts-loader": "^9.4.2",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"vite": "^4.2.1", "vite": "^4.2.1",

View file

@ -0,0 +1,8 @@
import { render } from 'layout-test-utils';
import Login from '../Login';
test('renders login form', () => {
const { getByLabelText } = render(<Login />);
expect(getByLabelText(/email/i)).toBeInTheDocument();
expect(getByLabelText(/password/i)).toBeInTheDocument();
});

View file

@ -0,0 +1,21 @@
import { render as rtlRender } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AuthContextProvider } from '~/hooks/AuthContext';
import { BrowserRouter as Router } from 'react-router-dom';
const client = new QueryClient();
function renderWithProvidersWrapper(ui, { ...options } = {}) {
function Wrapper({ children }) {
return (
<QueryClientProvider client={client}>
<Router>
<AuthContextProvider>{children}</AuthContextProvider>
</Router>
</QueryClientProvider>
);
}
return rtlRender(ui, { wrapper: Wrapper, ...options });
}
export * from '@testing-library/react';
export { renderWithProvidersWrapper as render };

18
client/test/setupTests.js Normal file
View file

@ -0,0 +1,18 @@
/* This file is automatically executed before running tests
* https://create-react-app.dev/docs/running-tests/#initializing-test-environment
*/
// react-testing-library renders your components to document.body,
// this adds jest-dom's custom assertions
// https://github.com/testing-library/jest-dom#table-of-contents
import '@testing-library/jest-dom';
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
// Mock canvas when run unit test cases with jest.
// 'react-lottie' uses canvas
import 'jest-canvas-mock';

View file

@ -2,14 +2,10 @@
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": [ "lib": ["DOM", "DOM.Iterable", "ESNext"],
"DOM",
"DOM.Iterable",
"ESNext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": false, "skipLibCheck": false,
"esModuleInterop": true, "esModuleInterop": false,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": false, "forceConsistentCasingInFileNames": false,
@ -19,13 +15,20 @@
"isolatedModules": true, "isolatedModules": true,
"noImplicitAny": false, "noImplicitAny": false,
"noEmit": false, "noEmit": false,
"declaration": true,
"declarationDir": "./types",
"jsx": "react-jsx", "jsx": "react-jsx",
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"~/*": ["./src/*"] "~/*": ["./src/*"]
} }
}, },
"include": [ "types": ["node", "jest", "@testing-library/jest-dom"],
"src" "exclude": ["node_modules"],
], "include": ["src/**/*", "setupTests.js", "vite.config.ts", "env.d.ts"],
} "references": [
{
"path": "./tsconfig.node.json"
}
]
}

View file

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View file

@ -14,10 +14,6 @@ export default defineConfig({
target: 'http://localhost:3080', target: 'http://localhost:3080',
changeOrigin: true changeOrigin: true
}, },
'/auth': {
target: 'http://localhost:3080',
changeOrigin: true
},
'/oauth': { '/oauth': {
target: 'http://localhost:3080', target: 'http://localhost:3080',
changeOrigin: true changeOrigin: true

22583
package-lock.json generated

File diff suppressed because it is too large Load diff