TypeError: Expected container to be an Element, a Document or a DocumentFragment but got string - react-testing-library

I'm trying to run the below testcase using RTL , and the test is failing with the error "TypeError: Expected container to be an Element, a Document or a DocumentFragment but got string.". I tried searching for solution but couldn't find one .
describe("Feedback Commponent",()=>{
it('should throw error when feedback textbox is empty', () => {
const { getByLabelText} = render(<Contact />);
fireEvent.change(getByLabelText('Feedback'), {
target: { value: '' },
});
fireEvent.blur(getByLabelText('Feedback'));
debug();
expect(getByTestId('feedback-error')).toBe(
'Please Enter Your Feedback'
);
});
});
The above snippet is suppose to test the feedback form , to be specific only the feedback text box , when it is empty and when user goes out of focus from the textbox ,there should be an error stating "Please Enter Your Feedback".

It seems that you imported getByTestId method straight from testing library instead of destructuring it from render like you did with getByLabelText. getByTestId, according to the docs is:
getByTestId(
// If you're using `screen`, then skip the container argument:
container: HTMLElement,
text: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
}): HTMLElement
So it expects the first argument to be the HTML container, hence the error.
One solution would be to destructure getByTestId as well:
const { getByLabelText, getByTestId } = render(<Contact />);
Another one is to use screen, as suggested by the docs:
import { screen } from '#testing-library/react';
...
expect(screen.getByTestId('feedback-error')).toBe(
'Please Enter Your Feedback'
);
In which case the first 'container' argument is skipped and the string 'feedback-error' is treated like test id 'text' argument. Docs:
https://testing-library.com/docs/queries/bytestid

I resolved the issue by changing the assertion to
expect(getByText('Please Enter Your Feedback')).toBeInTheDocument();
from
expect(getByTestId('feedback-error')).toBe('Please Enter Your Feedback');
Also I noticed that I had setupTest.js file with
import '#testing-library/jest-dom/extend-expect';
missing , which adds custom jest matchers for asserting on DOM nodes, so after adding the above changes my test case passed

Related

Testing text field input with Spectator

I'm trying to test my directive with #ngneat/spectator version 6.1.1
it('should allow all numbers', () => {
spectator = createDirective('<input appNumbersOnly type="number">')
const input = spectator.query('input') as HTMLInputElement
spectator.typeInElement('1234', input)
expect(input.value).toHaveExactText('1234')
})
That always comes back with input.value being blank. I'm just getting started with spectator. What's the right way to perform this test?
Just use:
expect(input.value).toEqual('1234');

Why isn't the function parameter showing in the dialog?

The function parameter that is a string isn't showing in the dialog content block. I need to know why and how to make this work. The parameter I am trying to show is string with php code.
I have used MessageToast.show() in my function to view the output string. The output string shows up. I have also tried adding "hello world" string the content block and that works.
Here is my code logic is a string.
logicDialog: function (logic) {
MessageToast.show(logic);
var dialog = new Dialog({
title: 'Boolean Logic',
type: 'Message',
content: new TextArea({
value: logic,
editable: false,
growing: true,
growingMaxLines: 20
}),
beginButton: new Button({
text: 'Close',
press: function () {
dialog.close();
}
}),
afterClose: function () {
dialog.destroy();
}
});
dialog.open();
}
The expected result is dialog that shows the string logic.
Since the example of #TiiJ7 works fine I will just try to throw some possible issues:
-is your Dialog a sap.m.Dialog()?;
-is there any error before the dialog is triggered? (that could somehow stop the functionality of the dialog);
-put a break point in the line of "dialog.close();" to check the content of the string on the scope of the dialog opened;
-is it possible that the string is being manipulated somewhere else and it could a be an issue with concurrency? 1 thread could be changing the value of the string (note that a string is a pointer).
I hope these points could bring you light, since you are the only one with access to the entire code.

React Native - How to validate Textinput correclty?

How to validate Textinput correclty? I want to validate my form correctly with custom form validation and after validation display errors in Text component, but how? Please, guys show me example!
install react-native-snackbar to show error messages.
import React, { Component } from 'react';
import { View, Text, TextInput } from 'react-native';
import Snackbar from 'react-native-snackbar';
export default class LoginPasswordScreen extends Component {
constructor(props) {
super(props);
this.state = {
password: ''
}
}
validate = () => {
//include your validation inside if condition
if (this.state.password == "") {
() => {
setTimeout(() => {
Snackbar.show({
title: 'Invalid Credintials',
backgroundColor: red,
})
}, 1000);
}
}
else {
Keyboard.dismiss();
// navigate to next screen
}
}
render() {
return (
<View>
<TextInput
returnKeyType="go"
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
autoFocus={true}
onChangeText={(password) => this.setState({ password })}
/>
<TouchableOpacity>
<Text onPress={this.validate}>Next</Text>
</TouchableOpacity>
</View>
);
}
}
Every field, you have to do a comparison and show the error message and as I see there is no direct form validation even though there is form component available in react native.
In One of my react native project, I added a form and later on click of Submit, I had written one validate function to check all my inputs.
For this, I used one nice javascript library-
npm library- validator
And for showing error message, you can use, Toast, ALert or Snackbar
Would be nice if you provide some thoughts or code on how you would think it can be approached. But the way i did it was pretty simple, on my component state i got the following object:
this.state = {
loading: false,
username: {
text: '',
valid: false
},
password: {
text: '',
valid: false
},
isLoginValid: false
};
Then on the TextInput for username, i would first, bind its value to this.state.username.text, also, during onChangeText I just do a simple validation of the field, if the form is quite big, you may have a switch(fieldtype) where you have for each field, what treatment you want to apply a.k.a validation.
onChangeText={ (text) => { this.validateInput(text, 'username')}} (username would be the form input on the state object)
For instance, for username you want only to be length != 0 and length <= 8 characters, for email you may run a RegExp() with the email validation and also its length, for password a different logic, etc... after that i just simply save the state for that field input and if it's valid or not. Like this:
validateInput(text, fieldname) {
let stateObject = {
text: text,
valid: text.length !== 0
}
this.setState({ [fieldname]: stateObject }, () => {
this.checkValidation();
});
}
In checkValidation I check for all the input fields and if every one is valid, i set formValid to true.
This formValid would for example, allow the user to tap on the "Login" button otherwise it applies an opacity of 0.5 to it and disables it.
The rest you may guess, is just playing around with the valid variables of each field to what you want to display and what not.
In a Register form I also added an X or a "Tick" icon if the input text field is ok or not. Let your imagination guide you.
Hope it helps.

Best practice for testing for data-testid in a nested component with React Testing Library?

I'm trying to write a test to check if my app is rendering correctly. On the initial page Ive added a data-testid of "start". So my top level test checks that the initial component has been rendered.
import React from "react";
import { render } from "react-testing-library";
import App from "../App";
test("App - Check the choose form is rendered", () => {
const wrapper = render(<App />);
const start = wrapper.getByTestId("start");
// console.log(start)
// start.debug();
});
If I console.log(start) the I can see all the properties of the node. However if I try and debug() then it errors saying it's not a function.
My test above does seem to work. If I change the getByTestId from start to anything else then it does error. But I'm not using the expect function so am I violating best practices?
There are two parts to this question -
Why console.log(start) works and why not start.debug()?
getByTestId returns an HTMLElement. When you use console.log(start), the HTMLElement details are logged. But an HTMLElement does not have debug function. Instead, react-testing-library provides you with a debug function when you use render to render a component. So instead of using start.debug(), you should use wrapper.debug().
Because you don't have an expect function, is it a good practice to write such tests ?
I am not sure about what could be a great answer to this, but I will tell the way I use it. There are two variants for getting an element using data-testid - getByTestId and queryByTestId. The difference is that getByTestId throws error if an element with the test id is not found whereas queryByTestId returns null in such case. This means that getByTestId in itself is an assertion for presence of element. So having another expect which checks if the element was found or not will be redundant in case you are using getByTestId. I would rather use queryByTestId if I am to assert the presence/absence of an element. Example below -
test("App - Check the "Submit" button is rendered", () => {
const { queryByTestId } = render(<App />)
expect(queryByTestId('submit')).toBeTruthy()
});
I would use getByTestId in such tests where I know that the element is present and we have expects for the element's properties (not on the element's presence/absence). Example below -
test("App - Check the "Submit" button is disabled by default", () => {
const { getByTestId } = render(<App />)
expect(getByTestId('submit')).toHaveClass('disabled')
});
In the above test, if getByTestId is not able to find the submit button, it fails by throwing an error, and does not execute the toHaveClass. Here we don't need to test for presence/absence of the element, as this test is concerned only with the "disabled" state of the button.

Protractor unable to select dropdown option

Hi I have been doing protractor test and I'm having a problem with my tests. My ionic app do have a drop down having a model name and I tried to access it using the model name and it works but the problem is it can not select the exact option that i need to select from that dropdown option. It selects only the first one? I wrote the protractor syntax like this.
element(by.model('generalPowerOfAttorney.grantorGeneralPowerOfAttorneyForm.region')).$('[value="59"]').click();
But this code selects not the value 59 rather value 0 which is the default option. Is there anyone who could help me?
You should add the html source to facilitate the answer.
You can use the filter method in order to get the correct element clicked.
var elements = element.all(by.model('generalPowerOfAttorney.grantorGeneralPowerOfAttorneyForm.region'));
elements.filter(function(elem, index) {
return elem.getText().then(function(text) {
return text === 'value="59"';
});
}).then(function(filteredElem){
if(filteredElem[0] !== undefined){
filteredElem[0].click();
}
else {
throw 'element not found'
}
});