I have several React tests using Jest and fetch-mock, each one of them doing some get operations, so what I initially did was:
beforeAll(){
fetchMock.get(`*`, JSON.stringify(CORRECTRESPONSE));
}
However, in some tests I need to return wrong data as answer, something like:
test('Wrong get answer', ()=> {
fetchMock.get('*', JSON.stringify(WRONGRESPONSE), {overwriteRoutes: true});
}));
So, since I need to reset the response for the following tests (and so return CORRECTRESPONSE, I came up with this solution:
beforeEach(){
fetchMock.get(`*`, JSON.stringify(CORRECTRESPONSE));
}
afterEach(fetchMock.restore);
Is there anyway better to do this?
I don't understand why the previous answer (https://stackoverflow.com/a/49778196/689944) was set as correct. Cleanup up after tests by afterEach(fetchMock.restore) is a very good way.
That's also what is described in the fetch-mock documentation: http://www.wheresrhys.co.uk/fetch-mock/#api-lifecyclerestore_reset
According to the docs this should do it
beforeEach(() => {
fetch.resetMocks()
})
Related
I have a test-suite containing 37 tests that are testing one of my views. Locally, all tests pass without any issues, but when I push my code, the test-suite fails in our pipeline (we are using GitLab).
The error-output from the logs in CI are extremely long (thousands of lines, it even exceeds the limit set by GitLab). The error consists of many "not wrapped in act()"-, and "nested calls to act() are not supported"-warnings (Moslty triggered by useTranslation() from I18Next and componens like Tooltip from Material-UI).
My guess is that async-data from the API (mocked using msw) triggers a state-update after a call to act() has completed, but I'm not sure how to prove this, or even figure out what tests are actually failing.
Has anyone experienced something similar, or knows what's up?
Example of a failing test:
it.each([
[Status.DRAFT, [PAGE_1, PAGE_11, PAGE_2, PAGE_22, PAGE_3]],
[Status.PUBLISHED, [PAGE_1, PAGE_12, PAGE_2, PAGE_21, PAGE_22, PAGE_221]],
])('should be possible to filter nodes by status %s', async (status, expectedVisiblePages) => {
renderComponent();
await waitFor(() => {
expect(screen.queryByRole('progressbar')).not.toBeInTheDocument();
});
userEvent.click(screen.getByLabelText('components.FilterMenu.MenuLabel'));
const overlay = await screen.findByRole('presentation');
await waitFor(() => expect(within(overlay).queryByRole('progressbar')).not.toBeInTheDocument());
userEvent.click(within(overlay).getByText(`SiteStatus.${status}`));
userEvent.keyboard('{Esc}');
const items = await screen.findAllByRole('link');
expect(items).toHaveLength(expectedVisiblePages.length);
expectedVisiblePages.forEach((page) => expect(screen.getByText(page.title)).toBeInTheDocument());
});
Update 1
Okay. So I've narrowed it down to this line:
const items = await screen.findAllByRole('link');
There seems to be a lot of stuff happening while waiting for things to appear. I believed that the call to findAllByRole was already wrapped in act() and that this would make sure all updates has been applied.
Update 2
It seems to be a problem partly caused by tests timing out.
I believe multiple calls to waitFor(...) and find[All]By(...) in the same test, in addition to a slow runner, collectively exceeds the timout for the test (5000ms by default). I've tried to adjust this limit by running the tests with --testTimeout 60000. And now, some of the tests are passing. I'm still struggling with the "act()"-warnings. Theese might be caused by a different problem entirely...
The bughunt continues...
After many attempts, I finally found the answer. The CI-server only has 2 CPUs available, and by running the tests with --maxWorkers=2 --maxConcurrent=2, instead of the default --maxWorkers=100% --maxConcurrent=5, proved to solve the problem.
This is a common issue ;)
I guess, you see this problem on CI Server because of the environment (less cpu/mem/etc).
This warning is because you do some async action but did not finish for complete it (because it's async).
You can read more about this issue in this article: https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning
The best solution is waiting for the operation to finish. For example by adding loading indicator and waiting for element remove.
For example:
it('should show empty table', async () => {
const [render] = createRenderAndStore()
mockResponse([])
const { container } = render(<CrmClientsView />) // - this view do async request in first render
await waitForElementToBeRemoved(screen.queryByRole('test-loading'))
await waitFor(() => expect(container).toHaveTextContent('There is no data'))
})
When ever I try to use require or require.requireActual in jest, to import '#material-ui/icons', jest claims it to be undefined.
I'm guessing this is a bug/issue, as I cant imagine any export to be undefined for require. The file being referenced by this ('#material-ui/icons/index.js') uses code like this to define its exports:
Object.defineProperty(exports, "AccessAlarm", {
enumerable: true,
get: function get() {
return _AccessAlarm.default;
}
});
I know jest does do funky stuff with the base require, perhaps this method of export definition is tripping jest up?
Does anyone have any insight into this, solutions or workarounds, etc?
Also does anybody know of a way to get a list of icons in '#material-ui/icons' given this restriction (get a list of the icons is all I'm trying to do in the first place), and no, I do not want to use a file reader.
To be clear, this is on a simple test file with no mocking.
So I am now using the following solution (automock all material-ui icons) since it looks like jest cant handle them to begin with, so no harm in just permanently automocking them:
const mockIconsList = new Map();
jest.mock('#material-ui/icons', () => new Proxy({__esModule: true}, {
get: (target, prop) => {
if (prop in target)
return target[prop];
if (!mockIconsList.has(prop))
mockIconsList.set(prop, mockComponent(`mockMaterialIcon${prop}`));
return mockIconsList.get(prop);
},
}));
I am still however curious about the above issue and any information regarding it.
I am trying to ensure that the right value is copied to the users clipboard when they click a button. This is my copy method. I am using a ref on the input to access the right value.
protected copyToClipboard() {
console.log("clicked!");
const text = this.controls.copyData;
if (!_.isNil(text)) {
text.current.focus();
text.current.select();
document.execCommand("copy");
this.setState({copied: true});
}
}
For my test:
test("Ensure right value is copied to clipboard", () => {
const wrapper = mount(<MyComponent />);
const copyButton = wrapper.find(".copyBtn");
copyButton.simulate("click");
const copyToClipboardSpy = jest.spyOn(document as any, "execCommand");
wrapper.update();
expect(copyToClipboardSpy).toHaveBeenCalledWith("copy");
});
The error I receive when I run the test is TypeError: document.execCommand is not a function which makes sense, but I am unsure how to approach this.
I am relatively new to testing, just to put that out there. I also have read that I may not be able to access the document.execCommand but have struggled to find a good alternative to hijack the test and access the value being copied. I appreciate any advice that can be given on the matter!
Posting this in case anyone else was in a similar boat. It's doesn't necessarily check the value yet, but one piece I managed was with the document.execCommand method.
I set up a mock function above the wrapper:
document.execCommand = jest.fn();
With this, the test stopped throwing the TypeError. Then my expectations included checking for the spy to have been called, expect my copy state to have changed to true, and:
expect(document.execCommand).toHaveBeenCalledWith("copy");
Test passes! A possible solution for the value is to see if I can "paste" the value and then check it. Will edit this response if/when I can manage that
When you use navigator.clipBoard.writeText instead of using document.exec("copy"), you can refer to this thread for an elegant solution that lets you assert on the content as well.
Being execCommand no longer an option as it is deprecated (see MDN), you should be using navigator.clipboard.writeText('your copied data');.
To mock navigator.clipboard, you could do the following:
// It's important to keep a copy, so your tests don't bleed
const originalClipboard = navigator.clipboard;
const mockedWriteText = jest.fn();
navigator.clipboard = {
writeText: mockedWriteText,
};
const copyComponent = await screen.findByTestId('copy-component');
await userEvent.click(copyComponent);
expect(mockedWriteText).toHaveBeenCalledTimes(1);
expect(mockedWriteText).toHaveBeenCalledWith('your copied data');
// Remember to restore the original clipboard
navigator.clipboard = originalClipboard;
jest.resetAllMocks();
You can also do Object.assignProperty instead of directly modifying the navigator object.
This snippet assumes you are using React Testing Library with User Event.
I am working through Angular Meteor tutorial and got deprecation warning on
this.helpers({
parties: () => {
return Parties.find({}); //deprecation warning. Use getReactively
}
});
But I am not quite sure how to get all records reactively. I do not have any queries to say return Parties.find({field: this.getReactively(query)}). I just want all records/everything similar to Collection.find({}) or Parties.find({}) but without deprecation warning.
Im often use parties: ()=> Parties.find() and I'm has no error or deprecated warning. You maybe should try it.
Beside, sometime I need handle data fetch from query. Im used a temp variable for save data before handle it.
You can try (some code is pseudo code)
parties: () {
this.temp = Parties.find().fetch();
if(this.temp) {
//handle data, adjust property like format string, format date....
return this.temp
}
}
Its work, no deprecated, no error. You can try it.
While rendering React components on server all of the propTypes warning messages are falling to general output or process.stdout. For example, this is visible only in terminal or in general application log:
Warning: Failed propType: Required prop `title` was not specified in `Page`.
Is there any way to catch this warnings and transform them or pipe them into another direction? For instance, I want to separate application log and React (as template engine) log. How can I do it?
Like #m01, I wanted to make sure that any react errors (and in fact any js errors) cause my unit tests to fail, but I found a much simpler way to do it. At the top level of your tests, put this:
beforeAll(() => {
console.error = (error) => {
throw new Error(error);
};
});
I needed the same thing, but for a different use case. I wanted to make sure that all my unit tests pass without any React warnings.
I use this in my test utils:
expectNoConsoleErrors: function() {
var savedErrors;
beforeEach(function () {
savedErrors = [];
spyOn(console, 'error').and.callFake(function () {
var stack = new Error(_.map(arguments).join(' ')).stack;
// if you need workarounds for come components, put them here
savedErrors.push(stack);
});
});
afterEach(function () {
if (savedErrors.length > 0) {
fail(savedErrors.join('\n'));
}
});
},
Then in describe block I put
myTestUtils.expectNoConsoleErrors()
And it works like a charm.
This is not very clean because it will also catch all other calls to console.error, but I don't want them during test anyway, so this seems ok for me.
Also, when I have some misbehaving component producing unnecessary warnings, like e.g. react-input-autosize I can ignore it using:
// Workaround for https://github.com/JedWatson/react-select/issues/371
if (_.contains(stack, 'react-input-autosize')) {
return;
}
Btw, note that before v0.14, React was using console.warn instead of console.error to produce these warnings.
I tried looking into the React src how they print those output messages, but then I realized that it should only be printing those messages in development mode. If your node/iojs runtime is set to the "production" env, React should not even be doing those checks and that's what you want for a real app running. Those warnings are meant just for devs running locally.