How to capture current set of classes used on an element - cypress

I have two elements on the page: button1 and button2. One of them has class active set on an initial load.
I want to grab button1 and check if active is set. If set, then for my tests I want to use button2, if not I stick with button1.
For my test, I need to test the switch action between two buttons, hence why I need to use the button which is not an active one.
Each button does have a text, i.e. <div class="active" data-cy="button1">Button 1</div>, so I was also thinking that maybe a different option would be to grab a button1 with class active set and check if it exists? Not sure if that is possible...
How can the above be achieved?

You want to avoid conditional processing in the test, try to use a selector that targets what you want.
Given
<button class="active">Button 1</button>
<button>Button 2</button>
use a :not() selector
cy.get('button:not(.active)')
.should('have.text', 'Button 2')
If there's other button, also not active but not wanted,
<button class="active">Button 1</button>
<button>Button 2</button>
<button>Ignore me</button>
you can add a filter
cy.get('button:not(.active)')
.filter((index, button) => ['Button 1', 'Button 2'].includes(button.innerText))
.should('have.text', 'Button 2')
If you really meant the active attribute,
<button active>Button 1</button>
<button>Button 2</button>
cy.get('button:not([active])')
.should('have.text', 'Button 2')

Related

Laravel Livewire/AlpineJS: Disable a button while loading data from an API and then enabling it again

This should be really simple, but I don't get it. I want to replicate the functionality of a button that when pressed goes to an API (which could take about a minute to get the data and process it), it gets diabled, and after loading the data it gets enabled.
I'm using Laravel/Livewire/Alpine
So, in my livewire component I have:
public $loading = false;
In my blade file, I have the declaration for the div where the button is:
<div
class="grid grid-cols-3 gap-4"
x-data="{
loading: #entangle('loading')
}"
>
Then the button x-binds the disabled property to the loading value, when the button is clicked, it changes the property of the loading variable, and calls the loader function
<button
type="button"
class="btn btn-sm btn-jbn"
x-on:click="loading = true"
x-bind:disabled="loading"
wire:click="loader"
>
Load API
</button>
And it does what it is supposed to do... the button is grayed, it becomes unusable, the cursor change, etc., it executes the loader function in my livewire component, but it never return to the normal state after loading the API. In my livewiere componente I have:
public function loader() {
// API call and logic goes here, this works
$this->loading = false;
}
So I would imagine that at the end of the API process the entangled variable loading would return the button to its normal state, but it doesn't
What am I missing?
Livewire already has incorporated functionality to handle loading-states. Instead of implementing your own, you can use this.
Get rid of all your current loading-logic, and simply use wire:loading with wire:target on your button.
wire:loading can toggle the disabled attribute directly by doing wire:loading.attr="disabled", and wire:target is to set the target for that loading-state to the method you are calling, so in your case that's wire:target="loader".
This means your button looks like this,
<button
type="button"
class="btn btn-sm btn-jbn"
wire:click="loader"
wire:loading.attr="disabled"
wire:target="loader"
>
Load API
</button>

Ui-tabset and bootstrap-daterangepicker not working together?

I have an application where I am using ui-bootstrap-tabs. documentation here. With the ng-bs-daterangepicker.
The behavior I am observing is that whenever I put the daterangepicker inside the ui-tab. It is not able to catch the events attached to it.
But when I move that input tag outside the ui-tabs, it's able to catch the events associated with it.
I have created a working plunker to highlight my issue.
<body ng-app="myApp">
<div ng-controller="testController">
<uib-tabset>
<uib-tab index="0" heading="Drivers"></uib-tab>
<uib-tab index="1" heading="Charts">
<input class="btn btn-danger" type="daterange" id="daterange1" ng-model="dates" format="DD MMM" ranges="ranges" />
</uib-tab>
</uib-tabset>
<!-- <input class="btn btn-danger" type="daterange" id="daterange1" ng-model="dates" format="DD MMM" ranges="ranges" /> -->
</div>
</body>
Here there are two input tags. One inside the uib-tab and other outside it.
$scope.dates = {
startDate: moment().startOf('day'),
endDate: moment().endOf('day')
};
$scope.ranges = {
'Today': [moment(), moment()],
'Yesterday': [moment().subtract('days', 1), moment().subtract('days', 1)],
'Last 7 days': [moment().subtract('days', 7), moment()],
'Last 30 days': [moment().subtract('days', 30), moment()],
'This month': [moment().startOf('month'), moment().endOf('month')]
};
$('#daterange1').on('apply.daterangepicker', function(ev, picker) {
console.log(picker.startDate);
console.log(picker.endDate);
});
The event apply.daterangepicker is not called when I activate the inside input button but is called when I activate the outside one.
My approach
I am guessing it's not a scope issue as highlighted in some posts. Because if it was that, then how come even the date is being populated.
Another thing could be the Known issue column which says
To use clickable elements within the tab, you have override the tab
template to use div elements instead of anchor elements, and replicate
the desired styles from Bootstrap's CSS. This is due to browsers
interpreting anchor elements as the target of any click event, which
triggers routing when certain elements such as buttons are nested
inside the anchor element.
maybe somehow this is stopping the event propagation. I am stuck at this point and can't think of a solution on how to fix it. Hoping the community would help here...
In case of $('#daterange1').on the object to which the event if getting attached must exist at the moment when .on() is invoked.
When daterangepicker is initialized inside Tabs component you could attach event like this:
$("body").on("apply.daterangepicker", "#daterange1", function(e,picker) {
console.log(picker.startDate);
console.log(picker.endDate);
});
Modified plunker

Why does this Web2Py ajax call fail to return the value of a variable?

Here is the relevant snippet from my Web2Py view:
{{for candidate in rows:}}
<div class="well col-sm-12">
<button type="button" name="up_button" onclick="ajax('{{=URL('default', 'arrow_button_callback')}}', ['name'], 'target')" class="fa fa-caret-up arrow-up fa-4x"></button>
<span>{{=candidate.votes}}</span>
<button type="button" name="down_button" onclick="ajax('{{=URL('default', 'arrow_button_callback')}}', ['name'], 'target')" class="fa fa-caret-down arrow-down fa-4x"></button>
{{=IMG(_src=URL('photos',candidate.path_to_photo), _alt="Photo of Candidate")}}
{{=candidate.name}}
<div id="target"></div>
</div>
{{pass}}
And the relevant snippet from my Web2Py controller:
def arrow_button_callback():
response.flash = str(request.post_vars.name)
return request.post_vars.name
So why do I see the string "None" in my target div (and in my flash)?
Thank you for your help. I read chapter 11 of the Web2Py book and I'm still confused.
I really want to be able to pass candidate.id (depending on which row's button was pressed) and the button direction to controller variables. Please let me know if there's a better way to do this.
--Benjamin
From the web2py documentation (emphasis added):
ajax(url, [name1, name2, ...], target)
It asynchronously calls the url (first argument), passes the values of the field inputs with the name equal to one of the names in the list (second argument)...
In your code, your second argument is ['name']. However, there is no input field with the name "name" anywhere, so no "name" variable gets posted to web2py (therefore, request.post_vars.name is None, which is the default value returned whenever you attempt to get a variable that does not exist in request.post_vars).
In this case, because you are not placing any data in form input fields, you can simply ignore the second argument to ajax(). Instead, pass the relevant data via the URL itself:
{{url = URL('default', 'arrow_button_callback',
vars=dict(id=candidate.id, direction='up'))}}
<button type="button" name="up_button" onclick="ajax('{{=url}}', [], 'target')"
class="fa fa-caret-up arrow-up fa-4x"></button>
Then in the arrow_button_callback controller, you can access the candidate id via request.get_vars.id (or just request.vars.id). And you can access the arrow direction via request.vars.direction. Create a new URL for the "down" button, with direction='down'.

Unable to click on 'Cancel' button

I'm writing automated script using Selenium WebDriver with Ruby. In the case, I've to click on 'Cancel' button and following is the html code for it:
<div class="ui-dialog-buttonset">
<button class="otherButtonClass" type="button" role="button" aria-disabled="false">
<span class="ui-button-text">Rename</span>
</button>
<button class="cancelButtonClass" type="button" role="button" aria-disabled="false">
<span class="ui-button-text">Cancel</span>
</button>
</div>
For clicking on 'Cancel' button, I wrote following:
driver.find_element(:xpath, "//button[#class='cancelButtonClass']").click
here click action doesn't happen. I tried sleep, wait.until { element.displayed? } still issue wasn't resolved. The error thrown is 'Element is not visible and hence may not be interacted with'
However, if I perform click action on 'Rename' button, it works:
driver.find_element(:xpath, "//button[#class='otherButtonClass']").click
Please help me to understand why this is happening. I'm confused, 'Rename' and 'Cancel' have similar html code and still clicking on 'Rename' passes and clicking on 'Cancel' fails. Why like this?
You can try the below :
script = <<-end
element = arguments[0];
element.setAttribute('aria-disabled','true');
return element;
end
# select the 'Cancel' button element
elem = driver.find_element(:css,'div.ui-dialog-buttonset>button')[1]
# setting the 'aria-disabled' to true
elem = driver.execute_script(script,elem)
#after enabling the css attribute 'aria-disabled' click on the
#cancel button
elem.click
Using CSS selection will not be a perfect solution in case if the Button CSS are dynamic for any hover actions. Also the Simple way of selecting given element is using the following xpath.
driver.find_element(:xpath, "//span[text()='Cancel']").click

prototype : onclick remove previous element

i have html as below in which onclick of <a> i want to remove the text box above it.
<input class="sku-box" class="input-text validate-number" type="text" title="file-number" name="sku[]">
<a value="remove" onclick="remove()" href="javascript:void(0);">remove</a>
<input class="sku-box" class="input-text validate-number" type="text" title="file-number" name="sku[]">
<a value="remove" onclick="remove()" href="javascript:void(0);">remove</a>
my solution to this, i changed the <a> tag to this.
remove
and my prototype function look like this
function removefield(ele)
{
$(ele).previous().remove();
ele.remove();
}
You have several options for observing click events, and usually I wire up click events after the dom is loaded using something like:
document.on('click', 'a', myFunction.BindAsEventListener())
The above statement sets you up for observing all click events occurring for the a element firing up the function myFunction. Once you have this established you can then either trap the event or simply allow it to bubble up or do both, trap and bubble up the event.
However, with the direction you are going there you would be better off to pass the identity of your a tag to the remove function, then it is easy to remove the previous element. So you would have:
<a id="a_tag1" value="remove" onclick="remove('a_tag1')" href="javascript:void(0);">remove</a>
Then your function remove might look something like this:
function remove(a) {
// prev element
var elm = $(a).previous('input');
// remove it
if(elm)
elm.remove();
}
This is very basic and in reality you would need to do things to guarantee that the previous input element you are grabbing is associated with the a element you clicked. You could do this by agreeing on a new data-xxxx element tag or an arbitrary class name you can use to compare the elements. For example, your a tag might look like:
<a data-group='monkeys'>...</a>
And the cooresponding input tag might look like:
<input data-group='monkeys'>...</input>
Then, in the above code where you call previous on the a tag, you would construct your select to include a test on the data-group value that matches the current a tag.
Karl..

Resources