Jest: Testing API Methods from Intercom - javascript

I am having trouble understanding what the testing flow would be for testing functions which use functions loaded from a JavaScript library from Intercom.
My method looks like this:
export const generateButton = (handleOnClick) => {
case "moo":
return <button onClick={() => Intercom('show')}>Sign Up</button>
default:
return 'moo'
The error I get when running this is:
ReferenceError: Intercom is not defined

So I figured it out, I needed to add a new file and point jest set up on package.json to it like so (the file added is mockObject)
"setupFiles": [
"./config/jest/setupJest.js",
"./config/jest/mockObject.js"
],
then in the file itself has this in it
global.Intercom = () => {
console.log('Intercom called')
}

If I understand what you're trying to do then create a dummyFunction to replace Intercom in your tests. Something like this...
const Intercom = jest.fn();
describe('button click', () => {
it('Intercom is called correctly', () => {
// whatever component contains the button should be mounted
const wrapper = mount(<YourComponentHere />);
// you may need to add a class to target the specific button
wrapper.find('button').simulate('click');
expect(dummyFunction).toHaveBeenCalledWith('show');
expect(dummyFunction).toHaveBeenCalledTimes(1);
});
});

Related

ReactJS - Adding (Mollie) script to React page

I'm trying to import a script called Mollie (used for payments), but I'm not sure how to do it in React.
Normally in Javascript you would do something like this:
<html>
<head>
<title>My Checkout</title>
</head>
<body>
<script src="https://js.mollie.com/v1/mollie.js"></script>
</body>
</html>
I've tried this (according to other Stackoverflow posts)
useEffect(() => {
const script = document.createElement("script");
script.src = "https://js.mollie.com/v1/mollie.js";
script.addEventListener("load", () => setScriptLoaded(true));
document.body.appendChild(script);
}, []);
const mollie = Mollie(); // Mollie is not defined
But then Mollie is undefined. Can anyone point in the right direction on how to import Mollie in React?
I'm following this guide (but it's for standard Javascript)
You can easily install this package from npmjs.com where you can find necessary documentations and examples to get started. Installation:
npm i #mollie/api-client
the point here is that the effect is being invoked after the react component mounted and rendered to the user.
The next line where you are trying to call Mollie in fact running earlier when component is being constructed but not rendered yet.
There are multiple options what you can do about it:
Import script in the index.html file as you do for standard non-React solution. There should be a "public" folder containing this file in case of create-react-app usage or other place in case of custom project setup. The HTML should exist in any form even in case it's being generated on the server side dynamically. A mollie instance can later be created in the component or globally.
Use multiple effects in the React component: one to load Mollie and another one to use it when loaded:
// MollieExample.jsx
const MollieExample = () => {
const [mollie, setMollie] = useState();
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://js.mollie.com/v1/mollie.js';
script.addEventListener("load", () => {
setMollie(window.Mollie(/* Mollie arguments */);
});
document.body.appendChild(script);
}, []);
useEffect(() => {
if (mollie) {
console.log('Mollie exists here');
console.log(typeof mollie);
}
} , [mollie]);
return <p>typeof mollie: {typeof mollie}</p>;
};
Use dynamic script loading in case it is required with globally shared Mollie instance via custom hook:
// useMollie.js
let molliePromise;
const useMollie = (effect, deps) => {
const [mollie, setMollie] = useState();
const mollieCb = useCallback((mollie) => effect(mollie), deps);
useEffect(() => {
if (!molliePromise) {
molliePromise = new Promise((resolve) => {
const script = document.createElement("script");
script.src = "https://js.mollie.com/v1/mollie.js";
script.addEventListener("load", () => {
resolve(window.Mollie(/* Mollie arguments */);
});
document.body.appendChild(script);
});
}
molliePromise.then((mollie) => setMollie(mollie));
}, []);
useEffect(() => {
if (mollie) {
mollieCb(mollie);
}
}, [mollie, mollieCb]);
};
// MollieConsumer.jsx
const MollieConsumer = () => {
useMollie((mollie) => {
console.log('Mollie exists here');
console.log(typeof mollie);
}, [/* useMollie effect callback dependencies array */]);
return (
<p>Mollie consumer</p>
);
};
// App.jsx
function App() {
/* Both consumers use the same instance of Mollie */
return (
<div>
<MollieConsumer/>
<MollieConsumer/>
</div>
);
}
I assume you will end up with using some middle option. For instance, with importing script in the index.html (or any other sort of the HTML page you have containing the React application host element) and global hook.

Export reusable Javascript function in Cypress test spec file

I am trying to have a reusable component in my Cypress spec file, which is organized by our system 'services'. There are code pieces that I would like to make available as component for other JS files.
However, I found the 'import' statement results in running of B.js's test cases, and I cannot find a way to avoid 'Testcase B' from running.
I know there is custom commands in Cypress, but in my case, I would like to use pure JS to organize my component. Thank you.
A.js
import {functionInB as helloB } from "./B"
describe(`A`, () => {
it(`01 Testcase A`, () => {
let result = helloB()
console.log(result)
})
});
B.js
describe(`B`, () => {
it(`01 Testcase B`, () => {
//let result = Hello2()
console.log("inside hello B")
})
});
export function functionInB(){
return "Do something in functionInB"
}
There doesn't seem to be a way to stop a script running on import (or require() or dynamic import()).
In Python __main__ (docs) is used to determine if a module is called at the "top level".
if __name__ == "__main__":
# execute only if run as a script
main()
It's a bit hacky, but you can use Cypress.spec.name to do the same thing and only run B's code when it is the current spec.
if (Cypress.spec.name === 'B.js') {
describe(`B`, () => {
it(`01 Testcase B`, () => {
//let result = Hello2()
console.log("inside hello B")
})
});
}
export function functionInB(){
return "Do something in functionInB"
}
The "javascript way" would be to move functionInB() to a utility file and import in it both tests, but I guess you already know that.

Enzyme/Jest static method mock not running

I'm trying to test out an imported static method, that ought be run on button click. Here is the component, on which the test ought be run:
const NextButton = (props: ISubmitButtonProps) => (
<button
onClick={Service.goToNext}
className="col-2 submit"
type="submit"
disabled={props.submitting}>
Next
</button>
);
export default NextButton;
Now, I want to test whether the method goToNext is actually being run.
For that I've used an example from here and have added a mock looking fine, like this:
const goToNextMock = jest.fn();
jest.mock('Services/service', () => ({
default: class {
public static goToNext() {
goToNextMock(); //the jest.fn from above
}
}
})
);
And later wrote the test for it, simply simulating the click event, and then checking if the jest.fn() was run:
it('should call goToNext on button click', () => {
//button is set before as shallow(<NextButton submitting={false}/>);
button.simulate('click');
expect(goToNext).toHaveBeenCalled();
})
The problem is, reasons unknown, the test does not persist. Giving me a
Expected mock function to have been called.
EDIT: Same result would occur when attempting to mock it this way (mind the additional preventDefault()
it('should call goToNextReport on button click', () => {
const mock = jest.fn();
Service.goToNext = mock.bind(Service);
button.simulate('click', { preventDefault() {} });
expect(mock).toHaveBeenCalled();
})
I'm fairly new to JEST and js testing. It seems the mock was done properly, yet still I can't get the method to be checked.
Any ideas on the subject?
All help would be amazing!
EDIT: The issue was also submitted to the JEST github channel: https://github.com/facebook/jest/issues/8695
Try this:
jest.mock('Services/service', () => ({
goToNext: jest.fn()
}));

Webpack 4: How to call a global function with arguments?

I'm writing a new build script for my project using Webpack 4, so far, I have not faced any issue until today, when I have to call a global function with parameters.
Below is an example, I did without parameters for Google reCaptcha:
const enableFormButton = () => {
var elements = "#form_submit_btn, #form_submit_btn_alt";
$(elements).removeAttr("disabled");
$(elements).css({"cursor":"pointer"});
$(elements).removeClass("button button-3d button-red button-small").addClass("button button-3d button-green-invert button-small");
}
const recaptcha = document.querySelectorAll(".g-recaptcha");
recaptcha.forEach((captcha) => {
captcha.setAttribute('data-callback', 'enableFormButton');
});
export { enableFormButton }
and in my entry index.js file, it would look like this:
import {enableFormButton} from './some_js_file'
window.enableFormButton = enableFormButton
Now, this is what I tried with a global function with parameters:
const exampleFunction = (arg1) => {
// do something here
}
export {exampleFunction}
and in the index.js file:
import {exampleFunction} from './some_js_file'
window.exampleFunction = exampleFunction
I tried it, there are no build errors but I get an error in the console saying
"Uncaught TypeError: exampleFunction is not a function"
Any idea on how to solve this? Btw, I'm kind of new to using Webpack.
Thanks to #CertainPerformance's tip, I added the export keyword to the function exampleFunction(arg1), which should look like this:
export exampleFunction(arg1){
// something
}
Imported the function to another .js file:
import {exampleFunction} from './somescript';
And then, it worked! :)
So turns out, I learnt something for the day!

Error in running unit test for Vue webapp

I am writing a webapp with VueJs, I am trying to setup unit test for it, I got inspired from vue-mdl unit-tests. But the tests are not running properly for my code and I am getting vm.$el as undefined, so not able to move forward at all.
Here is the component, I am trying to test:
Confirmation.vue
<template>
<div>
Your order has been confirmed with the following details.
</div>
</template>
<script type="text/javascript">
export default {
data () {
return {
data_from_pg: null
}
}
}
</script>
and here is test for it, which fails
Confirmation.spec.js
import Confirmation from 'src/components/Confirmation'
import { vueTest } from '../../utils'
describe('Confirmation', () => {
let vm
let confirmation
before(() => {
vm = vueTest(Confirmation)
console.log('vm.$el ' + vm.$el) => this prints undefined
confirmation = vm.$el.querySelector('#confirmation') => so this line gives error
// confirmation = vm.$('#confirmation')
})
it('exists', () => {
confirmation.should.exist
confirmation.should.be.visible
})
})
utils.js
export function vueTest (Component) {
const Class = Vue.extend(Component)
Class.prototype.$ = function (selector) {
return this.$el.querySelector(selector)
}
Class.prototype.nextTick = function () {
return new Promise((resolve) => {
this.$nextTick(resolve)
})
}
const vm = new Class({
replace: false,
el: 'body'
})
return vm
}
My complete code is available here, with all the test config, which I have tried to change many times, but could not figure out how to make it work. Please let me know if you see some error somewhere.
The vueTest function in utils is trying to load the Vue instance into the body tag:
const vm = new Class({
replace: false,
el: 'body'
})
return vm
The unit tests do not load index.html as an entry point into the app, but rather the individual components that you want to test; Therefore, you do not have access to document or html elements and the component is never mounted. I'd suggest using vm.$mount():
If elementOrSelector argument is not provided, the template will be rendered as an off-document element.
You could change the above lines to something like the following
const vm = new Class();
vm.$mount();
return vm;
Your tests should now have access to the $el property.

Categories

Resources