A component is importing a library that includes a native module. Here is a contrived example:
import React from 'react';
import { View } from 'react-native';
import { Answers } from 'react-native-fabric';
export default function MyTouchComponent({ params }) {
return <View onPress={() => { Answers.logContentView() }} />
}
And here is the relevant part of Answers from react-native-fabric:
var { NativeModules, Platform } = require('react-native');
var SMXAnswers = NativeModules.SMXAnswers;
When importing this component in a mocha test, this fails on account that SMXAnswers is undefined:
How do you mock SMXAnswers or react-native-fabric so that it doesn't break and allows you to test your components?
p.s.: you can see the full setup and the component I'm trying to test on GitHub.
Use mockery to mock any native modules like so:
import mockery from 'mockery';
mockery.enable();
mockery.warnOnUnregistered(false);
mockery.registerMock('react-native-fabric', {
Crashlytics: {
crash: () => {},
},
});
Here is a complete setup example:
import 'core-js/fn/object/values';
import 'react-native-mock/mock';
import mockery from 'mockery';
import fs from 'fs';
import path from 'path';
import register from 'babel-core/register';
mockery.enable();
mockery.warnOnUnregistered(false);
mockery.registerMock('react-native-fabric', {
Crashlytics: {
crash: () => {},
},
});
const modulesToCompile = [
'react-native',
].map((moduleName) => new RegExp(`/node_modules/${moduleName}`));
const rcPath = path.join(__dirname, '..', '.babelrc');
const source = fs.readFileSync(rcPath).toString();
const config = JSON.parse(source);
config.ignore = function(filename) {
if (!(/\/node_modules\//).test(filename)) {
return false;
} else {
const matches = modulesToCompile.filter((regex) => regex.test(filename));
const shouldIgnore = matches.length === 0;
return shouldIgnore;
}
}
register(config);
Related
Learning JEST and unit testing in Javascript, but have errors when testing. There's an error
Invalid hook call. Hooks can only be called inside of the body of a
function component
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
I believe that the error comes when the use of renderInTestApp.
import * as React from 'react';
import { render, RenderResult, screen } from '#testing-library/react';
import ReactDom from 'react-dom'
import { EntityJiraOverviewCard, isJiraAvailable } from '#roadiehq/backstage-plugin-jira';
import { permissionApiRef } from '#backstage/plugin-permission-react';
import { overviewContent } from './EntityPage';
import { renderWithEffects } from '#backstage/test-utils';
import {
MockPermissionApi,
renderInTestApp,
TestApiProvider,
} from '#backstage/test-utils';
import {
EntityProvider,
starredEntitiesApiRef,
MockStarredEntitiesApi,
} from '#backstage/plugin-catalog-react';
import { EntityLayout } from '#backstage/plugin-catalog';
describe('Developer Portal Plugin', () => {
const jira = {
apiVersion: 'vi',
kind: 'Component',
metadata: {
name: 'TestPlugin',
annotations: {
'jira/project-key': 'DET',
},
},
spec:{
'type': 'service',
'lifecycle' : 'production',
'owner' : 'test',
},
};
const mockedApi = {
listWorkflowRuns: jest.fn().mockResolvedValue([]),
};
const mockPermissionApi = new MockPermissionApi();
describe('jiraContent', () => {
it('Should render Jira View', async () => {
const rendered = await renderInTestApp(
<TestApiProvider
apis={[
[isJiraAvailable, mockedApi],
[starredEntitiesApiRef, new MockStarredEntitiesApi()],
[permissionApiRef, mockPermissionApi],
]}
>
<EntityProvider entity={jira}>
<EntityLayout>
<EntityLayout.Route path="/" title="Overview">
{overviewContent}
</EntityLayout.Route>
</EntityLayout>
</EntityProvider>
</TestApiProvider>,
);
expect(rendered.getByText('TestPlugin')).toBeInTheDocument();
await expect(
rendered.findByText('Devops Engineering Team | software'),
).resolves.toBeInTheDocument();
expect(rendered.getByText('Activity stream')).toBeInTheDocument();
});
});
})
When I write a react hook component which import '*.png' resource like this:
import React, { useState, useEffect } from 'react';
import './topMenu.less';
import intl from 'react-intl-universal';
import { Breadcrumb } from 'antd';
import home from '#/image/project/home.png';
const TopMenu: React.FC<{}> = () => {
const [page, setPage] = useState<string>('overview');
const [projectName, setProjectName] = useState<string>('');
useEffect(() => {
let hashUrl = window.location.hash;
let tempPage = hashUrl.split('/')[3];
let projectNameStr = sessionStorage.getItem('projectName');
setPage(tempPage);
setProjectName(projectNameStr ? projectNameStr : '');
}, []);
/**
* 功能描述 依据页面路由渲染面包屑组件的尾部文字
*
* #return {string} 面包屑的末级页面名称
*/
function getPageName(): string {
switch(page) {
case 'overview':
return intl.get('PROJECT_OVERVIEW');
case 'inference':
return intl.get('ROOT_CAUSE_INFERENCE');
case 'bigModel':
return intl.get('BIG_MODEL_DIAGNOSTICS');
default:
return intl.get('PROJECT_OVERVIEW');
}
}
return (<div className="slider-menu">
<Breadcrumb>
<Breadcrumb.Item href="#/kgom/packageList">
<img src={home} />
<span>{intl.get('PROJECT_LIST')}</span>
</Breadcrumb.Item>
<Breadcrumb.Item >
<span>{projectName}</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>{getPageName()}</span>
</Breadcrumb.Item>
</Breadcrumb>
</div>);
};
export default TopMenu;
then i write a easy test code like this:
import TopMenu from './topMenu';
import React from 'react';
import renderer from 'react-test-renderer';
jest.mock('#/image/project/home.png', () => '../image/project/home.png');
describe('TopMenu Test', () => {
it('render TopMenu', () => {
const component = renderer.create(<TopMenu />);
expect(component.toJSON()).toMatchSnapshot();
});
});
when i run npm test, it calls this error:
enter image description here
espicially this line: jest.mock('#/image/project/home.png', () => '../image/project/home.png');
I try many ways to rewrite this line, but it always error, when i not write this line, it calls that error:
enter image description here
how do i write this hook component jest render test???
In my React Native application, i am trying to add a component where i'll perform some config tasks but that component won't render anything. I have made the component already but when i import that on App.tsx the fucntion doesn't get called. How to import this following component properly to App.tsx. The component is given below:
var androidVersion = VersionInfo.appVersion;
var iosVersion = VersionInfo.buildVersion;
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const Config = () => {
console.log('check if get called >>',showVersionCodeStatus);
const prevAmount = usePrevious(iosVersion);
useEffect(() => {
if(prevAmount.iosVersion !== iosVersion) {
// process here
}
if(prevAmount.androidVersion !== androidVersion) {
// process here
}
}, [iosVersion,androidVersion])
// return {showVersionCodeStatus};
}
export default Config;
i'm importing the component in my App.tsx like the following:
import './config';
But it doesn't call the Config function. I have tried the following too:
import Config from './config';
That doesn't seem to work too. What am i doing wrong?
Since Config does not render anything, you should export it as a custom hook with a name such as useConfig. Subsequently you can import and use your custom hook in App.tsx, which will then run the config tasks specified in your useConfig custom hook.
useConfig.ts
var androidVersion = VersionInfo.appVersion;
var iosVersion = VersionInfo.buildVersion;
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
const useConfig = () => {
console.log('check if get called >>',showVersionCodeStatus);
const prevAmount = usePrevious(iosVersion);
useEffect(() => {
if(prevAmount.iosVersion !== iosVersion) {
// process here
}
if(prevAmount.androidVersion !== androidVersion) {
// process here
}
}, [iosVersion,androidVersion])
// return {showVersionCodeStatus};
}
export default useConfig;
App.tsx
import useConfig from "./useConfig";
export default function App() {
const config = useConfig();
return (
...
);
}
I am trying to write some unit tests and I am getting errors in the test and I am trying to understand why the errors happen.
The unit test is for the index.ts file that calls the features/index.ts file. I am stubbing the default export from features/index.ts with sinon. But when I run the tests I get the following error TypeError: Cannot read property 'resolve' of undefined pointing at the file features/feature1.ts
I have added the relavant extracts from the tests and typescript files below.
features/feature1.ts
import path from "path";
import fs from "fs";
import {Setup} from "../types";
const TEMPLATE_ROOT = path.resolve(__dirname,"../../templates");
const INDEX_TEMPLATE = fs.readFileSync(TEMPLATE_ROOT, "index.js"), "utf8");
export const setup: Setup = async ({config, options}) => {
// Internal code removed
}
features/index.ts
import {setup as feature1} from "./feature1.ts";
import {setup as feature2} from "./feature2.ts";
type FeatureTypes = "feature1" | "feature2"
type Features = {
[key in FeatureTypes]: Setup;
};
const features: Features = {
feature1: feature1,
feature2: feature2
}
export default features
index.ts
import features from "./features"
import { Config, Options } from "./types";
export async function init(config: Config, options: Options): Promise<void> {
const nextFeature = options.features ? options.features.shift() : undefined;
if (nextFeature) {
// Other irrelevant code
await Promise.resolve(features[nextFeature]({ config, options }));
return init(config, options);
}
}
index.spec.ts
import { expect } from "chai";
import * as sinon from "sinon";
import { init } from '.'
import * as features from "./features";
import { Config, Options } from "./types"
describe("init", () => {
const sandbox: sinon.SinonSandbox = sinon.createSandbox();
let featuresStub: sinon.SinonStub;
beforeEach(() => {
featuresStub = sandbox.stub(features, "default").returns({
feature1: sandbox.stub().resolves(),
feature2: sandbox.stub().resolves(),
});
});
afterEach(() => {
sandbox.restore();
});
it("should call setup features", async () => {
const setup: Setup = {
features: [
"feature1",
"feature2",
],
};
await init({}, options);
expect(featuresStub).to.have.been.calledOnce;
});
// rest of tests
});
I have also tried the changing the stub setup to be:
import * as feature1 from ".features/feature1";
import * as feature2 from ".features/feature2";
// Other code
describe("init", () => {
const sandbox: sinon.SinonSandbox = sinon.createSandbox();
let feature1Stub: sinon.SinonStub;
let feature2Stub: sinon.SinonStub;
beforeEach(() => {
feature1Stub = sandbox.stub(feature1, "setup");
feature2Stub = sandbox.stub(feature2, "setup");
feature1Stub.resolves()
feature2Stub.resolves()
});
// Rest of code and tests
});
I don't know why it would be trying to run code const TEMPLATE_ROOT = path.resolve(__dirname,"../../templates"); if I have stubbed the function that calls it.
Figured it out the imports were wrong
import path from "path";
import fs from "fs";
should be:
import * as path from "path";
import * as fs from "fs";
I am trying to write test scripts using mocha js for my react code. The test helper file is like this
browser.js
var baseDOM = '<!DOCTYPE html><html><head><meta charset="utf-8"></head><body></body></html>';
var jsdom = require('jsdom').jsdom;
global.document = jsdom(baseDOM);
global.window = document.defaultView;
if ( global.self != null) {
console.log(' global.self >>>>> ' + global.self);
} else {
global.self = global.this;
}
global.navigator = {
userAgent: 'node.js'
};
The test script is like this (component.specs.js)
'use strict';
import ReactDOM from 'react-dom';
import React from 'react';
import { expect } from 'chai';
import { mount, shallow, render } from 'enzyme';
import { App } from '../../js/app.js';
import sinon from 'sinon';
var dispatch = function() {
`enter code here` console.log('>>>>>>>> Mocking dispatch ');
};
describe('App', function () {
it('App calls check', () => {
sinon.spy(App.prototype, 'check');
const enzymeWrapper = mount(<App {...props} />);
expect(App.prototype.check.calledOnce).to.equal(true);
});
So, when I run npm test, I get the error as 'jsdom is not a function'. What is wrong with the code?
I'm not familiar with jsdom but based on the examples I don't think you're using it correctly. jsdom's example code:
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
What this means for you is that var jsdom = require('jsdom').jsdom; should be var jsdom = require('jsdom').JSDOM;. The example also uses new.
That said, you may want to check out jest for testing node/react - it's very easy to setup and works well.