webdriverio getText method and promises - webdriver-io

hi i am using cucumber with webdriverio (with chai and chai-as-promised) - following test work -
browser.getText('#copyright').to.eventually.equal('my text').and.notify(callback),
but if i use element like h1 instead of '#copyright' than it does not work, so
browser.getText('h1').to.eventually.equal('my text').and.notify(callback)
does not work,
but callback return me text if i rewrite test as -
browser.getText('h1', function(err, text)
{
expect(text).to.equal('my test');
callback();
});
i wonder what is difference and why second one 'h1' does not work with chai-as-promised' thanks

It doesn't work, because h1 is an element selector, but #copyright is an ID selector.
I'd assume, that getText('h1') returns an array, and getText('#copyright') = single element.

Related

How to get all <a> tag under the <div> in testcafe

In selenium query for selector, if my selector value was (#div-id a). It return all a tags.
Does in testcafe is it posible this to selector function? i just want to avoid looping to get all a tags.
Code Sample
const element = selector('#div-id').find()
var get = await brandUrls.hasAttribute();
console.log(get);
Actual element attached
Yes, it is also possible to achieve the desired behavior with TestCafè in a similar way:
import { Selector } from "testcafe";
// one option
const firstLinkSelector = Selector("#directoryLink-1 a");
// another option
const secondLinkSelector = Selector("#directoryLink-1").find("a");
Read more about the find()-method here.

Cypress, how to check property exists

I'm new to cypress and am trying a couple of different methods to get a checkbox property...
checkBox().should('have.prop', 'checked')
checkBox().its('checked').should('exist')
The first line works fine but I was expecting the second to also pass but I get a "expected Undefined to exist" response.
Thanks
Assuming checkBox() function returns cy.get('.checkbox'), I think
checkBox().its('checked').should('exist')
fails because checkBox() does not return an object containing just the attributes. It returns the whole element (I think as an array). so you can't use its('checked') directly on checkbox().
Anyways, to do what you are expecting to do, you can use several methods,
using invoke('attr', 'checked')
checkBox().invoke('attr', 'checked')
.should('exist')
using getAttribute js function and expect chai assertion
checkBox().then($el => {
expect($el[0].getAttribute('checked')).to.exist;
})
using attributes in js and (its, wrap) in cypress.
Note: As mentioned earlier, you can't directly use its on the cy.get(). You need to extract the attributes from the object and use cy.wrap()
checkBox().then($el => {
cy.wrap($el[0].attributes)
.its('checked')
.should('exist')
})
you can use any of those methods, but the one I recommend is your first method.
cheers. Hope it helps.

Select element by containing text in TestCafe

How can I select an HTML element containing specific text?
In Selenium Xpath selectors are used, but TestCafe doesn't support Xpath.
How do I do it in TestCafe?
According to the official documentation, in order to select an element containing a certain text, I should use the .withText() method with the selected element, example given is:
import { Selector } from 'testcafe';
fixture `Example`
.page `https://devexpress.github.io/testcafe/example/`;
test('Click test', async t => {
const selectBasedOnText = Selector('label').withText('I have tried TestCafe');
await t
.click(selectBasedOnText);
});
for selecting a label element with the text "I have tried TestCafe".
what worked for me:
import xPathToCss from 'xpath-to-css'
and then use as ex:
.click(xPathToCss("//a[#class='icon-menu login show-login-panel list-item list-item-next']"));
no,
testcafe supports xpath
https://github.com/Programmingarea/XpathSelectorIhaveNotCreated
download the xpath selector
after downloading that
go to your testcafe file and type:
importing xpath-selector
import XPathSelector from './xpath-selector';
using it:
const usingxpath = XPathSelector("your xpath");
simple!
if any doubt ask it in reply

Selenium send keys incorrect order in Stripe credit card input

After sending keys to an input field with selenium, the result is not as expected - the keys are inserted in incorrect order.
e.g. send_keys('4242424242424242') -> result is "4224242424242424"
EDIT: On some machines I observe the issue only randomly, 1 case out of 10 attempts. On another machine it is 10/10
This happens specifically with Stripe payment form + I see this problem only in Chrome version 69 (in previous versions it worked OK)
This can be easily reproduced on sample Stripe site: https://stripe.github.io/elements-examples/
Sample python code:
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://stripe.github.io/elements-examples/')
driver.switch_to.frame(driver.find_element_by_tag_name('iframe')) # First iframe
cc_input = driver.find_element_by_css_selector('input[name="cardnumber"]')
cc_input.send_keys('4242424242424242')
Result:
I am able to get pass this by sending the keys one by one with slight delay - but this is also not 100% reliable (plus terribly slow)
I am not sure if this is a problem with selenium (3.14.1)/chromedriver (2.41.578737) or if I am doing something wrong.
Any ideas please?
We are having the exact same problem on MacOS and Ubuntu 18.04, as well as on our CI server with protractor 5.4.1 and the same version of selenium and chromedriver. It has only started failing since Chrome 69, worse in v70.
Update - Working (for the moment)
After much further investigation, I remembered that React tends to override change/input events, and that the values in the credit card input, ccv input etc are being rendered from the React Component State, not from just the input value. So I started looking, and found What is the best way to trigger onchange event in react js
Our tests are working (for the moment):
//Example credit input
function creditCardInput (): ElementFinder {
return element(by.xpath('//input[contains(#name, "cardnumber")]'))
}
/// ... snippet of our method ...
await ensureCreditCardInputIsReady()
await stripeInput(creditCardInput, ccNumber)
await stripeInput(creditCardExpiry, ccExpiry)
await stripeInput(creditCardCvc, ccCvc)
await browser.wait(this.hasCreditCardZip(), undefined, 'Should have a credit card zip')
await stripeInput(creditCardZip, ccZip)
await browser.switchTo().defaultContent()
/// ... snip ...
async function ensureCreditCardInputIsReady (): Promise<void> {
await browser.wait(ExpectedConditions.presenceOf(paymentIFrame()), undefined, 'Should have a payment iframe')
await browser.switchTo().frame(await paymentIFrame().getWebElement())
await browser.wait(
ExpectedConditions.presenceOf(creditCardInput()),
undefined,
'Should have a credit card input'
)
}
/**
* SendKeys for the Stripe gateway was having issues in Chrome since version 69. Keys were coming in out of order,
* which resulted in failed tests.
*/
async function stripeInput (inputElement: Function, value: string): Promise<void> {
await browser.executeScript(`
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(arguments[0], '${value}');
var inputEvent = new Event('input', { bubbles: true});
arguments[0].dispatchEvent(inputEvent);
`, inputElement()
)
await browser.sleep(100)
const typedInValue = await inputElement().getWebElement().getAttribute('value')
if (typedInValue.replace(/\s/g, '') === value) {
return
}
throw new Error(`Failed set '${typedInValue}' on ${inputElement}`)
}
Previous Idea (only worked occasionally):
I have setup a minimal repro using https://stripe.com/docs/stripe-js/elements/quickstart and it succeeds when tests are run sequentially, but not in parallel (we think due to focus/blur issues when switching to the iframes).
Our solution is similar, although we noticed from watching the tests that input.clear() wasn't work on tel inputs which are used in the iframe.
This still fails occasionally, but far less frequently.
/**
* Types a value into an input field, and checks if the value of the input
* matches the expected value. If not, it attempts for `maxAttempts` times to
* type the value into the input again.
*
* This works around an issue with ChromeDriver where sendKeys() can send keys out of order,
* so a string like "0260" gets typed as "0206" for example.
*
* It also works around an issue with IEDriver where sendKeys() can press the SHIFT key too soon
* and cause letters or numbers to be converted to their SHIFT variants, "6" gets typed as "^", for example.
*/
export async function slowlyTypeOutField (
value: string,
inputElement: Function,
maxAttempts = 20
): Promise<void> {
for (let attemptNumber = 0; attemptNumber < maxAttempts; attemptNumber++) {
if (attemptNumber > 0) {
await browser.sleep(100)
}
/*
Executing a script seems to be a lot more reliable in setting these flaky fields than using the sendKeys built-in
method. However, I struggled in finding out which JavaScript events Stripe listens to. So we send the last key to
the input field to trigger all events we need.
*/
const firstPart = value.substring(0, value.length - 1)
const secondPart = value.substring(value.length - 1, value.length)
await browser.executeScript(`
arguments[0].focus();
arguments[0].value = "${firstPart}";
`,
inputElement()
)
await inputElement().sendKeys(secondPart)
const typedInValue = await inputElement().getAttribute('value')
if (typedInValue === value) {
return
}
console.log(`Tried to set value ${value}, but instead set ${typedInValue} on ${inputElement}`)
}
throw new Error(`Failed after ${maxAttempts} attempts to set value on ${inputElement}`)
}
I faced a similar issue in ubuntu 14.04, the following trick helped me.
Have not got any issue since.
First I used the regular send_keys method.
Then I called the execute script to update the value
input_data = "someimputdata"
some_xpath = "//*[contains(#id,'input_fax.number_')]"
element = web_driver_obj.find_element_by_xpath(some_xpath)
element.clear()
element.send_keys(input_data)
web_driver_obj.execute_script("arguments[0].value = '{0}';".format(input_data), element)
Edit
Thanks a lot to #Benno - his answer was correct.
I will just add python solution that worked for me, based on his JS
driver.get('https://stripe.github.io/elements-examples/')
driver.switch_to.frame(driver.find_element_by_tag_name('iframe')) # First iframe
cc_input = driver.find_element_by_css_selector('input[name="cardnumber"]')
value = "4242424242424242"
driver.execute_script('''
input = arguments[0];
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, "{}");
var eventCard = new Event("input", {{bubbles: true}});
input.dispatchEvent(eventCard);
'''.format(value), cc_input)
driver.switch_to.default_content()
driver.quit()
After couple of hours of trying, I gave up and accepted the fact that this is really a random issue and went with a workaround.
Where it is not necessary to update, I will stay with Chrome version < 69
In order to test latest Chrome, I will use React solution
What I've found out
The issue manifested itself mostly on MacOS, quite rarely on Windows (there are most probably other factors in play, this is just an observation)
I've run an experiment with 100 repetitions of filling the form.
Mac - 68 failures
Windows - 6 failures
The cookies/local history (as suggested in comments) do not seem to be the problem. The webdriver always spawned a "clean" instance of the browser, with no cookies or local storage.
Maybe my solution will help for somebody:
I used sendKeys(" 4242424242424242")
Same for cvc field
With a space before string, it actually works for selenide + chrome + java
You could make your own generic SendKeys method that takes the input element and the string you would like to send. The method would split the string into individual characters and then use the selenium sendkeys method on each character.
Adding in some backspaces worked for me for whatever reason:
from selenium.webdriver.common.keys import Keys
my_value = "123"
my_xpath="//input[#class='form-text']"
element = driver.find_element_by_xpath(my_xpath)
element.clear()
element.send_keys(Keys.BACKSPACE * 3, my_value)
I was having the same issue using RSelenium and got the idea to try adding spaces to the credit card number as they appear on the card from #Pavel's answer, since adding a space before the card number didn't work for me.
Using RSelenium this would be:
element$sendKeysToElement(list("4242 4242 4242 4242"))

Counting elements with the same selector in webdriver.io

I am using webdriver.io with chai and mocha for testing.
In one of my tests I need to count how many elements with the same CSS class are in the page. None of the webdriver.io API seems to return an array.
How can it be achieved?
This is how you do it:
client.elements('.myElements', function(err,res) {
console.log('element count: ',res.value.length);
});
Explanation: with elements you fetch all elements according given selector. It returns an array of webdriver elements which represents the amount of existing elements on the page.
For version 4 of webdriver.io, this is the way
client.elements('.selector').then(function (elems) {
console.log(elems.value.length);
});
For version 7.13.2 of webdriver.io, you can try this
const count = await $$('selector').length
Or you can write to a variable and later use it
let smth = browser.elements('selector').value.length;