React Native Linking.openURL not working in Headless - linker

I am using #react-native-firebase/messaging and running a headless task in which I am successfully getting the Notification when console.logging, but if I use Linking.openURL('myappurl://screen') nothing is working.
Here are the code for index.js
import {AppRegistry, DeviceEventEmitter, Linking} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
const getNofification = async (message) => {
console.log('Console => ', message);
Linking.openURL('whatsup://call/');
return Promise.resolve();
};
// messaging().setBackgroundMessageHandler(async (notif) => {
// console.log('Notification => ', notif);
// Linking.openURL(
// 'whatsup://chat/1234/',
// );
// });
AppRegistry.registerHeadlessTask(
'ReactNativeFirebaseMessagingHeadlessTask',
() => getNofification,
);
AppRegistry.registerComponent(appName, () => App);
Same are working fine if i open the deeplink inside App.js.

Related

Need do write unit tests with jest for react app, that uses okta, but getting warning

import { SecureRoute, Security, LoginCallback } from '#okta/okta-react';
import React, { useMemo } from 'react';
import { Route, Switch } from 'react-router-dom';
import { OktaAuth, toRelativeUrl } from '#okta/okta-auth-js';
import Comp from './Comp';
const config = {oidc: {....}};
const AppRouter = () => {
const oktaAuth = useMemo(() => new OktaAuth(config.oidc), []);
const restoreOriginalUri = async (sth, originalUri) => {
history.replace(toRelativeUrl(originalUri || '/', 'some-path-here'));
};
return (
<Security oktaAuth={oktaAuth} restoreOriginalUri={restoreOriginalUri}>
<Switch>
<SecureRoute path="/" exact component={Comp} />
<Route path="/login/callback" component={LoginCallback} />
</Switch>
</Security>
);
};
export default AppRouter;
I have this in my app... how to write unit tests for it if I have the following warning?
Warning: An update to Security inside a test was not wrapped in act(...).
The waitFor thing doesn't seem to work. The okta Security, SecureRoute, and LoginCallback, all trigger test failures (element is undefined, etc).
I got it running this way: I figure I don't need to test any of those elements, I just need to test if my app is rendering. So I mocked everything and this passed the test without undue hackery.
Hope this helps, I see a lot of people out there struggle with this one:
import {cleanup, screen, waitFor} from '#testing-library/react';
import App from './App';
import {render} from "react-dom";
import {BrowserRouter} from "react-router-dom";
jest.mock('#okta/okta-react', () => {
return ({
useOktaAuth: () => ({
authState: {isAuthenticated: true},
authService: {handleAuthentication: jest.fn()},
oktaAuth: {getUser: () => new Promise((resolve, reject) => { resolve('foo')})},
}),
withOktaAuth: (x: any) => x,
Security: () => <div></div>,
SecureRoute: () => <div></div>,
LoginCallback: () => <div></div>
});
});
describe('<App />', () => {
let container: any = null;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(cleanup);
test("Render App", async () => {
render(<BrowserRouter><App /></BrowserRouter>, container);
const comp = await screen.findByTestId('App', container);
expect(comp).toBeInTheDocument();
});
});
The thing is you have to await for Okta asynchronous updates in the component.
You could just wrap your "expects" block with await waitFor(() => { } assuming you're using React Testing Library.
Example:
await waitFor(() => {
expect(getAllByText(/Home/i)[0]).toBeInTheDocument;
expect(getAllByText(/Home/i)[1]).toBeInTheDocument;
});

Unit testing error with act and useState, Making an API call in useEffect

I have a react functional component where onload of the App I am smoking an API call which I have wrapped in useEffect and when the data is received I am updating the useState variable.
While writing a unit test case for the below component using jest and react testing library I am receiving act error and useState variable error.
Code:
import React, { useState, useEffect } from "react";
import TextField from "#material-ui/core/TextField";
import Media from "./card";
import Alert from "#material-ui/lab/Alert";
import fetchData from "../API";
const Dashboard = () => {
const [searchInput, setSearchInput] = useState(null);
const [receipeNotFound, setReceipeNotFound] = useState(false);
useEffect(() => {
fetchData(
`https://www.themealdb.com/api/json/v1/1/random.php`,
randomReceipe
);
}, []);
const randomReceipe = (result) => {
result.meals ? setSearchInput(result.meals[0]) : setSearchInput(null);
};
const searchData = (value) => {
if (value) {
setSearchInput(null);
setReceipeNotFound(false);
fetchData(
`https://www.themealdb.com/api/json/v1/1/search.php?s=${value}`,
searchRecepie
);
} else {
setSearchInput(null);
}
};
const notFound = (status) => {
setReceipeNotFound(status);
setSearchInput(null);
};
const searchRecepie = (result) => {
result.meals ? setSearchInput(result.meals[0]) : notFound(true);
};
return (
<div>
<TextField
data-testid="searchInput"
id="standard-basic"
label="Search"
onBlur={(event) => searchData(event.currentTarget.value)}
/>
{receipeNotFound ? (
<Alert data-testid="alert" severity="error">
Receipe not found!
</Alert>
) : null}
{Boolean(searchInput) ? (
<Media data-testid="mediaLoading" loading={false} data={searchInput} />
) : (
<Media data-testid="Media" loading={true} data={{}} />
)}
</div>
);
};
export default Dashboard;
Test:
import React from "react";
import Dashboard from "./dashboard";
import ReactDOM from "react-dom";
import {
render,
screen,
act,
waitFor,
fireEvent,
} from "#testing-library/react";
import { randomMockWithOutVideo, randomMockWithVideo } from "./API.mock";
global.fetch = jest.fn();
let container;
describe("Card ", () => {
test("Should renders data when API request is called with meals data", async (done) => {
jest.spyOn(global, "fetch").mockResolvedValue({
json: jest.fn().mockResolvedValue({ meals: [randomMockWithVideo] }),
});
const { getByTestId, container } = render(<Dashboard />);
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(global.fetch).toHaveBeenCalledWith(
`https://www.themealdb.com/api/json/v1/1/random.php`
);
});
afterEach(() => {
jest.restoreAllMocks();
});
});
error:
Warning: An update to Dashboard inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
19 | result.meals ? setSearchInput(result.meals[0]) : setSearchInput(null);
| ^
This warning indicates that an update has been made to your component (through useState) after it was tear down. You can read a full explanation by Kent C. Dodds here : https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning/.
I would try this syntax to get rid of the warning :
await act(() => {
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(global.fetch).toHaveBeenCalledWith(`https://www.themealdb.com/api/json/v1/1/random.php`);
});

React Storybook showing only three unit tests

I am facing a very strange situation. I have 9 unit tests that are running perfectly well when I run it through jest command. But when I run storybook it just shows 3 tests out of 9. Storybook version is 5.1.7.
Test which is showing on storybook - Filter Search Orders -
import React from "react";
import expect from "expect";
import { mount } from "enzyme";
import _ from "lodash";
import Root from "../../root";
import { storiesOf, describe, it, specs, beforeEach, afterEach } from "../../../.storybook/facade";
import FilterSearchOrders from "../search-orders/filter-search-orders";
import ordersFilterOptions from "../search-orders/orders-filters-options";
const filterSearchOrdersInfo = {
searchedText: "",
status: "",
pageNo: 0,
};
const getElement = () => {
return (
<Root>
<FilterSearchOrders
searchedText={filterSearchOrdersInfo.searchedText}
status={filterSearchOrdersInfo.status}
pageNo={filterSearchOrdersInfo.pageNo}
/>
</Root>
);
};
storiesOf("Filter Search Orders", module).add("Filter Search Orders", () => {
specs(() =>
describe("Test case for filter search order component", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(getElement());
});
afterEach(() => {
wrapper.unmount();
});
it("should render an input box for searching", () => {
expect(wrapper.find(".search-orders-input"));
});
it("should render a dropdown with options", () => {
const dropdownOptions = wrapper.find(".item").map((node) => node.text());
const items = _.map(ordersFilterOptions, _.property("text"));
expect(dropdownOptions).toEqual(items);
});
it("should render a primary button called search", () => {
expect(wrapper.find(".primary").text()).toEqual("SEARCH");
});
})
);
return getElement();
});
Test not showing on storybook - Menu story -
import React from "react";
import expect from "expect";
import { mount } from "enzyme";
import _ from "lodash";
import { DASHBOARDITEMS } from "../menu/menuItems";
import { storiesOf, describe, it, specs } from "../../../.storybook/facade";
import NavBar from "../menu";
import Root from "../../root";
const getElement = () => {
return (
<Root>
<NavBar items={DASHBOARDITEMS} />
</Root>
);
};
storiesOf("Menu", module).add("Navigation Bar component", () => {
specs(() =>
describe("Main menu", () => {
it("should render menu", () => {
const wrapper = mount(getElement());
expect(wrapper.find(".nestedMenu"));
});
it("should render an image to show logo", () => {
const wrapper = mount(getElement());
expect(
wrapper
.find(".item")
.at(0)
.find("img").length > 0
).toEqual(true);
});
it("should render all links", () => {
const wrapper = mount(<Root>{getElement()}</Root>);
const links = wrapper.find("a").map((node) => node.text());
const items = _.map(DASHBOARDITEMS, _.property("content"));
expect(links).toEqual(items);
});
})
);
return getElement();
});
Okay so I seemed to figure out what was wrong the whole time.
A very stupid mistake by me but I hope it helps anyone who is dealing with same issue.
I checked the console and seems like the fourth story (in order) that I had written had some error and as soon as I resolved that error that story itself and all other story started showing on storybook.
I upgraded my storybook version and seems like all the stories are shown on the UI with errors.

Changes to mounted component not showing in enzyme even after timeout

how can i test the child component has mounted. Please see the below implmentation.
i have an api call, until the data resolves it will show a loader then it will show the actual component.
Using mount i need to simulate a click on the child component also. What am i doing wrong here.
Please see the below snippet.
// App.js
import React, {Component, Fragment} from 'react'
import Child from './child'
class App extends Component{
state = {
data: null,
enable: false
}
componentDidMount(){
this.getData()
}
getData = async () => {
const response = await fetch('http://www.example.com');
const data = await response.json();
this.setState({
data
})
}
_handleChildClick = () => {
this.setState({
enable: true
})
}
render(){
const {data, enable} = this.state
if(!data){
return (
<div>
Loading
</div>
)
}else{
<Fragment>
<Child
handleChildClick={this._handleChildClick}
/>
</Fragment>
}
}
}
export default App
import React from 'react';
const child = () => {
return(
<div>
<button
className="toggle"
onClick={props.handleChildClick}
>
Toggle
</button>
</div>
)
}
export default child
// App.test.js
import React from 'react';
import {enzyme} from 'enzyme';
import App from './App';
describe("App test cases", () => {
it('should trigger _handleChildClick', async () => {
window.fetch = jest.fn().mockImplementation(() => ({
status: 200,
json: () => new Promise((resolve, reject) => {
resolve(
{
name: "some data"
}
)
})
}))
const mountWrapper = await mount(<App />)
mountWrapper.update()
console.log("mountWrapper", mountWrapper.debug()) // showing the loader one
setTimeout(() => {
console.log("mountWrapper", mountWrapper.debug()) // nothing showing
// expect(mountWrapper.find('.toggle').length).toEqual(1)
},0)
})
})
You have to update your enzyme wrapper inside the timeout.
import React from 'react';
import {enzyme} from 'enzyme';
import App from './App';
describe("App test cases", () => {
it('should trigger _handleChildClick', async () => {
window.fetch = jest.fn().mockImplementation(() => ({
status: 200,
json: () => new Promise((resolve, reject) => {
resolve(
{
name: "some data"
}
)
})
}))
const mountWrapper = await mount(<App />)
mountWrapper.update()
console.log("mountWrapper", mountWrapper.debug()) // showing the loader one
setTimeout(() => {
//**An update required here
mountWrapper.update();
console.log("mountWrapper", mountWrapper.debug()) // nothing showing
// expect(mountWrapper.find('.toggle').length).toEqual(1)
},0)
})
})
Live example created here: https://codesandbox.io/s/5083l6vmjk

Enzyme onclick spy toHaveBeenCalled test does not work when testing on arrow function

how can i test the child component onclick.
Please see the below snippet.
// App.js
import React, {Component, Fragment} from 'react'
import Child from './child'
class App extends Component{
state = {
data: null,
enable: false
}
componentDidMount(){
this.getData()
}
getData = async () => {
const response = await fetch('http://www.example.com');
const data = await response.json();
this.setState({
data
})
}
_handleChildClick = () => {
this.setState({
enable: true
})
}
render(){
const {data, enable} = this.state
if(!data){
return (
<div>
Loading
</div>
)
}else{
<Fragment>
<Child
handleChildClick={this._handleChildClick}
/>
</Fragment>
}
}
}
export default App
import React from 'react';
const child = () => {
return(
<div>
<button
className="toggle"
onClick={props.handleChildClick}
>
Toggle
</button>
</div>
)
}
export default child
// App.test.js
import React from 'react';
import {enzyme} from 'enzyme';
import App from './App';
describe("App test cases", () => {
it('should trigger _handleChildClick', async () => {
window.fetch = jest.fn().mockImplementation(() => ({
status: 200,
json: () => new Promise((resolve, reject) => {
resolve(
{
name: "some data"
}
)
})
}))
const mountWrapper = await mount(<App />)
setTimeout(() => {
mountWrapper.update()
const SpyhandleChildClick = jest.spyOn(mountWrapper.instance(),'_handleChildClick')
mountWrapper.find('.toggle').simulate('click')
expect(SpyhandleChildClick).toHaveBeenCalled() // not called
},0)
})
})
Some important points to consider.
Asynchronous code in your tests
If you have to do asynchronous tasks in your tests you always have to await until the asynchronous stuff is completed.
setTimeout(() => {
mountWrapper.update()
const SpyhandleChildClick = jest.spyOn(mountWrapper.instance(),'_handleChildClick')
mountWrapper.find('.toggle').simulate('click')
expect(SpyhandleChildClick).toHaveBeenCalled() // not called
},0)
Above in your code you have a timeout segment. Any test condition inside this code block will not be evaluated since by the time they are evaluated you 'test session' will already be over due to the aync nature.
Testing arrow functions in React with enzyme - forceUpdate()
There seem to be a problem with the enzyme library where you have to force update the react component after spying for it to latch on to the method.
Please follow the github issue for more information : https://github.com/airbnb/enzyme/issues/365
I also cleaned up your test code a bit to make it more understandable!
// App.test.js
import React from 'react';
import {enzyme} from 'enzyme';
import App from './App';
describe("App test cases", () => {
it("should trigger _handleChildClick", async () => {
window.fetch = jest.fn().mockImplementation(() => ({
status: 200,
json: () =>
new Promise((resolve, reject) => {
resolve({
name: "some data"
});
})
}));
const mountWrapper = mount(<App />);
mountWrapper.update();
console.log("mountWrapper", mountWrapper.debug()); // showing the loader one
//[FIX]This code will block and wait for your asynchronous tasks to be completed
await new Promise(res => setTimeout(() => res(), 0));
mountWrapper.update();
console.log("mountWrapper", mountWrapper.debug()); // nothing showing
expect(mountWrapper.find(".toggle").length).toEqual(1);
//[FIX]Get a reference from the wrapper and force update after the spyOn call
const instance = mountWrapper.instance();
const spy = jest.spyOn(instance, "_handleChildClick");
instance.forceUpdate();
mountWrapper.find(".toggle").simulate("click");
expect(spy).toHaveBeenCalled();
});
});
Live Demo Link: Click on the 'Tests' tab on the browser to see the test results
https://codesandbox.io/s/mz21kpm37j

Resources