Testing scrollintoview Jest - reactjs

I have a simple function:
scrollToSecondPart() {
this.nextPartRef.current.scrollIntoView({ behavior: "smooth" });
}
and I would like to test it using Jest. When I call my function I have this error:
TypeError: Cannot read property 'scrollIntoView' of null
The code works great in the application but not in the unit test.
Here is the unit test:
it("should scroll to the second block", () => {
const scrollToSecondPart = jest.spyOn(LandingPage.prototype, 'scrollToSecondPart');
const wrapper = shallow(<LandingPage />);
const instance = wrapper.instance();
instance.scrollToSecondPart();
expect(scrollToSecondPart).toHaveBeenCalled();
});
I guess the problem is that the unit test can't access to this.nextPartRef but I don't know how I should mock this element.
By the way, I'm using "Refs" has described in https://reactjs.org/docs/refs-and-the-dom.html (I'm using React.createRef()).
Thank you!

So I stumbled on this question because I thought it was about how to test Element.scrollIntoView(), which can be done by mocking it with jest as follows:
let scrollIntoViewMock = jest.fn();
window.HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
<execute application code that triggers scrollIntoView>
expect(scrollIntoViewMock).toBeCalled();
However, that does not seem to be your goal. In your test you are calling scrollToSecondPart() in your test and then expect(scrollToSecondPart).toHaveBeenCalled() which is essentially testing that scrollToSecondPart is a function of LandingPage or am I missing something?

for me i had to mock the scrollIntoView for elements like this:
const scrollIntoViewMock = jest.fn();
Element.prototype.scrollIntoView = scrollIntoViewMock()
as found here:
https://github.com/jsdom/jsdom/issues/1695#issuecomment-449931788

Related

Enzyme shallow test on custom component

I have this test:
it('renders', () => {
wrapper = shallow(<CustomComponent {...props} />);
expect(wrapper.length).toBeGreaterThan(0);
expect(wrapper.text()).toContain('Text example');
});
I had to change on the code the string 'Text example' with a custom component:
<Custom resourceKey='Text example'/>
so now the second expect won't work, I don't have much experience with enzyme testing, I would check the text ignoring the Custom tag and that's where I got lost, I tried many different properties, but still can't find the correct one.
The closets I got was this:
expect(wrapper.children().text()).toContain('Text example');
But still not working.
Can anyone help?
Thanks

How to debug Jest unit test for a debounced function

I have the following function:
myFunction = _.debounce((viewport) => {
...
otherFunction();
...
someOtherFunction();
...
}, 650)
I'm trying to use jest to assert whether the otherFunction and someOtherFunction were run. In order to do this, I wanted to use WebStorm's debugger to check the function flow.
I've tried mocking the _.debounce:
_.debounce = jest.fn((fn) => fn);
Even though I tried adding a spy on the myFunction and jest says that the function ran, I can't make the debugger stop inside the function.
If I try to remove the _.debounce function, the debugger stops successfully.
Is there any way to make this work?
I ended up moving my function logic to a new function: myFunctionNoDebounce and I kept the old myFunction only as a debounce wrapper. This way, I can test the myFunctionNoDebounce and I don't really need to test the myFunction.
This is certainly not the most elegant solution, but it works for now. I'm open to more suggestions to solve this problem in a better way.
myFunctionNoDebounce = (viewport) => {
...
otherFunction();
...
someOtherFunction();
...
}
myFunction = _.debounce((viewport) => {
this.myFunctionNoDebounce(viewport)
}, 650)
This is untested, but I believe you would have to execute the function in your mock implementation:
_.debounce = jest.fn(fn => fn());

Modal Enzyme mount unit test: MutationObserver is not defined [duplicate]

I wrote a script with the main purpose of adding new elements to some table's cells.
The test is done with something like that:
document.body.innerHTML = `
<body>
<div id="${containerID}">
<table>
<tr id="meta-1"><td> </td></tr>
<tr id="meta-2"><td> </td></tr>
<tr id="meta-3"><td> </td></tr>
<tr id="no-meta-1"><td> </td></tr>
</table>
</div>
</body>
`;
const element = document.querySelector(`#${containerID}`);
const subject = new WPMLCFInfoHelper(containerID);
subject.addInfo();
expect(mockWPMLCFInfoInit).toHaveBeenCalledTimes(3);
mockWPMLCFInfoInit, when called, is what tells me that the element has been added to the cell.
Part of the code is using MutationObserver to call again mockWPMLCFInfoInit when a new row is added to a table:
new MutationObserver((mutations) => {
mutations.map((mutation) => {
mutation.addedNodes && Array.from(mutation.addedNodes).filter((node) => {
console.log('New row added');
return node.tagName.toLowerCase() === 'tr';
}).map((element) => WPMLCFInfoHelper.addInfo(element))
});
}).observe(metasTable, {
subtree: true,
childList: true
});
WPMLCFInfoHelper.addInfo is the real version of mockWPMLCFInfoInit (which is a mocked method, of course).
From the above test, if add something like that...
const table = element.querySelector(`table`);
var row = table.insertRow(0);
console.log('New row added'); never gets called.
To be sure, I've also tried adding the required cells in the new row.
Of course, a manual test is telling me that the code works.
Searching around, my understanding is that MutationObserver is not supported and there is no plan to support it.
Fair enough, but in this case, how can I test this part of my code? Except manually, that is :)
I know I'm late to the party here, but in my jest setup file, I simply added the following mock MutationObserver class.
global.MutationObserver = class {
constructor(callback) {}
disconnect() {}
observe(element, initObject) {}
};
This obviously won't allow you to test that the observer does what you want, but will allow the rest of your code's tests to run which is the path to a working solution.
I think a fair portion of the solution is just a mindset shift. Unit tests shouldn't determine whether MutationObserver is working properly. Assume that it is, and mock the pieces of it that your code leverages.
Simply extract your callback function so it can be tested independently; then, mock MutationObserver (as in samuraiseoul's answer) to prevent errors. Pass a mocked MutationRecord list to your callback and test that the outcome is expected.
That said, using Jest mock functions to mock MutationObserver and its observe() and disconnect() methods would at least allow you to check the number of MutationObserver instances that have been created and whether the methods have been called at expected times.
const mutationObserverMock = jest.fn(function MutationObserver(callback) {
this.observe = jest.fn();
this.disconnect = jest.fn();
// Optionally add a trigger() method to manually trigger a change
this.trigger = (mockedMutationsList) => {
callback(mockedMutationsList, this);
};
});
global.MutationObserver = mutationObserverMock;
it('your test case', () => {
// after new MutationObserver() is called in your code
expect(mutationObserverMock.mock.instances).toBe(1);
const [observerInstance] = mutationObserverMock.mock.instances;
expect(observerInstance.observe).toHaveBeenCalledTimes(1);
});
The problem is actually appears because of JSDom doesn't support MutationObserver, so you have to provide an appropriate polyfill.
Little tricky thought may not the best solution (let's use library intend for compatibility with IE9-10).
you can take opensource project like this one https://github.com/webmodules/mutation-observer which represents similar logic
import to your test file and make global
Step 1 (install this library to devDependencies)
npm install --save-dev mutation-observer
Step 2 (Import and make global)
import MutationObserver from 'mutation-observer'
global.MutationObserver = MutationObserver
test('your test case', () => {
...
})
You can use mutationobserver-shim.
Add this in setup.js
import "mutationobserver-shim"
and install
npm i -D mutationobserver-shim
Since it's not mentioned here: jsdom has supported MutationObserver for a while now.
Here's the PR implementing it https://github.com/jsdom/jsdom/pull/2398
This is a typescript rewrite of Matt's answer above.
// Test setup
const mutationObserverMock = jest
.fn<MutationObserver, [MutationCallback]>()
.mockImplementation(() => {
return {
observe: jest.fn(),
disconnect: jest.fn(),
takeRecords: jest.fn(),
};
});
global.MutationObserver = mutationObserverMock;
// Usage
new MutationObserver(() => {
console.log("lol");
}).observe(document, {});
// Test
const observerCb = mutationObserverMock.mock.calls[0][0];
observerCb([], mutationObserverMock.mock.instances[0]);
Addition for TypeScript users:
declare the module with adding a file called: mutation-observer.d.ts
/// <reference path="../../node_modules/mutation-observer" />
declare module "mutation-observer";
Then in your jest file.
import MutationObserver from 'mutation-observer'
(global as any).MutationObserver = MutationObserver
Recently I had a similar problem, where I wanted to assert on something that should be set by MutationObserver and I think I found fairly simple solution.
I made my test method async and added await new Promise(process.nextTick); just before my assertion. It puts the new promise at the end on microtask queue and holds the test execution until it is resolved. This allows for the MutationObserver callback, which was put on the microtask queue before our promise, to be executed and make changes that we expect.
So in general the test should look somewhat like this:
it('my test', async () => {
somethingThatTriggersMutationObserver();
await new Promise(process.nextTick);
expect(mock).toHaveBeenCalledTimes(3);
});

How to test if canvas is filled with given colour?

I have a React component which is rendering canvas element.
Inside of this component I have this method:
renderCanvas(canvas) {
canvas.fillStyle = this.props.given_colour;
canvas.fillRect(0, 0, 800, 800);
}
...which is used for creating a coloured layer.
So I have tests for calling functions in my component and for props, and they're working fine, but now I want to have a scenario checking if method above is using proper colour.
For tests I am using jest with enzyme and sinon.
I have prepared this scenario:
it("calls to fill canvas with the given colour", () => {
const canvasSpy = sinon.spy();
const component = mount(<MyCanvas given_colour="#0000FF" />);
component.instance().renderCanvas(canvasSpy);
sinon.assert.calledWith(canvasSpy, "fillStyle");
sinon.assert.calledWith(canvasSpy, "#0000FF");
sinon.restore();
});
but I am getting TypeError: canvas.fillRect is not a function
I don't know why this is happening and I am not sure about my approach to this scenario in general.
My expectations are to have a scenario which will tell me that my component, in this specific method, is using given colour. But I have no idea how to achieve that.
I'm a Rails dev and I'm new to React. Please, could someone point me what am I doing wrong with this test?
renderCanvas expects canvas to be an object that has methods, while it's a function. Assertions are wrong because canvasSpy is asserted to be called with fillStyle, while it isn't.
It likely should be:
const canvasMock = { fillRect: sinon.spy() };
component.instance().renderCanvas(canvasMock);
expect(canvasMock.fillStyle).to.be("#0000FF");
sinon.assert.calledWith(canvasMock.fillRect, 0, 0, 800, 800);

How do you test for the non-existence of an element using jest and react-testing-library?

I have a component library that I'm writing unit tests for using Jest and react-testing-library. Based on certain props or events I want to verify that certain elements aren't being rendered.
getByText, getByTestId, etc throw and error in react-testing-library if the element isn't found causing the test to fail before the expect function fires.
How do you test for something not existing in jest using react-testing-library?
From DOM Testing-library Docs - Appearance and Disappearance
Asserting elements are not present
The standard getBy methods throw an error when they can't find an element, so
if you want to make an assertion that an element is not present in the DOM,
you can use queryBy APIs instead:
const submitButton = screen.queryByText('submit')
expect(submitButton).toBeNull() // it doesn't exist
The queryAll APIs version return an array of matching nodes. The length of the
array can be useful for assertions after elements are added or removed from the
DOM.
const submitButtons = screen.queryAllByText('submit')
expect(submitButtons).toHaveLength(2) // expect 2 elements
not.toBeInTheDocument
The jest-dom utility library provides the
.toBeInTheDocument() matcher, which can be used to assert that an element is
in the body of the document, or not. This can be more meaningful than asserting
a query result is null.
import '#testing-library/jest-dom/extend-expect'
// use `queryBy` to avoid throwing an error with `getBy`
const submitButton = screen.queryByText('submit')
expect(submitButton).not.toBeInTheDocument()
Use queryBy / queryAllBy.
As you say, getBy* and getAllBy* throw an error if nothing is found.
However, the equivalent methods queryBy* and queryAllBy* instead return null or []:
queryBy
queryBy* queries return the first matching node for a query, and return null if no elements match. This is useful for asserting an element that is not present. This throws if more than one match is found (use queryAllBy instead).
queryAllBy
queryAllBy* queries return an array of all matching nodes for a query, and return an empty array ([]) if no elements match.
https://testing-library.com/docs/dom-testing-library/api-queries#queryby
So for the specific two you mentioned, you'd instead use queryByText and queryByTestId, but these work for all queries, not just those two.
getBy* throws an error when not finding an elements, so you can check for that
expect(() => getByText('your text')).toThrow('Unable to find an element');
const submitButton = screen.queryByText('submit')
expect(submitButton).toBeNull() // it doesn't exist
expect(submitButton).not.toBeNull() // it exist
You have to use queryByTestId instead of getByTestId.
Here a code example where i want to test if the component with "car" id isn't existing.
describe('And there is no car', () => {
it('Should not display car mark', () => {
const props = {
...defaultProps,
base: null,
}
const { queryByTestId } = render(
<IntlProvider locale="fr" messages={fr}>
<CarContainer{...props} />
</IntlProvider>,
);
expect(queryByTestId(/car/)).toBeNull();
});
});
Worked out for me (if you want to use getByTestId):
expect(() => getByTestId('time-label')).toThrow()
Hope this will be helpfull
this table shows why/when function errors
which functions are asynchronous
what is return statement for function
Another solution: you could also use a try/catch block
expect.assertions(1)
try {
// if the element is found, the following expect will fail the test
expect(getByTestId('your-test-id')).not.toBeVisible();
} catch (error) {
// otherwise, the expect will throw, and the following expect will pass the test
expect(true).toBeTruthy();
}
You can use react-native-testing-library "getAllByType" and then check to see if the component is null. Has the advantage of not having to set TestID, also should work with third party components
it('should contain Customer component', () => {
const component = render(<Details/>);
const customerComponent = component.getAllByType(Customer);
expect(customerComponent).not.toBeNull();
});
// check if modal can be open
const openModalBtn = await screen.findByTestId("open-modal-btn");
fireEvent.click(openModalBtn);
expect(
await screen.findByTestId(`title-modal`)
).toBeInTheDocument();
// check if modal can be close
const closeModalBtn = await screen.findByTestId(
"close-modal-btn"
);
fireEvent.click(closeModalBtn);
const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
await sleep(500);
expect(screen.queryByTestId("title-modal")).toBeNull();
I recently wrote a method to check visibility of element for a jest cucumber project.
Hope it is useful.
public async checknotVisibility(page:Page,location:string) :Promise<void>
{
const element = await page.waitForSelector(location);
expect(element).not.toBe(location);
}
don't want to bury the lead, so here's the right solution ✅
waitFor(() => queryByTestId(/car/) === null)
There are issues with all of the answers here so far...
don't use getByTestId, that'll have to wait 😴 for the timeout because it's expecting the element to eventually be there. Then it'll throw and you'll have to catch that, which is a less readable test. Finally you could have a RACE CONDITION 🚫 where getByTestId is evaluated before the element disappears and our test will flake.
Just using queryByTestId without waitFor is a problem if the page is changing at all and the element has not disappeared yet. RACE CONDITION 🚫
deleteCarButton.click();
expect(queryByTestId(/car/)).toBeNull(); //
if expect() gets evaluated before the click handler and render completes we'll have a bad time.
The default behavior of queryByRole is to find exactly one element. If not, it throws an error. So if you catch an error, this means the current query finds 0 element
expect(
()=>screen.getByRole('button')
).toThrow()
getByRole returns 'null', if it does not find anthing
expect(screen.queryByRole('button')).toEqual((null))
findByRole runs asynchronously, so it returns a Promise. If it does not find an element, it rejects the promise. If you are using this, you need to run async callback
test("testing", async () => {
let nonExist = false;
try {
await screen.findByRole("button");
} catch (error) {
nonExist = true;
}
expect(nonExist).toEqual(true);
});

Resources