Slow performance with Testcafe and ids and custom radio buttons - testing

I have a test where testcafe says "Waiting for element to appear" and I dont understand why.
I use a ID selector
My test is fairly simple and I made a slow version using a ID and a fast one using a non ID
Is this a bug in testcafe?
Using Testcafe v1.11.0 on Chrome 88.0.4324.182 / Windows 10
Test output says:
Book
√ slow (17s)
√ fast (3.69s)
Why is selecting using ID so much slower? Shouldn't it be faster?
This is my test:
import { Selector } from 'testcafe';
fixture('Book').page('https://book.dinnerbooking.com/dk/en-US/book/table/pax/458/2');
test('slow', async t => {
'use strict';
await t
.maximizeWindow()
.click('button.ui-spinner-up')
.click('button.black-button')
.click('#RestaurantAreaConfigAreaId6');
});
test('fast', async t => {
'use strict';
await t
.maximizeWindow()
.click('button.ui-spinner-up')
.click('button.black-button')
.click(new Selector('.restaurant-area-label').nth(1));
});

These two different selectors select two different elements. '#RestaurantAreaConfigAreaId6' selects the <input type="radio" id="RestaurantAreaConfigAreaId6" ... /> element and Selector('.restaurant-area-label').nth(1) selects the <label for="RestaurantAreaConfigAreaId6" class="restaurant-area-label font-large">Café</label> element.
The input element, in turn, is overlapped by another transparent element. In such cases, TestCafe waits until the target element becomes uncovered within the timeout. If the target element stays still, TestCafe clicks on the element above.

Related

With TestCafe Selector, How to verify the text of the selected item in a <select>?

I'm using TestCafe 1.8.1 and have a slightly different case than the documentation at https://devexpress.github.io/testcafe/documentation/recipes/test-select-elements.html - my problem is that the example assumes the value of an <option> and its text content will be the same, and in my case, the value is a very unpredictable value.
I can select an item in the dropdown without trouble, using .withText(value) to filter the options, and .click(item) to select it. However, my app then refreshes the page, and ought to re-select the relevant item as it loads up. This is not working and I want to test for it.
So I might have options in the select like:
<select id="foo">
<option value="1234">100x100</option>
<option value="5432">200x100</option>
<option value="9999">100x200</option>
</select>
Obviously, if I test with .expect(citySelect.value).eql('London'); as in the docs it'll fail because the values are nothing like the text content e.g. having clicked '200x100' in the dropdown the value becomes "5432".
Do I need to use a ClientFunction to get the text of the selected item? I understand it's quite awkward passing data into a ClientFunction, would I need to pass the id of the select so the ClientFunction can getElementById to find the select and retrieve it's selected option's text content? It all sounds like the wrong way to be doing things.
Please check the following example that uses ClientFunction API to obtain an option value:
import { Selector, ClientFunction } from 'testcafe';
fixture `Fixture 1`
.page `https://kys0l.csb.app/`;
test('Test 1', async t => {
const selector = Selector('select');
const getValue = ClientFunction((index) => {
const select = selector();
return select.options[index].value;
}, { dependencies: { selector } });
await t
.expect(getValue(0)).eql('1234')
.expect(getValue(1)).eql('5432')
.expect(getValue(2)).eql('9999');
});
See also: Obtain Client-Side Info.
Try using
.expect(citySelect.innertext).eql('London');

TestCafe and grabbing an element/selector when no ID or CSS is present

Hello I have a simple page of our application, the start page that has a disclaimer on it and one button. Because of the technology we are using the IDs are stripped out and obfuscated.
So the button's name is 'ok' as it appears in the page-source view below. Note no ID. Even if I put it in our code, when it is produced the ID is removed.
<div style="text-align:center;">
<input type="button" name="ok" value="Ok" width="40" onclick="swap()" />
</div>
I have the following in my TestCafe js file:
import {Selector } from 'testcafe';
import { ClientFunction } from 'testcafe';
fixture `My fixture`
.page `http://192.168.2.86/mpes/login.jsp`;
test('Disclaimer Page 1', async t => {
const okBtn = Selector('button').withAttribute('ok');
await t
.click(okBtn);
});
test('Disclaimer Page 2 -- of course tried this ', async t => {
await t
.click('ok');
});
.click('ok') -- does not work either
Tried numerous things even trying findElementByName .. but nothing works with Window vs Document and Selector vs element.
Any help would be greatly appreciated.
I have been all over looking at how to grab selectors on TestCafe docs. None seem to fit this scenario which is quite simple -- but highly problematic.
Thanks in advance.
If you want to select this element:
<input type="button" name="ok" value="Ok" width="40" onclick="swap()" />
you can use (one or more) attribute selectors like this:
input[type="button"][name="ok"]
If only one element on the page has the attribute name="ok", you can grab that single element in javascript, using document.querySelector(), like this:
const myButton = document.querySelector('input[type="button"][name="ok"]');
N.B. If multiple elements on the page have the attribute name="ok", you can grab all of them in javascript, using document.querySelectorALL().

Uncaught TypeError: Cannot read property 'host' of undefined in Durandal when showing/closing modal dialog

Using Durandal 2.0.1 (can't update to 2.1.0 yet due to restrictions on project development) and I have an intermittent issue with the error shown in this question title.
All I'm doing is defining a custom dialog box then showing it:
var pleaseWaitModal = new modalBusy();
dialog.show(pleaseWaitModal);
And when my ajax call is finished I do:
dialog.close(pleaseWaitModal);
...and then display another modal with the results of my ajax call.
This all works perfectly IF the ajax call takes half a second to a second. If it's a quicker call then I get Uncaught TypeError: Cannot read property 'host' of undefined in my console window. The box still closes, it's just that I get a panicky project manager asking what the red error is for...
Is this purely because I'm trying to run "dialog.close()" before "dialog.show()" has properly completed in some circumstances?
The sequence of events is basically:
*user instigates action requiring a detailed modal dialog to appear with data in it
*as it takes several seconds to populate on some occasions, an interim modal dialog is shown with "please wait" in it
*once the ajax request is complete, the "pleasewait" modal is closed and the "detail" modal is shown
*so a bit like:
var pleaseWaitModal = new modalBusy();
dialog.show(pleaseWaitModal);
//set up deferred calls for ajax data and call ...
var deferredAjax = callDataFunction(myparams...);
return deferredAjax.then(function(result) {
dialog.close(pleaseWaitModal);
var detailModal = new detailModal();
detailModal.show(result);
});
So I don't think I can achieve this using the promise on the dialog.show(pleaseWaitModal) call, can I?
Are you using the promise that is returned from the dialog.close function to open your new modal? You might try this:
From your initial dialog:
dialog.show(new modal()).then(function(responseData) {
dialog.show(new pleaseWaitModal(responseData));
});
I think the problem you're running into is async timing related, which is why using the deferred works so well.
EDIT: Related to my comment below, you might look at using only one modal, and putting a loading indicator inside of it, like so:
view.html
<div data-bind="visible: isLoading">
<h1>Please wait...</h1>
<i class="icon-spin icon-spinner icon-4x"></i>
</div>
modalViewModel.js
var vm = {
isLoading: ko.observable(true)
};
vm.activate = function() {
makeAjaxCall().then(function(data) {
vm.isLoading(false);
**Do whatever you need for your ajax return**
return true;
});
});
I think that should work for what you need as an alternative.

JavaScript Protractor (Selenium) verify if input is focused

I'm trying to to test whether an element is focused using selenium webdriver in protractor. This is before AngularJS is loaded so I am having to use the driver as seen here:
var ptor = protractor.getInstance(),
driver = ptor.driver;
I also need to know how to make the test wait until the input is focused. I have to wait until a model is fired so the input is not focused for half a second as seen here:
window.setTimeout(function(){
$("input#email").focus();
}, 500);
Any idea how to verify if an input has focus after 500ms?
Based on my answer to this question, and adapting it to your case, it would look like:
it('should focus on foo input', function () {
// to wait 500ms+
browser.driver.sleep(600);
// using the Protractor 'element' helper
// https://github.com/angular/protractor/blob/master/docs/api.md#element
// var input = element(by.id('foo'));
// using findElement with protractor instance
var input = driver.findElement(protractor.By.id('foo'));
expect(input.getAttribute('id')).toEqual(browser.driver.switchTo().activeElement().getAttribute('id'));
});
I used glepretre's answer, but had to resolve the getAttribute promises for both elements using promise.all
let activeElement = browser.driver.switchTo().activeElement().getAttribute('id');
let compareElement = element(by.id('your-element-id')).getAttribute('id');
webdriver.promise.all([compareElement, activeElement]).then((id) => {
expect(id[0]).to.equal(id[1]);
});

How to trigger <enter> in an input in Angular scenario test?

I'm writing tests with Angular Scenario test runner. Within a traditional form, I can enter text into an input, but I need to press enter to execute the query and there is no button to click on. Surely there is some easy way to do this, but I do not know what it is.
input('query').enter('foo bar');
// ... now what?
I tried to simulate a keypress with JQuery, but as this answer indicates JQuery is not loaded in the e2e scenarios scope. So I followed his advice (as well as that of this answer) to simulate the keypress:
element('#search_input').query(function(el, done){
var press = document.createEvent('keypress');
press.which = 13;
press.trigger(evt);
done();
});
But to this Angular replies:
NotSupportedError: DOM Exception 9
Error: The implementation did not support the requested type of object or operation.
Update
I realized that a very easy workaround is to include a hidden submit input in my form:
<input id="search-submit" type="submit" style="display:none;">
Then in the scenario: element('#search-submit').click(); does what is needed.
For a purer solution which doesn't involve modifying the HTML for the sake of testing, #florian-f's answer (as well as this one) provides access to jQuery within the DSL via:
var $ = $window.$;
which can be used there or passed to the callback. However, even with this access when triggering a press of enter I was not able to submit my form in the following manner:
$(selector).trigger($.Event('keypress', { which: 13 }));
This must be another issue all together. But I did find jQuery's submit function to do the trick:
$(#the_form).submit();
You can access to the app (runner in an iframe) instance of jQuery :
angular.scenario.dsl('appElement', function() {
return function(selector, fn) {
return this.addFutureAction('element ' + selector, function($window, $document, done) {
fn.call(this, $window.angular.element(selector));
done();
});
};
});
Then you can call the trigger method of jQuery in your test :
appElement('yourSelector', function(elm) {
elm.trigger('enter');//or keypress
});
There is also another possibility to trigger a key event. While your first approach
element('#search_input').query(function(el, done){
var press = document.createEvent('keypress');
press.which = 13;
press.trigger(evt);
done();
});
will be blocked by angular, this one
element(<selector>).query(function($el, done) {
var event = new CustomEvent('keyup');
event.keyCode = 13;
$el.val(2);
$el.get(0).dispatchEvent(event);
done();
});
will pass and trigger a keyup event on the element specified by the selector (keyCode = 13 = Enter Key). See https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent for further information.