How to validate a checkbox is checked or not and then performing some action in Cypress? - checkbox

<div class="ag-react-container">
<div class="text-center"><input type="checkbox" class="" checked=""></div>
</div>
cy.("#checkbox").then(($ele) => {
if($ele.next().is(':checked')){
cy.("#checkbox").next().should("be.checked");
}
else {
cy.("#checkbox").next().find("input").check();
cy.("#checkbox").next().find("input").should("be.checked");
}
});
My problem is the code above is not going into if condition and keeps on executing else condition.
Learning cypress so code can be wrong and would expect correct full code along with some more examples of same if else condition check for buttons, checkboxes or elements.

The problem appears to be .next(). If you remove it, the test works ok.
Also cy.wrap() command would be useful to perform multiple steps on $el.
cy.get('.ag-react-container')
.find('input')
.then($el => {
if ($el.is(':checked')) {
cy.wrap($el).should("be.checked") // passes if checked
} else {
cy.wrap($el).click()
cy.wrap($el).should("be.checked") // passes if not checked
}
})
You can also do this
cy.get('.ag-react-container')
.find('input')
.then($el => {
if ($el.is(':not(:checked)')) {
cy.wrap($el).click() // checks if not checked
}
})
.should("be.checked") // passes either way

As I can see in your HTML for the checked checkbox you have checked="". So to validate that the checkbox is checked, you have to put in the if condition that element has the attribute checked.
cy.get('input[type="checkbox"]').then(($ele) => {
if ($ele.attr('checked') == '') {
//Do Something
} else {
//Do Something
}
})

Related

Checkmark group of checkboxes based on array values

Forgive me there are a lot of questions asking this same thing but from over 10+ years ago.
Is there any way to checkmark a group of checkboxes based on an array in React? I have an array saved within state (stepThree) that I need to pulldown when a user returns to this screen within a multistep form. I'm looking for a way that the values within that array become/stay checked upon return to that screen so it shows the user their previous selections.
Current set-up explained below
State is opened with empty checkedBox array and stepThree initialized to pull responses later. checkedBox is eventually cloned into stepThree.
this.state = {
checkedBox: [],
stepThree: this.props.getStore().stepThree,
};
Boxes that are checked by the user are added to checkedBox array or removed if unchecked.
handleCheckboxChange = (event) =>{
const isChecked = event.target.value; //Grab the value of the clicked checkbox
if (this.state.checkedBox.includes(isChecked)) {
// If the checked value already exists in the array remove it
} else {
// If it does not exist, add it
}
}
Validate and store the completed array on clicking next
if (Object.keys(validateNewInput).every((k) => { return validateNewInput[k] === true })) {
if (this.props.getStore().stepThreeObjects != this.state.checkedBox) { // only update store of something changed
this.props.updateStore({
// Store the values of checkedBox inside stepThree and run updateStore to save the responses
});
} else {
// Return an error
}
Sample checkbox
<label className="choice-contain">
<span>Checkbox Sample</span>
<input
value="Checkbox Sample"
name="Question 3"
type="checkbox"
onChange={this.handleCheckboxChange}
/>
</label>
I've tried to create a persistCheckmark function that pulls the values of the array from stepThree and then does a comparison returning true/false like I do in the handler but since this is not an event I can't figure out how to trigger that function on return to the step.
Currently when returning to the step nothing is checked again and I believe that has to do with checkedBox being initiated as empty.
persistCheckmark(event) {
const isChecked = event.target.value; //Grab the value of the clicked checkbox
if (this.state.stepThree.includes(isChecked)) {
return true;
} else {
return false
}
}
Figured it out thanks to an old post here: How do I set a group of checkboxes using values?
Just updated the filter for when the component mounts
componentDidMount() {
if (this.state.stepThree != undefined) {
var isChecked = this.state.stepThree
$('input[type="checkbox"]').filter(function() {
return $.inArray(this.value, isChecked) != -1;
}).prop('checked', true);
} else { return }
}
and then added a ternary in the state initiation to check the storage and copy it over so it doesn't initialize as empty every time.
checkedBox: this.props.getStore().stepThree != undefined ? this.props.getStore().stepThree : [],

Modal popping up at the wrong time due to state

So I have two modals that I am using one of them was already implemented and behaves as expected however when I've added the other modal depending on the condition of if there is any true value when mapping over the array the way it works right now both modals show when there is a true value. I think this is because there are multiple false values returned from my .includes() function before the true appears. I think a good solution for this would be to make an array of all the values returned when I run .includes() on the entries then I can check that array for any true values but I cant seem to get the values into an array. When I try and push them into an array they just all push into their own separate arrays. This may be the wrong approach if it is can you explain what a better approach would be:
const checkPending = () => {
if(entries){
entries.map(descriptions => {
const desc = descriptions.description
//check if there are any pending tests
const check = desc.includes("pending")
//if the check returns true show the pending modal if it doesnt set the other modal to true
if(check === true){
setShowModal(false)
setShowPendingM(true)
}else{
setShowModal(true)
}
})
}
}
return(
<Button
onClick={() => checkPending()}
className={`${styles.headerButton} mr-2`}
>
Add File
<Plus />
</Button>
)
setShowModal & setShowPendingM are both passed from a parent component as props. They are both initialized as false. The most straightforward question I can pose is is there any way to say if there are any true values returned from .includes then do something even if there are false values present
I think this is how your checkingPending method should look like.
const checkPending = () => {
if(entries){
let pending = false;
entries.forEach((descriptions) => {
const desc = descriptions.description
if(desc.includes('pending')){
pending = true;
}
});
if(pending) {
setShowModal(false);
setShowPendingM(true);
} else {
setShowModal(true);
setShowPendingM(false);
}
}
}
Let me know if you have any additional questions.

React list, how does if else exactly work?

so in my React App, I basically have three buttons. When the specific button is clicked, I want to update the clicked value to be true. I also want the rest of the items that weren't clicked to be false. Is there another way to target the elements that weren't clicked on? I got this solution, but am confused on how it exactly works. I thought that if the first if statement returned true, the else if wouldn't run? So can someone explain how these are both running?
class App extends React.Component {
// state
state = {
list: this.props.tabs,
currentTabContent: '',
};
// event handlers
onButtonClick(tab) {
// ======THIS IS WHAT I DON'T UNDERSTAND========
const newList = this.state.list.map((item) => {
if (item === tab) {
item.clicked = true;
} else if (item !== tab) {
item.clicked = false;
}
return item;
});
// ==============================================
this.setState({
currentTabContent: tab.content,
list: newList,
});
}
// helpers
renderButtons() {
return this.props.tabs.map((tab, index) => (
<li key={index}>
<button
className={tab.clicked ? 'offset' : null}
onClick={() => this.onButtonClick(tab)}
>
{tab.name}
</button>
</li>
));
}
renderContent() {
return this.state.currentTabContent;
}
render() {
return (
<div className="App">
<ul>{this.renderButtons()}</ul>
<div className="display">{this.renderContent()}</div>
</div>
);
}
}
export default App;
I think your misunderstanding lies more in not quite understanding if...else if rather than anything to do with React. Let's take a look at your condition:
if (item === tab) {
item.clicked = true;
} else if (item !== tab) {
item.clicked = false;
}
return item;
This function runs when the following is called by the button's click handler:
() => this.onButtonClick(tab)
Where tab is a specific object corresponding to a specific button. You then map over list in state, which just appears to be the same list of tabs. For each object it checks if tab === listItem if that is true the stuff in the first block executes, that's why the correct button gets set to true. It then does not evaluate the second condition for that item, and just returns the item.
It then moves on to the other items, who will not be equal to tab, and they evaluate in the second condition, so they are marked as false for clicked.
There are some much more worrisome and larger issues in your code here that have more to do with you making comparisons between objects and the dataflow of your components, but those aren't the subject of your question here, I just wanted to warn you to look out for them in the future.

React (Reakit): How to ask for confirmation, when toggling a checkbox?

I'm wondering, if there's a way to ask for confirmation with Reakit's checkbox. I'm using Reakit, since I found a quick way to get it to read database's boolean information, but I welcome other methods too!
I'm used to doing confirmations with buttons with async and window.confirm:
<button onClick={async hiStackOverflow => {
if (window.confirm("Want to do this?")) {
// saving to database here
}
}}>
But I didn't figure out how to do it with a checkbox. In short, I want for the page to confirm (and then save to database), when the user toggles on/off the checkbox.
// personData = database table, with boolean "recurring"
// row = which entity in a table we are talking about
function CheckboxThing ({ row, personData }) {
const checkbox = useCheckboxState({state: personData[row].recurring});
return (
<div className="checkbox-admin-other">
<Checkbox
{...checkbox}
// what here?? onClick or something?
/>
</div>
);
}
Reakit's checkbox can be used like this:
const toggle = () => setChecked(!checked);
return <Checkbox checked={checked} onChange={toggle} />;
This means that the checkbox will be checked if the variable 'checked', which needs to be put in the state of your React component, is true and that the method called 'toggle' will be called when the user toggles the checkbox. In that method, you can put the code which will show the Confirmation Prompt and then change checked if user clicked 'Yes' or leave it as it is if they check 'No'.
You can "observe" changes on checkbox.state using React Hooks:
function CheckboxThing({ row, personData }) {
const checkbox = useCheckboxState({ state: personData[row].recurring });
React.useEffect(() => {
// checking if state has changed
if (checkbox.state !== personData[row].recurring) {
if (window.confirm("Want to do this?")) {
// saving to database here
} else {
// revert checkbox state otherwise
checkbox.setState(!checkbox.state);
}
}
}, [checkbox.state, checkbox.setState, personData[row].recurring]);
return (
<div className="checkbox-admin-other">
<Checkbox {...checkbox} />
</div>
);
}
With React.useEffect, the user will see the checkbox checked before window.confirm opens. But you can use React.useLayoutEffect instead if you want it to open before checkbox state changes on the UI.
After coding around a little while, I found the solution! It turns out, you can put async inside Reakit Checkbox. Thanks to Tomislav and Diego, their answers helped me try different things and get it clean!
Here's the full function:
// admin can edit the right to join back to the queue after getting to the front
function RecurringBox ({ row, personData }) {
// sets the original values
const checkbox = useCheckboxState({state: personData[row - 1].recurring});
return (
<Checkbox {...checkbox} onChange={async checkboxSwitch => {
if (window.confirm("Change it?")) {
checkboxSwitch.persist();
// saving it to the database
await put(`${process.env.API_PATH}/person`,
{
"id": personData[row - 1].id,
"name": personData[row - 1].name,
"recurring": checkboxSwitch.target.checked
});
reload(`${process.env.API_PATH}/person`);
} else {
return null;
}
}}/>
);
}

Trying to validate that at least one checkbox is checked angularJS

Trying to figure out the best way to stay on the same page alerting the user if they have failed to check at least one checkbox.
HTML:
<div class="col3">
<input type="checkbox" ng-model="$parent.value5" ng-true-value="'Togetherness'" ng-false-value="">
<span class="checkboxtext">
Togetherness
</span><br>
<!--<p>We value our people and recognize that <strong>Together</strong> we achieve superior results.</p><br>-->
<div class="col3">
<a ui-sref="form.submit">
<button name="button" ng-click="SaveValue()">Continue</button>
</a>
Back-end angularJS to check if one of the boxes was checked-
$scope.SaveValue = function () {
var valueStatus = [];
if ($scope.value1 === "Methodical")
{
valueStatus.push($scope.value1);
}
if ($scope.value2 === "Relentless")
{
valueStatus.push($scope.value2);
}
if ($scope.value3 === "Togetherness")
{
valueStatus.push($scope.value3)
}
if ($scope.value4 === "Excellent") {
valueStatus.push($scope.value4)
}
if ($scope.value5 === "Ingenious") {
valueStatus.push($scope.value5)
}
return valueStatus
};
Basically I'm wanting to make an array of these values and then return it. However, I want the user to check at least one box. I've tried redirecting back to the page if valueStatus[0] == null. However, I don't think this is the best way to validate and it does not work completely how I think it ought to.
The way I solve this is putting validation on the length of array (valueStatus in your case) with hidden number input. The input will have min validation on. So, if user fails to check at least one, the form is not submitted;
<input type="number" name="valueStatus" ng-model="valueStatus.length" min="1" style="display: none">
Then, you can use normal validation on valueStatus that is available on the form model
myFormName.valueStatus.$valid
This way, most of the logic is put into the template, which is called angularjs way ;)
UPDATE
Forgot to mention:
You need to update the list of checked values on on-change checkbox event
<input type="checkbox" ng-model="checkboxValue1" on-change="updateValueStatus(checkboxValue1)">
<input type="checkbox" ng-model="checkboxValue2" on-change="updateValueStatus(checkboxValue2)">
and in controller
$scope.updateValueStatus = function(value){
var indexOf = $scope.valueStatus.indexOf(value);
if(indexOf < 0) {
$scope.valueStatus.push(value);
} else {
$scope.valueStatus.splice(indexOf, 1);
}
}
Hope it will help people with the same issue
simply just check the valueStatus length is equal to 0 or not
$scope.SaveValue = function () {
var valueStatus = [];
if ($scope.value1 === "Methodical")
{
valueStatus.push($scope.value1);
}
if ($scope.value2 === "Relentless")
{
valueStatus.push($scope.value2);
}
if ($scope.value3 === "Togetherness")
{
valueStatus.push($scope.value3)
}
if ($scope.value4 === "Excellent") {
valueStatus.push($scope.value4)
}
if ($scope.value5 === "Ingenious") {
valueStatus.push($scope.value5)
}
if (valueStatus.length === 0 ) {
console.log('please select atleast one select box')
}
return valueStatus
};
Edited
remove the ui-sref tag and change the state inside your click function
<button name="button" ng-click="SaveValue()">Continue</button>
in the saveValue function add this
if (valueStatus.length === 0 ) {
console.log('please select atleast one select box')
}else{
$state.go('form.submit') // if atleast one selected then the page will change
}

Resources