I am trying to use puppeteer to type in an input field but I get the following error.
Error: failed to find element matching selector "#cardCvc-input"
My code is:
await page.$eval('#cardCvc-input', e => e.value = "000")
The node in the DOM is as follows:
<input _ngcontent-ng-cli-universal-c1="" autocomplete="off" cccvc="" class="form-control ng-pristine ng-
invalid ng-touched" id="cardCvc-input" maxlength="4" minlength="3" name="cardCvc" pattern="[0-9]{4}|[0-
9]{3}" required="" type="tel">
Hoping someone could help tell me what I am doing wrong. Note that when I check the page source, I don't actually see the DOM. It is a bunch of javascript code. Does that have anything to do with it?
Also noticed that there are iframe parent nodes.
Since the element is in an iframe you need to select the iframe first and then select the element:
await page.waitForSelector('iframe'); //make sure you use the correct selector for the iframe
const iframeElement = await page.$('iframe'); //make sure you use the correct selector for the iframe
const frame = await iframeElement.contentFrame();
await frame.waitForSelector('#cardCvc-input');
await frame.$eval('#cardCvc-input', e => e.value = "000");
You can add a waitForSelector before calling evaluate. So you would wait for that DOM element to be created by the javascript code.
await page.waitForSelector('#cardCvc-input');
await page.$eval('#cardCvc-input', e => e.value = "000")
Related
Ran into the issue, where the test code should click the button Process in the iframe. Used npm i cypress-iframe lib, but came up to nothing. Cypress could not find the button.
Tried cy.iframe('[class="resp-iframe"]').find('resp-iframe[id="submit"]')
HTML of the problem
Tried the other ways to click on iframe button:
cy.get('iframe[class="resp-iframe"]').then($element => {
const $body = $element.contents().find('body')
cy.wrap($body).find('resp-iframe[class="btn btn-block btn-primary"]').eq(0).click();
})
also
cy.get('[class="resp-iframe"]').then($element => {
const $body = $element.contents().find('body')
let stripe = cy.wrap($body)
stripe.find('[class="resp-iframe"]').click(150,150)
})
and
cy.iframe('#resp-iframe').find('[name="submitButton"]')
Error
Error 2
Updated FYI:
The first part of code - clicking the Google button in bottom-right:
const getIframeBody = () => {
// get the iframe > document > body
// and retry until the body element is not empty
return cy
.get('[id="popup-contentIframe"]')
.its('0.contentDocument.body')
// wraps "body" DOM element to allow
// chaining more Cypress commands, like ".find(...)"
// https://on.cypress.io/wrap
.then(cy.wrap)
}
getIframeBody().find('[id="payWithout3DS"]').click()
Then, waiting for secure payment preloader to finish up:
cy.wait(20000)
Then, trying to catch the Process button by suggestions:
cy.iframe('[name="AcsFrame"]').find('#submit').click()
or
cy.iframe('[class="resp-iframe"]').find('[id="submit"]')
whole code part looks:
const getIframeBody = () => {
// get the iframe > document > body
// and retry until the body element is not empty
return cy
.get('[id="popup-contentIframe"]')
.its('0.contentDocument.body')
// wraps "body" DOM element to allow
// chaining more Cypress commands, like ".find(...)"
// https://on.cypress.io/wrap
.then(cy.wrap)
}
getIframeBody().find('[id="payWithout3DS"]').click()
cy.wait(20000)
cy.iframe('[name="AcsFrame"]').find('#submit').click()
But still, getting:
Maybe anyone had something like that?
Thanks.
How about you try this:
cy.iframe('[name="AcsFrame"]').find('#submit').click()
You don't need to repeat the resp-iframe inside the .find().
The selector .find('resp-iframe[id="submit"]') means look for HTML like this: <resp-iframe id="submit"> but the element you want is <input id="submit">.
Everything else looks ok
cy.iframe('[class="resp-iframe"]').find('[id="submit"]')
I have tried many options, but not successful so far to click on checkbox that are custom checkboxes with :before tag and are hidden. Can someone show me the way to resolve this issue. I tried X-Path and other selector, it finds and clicks on those checkboxes but those checkboxes don't get checked for some reason.
<fieldset class="checkbox">
<legend>Services</legend>
<ul class="multiColumnList">
<li><label for="AccountUsers_0__ViewOrders"><input data-val="true" data-val-required="The View Orders field is required." id="AccountUsers_0__ViewOrders" name="AccountUsers[0].ViewOrders" type="checkbox" value="true" class="hidden-field"><span class="custom checkbox"></span><input name="AccountUsers[0].ViewOrders" type="hidden" value="false">View Orders</label></li>
Here is the screenshot of HTML
Try to click on the checkbox in the following way:
const checkboxSelector = Selector('#AccountUsers_0__ViewOrders');
const labelSelector = Selector('[for="AccountUsers_0__ViewOrders"]')
await t.click(labelSelector);
await t.expect(checkboxSelector.checked).ok();
If this does not help, let me know. I will find a suitable solution for you.
async Check() {
const checkboxSelector = Selector(`[id="AccountUsers_0__ViewOrders"]`)
.with({visibilityCheck: true});
if(!checkboxSelector.checked){
await t.click(checkboxSelector);
}
await t.expect(checkboxSelector.checked).eql(true, 'Should be checked')
}
async UnCheck() {
const checkboxSelector = Selector(`[id="AccountUsers_0__ViewOrders"]`)
.with({visibilityCheck: true});
if(checkboxSelector.checked){
await t.click(checkboxSelector);
}
await t.expect(checkboxSelector.checked).eql(false, 'Should be unchecked')
}
Please try this code and let me know
<form
class="" id="form" hx-post="/add/" hx-swap="afterbegin" hx-target="#big_list" hx-trigger="submit">
<input type="text" name="langue1" >
<input type="text" name="langue2">
<div id="errors"></div>
<button type="submit">GO</button>
</form>
<div id="big_list">
.....
</div>
I have a big list in #big_list, and I want my #form appends only one row when submitted.
How with htmx, can I handle errors and show message in #errors ?
I created this solution so you can use hx-target-error = to define which HTML will be displayed after a failed request
document.body.addEventListener('htmx:afterRequest', function (evt) {
const targetError = evt.target.attributes.getNamedItem('hx-target-error')
if (evt.detail.failed && targetError) {
document.getElementById(targetError.value).style.display = "inline";
}
});
document.body.addEventListener('htmx:beforeRequest', function (evt) {
const targetError = evt.target.attributes.getNamedItem('hx-target-error')
if (targetError) {
document.getElementById(targetError.value).style.display = "none";
}
});
If your code raises the errors (validation?), you can change target and swap behavior with response headers.
Response.Headers.Add("HX-Retarget", "#errors");
Response.Headers.Add("HX-Reswap", "innerHTML");
If you want to return a status other than 200, you have to tell htmx to accept it.
4xx would normally not do a swap in htmx. In case of validation errors you could use 422.
document.body.addEventListener('htmx:beforeOnLoad', function (evt) {
if (evt.detail.xhr.status === 422) {
evt.detail.shouldSwap = true;
evt.detail.isError = false;
}
});
It works in htmx 1.8.
If you want to remove the error message on then next sucessfull request, you could use hx-swap-oob. Out of band elements must be in the top level of the response.
So the response could look like this:
<div>
your new row data...
</div>
<div id="errors" hx-swap-oob="true"></div>
Update
You can now use the new powerful extension multi-swap to swap multiple elements arbitrarily placed and nested in the DOM tree.
See https://htmx.org/extensions/multi-swap/
Although it doesn't follow REST principles, you might consider using an swap-oob to report your error back to your user. For example, your request might return a (slightly misleading) status 200, but include content like this:
<div id="errors" hx-swap-oob="true">
There was an error processing your request...
</div>
If it's important to follow REST more precisely, then you'll want to listen to the htmx:responseError event, as mentioned by #guettli in his previous answer.
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.
In my Vue app I have an image element with a bound src. this code is in a stepper. In one step the user selects a picture to upload an in the next step the image gets shown (which works as intended). However I am trying to initiate a library on the element in the next step (the Taggd library) but the element seems to be undefined.
I tried the nextTick() to wait for the image to load but without luck.
the image element:
<div class="orientation-landscape">
<div class="justify-center q-px-lg">
<img v-if="photo.url" :src="photo.url" id="workingPhoto"
style="width: 80vw;" ref="workingPhoto"/>
</div>
</div>
The functions. I first use uploadFile to set the url of the image element and go to the next step where the image will be loaded. then I call initTaggd() on the nextTick however that fails because the element is undefined.
uploadFile(file) {
const self = this;
self.photo.file = file;
self.setPhotoUrl(file);
self.$refs.stepper2.next();
console.log('We should be at next step now. doing the init');
self.nextTick(self.initTaggd());
},
setPhotoUrl(file) {
const self = this;
self.photo.url = URL.createObjectURL(file);
console.log('ping1');
},
initTaggd() {
const self = this;
const image = document.getElementById('workingPhoto');
console.log('image:', image); //THIS RETURNS UNDEFINED
console.log('Trying ANOTHER element by refs:', self.$refs.stepper2); // This returns the element
console.log('trying the real element by reference', self.$refs.workingPhoto); // Returns undefined again
const taggd = new Taggd(image); //All this here fails because image is undefined.
console.log('ping2.5');
taggd.setTags([
self.createTag(),
self.createTag(),
self.createTag(),
]);
console.log('ping3');
},
I think I am looking for a way to wait for the image to fully load before calling initTaggd() but I am lost on how to achieve this.
You could listen for the load event:
<img
v-if="photo.url"
id="workingPhoto"
ref="workingPhoto"
:src="photo.url"
style="width: 80vw;"
#load="initTaggd"
/>