I have a use case where I need to set VALUE for a input tag.
'''
module.exports = async function(selectorId, inputString) {
const randomDate = inputString;
console.log(rdate)
await this.page.evaluate(((el) => {el.value = ""}), selector[0]);
await this.page.evaluate(((el,randomDate ) => {el.value = randomDate },randomDate ), selectorId[0]);
await this.page.waitForTimeout(1000);
};
'''
Here this code is working fine but it is not doing any action on UI, like not setting the selectorId.value as per definition, I am using xPath for the object. Can any one help on same.
I am trying to get the element of day 18, and check if it has disabled on its class.
<div class="react-datepicker__day react-datepicker__day--tue" aria-label="day-16" role="option">16</div>
<div class="react-datepicker__day react-datepicker__day--wed react-datepicker__day--today" aria-label="day-17" role="option">17</div>
<div class="react-datepicker__day react-datepicker__day--thu react-datepicker__day--disabled" aria-label="day-18" role="option">18</div>
this is my code, assume
this.xpath = 'xpath=.//*[contains(#class, "react-datepicker__day") and not (contains(#class, "outside-month")) and ./text()="18"]'
async isDateAvailable () {
const dayElt = await this.page.$(this.xpath)
console.log(dayElt.classList.contains('disabled'))) \\this should return true
I can't seem to make it work. Error says TypeError: Cannot read property 'contains' of undefined. Can you help point what I am doing wrong here?
Looks like you can just write
await expect(page.locator('.selector-name')).toHaveClass(/target-class/)
/target-class/ - slashes is required because it's RegExp
For check few classes by one a call I use this helper (It's because api way doesn't work for me https://playwright.dev/docs/test-assertions#locator-assertions-to-have-class):
async function expectHaveClasses(locator: Locator, className: string) {
// get current classes of element
const attrClass = await locator.getAttribute('class')
const elementClasses: string[] = attrClass ? attrClass.split(' ') : []
const targetClasses: string[] = className.split(' ')
// Every class should be present in the current class list
const isValid = targetClasses.every(classItem => elementClasses.includes(classItem))
expect(isValid).toBeTruthy()
}
In className you can write few classes separated by space:
const result = await expectHaveClasses(page.locator('.item'), 'class-a class-b')
You have to evaluate it inside the browser. $ will return an ElementHandle which is a wrapper around the browser DOM element, so you have to use e.g. evaluate then on it. Or simply $eval which will lookup the element, pass it into a callback which gets executed inside the browsers JavaScript engine. This means something like that would work:
// #ts-check
const playwright = require("playwright");
(async () => {
const browser = await playwright.chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
await page.setContent(`
<div id="a1" class="foo"></div>
`)
console.log(
await page.$eval("#a1", el => el.classList.contains("foo1"))
)
await browser.close();
})();
I am trying to create a higher order selector which I can reuse by changing a text parameter. In the snippet below, when I am trying to perform t.click using actualSelector, it's not working as expected. When I console log the value for the actualSelector, i see whole item values are printed.
Seems I am not using it right way. Can you please help me with this.
e.g
const testItemNameGenericSelector = (itemName) =>
Selector(
".ms-Callout-container .ms-Callout-main div div"
)
.withText(itemName);
...
const itemNameToSelect = "Test Item-8ab1ec12-e719-4ab6-a0a3-ed538143d6d3";
const actualSelector = testItemNameGenericSelector(itemNameToSelect)
...
console.log(`selecting ${await actualSelector().textContent}`)
Below is full testcafe code for this test.
fixture`Getting Started`
.page // declare the fixture
`https://hasans30.github.io/testpage/dropdown.html`; // specify the start page
//then create a test and place your code there
test("My first test", async (t) => {
const testItemNameGenericSelector = (itemName) =>
Selector(
".ms-Callout-container .ms-Callout-main div div"
)
.withText(itemName);
const buttonSelector = Selector('.ms-Button-label');
const selectedValue = Selector('.ms-Dropdown-title');
const itemNameToSelect = "Test Item-8ab1ec12-e719-4ab6-a0a3-ed538143d6d3";
const actualSelector = testItemNameGenericSelector(itemNameToSelect);
await t.click(buttonSelector,{speed:0.51})
console.log(`selecting ${await actualSelector().textContent}`)
await t.click(actualSelector,{speed:0.51})
await t.expect(await selectedValue().textContent).eql(itemNameToSelect);
});
I'm trying to test amending text in an editable input which contains the title of the current record - and I want to able to test editing such text, replacing it with something else.
I know I can use await page.type('#inputID', 'blah'); to insert "blah" into the textbox (which in my case, having existing text, only appends "blah"), however, I cannot find any page methods1 that allow deleting or replacing existing text.
You can use page.evaluate to manipulate DOM as you see fit:
await page.evaluate( () => document.getElementById("inputID").value = "")
However sometimes just manipulating a given field might not be enough (a target page could be an SPA with event listeners), so emulating real keypresses is preferable. The examples below are from the informative issue in puppeteer's Github concerning this task.
Here we press Backspace as many times as there are characters in that field:
const inputValue = await page.$eval('#inputID', el => el.value);
// focus on the input field
await page.click('#inputID');
for (let i = 0; i < inputValue.length; i++) {
await page.keyboard.press('Backspace');
}
Another interesting solution is to click the target field 3 times so that the browser would select all the text in it and then you could just type what you want:
const input = await page.$('#inputID');
await input.click({ clickCount: 3 })
await input.type("Blah");
You can use the page.keyboard methods to change input values, or you can use page.evaluate().
Replace All Text:
// Using page.keyboard:
await page.focus('#example');
await page.keyboard.down('Control');
await page.keyboard.press('A');
await page.keyboard.up('Control');
await page.keyboard.press('Backspace');
await page.keyboard.type('foo');
// Using page.evaluate:
await page.evaluate(() => {
const example = document.getElementById('example');
example.value = 'foo';
});
Append Text:
// Using page.keyboard:
await page.focus('#example');
await page.keyboard.press('End');
await page.keyboard.type(' bar qux');
// Using page.evaluate:
await page.evaluate(() => {
const example = document.getElementById('example');
example.value += ' bar qux';
});
Backspace Last Character:
// Using page.keyboard:
await page.focus('#example');
await page.keyboard.press('End');
await page.keyboard.press('Backspace');
// Using page.evaluate:
await page.evaluate(() => {
const example = document.getElementById('example');
example.value = example.value.slice(0, -1);
});
Delete First Character:
// Using page.keyboard:
await page.focus('#example');
await page.keyboard.press('Home');
await page.keyboard.press('Delete');
// Using page.evaluate:
await page.evaluate(() => {
const example = document.getElementById('example');
example.value = example.value.slice(1);
});
If you are not interested in simulating any key events, you could also use puppeteer's page.$eval method as a concise means to remove the textarea's value...
await page.$eval('#inputID', el => el.value = '');
await page.type('#inputID', 'blah');
...or even completely replace the value in one step, without simulating the subsequent typing:
await page.$eval('#inputID', el => el.value = 'blah');
This works perfect for "clear only" method:
const input = await page.$('#inputID');
await input.click({ clickCount: 3 })
await page.keyboard.press('Backspace')
above answers has an ESLint issues.
the following solution passing ESLint varification:
await page.evaluate(
(selector) => { (document.querySelector(selector).value = ''); },
inputBoxSelector,
);
Use the Keyboard API which simulates keystrokes:
await page.focus(css); // CSS selector of the input element
await page.keyboard.down('Shift');
await page.keyboard.press('Home');
await page.keyboard.up('Shift'); // Release the pressed 'Shift' key
await page.keyboard.press('Backspace');
This keystroke is cross-platform as opposed to using ctrl + A(does not work in Mac to select all characters in a input field)
The most clean way for me is:
Setup
const clearInput = async (page, { selector }) => {
const input = await page.$(selector)
await input.click({ clickCount: 3 })
await page.keyboard.press('Backspace')
}
Usage
const page = await context.newPage()
await clearInput(page, { selector: 'input[name="session[username_or_email]"]' })
await clearInput(page, { selector: 'input[name="session[password]"]' })
Well, the reason you want to delete existing text generally may be want to replace it.
You can use page.evalute
let title = getTitle()
let selector = getSelector()
await page.evaluate(
({selector, title}) => {
let el = document.querySelector(selector)
if ('value' in el) el.value = title
else el.innerText = title
},
{selector, title}
)
someField.type("");
Pass the empty string before typing your content.
This worked for me.
In a specific case, I need to access a series of checkboxes using Puppeteer. Each checkbox has the same id and name, but different values for value. I want to get the value of each selector and then use that to click the checkbox that matches up with the text selector I'm scanning for. Here is the code segment:
await page.waitForSelector("input[type='checkbox']");
let boxArray = await page.$$eval("input[type='checkbox']", el => el.value);
const boxes = (await page.$$("input[type='checkbox']")).length;
for(var box=2;box<boxes+1;box++)
{
await page.waitForSelector("this is inconsequential");
const title = await page.$eval("this is inconsequential", el => el.innerText);
if(title.includes("string I'm searching for"))
{
console.log('Clicking group check box: %s', title);
console.log('Value: %s', boxArray[box]);
await page.waitForSelector("input[value='"+boxArray[box]+"']");
// the rest doesn't matter
}
}
Once the code reaches console.log('Value: %s', boxArray[box]), I get this error:
Value: undefined
TypeError: Cannot read property '5' of undefined
I can't figure out why boxArray is undefined, as page.$$eval() should be populating an array with the value of every checkbox on the page.
boxArray is undefined.
$$eval will run Array.from(document.querySelectorAll("input")) and pass it to the pageFunction. It doesn't create a loop to get the value of each function. https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageevalselector-pagefunction-args
Use a for ... of loop to get the value instead of $$eval. Consider this example:
const inputsValues = [];
const inputElements = await page.$$('input');
for (const element of inputElements) {
let inputValue;
inputValue = await element.getProperty('checked');
inputValue = await inputValue.jsonValue();
inputsValues.push(inputValue);
}