Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup Testing #92

Merged
merged 12 commits into from
Jul 7, 2023
5 changes: 3 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"env": {
"browser": true,
"es2022": true,
"node": true
"node": true,
"jest/globals": true
},
"extends": [
"eslint:recommended",
Expand All @@ -14,7 +15,7 @@
"prettier"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint", "react-hooks", "prettier"],
"plugins": ["@typescript-eslint", "react-hooks", "prettier", "jest"],
"settings": {
"react": {
"version": "detect"
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ node_modules
packages/**/*/generated
packages/nextjs/**

# testing
coverage

# parcel
.parcel-cache
dist
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"release": "propel-release",
"test": "echo 'No tests yet'",
"test": "turbo run test",
"postinstall": "husky install",
"commit": "cz",
"storybook": "yarn workspace ui-kit-storybook storybook",
Expand Down Expand Up @@ -44,6 +44,7 @@
"cz-conventional-changelog": "^3.3.0",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-jest": "^27.2.2",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.24.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/core/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
},
"devDependencies": {
"parcel": "^2.8.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.9.3"
},
"peerDependencies": {
Expand Down
6 changes: 5 additions & 1 deletion packages/core/graphql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@
"graphql-request": "^6.1.0"
},
"peerDependencies": {
"graphql": "^16.6"
"graphql": "^16.6",
"react": "^16.8 || ^17 || ^18",
"react-dom": "^16.8 || ^17 || ^18"
},
"devDependencies": {
"@graphql-codegen/cli": "^4.0.1",
"@graphql-codegen/typescript": "^4.0.1",
"@graphql-codegen/typescript-operations": "^4.0.1",
"@graphql-codegen/typescript-react-query": "^4.1.0",
"parcel": "^2.8.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.9.4"
}
}
18 changes: 18 additions & 0 deletions packages/react/counter/jest-environment-jsdom.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict'

const { TextEncoder, TextDecoder } = require('util')
const OriginalJSDOMEnvironment = require('jest-environment-jsdom')

Object.defineProperty(exports, '__esModule', {
value: true
})

class JSDOMEnvironment extends OriginalJSDOMEnvironment {
constructor (...args) {
const { global } = super(...args)
if (!global.TextEncoder) global.TextEncoder = TextEncoder
if (!global.TextDecoder) global.TextDecoder = TextDecoder
}
}

exports.default = JSDOMEnvironment
36 changes: 36 additions & 0 deletions packages/react/counter/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
*/

/** @type {import('jest').Config} */
const config = {
moduleFileExtensions: ['ts', 'tsx', 'js', 'json', 'jsx'],
rootDir: '.',
roots: ['<rootDir>'],
moduleNameMapper: {
'@/testing/(.*)': '<rootDir>/testing/$1',
'@/testing': '<rootDir>/testing',
'@/counter/(.*)': '<rootDir>/src/(.*)',
'@/counter': '<rootDir>/src'
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testEnvironment: './jest-environment-jsdom.cjs',
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(ts|tsx)$'],
transform: {
'^.+\\.(js|ts|tsx)$': [
'@swc/jest',
{
jsc: {
transform: {
react: {
runtime: 'automatic'
}
}
}
}
]
}
}

module.exports = config
21 changes: 21 additions & 0 deletions packages/react/counter/jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Optional: configure or set up a testing framework before each test.
// If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js`

// Used for __tests__/testing-library.js
// Learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect'

import { server } from './testing/mocks/server'

process.env.NEXT_PUBLIC_ACCOUNT_MANAGEMENT_GRAPHQL_ENDPOINT ??= 'http://localhost:3000/in/graphql'
process.env.NEXT_PUBLIC_CONFIG_MANAGEMENT_GRAPHQL_ENDPOINT ??= 'http://localhost:3000/graphql'

// Establish API mocking before all tests.
beforeAll(() => server.listen())

// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers())

// Clean up after the tests are finished.
afterAll(() => server.close())
18 changes: 17 additions & 1 deletion packages/react/counter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"types": "dist/index.d.ts",
"scripts": {
"dev": "parcel watch",
"build": "parcel build"
"build": "parcel build",
"test": "jest",
"test:coverage": "jest --ci --coverage --json --outputFile=coverage/coverage.json"
},
"publishConfig": {
"access": "public",
Expand All @@ -23,7 +25,21 @@
"graphql-request": "^6.1.0"
},
"devDependencies": {
"@swc/core": "^1.3.68",
"@swc/jest": "^0.2.26",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^27.5.1",
"@types/react": "latest",
"@types/react-dom": "latest",
"@types/testing-library__jest-dom": "^5.14.7",
"jest": "^27.5.1",
"jest-environment-jsdom": "^27.5.1",
"msw": "^1.2.2",
"parcel": "^2.8.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.9.3"
},
"peerDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/react/counter/src/ErrorFallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface ErrorFallbackProps {
}

const Icon = ({ color }: { color?: string }) => (
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg role="img" width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M11.0006 14.8109C10.4831 14.8109 10.0984 15.2304 10.0984 15.7483C10.0984 16.2658 10.5183 16.6857 11.0006 16.6857C11.5181 16.6857 11.9029 16.2658 11.9029 15.7483C11.938 15.2288 11.5201 14.8109 11.0006 14.8109ZM11.0006 13.2486C11.346 13.2486 11.5904 12.9691 11.5904 12.6237V6.99937C11.5904 6.65395 11.3109 6.37445 11.0006 6.37445C10.6903 6.37445 10.3757 6.65567 10.3757 6.99937V12.6237C10.3757 12.9674 10.6569 13.2486 11.0006 13.2486ZM20.6986 16.2677L12.8949 3.0694C12.5004 2.40112 11.7896 2.00117 11.0006 2C10.2117 2 9.50472 2.39917 9.10633 3.06862L1.29871 16.2701C0.906184 16.9329 0.900326 17.7285 1.28224 18.3987C1.67718 19.0877 2.39037 19.4978 3.19301 19.4978H18.8121C19.6132 19.4978 20.3256 19.0865 20.7185 18.3976C21.1009 17.7285 21.0931 16.9317 20.6986 16.2677ZM19.5972 17.7441C19.4644 18.0722 19.1559 18.248 18.777 18.248H3.19301C2.84516 18.248 2.53762 18.0728 2.36967 17.7793C2.2104 17.4998 2.21286 17.1817 2.37577 16.9059L10.1843 3.70526C10.3523 3.42013 10.6569 3.24984 11.0006 3.24984C11.0006 3.24984 11 3.24984 11.0006 3.24984C11.343 3.25045 11.6474 3.4201 11.8154 3.70447L19.623 16.9059C19.7534 17.1817 19.7925 17.4981 19.5972 17.7441Z"
fill={color || '#475569'}
Expand Down
68 changes: 68 additions & 0 deletions packages/react/counter/src/__test__/Counter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react'
import { render, waitFor } from '@testing-library/react'

import { Counter, RelativeTimeRange } from '@/counter'
import { Dom } from '@/testing'

import { setupHandlers } from './mswHandlers'
import { counter } from './mockData'

describe('Counter', () => {
let dom: Dom

beforeEach(() => {
setupHandlers()
})

it('should render a static counter', async () => {
dom = render(<Counter value={counter.value} />)

await dom.findByText(counter.value)
})

it('should show value from server', async () => {
dom = render(
<Counter
query={{
metric: 'test-metric',
accessToken: 'test-token',
timeRange: { relative: RelativeTimeRange.LastNDays, n: 30 }
}}
/>
)

await dom.findByText(counter.value)
})

it('Should show "-" when value is null', async () => {
dom = render(
<Counter
query={{
metric: 'lack-of-data',
accessToken: 'test-token',
timeRange: { relative: RelativeTimeRange.LastNDays, n: 30 }
}}
/>
)

await waitFor(async () => {
await dom.findByText('-')
})
})

it('Should show error fallback when request fails', async () => {
dom = render(
<Counter
query={{
metric: 'should-fail',
accessToken: 'test-token',
timeRange: { relative: RelativeTimeRange.LastNDays, n: 30 }
}}
/>
)

await waitFor(async () => {
await dom.findByRole('img')
})
})
})
3 changes: 3 additions & 0 deletions packages/react/counter/src/__test__/mockData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const counter = {
value: '123'
}
41 changes: 41 additions & 0 deletions packages/react/counter/src/__test__/mswHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { graphql } from 'msw'

import { server } from '@/testing/mocks/server'

import { counter } from './mockData'

const handlers = [
graphql.query('Counter', (req, res, ctx) => {
const { metricName } = req.variables.counterInput

if (metricName === 'lack-of-data') {
return res(
ctx.data({
counter: {
value: null
}
})
)
}

if (metricName === 'should-fail') {
return res(
ctx.errors([
{
message: 'something went wrong'
}
])
)
}

return res(
ctx.data({
counter
})
)
Comment on lines +31 to +35
Copy link
Contributor

@markandrus markandrus Jul 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍 Since we are here, how about expanding this to show

  • Returning a value.
  • Returning null due to lack of data (this will happen for AVERAGE, MIN, MAX, etc.).
  • Returning null due to error (this could happen due to timeout, permissions, etc.).

And going ahead and getting the tests for the second two into the <Counter> component's tests? I'm assuming they will just pass but, if they don't we can skip them and address in a followup.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I just added tests for those

})
]

export const setupHandlers = () => {
server.use(...handlers)
}
6 changes: 6 additions & 0 deletions packages/react/counter/testing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { render } from '@testing-library/react'

/**
* This is a convenience type for referring to the result of calling `render`.
*/
export type Dom = ReturnType<typeof render>
3 changes: 3 additions & 0 deletions packages/react/counter/testing/mocks/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { setupServer } from 'msw/node'

export const server = setupServer()
19 changes: 19 additions & 0 deletions packages/react/counter/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"baseUrl": ".",
"jsx": "preserve",
"target": "es2016",
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"@/testing/*": ["testing/*"],
"@/testing": ["testing"],
"@/counter": ["src"]
}
},
"include": ["**/*.ts", "**/*.tsx", "./jest.config.js", "./jest.setup.js"],
"exclude": ["node_modules"]
}
18 changes: 18 additions & 0 deletions packages/react/leaderboard/jest-environment-jsdom.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict'

const { TextEncoder, TextDecoder } = require('util')
const OriginalJSDOMEnvironment = require('jest-environment-jsdom')

Object.defineProperty(exports, '__esModule', {
value: true
})

class JSDOMEnvironment extends OriginalJSDOMEnvironment {
constructor (...args) {
const { global } = super(...args)
if (!global.TextEncoder) global.TextEncoder = TextEncoder
if (!global.TextDecoder) global.TextDecoder = TextDecoder
}
}

exports.default = JSDOMEnvironment
36 changes: 36 additions & 0 deletions packages/react/leaderboard/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
*/

/** @type {import('jest').Config} */
const config = {
moduleFileExtensions: ['ts', 'tsx', 'js', 'json', 'jsx'],
rootDir: '.',
roots: ['<rootDir>'],
moduleNameMapper: {
'@/testing/(.*)': '<rootDir>/testing/$1',
'@/testing': '<rootDir>/testing',
'@/leaderboard/(.*)': '<rootDir>/src/(.*)',
'@/leaderboard': '<rootDir>/src'
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.js', 'jest-canvas-mock'],
testEnvironment: './jest-environment-jsdom.cjs',
transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(ts|tsx)$'],
transform: {
'^.+\\.(js|ts|tsx)$': [
'@swc/jest',
{
jsc: {
transform: {
react: {
runtime: 'automatic'
}
}
}
}
]
}
}

module.exports = config
Loading