Whats the difference between toBeInTheDocument and getBy* in #testing-library/react - javascript

Is there a difference between
expect(screen.queryByText('<something>')).toBeInTheDocument();
And
screen.getByText('<something>');
(The specific getBy* and queryBy* operation are not relevant)
In react-testing-library?

getByText tries to find the node and throws an error when it is not able to find it. This will cause the test to fail immediately.
queryByText on the other hand will return null if it is unable to find the node.
Let's suppose you have text <something> in the component that was rendered, you can assert if it has been rendered or not.
If sure that the text has been rendered, then you can simply use getByText
expect(screen.getByText('<something>')).toBeInTheDocument();
and the test would pass.
If for the scenario the text did not render then the above assertion will thrown an error and fail the test case.
In such cases queryByText text makes the most sense
When has not been rendered
expect(screen.getByText('<something>')).not.toBeInTheDocument(); // throws an error
expect(screen.queryByText('<something>')).not.toBeInTheDocument(); // asserts as true
Output comparison

Related

Forbidden non-null assertion lint error for document.getElementById()

I am using TypeScript, and I have the following definition:
const contentElement = document.getElementById("contentId")!;
Where I know for sure the relevant element is defined in the HTML file with contendId.
I have ran eslint, and I get the following error:
Forbidden non-null assertion.
So... What would be the proper way to handle this case? Tending to simply add a suppress warning, as the other options would simply just increase complexity. But maybe I am overlooking a simple solution here, other than adding:
// eslint-disable-next-line #typescript-eslint/no-non-null-assertion
Forbidden non-null assertion. is just a lint warning/error because your value can be nullable. If you are sure of the value then you can cast it to the expected type like below
const contentElement = document.getElementById("contentId") as HTMLElement

How to assert that an alert does not appear?

When a certain UI bug appears, an alert pops up with an error message. I fixed that bug, and now I want to write a Cypress test.
How do I write a Cypress test that passes if the alert does not appear?
I realize that I must be careful with negative assertions, so I'll want to make the test as robust as I can.
Related post:
Cypress: Test if element does not exist
How do I write a Cypress test that passes if the alert does not appear?
Spy on the window:alert event.
Wait a few seconds to give the alert time to appear.
Use a positive assertion to make sure the spy is what you're expecting.
Assert that the spy isn't called.
// Typing enter should not produce an alert
let spy = cy.spy(window, 'alert');
cy.get('input[name="MyField"]')
.type('{enter}')
.wait(4000)
.then(() => {
expect(spy).to.haveOwnProperty('callCount');
expect(spy).to.not.be.called;
});
Also make a positive assertion for whatever is supposed to happen when the bug is fixed.

How does render() actually execute?

I'm working on a project and I have created an interesting challenge for myself.
render() {
const sysObjs = this.state.systemObjects;
const jsonPackage = sysObjs[0];
if(typeof jsonPackage === 'undefined'){
console.log("undefined!!!")
} else {
//let jsonData = JSON.parse(jsonPackage.replaceAll("\\\\", "\\"));
console.log(jsonPackage.replaceAll("\\\\", "\\"));
}
console.log(jsonPackage.replaceAll("\\\\", "\\"));
If I run that it will fail and it will fail on the last line of the snippet and it will fail because from the perspective of that line of code jsonPackage is undefined and we get a "cannot read property 'replaceAll' of undefined. That makes sense to me as doing any sort of operation on something that is undefined would fairly difficult. The part that is confusing me though is that if I remove that last line then the code will execute. That's confusing to me because that console.log is also there within the if-else block so I would imagine that both should throw errors but they don't. In fact if I run that after removing the last line I will actually get both sections of the if-else block to log to the console and that tells me that this is executing in a way that I don't understand. Can anyone explain what's happening for me? At the root here something is doing something such that the const jsonPackage is not populated immediately when it is set so I'm curious how react actually handles something like that.
Lastly, I commented out that line because that line throws "Unhandled Rejection(Error) A cross-origin error was thrown." I'm also not too sure what that actually means either as it wasn't covered in the course I took.
render() is called each time your component's state updates. It can also get called in a number of other circumstances, but I think that's the relevant one for this situation.
You're not showing all of your code, but there is probably something running on mount or something similar that updates this.state.systemObjects.
On the first render of the component, this.state.systemObjects is undefined. So, if you have your console.log(jsonPackage.replaceAll("\\\\", "\\")); line in at the end of your render, it will fail because it can't do an operation on undefined. However, with it commented out, it runs fine, because it goes through the if(typeof jsonPackage === 'undefined'){ section of your if clause and not the else clause, where an operation is done on jsonPackage.
Then, once systemObjects is updated, render runs again. On that pass, systemObjects now is not undefined, so the else clause gets run -- since you don't have an undefined value any more, that else clause runs fine.
Your cross-origin error is not caused by any code you've shown -- it's probably caused by an API request somewhere. There are plenty of answers on SO about what can cause those.
I think you answered your own question. This is happening because the first time your code is executing, jsonPackage is not defined. This is why both blocks of your if statement are logging, because during the first pass though it is undefined and during the second it is, which will cause the else block to fire.
And this is why your code is breaking, during the first pass through of render, jsonPackage is not defined which will throw an error.
Its not really possible to explain why it's not defined the first time around without seeing the rest of your code. As a rule of thumb however, you should generally never assume that a React state value will always be defined and add checks accordingly.

Cypress Command log truncate

Is it possible to truncate the Command Log for the assertion methods?
I have such test
cy.request('/').its('body').should('contain' 'somestring');
The body contains the whole page HTML as a string which results in massive pollution in the Command log in cypress which leads to memory issues. If I would like to do more assertions it will crash cypress.
How can truncate the assert log so it doesn't load the whole body in the assertion Command Log
I've tried saving the HTML string to file and reading it so I could make an assertion like on a normal page but currently, it seems not possible in Cypress to load the local file.
Cherrio is also a thing I could potentially use to retrieve data from that HTML string but I would like to avoid using it.
The trick to suppressing the verbiage in the log is to pre-test the condition,
cy.request('http://example.com')
.its('body')
.then(body => body.includes('Example')) // map to a boolean
.should(result => expect(result, 'Body contains "Example"').to.be.true)
results in
assert Body contains "Example": expected true to be true
As per documentation here, you can pass { log: false } to request.
Check the options section on above documentation

`waitForEnabled()` says "selector needs to be typeof `string`" even though string was passed

I was originally using the mocha command line tool to run my tests and they were working fine. I switched to using the wdio command to run my tests. My tests now throw an error with this line of code:
browser.waitForEnabled('#div_id');
With this error:
Promise was rejected with the following reason: Error: selector needs to be typeof `string`
running chrome
Error: Promise was rejected with the following reason: Error: selector needs to be typeof `string`
at elements() - isEnabled.js:18:17
at isEnabled() - waitForEnabled.js:37:22
This was working fine until I started using wdio (specifically I run wdio --spec path/to/file.js). I've run the typeof function on the selector in question and verified that it is, in fact, a string.
The div in question looks like this:
<div class="highlight" id="div_id">
A fair bit of content goes here.
</div>
Why am I seeing this error? How do I fix it?
waitForEnabled() documentation => http://webdriver.io/api/utility/waitForEnabled.html
wdio documentation => http://webdriver.io/guide/testrunner/gettingstarted.html
Update:
I've tried adding a timeout to the waitForEnabled() function. Since I've done so, it sometimes fails, and sometimes does not. More often it fails though.
I'm not marking this as the answer because I have no idea why it works. But passing in all of the optional params to waitForEnabled() makes it work just fine.
As in:
waitForEnabled('#div_id'); Fails. Wheras:
waitForEnabled('#div_id', 99999, false); works without errors.
The fact that you're passing milliseconds & the false option should not make the test pass.
I'd suggest using some of the other waitFor commands, like waitForVisible() or waitForExist(), because I'm assuming this is what you really want from the code example you gave above. The waitForEnabled() command waits for a field that had a disabled html attribute, to not have it anymore. You can read more on that here: https://www.w3schools.com/tags/att_disabled.asp.

Categories

Resources