How to simulate an Appearance ("dark mode") change in React Native using Jest? - react-native

I am writing a puzzle app in React Native where the user can solve a puzzle by changing their device's Appearance to dark mode. I'm using React Native's Appearance module to accomplish this, by calling Appearance.getColorScheme() to get the initial color scheme, and registering an event listener for when the color scheme changes using Appearance.addChangeListener() within the useEffect hook:
useEffect(() => {
const originalColorScheme = Appearance.getColorScheme();
Appearance.addChangeListener((event) => {
if (Appearance.getColorScheme() !== originalColorScheme) {
setSolved(true);
}
});
}, []);
This is working fine, but I'd like to be able to unit test my component by simulating a change to dark mode in Jest and/or React Native Testing Library (or similar), after which I can check for changes to the content of the page (a "congratulations" message, in this case).
In my mind, it could look something like:
const text = await screen.findByText("good luck!");
// fireEvent("changeColorScheme", { colorScheme: "dark" });
const text2 = await screen.findByText("congratulations!");
Is anything like this possible? I'm guessing it has something to do with mocks, but it's a little over my head at the moment. Thanks for any help you can provide!

Related

onWillFocus React Native v6 Replacement?

I want to clear errorMessage, when we navigate from Sign in to Sign up and vice-versa, for that I tried using onWillFocus but it has been deprecated.
Can anyone suggest me the replacement of onWillFocus?
You can use the original useEffect hook prescribed which gets triggered when a screen is loaded and when a state changes. You can either pass [] parameters so it only runs once or you can use a state to check it.
useEffect(() => {
// do you work here
}, []);

How to use setTimeout in react native?

Hi I working on a react native project I need to support my app offline.
I used NetInfo Library from expo documentation.
like below
const netInfo = useNetInfo();
const networkCheck = () => {
setTimeout(() => {
const net = netInfo.isInternetReachable;
console.log(net);
if (net === false) {
setloadCachedListings(true);
}
}, 2000);
};
networkCheck();
I want to wait for some time at this step of my code because this hook always returns null first time then after few milliseconds it tells the real network status.
But this code is not working as I an trying to do.
Is there any way to achieve this?
I just want to wait a little bit to get real network connection and then go further with my code.
logs for netInfo hook.

How to detect screenshots with React Native (both Android and iOS)

I am trying to detect if a user takes a screenshot while using a smartphone app that I have built. I am building my project with React Native.
I have been told that I can possibly prevent screenshots for Android but not for iOS. but can I still detect whether a user attempts to take a screenshot so that I can at least send a warning via Alert?
Thanks in advance
I tried react-native-screenshot-detector but it did not work
you can use this package it supports android&ios screenshot detecting react-native-detector
import {
addScreenshotListener,
removeScreenshotListener,
} from 'react-native-detector';
// ...
React.useEffect(() => {
const userDidScreenshot = () => {
console.log('User took screenshot');
};
const listener = addScreenshotListener(userDidScreenshot);
return () => {
removeScreenshotListener(listener);
};
}, []);
There is no package for it currently.

React Native startup optimization

I am looking for a way to optimize the startup time of a pure react native mobile app.
As a JavaScript framework, is that possible to bundle the JavaScript files into separated files, say something like common.js and app.js. I was searching via Google with keywords something like "react native webpack" stuff but it seems like all these libraries are deprecated or out of date, such as react-native-webpack-server, react-native-webpack-starter-kit etc.
I am wondering if anybody here is also looking for a way to optimize the JavaScript bundle in react native. Or, maybe these third party bundle approach has been overcame by Facebook standard bundle?
You could dynamically load your component, in this way your bundle.js will contain only the fraction of js needed and as you navigate you will request the other different parts / fractions.
Rather than do the traditional way: import App from './containers/App/App'; you could do something like this:
class ImportedComponent extends Component {
state = {
component: null
}
componentWillMount() {
this.props.load()
.then((mod) => this.setState(() => ({
component: mod.default
})))
}
render() {
return this.props.children(this.state.component)
}
}
const App = (props) => (
<ImportedComponent load={() => import('./containers/App/App')}>
{(Component) => Component === null ? <h6 className="loading-message">Loading...</h6> : <Component {...props}/>}
</ImportedComponent>
)
or you can lazy load your component itself. Let's say for example that I have Moment JS and I don't want to load it until it's needed. so what I could do:
1) Create a state and set it to null.
constructor(props){
super(props);
this.state = {
lazyLoadedComponent: () => null
}
}
2) Use async componentDidMount with await, try and catch and update the state lazyLoadedComponent on componentDidMount
async componentDidMount(){
try {
const Moment = await import('react-moment');
this.setState({ lazyLoadedComponent: (data)=>{
return React.createElement(Moment.default, {format:'MM/DD/YY'}, data)
}
});
}
catch(err) {
this.setState({ lazyLoadedComponent: <div>{`Failed to load component: ${err}`}</div> });
}
}
3) Call the component on the render:
{this.state.lazyLoadedComponent(value.createdOn)}
By following these 2 examples you should, hopefully, be looking at a bundle.js under 250KB.
As a possible solution you can use ram-bundle format, that metro bundler provides.
In this case you will not load the entire js-bundle - you will load only part, that you need at a startup (in a lot of application are a lot of places, which user may not even see, and this feature allow you load such parts, only when they are required). So you can simplify your entry point and load only small piece of your bundle.
You can look at react-native-bundle-splitter. This library well integrated with almost all popular navigation libraries and allows you to postpone a loading of specific routes. For example, if you have a login screen, you can load at start up only this screen, and all others load in background or start the loading of them, only when user can see them.

How to use enzyme for react-native with jest

I have followed –or tried to– several posts on how to do it, including the airbnb enzyme's guide for (separatedly) react-native and jest. (E.g: https://medium.com/#childsmaidment/testing-react-native-components-with-enzyme-d46bf735540#.6sxq10kgt, https://blog.callstack.io/unit-testing-react-native-with-the-new-jest-i-snapshots-come-into-play-68ba19b1b9fe#.4iqylmqh5 or How to use Jest with React Native)
But I keep getting lots of warnings (I have multiple set of concurrent tests) whenever I try to render (not mount, it crashes) any native component. Warnings are always about a native prop not being recognised.
Warning: Unknown props `focus`, `secureTextEntry` on <TextInput> tag. Remove these props from the element.
in TextInput (created by TextInput)
in TextInput (created by PasswordInput)
Anyone who has a set up working, recognises how to remove the warning or how to solve it?
Thanks
So I know this is a bit old but I was having issues with Jest, Enzyme, and React Native and I found this post - hopefully this solution will help.
To start with - Enzyme doesn't support mounting React Native and only supports shallow rendering. This wasn't good enough for me as I needed end-to-end tests from the component to the api which lead me to react-native-mock-render. What this does is allow us to run react native inside a browser environment which let's us test using Enzyme - all the calls for React Native and the components work as you would expect.
To set this up you'll need to install JSDOM, react-native-mock-render, Enzyme 3.0+, and Jest 20.0.0+. And then inside your jest setup file (which is specified in your package.json) include the following code:
const { JSDOM } = require('jsdom');
const jsdom = new JSDOM();
const { window } = jsdom;
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === 'undefined')
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
Object.defineProperties(target, props);
}
global.window = window;
global.document = window.document;
global.navigator = {
userAgent: 'node.js',
};
copyProps(window, global);
// Setup adapter to work with enzyme 3.2.0
const Enzyme = require('enzyme');
const Adapter = require('enzyme-adapter-react-16');
Enzyme.configure({ adapter: new Adapter() });
// Ignore React Web errors when using React Native
console.error = (message) => {
return message;
};
require('react-native-mock-render/mock');
And that's it - you're all setup to mount components in Enzyme and test them.
If you want to see a full sample check out react-native-mock-render-example. This is working with React 16, React Native 0.51, and Enzyme 3.2.
In order to unit test your component with jest you can use enzyme-to-json
npm install --save enzyme-to-json
then your test would look like this:
import { shallow } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
import MyComponent from './MyComponent';
it('should render component', => {
expect(shallowToJson(shallow(<MyComponent />))).toMatchSnapshot();
});
I'm not sure regarding your case with react-native.
I can share my case of using jest + enzyme with standard react.
When I needed to test some component and isolate it from others I used jest.mock, e.g.
jest.mock('../ComponentToBeMocked', () => {
return () => null;
});
Initially I found examples when the second argument (a function) should return just a string representing a name of the mocked component. But in that case I saw that distracting Unknown props warning.