-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- added useStores() hook to retrieve MobX stores - added react-testing-library + jest-dom - added rtl aliased module with renderWithProvider that renders components with settings state This makes us almost ready to do proper integration test: we only need to define a new AllProviders HOC that will include all needed providers for the app
1 parent
b55117f
commit 77d1d1a
Showing
11 changed files
with
1,574 additions
and
157 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ | |
}, | ||
"parser": { | ||
"syntax": "typescript", | ||
"tsx": true, | ||
"tsx": true | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ | |
] | ||
}, | ||
"scripts": { | ||
"test": "npx jest", | ||
"test": "jest", | ||
"test:e2e": "npm run server:stop && cd e2e && npm run build && npm run server && npm run cypress:run --config video=false && pm2 stop cy-server", | ||
"test:all": "npm run test:e2e && npm run test", | ||
"build": "webpack --watch", | ||
|
@@ -61,6 +61,8 @@ | |
"license": "MIT", | ||
"devDependencies": { | ||
"@swc/core": "^1.3.10", | ||
"@testing-library/jest-dom": "^5.16.5", | ||
"@testing-library/react": "^12.1.5", | ||
"@types/copy-webpack-plugin": "^6.0.0", | ||
"@types/hoist-non-react-statics": "^3.3.1", | ||
"@types/html-webpack-plugin": "^3.2.4", | ||
|
@@ -91,6 +93,7 @@ | |
"husky": "^3.0.9", | ||
"jest": "^29.3.1", | ||
"jest-cli": "^29.3.1", | ||
"jest-environment-jsdom": "^29.3.1", | ||
"lint-staged": "^10.4.2", | ||
"mock-fs": "git+https://[email protected]/warpdesign/mock-fs.git", | ||
"native-ext-loader": "^2.3.0", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import '@testing-library/jest-dom' | ||
import '@testing-library/jest-dom/extend-expect' | ||
|
||
global.console.error = jest.fn() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,127 +1,97 @@ | ||
import * as React from 'react' | ||
import { inject, observer } from 'mobx-react' | ||
import { observer } from 'mobx-react' | ||
import { Navbar, Alignment, Button, Classes, Intent } from '@blueprintjs/core' | ||
import { IconNames } from '@blueprintjs/icons' | ||
import { Popover2 } from '@blueprintjs/popover2' | ||
import classnames from 'classnames' | ||
import { withTranslation, WithTranslation } from 'react-i18next' | ||
import { useTranslation } from 'react-i18next' | ||
import { HamburgerMenu } from './HamburgerMenu' | ||
import { Badge } from './Badge' | ||
import { AppState } from '../state/appState' | ||
import { runInAction } from 'mobx' | ||
|
||
interface InjectedProps extends WithTranslation { | ||
appState: AppState | ||
} | ||
|
||
const NavComponent = inject('appState')( | ||
observer( | ||
class NavComponent extends React.Component<WithTranslation> { | ||
appState: AppState = null | ||
|
||
constructor(props: WithTranslation) { | ||
super(props) | ||
|
||
this.appState = this.injected.appState | ||
} | ||
|
||
private get injected(): InjectedProps { | ||
return this.props as InjectedProps | ||
} | ||
|
||
showDownloadsTab = (): void => { | ||
this.appState.showDownloadsTab() | ||
} | ||
|
||
showExplorerTab = (): void => { | ||
this.appState.showExplorerTab() | ||
} | ||
|
||
navClick = (): void => { | ||
if (this.appState.isExplorer) { | ||
this.showDownloadsTab() | ||
} else { | ||
this.showExplorerTab() | ||
} | ||
} | ||
|
||
onToggleSplitView = (): void => { | ||
if (this.appState.isExplorer) { | ||
const winState = this.appState.winStates[0] | ||
winState.toggleSplitViewMode() | ||
} | ||
} | ||
|
||
onOpenPrefs = (): void => { | ||
runInAction(() => (this.appState.isPrefsOpen = true)) | ||
} | ||
|
||
onOpenShortcuts = (): void => { | ||
runInAction(() => (this.appState.isShortcutsOpen = true)) | ||
} | ||
|
||
render(): React.ReactNode { | ||
const { t } = this.props | ||
const isExplorer = this.appState.isExplorer | ||
const count = this.appState.pendingTransfers | ||
const badgeText = (count && count + '') || '10' | ||
const badgeProgress = this.appState.totalTransferProgress | ||
const downloadClass = classnames(Classes.MINIMAL, 'download') | ||
const isSplitViewActive = this.appState.winStates[0].splitView | ||
|
||
console.log('render nav', isSplitViewActive) | ||
|
||
return ( | ||
<Navbar> | ||
<Navbar.Group align={Alignment.LEFT} className="title-group"> | ||
<Navbar.Heading>{t('APP_MENUS.ABOUT_TITLE')}</Navbar.Heading> | ||
<Navbar.Divider /> | ||
<Button | ||
className={`${Classes.MINIMAL} data-cy-explorer-tab`} | ||
icon="home" | ||
text={t('NAV.EXPLORER')} | ||
onClick={this.navClick} | ||
intent={isExplorer ? Intent.PRIMARY : 'none'} | ||
/> | ||
<Button | ||
style={{ position: 'relative' }} | ||
className={`${downloadClass} data-cy-downloads-tab`} | ||
icon="download" | ||
onClick={this.navClick} | ||
intent={!isExplorer ? Intent.PRIMARY : 'none'} | ||
> | ||
{t('NAV.TRANSFERS')} | ||
<Badge intent="none" text={badgeText} progress={badgeProgress} /> | ||
</Button> | ||
</Navbar.Group> | ||
<Navbar.Group align={Alignment.RIGHT}> | ||
<Button | ||
className={`data-cy-toggle-splitview ${Classes.MINIMAL}`} | ||
active={isSplitViewActive} | ||
intent={(isSplitViewActive && 'primary') || 'none'} | ||
onClick={this.onToggleSplitView} | ||
icon={IconNames.PANEL_STATS} | ||
title={t('NAV.SPLITVIEW')} | ||
/> | ||
<Navbar.Divider /> | ||
<Popover2 | ||
content={ | ||
<HamburgerMenu | ||
onOpenShortcuts={this.onOpenShortcuts} | ||
onOpenPrefs={this.onOpenPrefs} | ||
/> | ||
} | ||
> | ||
<Button className={`data-cy-toggle-app-menu ${Classes.MINIMAL}`} icon="menu" /> | ||
</Popover2> | ||
</Navbar.Group> | ||
</Navbar> | ||
) | ||
} | ||
}, | ||
), | ||
) | ||
|
||
const Nav = withTranslation()(NavComponent) | ||
import { useStores } from '../hooks/useStores' | ||
|
||
const Nav = observer(() => { | ||
const { appState } = useStores('appState') | ||
const { t } = useTranslation() | ||
const isExplorer = appState.isExplorer | ||
const count = appState.pendingTransfers | ||
const badgeText = (count && count + '') || '' | ||
const badgeProgress = appState.totalTransferProgress | ||
const downloadClass = classnames(Classes.MINIMAL, 'download') | ||
const isSplitViewActive = appState.winStates[0].splitView | ||
|
||
const showDownloadsTab = (): void => { | ||
appState.showDownloadsTab() | ||
} | ||
|
||
const showExplorerTab = (): void => { | ||
appState.showExplorerTab() | ||
} | ||
|
||
const navClick = (): void => { | ||
if (appState.isExplorer) { | ||
showDownloadsTab() | ||
} else { | ||
showExplorerTab() | ||
} | ||
} | ||
|
||
const onToggleSplitView = (): void => { | ||
if (appState.isExplorer) { | ||
const winState = appState.winStates[0] | ||
winState.toggleSplitViewMode() | ||
} | ||
} | ||
|
||
const onOpenPrefs = (): void => { | ||
runInAction(() => (appState.isPrefsOpen = true)) | ||
} | ||
|
||
const onOpenShortcuts = (): void => { | ||
runInAction(() => (appState.isShortcutsOpen = true)) | ||
} | ||
|
||
console.log('render nav', isSplitViewActive) | ||
|
||
return ( | ||
<Navbar> | ||
<Navbar.Group align={Alignment.LEFT} className="title-group"> | ||
<Navbar.Heading>{t('APP_MENUS.ABOUT_TITLE')}</Navbar.Heading> | ||
<Navbar.Divider /> | ||
<Button | ||
className={`${Classes.MINIMAL} data-cy-explorer-tab`} | ||
icon="home" | ||
text={t('NAV.EXPLORER')} | ||
onClick={navClick} | ||
intent={isExplorer ? Intent.PRIMARY : 'none'} | ||
/> | ||
<Button | ||
style={{ position: 'relative' }} | ||
className={`${downloadClass} data-cy-downloads-tab`} | ||
icon="download" | ||
onClick={navClick} | ||
intent={!isExplorer ? Intent.PRIMARY : 'none'} | ||
> | ||
{t('NAV.TRANSFERS')} | ||
<Badge intent="none" text={badgeText} progress={badgeProgress} /> | ||
</Button> | ||
</Navbar.Group> | ||
<Navbar.Group align={Alignment.RIGHT}> | ||
<Button | ||
className={`data-cy-toggle-splitview ${Classes.MINIMAL}`} | ||
active={isSplitViewActive} | ||
intent={(isSplitViewActive && 'primary') || 'none'} | ||
onClick={onToggleSplitView} | ||
icon={IconNames.PANEL_STATS} | ||
title={t('NAV.SPLITVIEW')} | ||
/> | ||
<Navbar.Divider /> | ||
<Popover2 content={<HamburgerMenu onOpenShortcuts={onOpenShortcuts} onOpenPrefs={onOpenPrefs} />}> | ||
<Button className={`data-cy-toggle-app-menu ${Classes.MINIMAL}`} icon="menu" /> | ||
</Popover2> | ||
</Navbar.Group> | ||
</Navbar> | ||
) | ||
}) | ||
|
||
export { Nav } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* @jest-environment jsdom | ||
*/ | ||
import React from 'react' | ||
import { render, screen } from '@testing-library/react' | ||
import { useStores } from '../useStores' | ||
import { renderWithProvider } from 'rtl' | ||
|
||
const Component = () => { | ||
const { settingsState } = useStores('settingsState') | ||
return <div>{settingsState.lang}</div> | ||
} | ||
|
||
describe('useStores', () => { | ||
it('should return mobx store', () => { | ||
renderWithProvider(<Component />) | ||
expect(screen.getByText('fr')).toBeInTheDocument() | ||
}) | ||
|
||
it('should throw if store could not be found', () => { | ||
expect(() => render(<Component />)).toThrow("No MboXProvider for 'settingsState'!") | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from 'react' | ||
import { MobXProviderContext } from 'mobx-react' | ||
|
||
export const useStores = (...storeIds: string[]) => { | ||
const AllStores = React.useContext(MobXProviderContext) | ||
const stores: Partial<typeof AllStores> = {} | ||
|
||
for (const id of storeIds) { | ||
if (!AllStores[id]) { | ||
throw `No MboXProvider for '${id}'!` | ||
} | ||
stores[id] = AllStores[id] | ||
} | ||
return stores | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,54 @@ | ||
import { platform } from 'process'; | ||
import { homedir, tmpdir } from 'os'; | ||
import { sep } from 'path'; | ||
import { platform } from 'process' | ||
import { tmpdir } from 'os' | ||
import { sep } from 'path' | ||
|
||
// expects describe to be globally defined: should only be used from test environment | ||
// declare var describe: any; | ||
// declare var it: any; | ||
|
||
// assume Unix in that case: may need some tweaks for some exotic platform | ||
const isUnix = platform !== 'win32'; | ||
const TEST_FILES_DIR = tmpdir() + sep + 'react-explorer-tests'; | ||
const isUnix = platform !== 'win32' | ||
const TEST_FILES_DIR = tmpdir() + sep + 'react-explorer-tests' | ||
|
||
// call this to perform Unix specific tests | ||
export const describeUnix = (name: string, fn: jest.EmptyFunction): void => { | ||
if (isUnix) { | ||
describe(`${name} (Unix)`, fn); | ||
describe(`${name} (Unix)`, fn) | ||
} else { | ||
describe.skip(name, fn); | ||
describe.skip(name, fn) | ||
} | ||
}; | ||
} | ||
|
||
// call this to perform Windows specific tests | ||
export const describeWin = (name: string, fn: jest.EmptyFunction) => { | ||
if (!isUnix) { | ||
describe(`${name} (Win)`, fn); | ||
describe(`${name} (Win)`, fn) | ||
} else { | ||
describe.skip(name, fn); | ||
describe.skip(name, fn) | ||
} | ||
}; | ||
} | ||
|
||
// call this to perform Unix specific tests | ||
export const itUnix = (name: string, fn?: jest.ProvidesCallback, timeout?: number) => { | ||
if (isUnix) { | ||
it(`${name} (Unix)`, fn, timeout); | ||
it(`${name} (Unix)`, fn, timeout) | ||
} else { | ||
it.skip(name, fn, timeout); | ||
it.skip(name, fn, timeout) | ||
} | ||
}; | ||
} | ||
|
||
export const getPath = (id: string): string => { | ||
switch (id) { | ||
case 'home': | ||
return '/cy/home'; | ||
return '/cy/home' | ||
|
||
case 'temp': | ||
return tmpdir(); | ||
return tmpdir() | ||
|
||
case 'tests_dir': | ||
return TEST_FILES_DIR; | ||
return TEST_FILES_DIR | ||
|
||
default: | ||
return tmpdir(); | ||
return tmpdir() | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import React from 'react' | ||
import { render } from '@testing-library/react' | ||
import { makeAutoObservable } from 'mobx' | ||
import { Provider } from 'mobx-react' | ||
|
||
class State { | ||
lang = 'fr' | ||
darkMode = false | ||
|
||
constructor() { | ||
makeAutoObservable(this) | ||
} | ||
} | ||
|
||
const renderWithProvider = (jsx: React.ReactElement) => { | ||
const settingsState = new State() | ||
return render(<Provider settingsState={settingsState}>{jsx}</Provider>) | ||
} | ||
|
||
// TODO: provide all needed providers for react-explorer | ||
// const AllTheProviders = ({children}) => { | ||
// return ( | ||
// <ThemeProvider theme="light"> | ||
// <TranslationProvider messages={defaultStrings}> | ||
// {children} | ||
// </TranslationProvider> | ||
// </ThemeProvider> | ||
// ) | ||
// } | ||
|
||
// const customRender = (ui, options) => | ||
// render(ui, {wrapper: AllTheProviders, ...options}) | ||
|
||
// re-export everything | ||
export * from '@testing-library/react' | ||
|
||
// override render method | ||
export { renderWithProvider } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters