-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests(react): create tests for the react bindings to core (#17551)
* Add tests. * updates after API meeting. * test(): add tests for create controller components. * correct testing for controller component * Ensure tests properly cleanup after each run. * create common test utils for overlay and controllers. * initial tests for ion router outlet * simple update. * add mocks for jest tests.
- Loading branch information
Showing
22 changed files
with
595 additions
and
67 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 |
---|---|---|
@@ -0,0 +1 @@ | ||
exports.defineCustomElements = () => {}; |
File renamed without changes.
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,2 @@ | ||
|
||
exports.addIcons = () => {}; |
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 @@ | ||
global.crypto = require('@trust/webcrypto'); |
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
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
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,29 @@ | ||
import React from 'react'; | ||
|
||
|
||
type Props = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>; | ||
|
||
type InternalProps = Props & { | ||
forwardedRef?: React.RefObject<HTMLDivElement> | ||
}; | ||
|
||
type ExternalProps = Props & { | ||
ref?: React.RefObject<HTMLDivElement> | ||
}; | ||
|
||
const IonPage: React.SFC<InternalProps> = ({ children, forwardedRef, className, ...props }) => ( | ||
<div | ||
className={className ? `ion-page ${className}` : 'ion-page'} | ||
ref={forwardedRef} | ||
{...props} | ||
> | ||
{children} | ||
</div> | ||
); | ||
|
||
function forwardRef(props: InternalProps, ref: React.RefObject<HTMLDivElement>) { | ||
return <IonPage {...props} forwardedRef={ref} />; | ||
} | ||
forwardRef.displayName = 'IonPage'; | ||
|
||
export default React.forwardRef<HTMLDivElement, ExternalProps>(forwardRef); |
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,40 @@ | ||
import React, { SFC, ReactElement } from 'react'; | ||
import { Route, Router } from 'react-router-dom'; | ||
import { createMemoryHistory } from 'history'; | ||
import { render, cleanup } from 'react-testing-library'; | ||
import { IonRouterOutlet } from '../navigation/IonRouterOutlet'; | ||
|
||
afterEach(cleanup) | ||
|
||
function createReactPage(text: string) { | ||
const ReactPage: SFC = () => <span>{text}</span>; | ||
return ReactPage; | ||
} | ||
|
||
function renderWithRouter( | ||
ui: ReactElement<any>, | ||
{ | ||
route = '/', | ||
history = createMemoryHistory({ initialEntries: [route] }), | ||
} = {} | ||
) { | ||
return { | ||
...render(<Router history={history}>{ui}</Router>), | ||
history | ||
} | ||
} | ||
|
||
test('landing on a bad page', () => { | ||
const { container } = renderWithRouter( | ||
<IonRouterOutlet> | ||
<Route path="/:tab(schedule)" component={createReactPage('Schedule Home')} exact={true} /> | ||
<Route path="/:tab(speakers)" component={createReactPage('Speakers Home')} exact={true} /> | ||
<Route path="/:tab(speakers)/speaker/:id" component={createReactPage('Speaker Detail')} /> | ||
<Route path="/:tab(schedule|speakers)/sessions/:id" component={createReactPage('Session Detail')} /> | ||
</IonRouterOutlet> | ||
, { | ||
route: '/schedule', | ||
}); | ||
|
||
expect(container.innerHTML).toContain('<ion-router-outlet><div class="ion-page"><span>Schedule Home</span></div></ion-router-outlet>'); | ||
}) |
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,49 @@ | ||
import React from 'react'; | ||
import { Components } from '@ionic/core' | ||
import { createReactComponent } from '../createComponent'; | ||
import { render, fireEvent, cleanup } from 'react-testing-library'; | ||
|
||
afterEach(cleanup); | ||
|
||
describe('createComponent - events', () => { | ||
test('should set events on handler', () => { | ||
const FakeOnClick = jest.fn((e) => e); | ||
const IonButton = createReactComponent<Components.IonButtonAttributes, HTMLIonButtonElement>('ion-button'); | ||
|
||
const { getByText } = render( | ||
<IonButton onClick={FakeOnClick}> | ||
ButtonNameA | ||
</IonButton> | ||
); | ||
fireEvent.click(getByText('ButtonNameA')); | ||
expect(FakeOnClick).toBeCalledTimes(1); | ||
}); | ||
|
||
test('should add custom events', () => { | ||
const FakeIonFocus = jest.fn((e) => e); | ||
const IonInput = createReactComponent<Components.IonInputAttributes, HTMLIonInputElement>('ion-input'); | ||
|
||
const { getByText } = render( | ||
<IonInput onIonFocus={FakeIonFocus}> | ||
ButtonNameA | ||
</IonInput> | ||
); | ||
const ionInputItem = getByText('ButtonNameA'); | ||
expect(Object.keys((ionInputItem as any).__events)).toEqual(['ionFocus']); | ||
}); | ||
}); | ||
|
||
describe('createComponent - ref', () => { | ||
test('should pass ref on to web component instance', () => { | ||
const ionButtonRef: React.RefObject<any> = React.createRef(); | ||
const IonButton = createReactComponent<Components.IonButtonAttributes, HTMLIonButtonElement>('ion-button'); | ||
|
||
const { getByText } = render( | ||
<IonButton ref={ionButtonRef}> | ||
ButtonNameA | ||
</IonButton> | ||
) | ||
const ionButtonItem = getByText('ButtonNameA'); | ||
expect(ionButtonRef.current).toEqual(ionButtonItem); | ||
}); | ||
}); |
120 changes: 120 additions & 0 deletions
120
react/src/components/__tests__/createControllerComponent.tsx
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,120 @@ | ||
import React from 'react'; | ||
import { Components } from '@ionic/core'; | ||
import { createControllerComponent } from '../createControllerComponent'; | ||
import { render, waitForElement, wait } from 'react-testing-library'; | ||
import * as utils from '../utils'; | ||
import { createControllerUtils } from '../utils/controller-test-utils'; | ||
import 'jest-dom/extend-expect'; | ||
|
||
describe('createControllerComponent - events', () => { | ||
const { cleanupAfterController, createControllerElement, augmentController} = createControllerUtils('ion-loading'); | ||
type LoadingOptions = Components.IonLoadingAttributes; | ||
const IonLoading = createControllerComponent<LoadingOptions, HTMLIonLoadingElement, HTMLIonLoadingControllerElement>('ion-loading', 'ion-loading-controller') | ||
|
||
afterEach(cleanupAfterController); | ||
|
||
test('should create controller component outside of the react component', async () => { | ||
const { container, baseElement } = render( | ||
<> | ||
<IonLoading | ||
isOpen={false} | ||
onDidDismiss={jest.fn()} | ||
duration={2000} | ||
> | ||
</IonLoading> | ||
<span>ButtonNameA</span> | ||
</> | ||
); | ||
expect(container).toContainHTML('<div><span>ButtonNameA</span></div>'); | ||
expect(baseElement.querySelector('ion-loading-controller')).toBeInTheDocument(); | ||
}); | ||
|
||
test('should create component and attach props on opening', async () => { | ||
const onDidDismiss = jest.fn(); | ||
const { baseElement, container, rerender } = render( | ||
<IonLoading | ||
isOpen={false} | ||
onDidDismiss={onDidDismiss} | ||
duration={2000} | ||
> | ||
ButtonNameA | ||
</IonLoading> | ||
); | ||
|
||
const [element, presentFunction] = createControllerElement(); | ||
const loadingController = augmentController(baseElement, container, element); | ||
|
||
const attachEventPropsSpy = jest.spyOn(utils, "attachEventProps"); | ||
|
||
rerender( | ||
<IonLoading | ||
isOpen={true} | ||
onDidDismiss={onDidDismiss} | ||
duration={2000} | ||
> | ||
ButtonNameA | ||
</IonLoading> | ||
); | ||
|
||
await waitForElement(() => container.querySelector('ion-loading')); | ||
|
||
expect((loadingController as any).create).toHaveBeenCalledWith({ | ||
duration: 2000, | ||
children: 'ButtonNameA', | ||
onIonLoadingDidDismiss: onDidDismiss | ||
}); | ||
expect(presentFunction).toHaveBeenCalled(); | ||
expect(attachEventPropsSpy).toHaveBeenCalledWith(element, { | ||
duration: 2000, | ||
children: 'ButtonNameA', | ||
onIonLoadingDidDismiss: onDidDismiss | ||
}); | ||
}); | ||
|
||
test('should dismiss component on hiding', async () => { | ||
const { container, baseElement, rerender } = render( | ||
<IonLoading | ||
isOpen={false} | ||
onDidDismiss={jest.fn()} | ||
duration={2000} | ||
> | ||
ButtonNameA | ||
</IonLoading> | ||
); | ||
|
||
const [element, , dismissFunction] = createControllerElement(); | ||
augmentController(baseElement, container, element); | ||
|
||
rerender( | ||
<IonLoading | ||
isOpen={true} | ||
onDidDismiss={jest.fn()} | ||
duration={2000} | ||
> | ||
ButtonNameA | ||
</IonLoading> | ||
); | ||
|
||
await waitForElement(() => container.querySelector('ion-loading')); | ||
|
||
rerender( | ||
<IonLoading | ||
isOpen={false} | ||
onDidDismiss={jest.fn()} | ||
duration={2000} | ||
> | ||
ButtonNameA | ||
</IonLoading> | ||
); | ||
|
||
await wait(() => { | ||
const item = container.querySelector('ion-loading'); | ||
if (item) { | ||
throw new Error(); | ||
} | ||
}); | ||
|
||
expect(dismissFunction).toHaveBeenCalled(); | ||
}); | ||
|
||
}); |
Oops, something went wrong.