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

Set up testing environment #682

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1cf6413
refactor: update Jest & TS config to work with JSX files
ManpreetSL Mar 16, 2023
33dd3b7
fix: renamed .js to .tsx to fix Vite errors
ManpreetSL Mar 16, 2023
d6a88a0
fix: upgrade to MUI 51.11.13 to fix React 18 incompatibility error
ManpreetSL Mar 17, 2023
af00a32
build: add React Testing Library package for use in frontend tests
ManpreetSL Mar 17, 2023
e9932e4
fix: add styles mock to Jest config to prevent CSS mapping errors
ManpreetSL Mar 23, 2023
a0475aa
style: format frontend Jest config with ESLint
ManpreetSL Mar 23, 2023
e3940c2
build: add Jest DOM packages for testing
ManpreetSL Mar 23, 2023
641244a
refactor: make name an optional prop for ThemeLoader
ManpreetSL Mar 23, 2023
a9724fe
fix: fix ESLint error with link key in ThemeLoader
ManpreetSL Mar 23, 2023
84b0047
style: update ThemeLoader style using ESLint
ManpreetSL Mar 23, 2023
4adcbcf
feat: update TS, ESLint and Jest config to enable easy running of tests
ManpreetSL Mar 24, 2023
7199389
style: update .swcrc spacing format with ESlint
ManpreetSL Mar 24, 2023
ea341af
build: ignore tsbuildinfo files in Git
ManpreetSL Mar 24, 2023
c32b939
test: add ThemeLoader tests
ManpreetSL Mar 24, 2023
6215f86
test: move isMac const mock to a dedicated consts mock file
ManpreetSL Mar 24, 2023
3e540d9
test: add test file for the About component
ManpreetSL Mar 24, 2023
550699a
test: change generic consts mock to mock detect() from detect-browser
ManpreetSL Mar 25, 2023
ca5f00d
test: add mapPlatformKey() utils test
ManpreetSL Mar 25, 2023
f3de2f0
test: add tests for useCopyToClipboard() hook
ManpreetSL Mar 31, 2023
533707a
build: add testing library user-event package for tests
ManpreetSL Apr 11, 2023
77e2696
test: add mapPlatformKeys tests
ManpreetSL May 4, 2023
8279e5a
style: format the Hotkeys file
ManpreetSL Jul 29, 2023
c0b3770
refactor: move setupTests file to a tests folder
ManpreetSL Jul 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 9 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,13 @@
"extends": ["@shabados/eslint-config", "@shabados/eslint-config/typescript"],
"parserOptions": {
"project": "./tsconfig.json"
}
},
"overrides": [
{
"files": ["apps/frontend/tests/setupTests.ts"],
"rules": {
"import/no-extraneous-dependencies": 0
}
}
]
}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ dist/
build/
.nyc_output/
coverage/
!.vscode/extensions.json
!.vscode/extensions.json
*.tsbuildinfo
13 changes: 13 additions & 0 deletions .swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic"
}
}
}
}
3 changes: 3 additions & 0 deletions apps/frontend/__mocks__/detect-browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const detect = jest.fn().mockReturnValue( {
os: 'Windows 10',
} )
26 changes: 26 additions & 0 deletions apps/frontend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module.exports = {
transform: {
'^.+\\.(t|j)sx?$': [ '@swc/jest',
{
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
},
},
},
},
],
},
testEnvironment: 'jsdom',
moduleNameMapper: {
'\\.css$': '<rootDir>/src/__mocks__/styleMock.ts',
},
setupFilesAfterEnv: [
'<rootDir>/tests/setupTests.ts',
],
}
9 changes: 8 additions & 1 deletion apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
},
"homepage": "/",
"dependencies": {
"@emotion/react": "^11.10.6",
"@emotion/styled": "^11.10.6",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
"@mui/material": "^5.6.4",
"@mui/material": "^5.11.13",
"@sentry/browser": "^6.19.7",
"classnames": "^2.3.1",
"copy-to-clipboard": "^3.3.1",
Expand Down Expand Up @@ -48,13 +50,18 @@
"lint": "eslint ."
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/lodash": "^4.14.182",
"@types/memoizee": "^0.4.8",
"@types/qs": "^6.9.7",
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.3",
"@types/scroll-into-view": "^1.16.0",
"@types/testing-library__jest-dom": "^5.14.5",
"@vitejs/plugin-react": "^1.3.2",
"jest-environment-jsdom": "^29.5.0",
"source-map-explorer": "^2.5.2",
"vite": "^2.9.8"
}
Expand Down
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions apps/frontend/src/Settings/About.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { render, screen } from '@testing-library/react'

import About from './About'

jest.mock( '../lib/controller', jest.fn() )

describe( '<About />', () => {
it( 'should display a loading spinner when the page is loading', async () => {
render( <About connected={0} /> )

const loadingSpinner = await screen.findByRole( 'progressbar' )
expect( loadingSpinner ).toBeInTheDocument()
} )

it.todo( 'should show the number of connected devices' )
} )
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import './index.css'

import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { Button, Grid, List, ListItem, Tooltip, Typography } from '@material-ui/core'
import {
Button,
Grid,
List,
ListItem,
Tooltip,
Typography,
} from '@mui/material'
import classNames from 'classnames'
import { groupBy } from 'lodash'
import { arrayOf, objectOf, shape, string } from 'prop-types'
Expand Down Expand Up @@ -113,11 +120,11 @@ const Hotkeys = ( { keys, shortcuts, device } ) => {

<Grid item xs={1}>
{description && (
<Tooltip title={description}>
<span>
<FontAwesomeIcon icon={faQuestionCircle} />
</span>
</Tooltip>
<Tooltip title={description}>
<span>
<FontAwesomeIcon icon={faQuestionCircle} />
</span>
</Tooltip>
)}
</Grid>

Expand Down
3 changes: 3 additions & 0 deletions apps/frontend/src/__mocks__/styleMock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {}

export default {}
1 change: 1 addition & 0 deletions apps/frontend/src/lib/__mocks__/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const isMac = false
51 changes: 51 additions & 0 deletions apps/frontend/src/lib/hooks.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import copy from 'copy-to-clipboard'
import { useSnackbar } from 'notistack'

import { useCopyToClipboard } from './hooks'

jest.mock( 'copy-to-clipboard', () => jest.fn() )
jest.mock( 'notistack', () => ( { useSnackbar: jest.fn().mockReturnValue( { enqueueSnackbar: jest.fn() } ) } ) )

const TestHooksComponent = () => {
const copyToClipboard = useCopyToClipboard()

return { copyToClipboard }
}

describe( 'hooks', () => {
describe( 'useCopyToClipboard()', () => {
Copy link
Member

Choose a reason for hiding this comment

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

This suite ends up testing a lot of the internals of the function, rather than outcome! You can try to test the outcome of hooks by setting up a scaffolding React component, and then asserting against the outcomes, as we'd normally do :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So with this, I'm looking to implement:

  • Check what was copied to the clipboard
  • Check for a snackbar and its text to appear on the screen using RTL
    Does that sound sensible?

let copyToClipboard: ( text: string, fallback?: string ) => void
let enqueueSnackbar

beforeAll( () => {
( { copyToClipboard } = TestHooksComponent() )
;( { enqueueSnackbar } = useSnackbar() )
} )

afterEach( () => {
jest.clearAllMocks()
} )

it( 'should return a function to copy the given text to the clipboard', () => {
copyToClipboard( 'text to copy to the clipboard' )

expect( copy ).toHaveBeenCalledTimes( 2 )
expect( copy ).toHaveBeenCalledWith( 'text to copy to the clipboard' )
} )

it( 'should truncate the snackbar message to a max of 30 characters', () => {
copyToClipboard( 'this is 30 characters longggg. this should be truncated.' )

// eslint-disable-next-line no-useless-escape
expect( enqueueSnackbar ).toHaveBeenCalledWith( 'Copied \"this is 30 characters longggg....\" to clipboard', expect.any( Object ) )
} )

it( 'should return the fallback text in the snackbar if a falsy value is passed', () => {
const { enqueueSnackbar } = useSnackbar()
copyToClipboard( '', 'fallback text' )

expect( copy ).not.toHaveBeenCalled()
expect( enqueueSnackbar ).toHaveBeenCalledWith( 'fallback text', expect.any( Object ) )
} )
} )
} )
42 changes: 42 additions & 0 deletions apps/frontend/src/lib/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { mapPlatformKey, mapPlatformKeys } from './utils'

const isMacMockGetter = jest.fn()

jest.mock( './consts', () => ( {
get isMac() {
return isMacMockGetter()
},
} ) )
Comment on lines +3 to +9
Copy link
Member

Choose a reason for hiding this comment

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

Very interesting approach! I think there might be a simpler option:

import * as consts from './consts'

consts.isMac = true

might work

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cannot assign to 'isMac' because it is a read-only property.


describe( 'utils', () => {
describe( 'mapPlatformKey', () => {
it( 'should map ctrl to cmd if on Mac', () => {
isMacMockGetter.mockReturnValue( true )

expect( mapPlatformKey( 'alt+V' ) ).toBe( 'alt+V' )
expect( mapPlatformKey( 'ctrl+V' ) ).toBe( 'cmd+V' )
} )

it( 'should keep the key as-is if not on Mac', () => {
isMacMockGetter.mockReturnValue( false )

expect( mapPlatformKey( '' ) ).toBe( '' )
expect( mapPlatformKey( 'ctrl+D' ) ).toBe( 'ctrl+D' )
} )
} )

describe( 'mapPlatformKeys', () => {
it( 'should not fail if passed an empty object', () => {
const result = mapPlatformKeys( {} )

expect( result ).toEqual( {} )
} )

it( 'should return the keyMap argument as-is if not on Mac', () => {
isMacMockGetter.mockReturnValue( false )
mapPlatformKeys( {} )
} )

it.todo( 'should transform ctrl to cmd in key bindings if on Mac' )
} )
} )
39 changes: 39 additions & 0 deletions apps/frontend/src/shared/ThemeLoader.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { render } from '@testing-library/react'

import { StatusContext } from '../lib/contexts'
import ThemeLoader from './ThemeLoader'

describe( 'ThemeLoader', () => {
it( 'should generate a link element with a href of the default Day.css value', () => {
const { container } = render(
<StatusContext.Provider
value={{ connected: false, connectedAt: null, status: null }}
>
<ThemeLoader />
</StatusContext.Provider>
)

const link = container.querySelector( 'link' )

expect( link ).toBeInTheDocument()
expect( link?.getAttribute( 'href' ) ).toMatch( /Day.css/ )
} )

it(
'should generate a link element with a href of the given name value',
() => {
const { container } = render(
<StatusContext.Provider
value={{ connected: false, connectedAt: null, status: null }}
>
<ThemeLoader name="Night" />
</StatusContext.Provider>
)

const link = container.querySelector( 'link' )

expect( link ).toBeInTheDocument()
expect( link?.getAttribute( 'href' ) ).toMatch( /Night.css/ )
}
)
} )
20 changes: 10 additions & 10 deletions apps/frontend/src/shared/ThemeLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ import { THEMES_URL } from '../lib/consts'
import { StatusContext } from '../lib/contexts'
import defaultTheme from '../Presenter/themes/Day.css'

type ThemeLoaderProps = { name: String }
type ThemeLoaderProps = { name?: string }

/**
* Component to load a theme using a `<link>` tag.
* @param {string} name The name of the CSS theme to load from the server.
* @constructor
*/
const ThemeLoader = ( { name = 'Day' }: ThemeLoaderProps ) => {
const { connectedAt } = useContext(StatusContext)
const { connectedAt } = useContext( StatusContext )

return (
<link
key={`${name}-${connectedAt}`}
rel="stylesheet"
href={name ? `${THEMES_URL}/${name}.css` : defaultTheme}
/>
)
}
<link
key={`${name}-${connectedAt?.toDateString() || ''}`}
rel="stylesheet"
href={name ? `${THEMES_URL}/${name}.css` : defaultTheme}
/>
)
}

export default ThemeLoader
export default ThemeLoader
7 changes: 7 additions & 0 deletions apps/frontend/tests/setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import '@testing-library/jest-dom'

jest.mock( 'detect-browser' )

global.fetch = jest.fn().mockResolvedValue( {
json: () => Promise.resolve( {} ),
} )
2 changes: 1 addition & 1 deletion apps/frontend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"jsx": "react-jsx"
},
"include": ["src", "src/types/vite-env.d.ts"],
"include": ["*.ts", "*.js", "*.tsx", "*.jsx", "__mocks__", "__utils__", "tests/setupTests.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}
18 changes: 14 additions & 4 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
/// jest.d.ts

module.exports = {
projects: [ '<rootDir>/apps/*', '<rootDir>/packages/*' ],
transform: {
'^.+\\.(t|j)sx?$': [ '@swc/jest' ],
'^.+\\.(t|j)sx?$': '@swc/jest',
},
testEnvironment: 'jsdom',
moduleNameMapper: {
'\\.css$': '<rootDir>/apps/frontend/src/__mocks__/styleMock.ts',
},
resetMocks: true,
// resetMocks: true,
// Indicates whether the coverage information should be collected while executing the test
collectCoverage: true,

// The directory where Jest should output its coverage files
coverageDirectory: 'coverage',

// Indicates which provider should be used to instrument code for coverage
coverageProvider: 'v8',
Comment on lines +15 to +18
Copy link
Member

Choose a reason for hiding this comment

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

Are these options required?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These were created from the initial Jest setup, I think. Do you want me to remove them?

}