I have this test :
it('Should return the date correctly formatted', async () => {
const page = await newE2EPage();
await page.setContent('<dsd-datepicker></dsd-datepicker>');
const input = await page.find('dsd-datepicker .duet-date__input');
await input.press('1');
await input.press('2');
await input.press('/');
await input.press('0');
await input.press('1');
await input.press('/');
await input.press('2');
await input.press('0');
await input.press('2');
await input.press('2');
const value = await input.getProperty('value');
expect(value).toBe('12 / 01 / 2022z');
});
It fails like this :
if I remove the "z" I added at the end it fails without any explanation :
I'm using stencjs e2e that uses pupperteer, I am wondering if this has to do with encoding, it just seems weird.
I tried to reproduce this, but wasn't able to with a 'plain' index tag. With the following component:
#Component({
tag: 'my-component',
styleUrl: 'my-component.css',
shadow: true,
})
export class MyComponent {
render() {
return <input/>;
}
}
I created the following test
describe('my-component', () => {
it('renders', async () => {
const page = await newE2EPage();
await page.setContent('<my-component></my-component>');
// the '>>>' pierces the shadow dom
const input = await page.find('my-component >>> input');
await input.press('1');
await input.press('2');
await input.press('/');
await input.press('0');
await input.press('1');
await input.press('/');
await input.press('2');
await input.press('0');
await input.press('2');
await input.press('2');
const value = await input.getProperty('value');
expect(value).toBe('12/01/2022');
});
});
but the test passes for me. My tests are different from yours in two key regards though:
I'm using the standard input tag instead of dsd-datepicker
The value you're expecting has spaces around the '/' characters (which I'd guess is related to the datepicker).
I'd wager that this is related to dsd-datepicker but without seeing a full reproduction case, it's a little tricky to diagnose. But taking a closer look at dsd-datepicker is the first thing I'd do here. Not to say it isn't Stencil, but something interesting is happening here.
My shot in the dark guess/debugging suggestion would be to remove the calls await input.press('/'); in your test (and update the assertion accordingly).
[ ] Does it fail with expect(value).toBe('12012022z');?
[ ] Does is pass with expect(value).toBe('12012022');?
[ ] If yes to the above, what happens if we reintroduce just one of those slashes?
[ ] If no to the above, maybe there's some string trimming to be done here?
That might point you in the right direction to determine if this is a dsd-datepicker issue or a Stencil issue.
Some additional stats:
Node Version 16.11.0
npm Version 8.0.0
Stencil version 2.13.0
Related
I'm trying to automate away downloading multiple files on thingiverse. I choose an object at random. But I'm having a hard time locating the link I need, clicking and then downloading. Has someone run into this before can I get some help?
I've tried several other variations.
import puppeteer from 'puppeteer';
async function main() {
const browser = await puppeteer.launch({
headless: true,
});
const page = await browser.newPage();
const response = await page.goto('https://www.thingiverse.com/thing:2033856/files');
const buttons = await page.$x(`//a[contains(text(), 'Download')]`);
if(buttons.length > 0){
console.log(buttons.length);
} else {
console.log('no buttons');
}
await wait(5000);
await browser.close();
return 'Finish';
}
async function wait(time: number) {
return new Promise(function (resolve) {
setTimeout(resolve, time);
});
}
function start() {
main()
.then((test) => console.log('DONE'))
.catch((reason) => console.log('Error: ', reason));
}
start();
Download Page
Code
I was able to get it to work.
The selector is: a[class^="ThingFile__download"]
Puppeteer is: const puppeteer = require('puppeteer-extra');
Before the await page.goto() I always recommend setting the viewport:
await page.setViewport({width: 1920, height: 720});
After that is set, change the await page.goto() to have a waitUntil option:
const response = await page.goto('https://www.thingiverse.com/thing:2033856/files', { waitUntil: 'networkidle0' }); // wait until page load
Next, this is a very important part. You have to do what is called waitForSelector() or waitForFunction().
I added both of these lines of code after the const response:
await page.waitForSelector('a[class^="ThingFile__download"]', {visible: true})
await page.waitForFunction("document.querySelector('a[class^=\"ThingFile__download\"]') && document.querySelector('a[class^=\"ThingFile__download\"]').clientHeight != 0");
Next, get the buttons. For my testing I just grabbed the button href.
const buttons = await page.$eval('a[class^="ThingFile__download"]', anchor => anchor.getAttribute('href'));
Lastly, do not check the .length of this variable. In this case we are just returning the href value which is a string. You will get a Promise of an ElementHandle when you try getting just the button:
const button = await page.$('a[class^="ThingFile__download"]');
console.log(button)
if (button) { ... }
Now if you change that page.$ to be page.$$, you will be getting a Promise of an Array<ElementHandle>, and will be able to use .length there.
const buttonsAll = await page.$$('a[class^="ThingFile__download"]');
console.log(buttonsAll)
if (buttons.length > 0) { ... }
Hopefully this helps, and if you can't figure it out I can post my full source later if I have time to make it look better.
I'm trying to select a certain size on this website, I have tried multiple approaches that have worked for me so far in puppeteer but none of them seems to work on this instance. I can get the size tab open but cannot figure how to select a specific size.
my code:
await page.goto(data[ii][0]), { //the website link
waitUntil: 'load',
timeout: 0
};
//part 1
await page.click('span[class="default-text__21bVM"]'); //opens size menu
let size = data[ii][1]; //gets size from an array, for example 9
// const xp = `//div[contains(#class, "col-3") and text()="${size}"]`;
// await page.waitForXPath(xp);
// const [sizeButton] = await page.$x(xp);
// await sizeButton.evaluate(btn => {
// btn.parentNode.dispatchEvent(new Event("mousedown"));
// });
await delay(1500);
await page.evaluate((size) => {
document.querySelector(`div > div[class="col-3"][text="${size}"]`).parentElement.click()
});
await page.click('span[class="text__1S19c"]'); // click on submit button
Neither of my approaches worked. I get Error: Evaluation failed: TypeError: Cannot read property 'parentElement' of null meaning the div wasn't found for whatever reason
this is the html of the div I'm trying to click on:
I tried different variations of the querySelector but none of them worked so I'm posting the problem here to see if this is even possible, or if I just made a mistake along the way
This seems working:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: false, defaultViewport: null });
try {
const [page] = await browser.pages();
await page.goto('https://releases.footshop.com/nike-air-force-1-07-lx-wmns-5iBRxXsBHBhvh4GFc9ge');
await page.click('span[class="default-text__21bVM"]');
const size = 9;
const xp = `//div[contains(#class, "col-3") and text()="${size}"]`;
await page.waitForXPath(xp);
const [sizeButton] = await page.$x(xp);
await sizeButton.click();
await page.click('span[class="text__1S19c"]');
} catch (err) { console.error(err); }
I've this simple code chunk:
const BUSINESS = 'lalala';
await page.waitForSelector('#searchboxinput').then(
page.evaluate( (BUSINESS) => {
document.querySelector('#searchboxinput').value = BUSINESS
}, BUSINESS)
),
If I set wait for selector -> then, I would expect then to be executed when the selector exists, but I'm getting a Cannot set property value of 'null'.
So I uderstand that document.querySelector('#searchboxinput') == undefined while I suppose that it cannot be possible as it's executed when waitForSelector promise is finished...
is that a bug or I'm missing something?
Not sure if I understand correctly as your chunk is syntactically not complete and not reprducible. But what if you use the returned value of page.waitForSelector()? This seems working:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: false, defaultViewport: null });
try {
const [page] = await browser.pages();
await page.goto('https://example.org/');
const BUSINESS = 'lalala';
await page.waitForSelector('a').then((element) => {
page.evaluate((element, BUSINESS) => {
element.textContent = BUSINESS;
}, element, BUSINESS);
});
} catch (err) { console.error(err); }
I am getting this error, when I try to run the script (which uses webpack)
Error: Evaluation failed: ReferenceError: _babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_1___default is not defined at __puppeteer_evaluation_script__:2:27
but when I run same code which doesn't use webpack I got the expected result.
here is my function.
const getMeenaClickProducts = async (title) => {
const url = ` ${MEENACLICK}/${title}`;
console.log({ url });
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.goto(url);
await page.waitForSelector('.ant-pagination-total-text');
const products = await page.evaluate(() => {
const cards = [...document.querySelectorAll('.card-thumb')];
console.log({ cards });
return cards.map((card) => {
const productTitle = card.querySelector('.title').innerText;
const priceElement = card.querySelector('.reg-price');
const price = priceElement ? priceElement.innerText : '';
const image = card.querySelector('.img').src;
const link = card.querySelector('.main-link').href;
return {
title: productTitle,
price,
image,
link,
};
});
});
await browser.close();
const filteredProducts = products
.filter((product) =>
product.title.toLowerCase().includes(title.toLowerCase())
)
.filter((item) => item.price);
return filteredProducts;
};
what could be the reason?
The problem is with Babel, and with this part:
const products = await page.evaluate(() => {
const cards = [...document.querySelectorAll('.card-thumb')];
console.log({ cards });
return cards.map((card) => {
const productTitle = card.querySelector('.title').innerText;
const priceElement = card.querySelector('.reg-price');
const price = priceElement ? priceElement.innerText : '';
const image = card.querySelector('.img').src;
const link = card.querySelector('.main-link').href;
return {
title: productTitle,
price,
image,
link,
};
});
});
The inside of the page.evaluate() script you are passing as a function parameter, is not the actual code that is being passed to the page instance, because first you are using babel to transform it.
The array spread operator you have in this part:
const cards = [...document.querySelectorAll('.card-thumb')];
Is most likely being transformed in your build to a function named _babel_runtime_helpers_toConsumableArray__WEBPACK_IMPORTED_MODULE_1___default, which is then passed to the puppeteer page context, and ultimately executed in that page. But such function is not defined in that context, that's why you get a ReferenceError.
Some options to fix it:
Don't use the spread operator combined with the current babel config you are using, so the transformed build doesn't includ a polyfill/replacement of it. Think of a replacement with an equivalent effect, such as:
const cards = Array.from(document.querySelectorAll('.card-thumb'));
Or more traditional for / forEach() loops and build up the array yourself will get job done.
Update your babel config / target language level to support the spread operator natively.
I have spent a lot of time experimenting and using google trying to find a solution to this issue but I have not had any success and I am hoping that someone will be able to provide some guidance. So here is my situation, I am trying to run javascript mocha selenium-webdriver tests on my company's application using docker containers. I seem to have everything working except that I am unable to upload files. Prior to trying use my tests in a docker environment I was able to use it on our local servers and upload files with no issue using the a method like this:
const companyImage = process.cwd()+ '/img/backgroundmario.jpg';
const companyImageElem = await driver.findElement(By.xpath("//div/input[#type='file']"));
await companyImageElem.sendKeys(companyImage);
However, I have not been able to have any success when using docker containers. I mounted my img folder to my selenium/node-chrome-debug container which includes a VNC viewer and I can see that the images are present (and I can manually upload the images via the VNC viewer). However, despite numerous variations of providing paths to the images I can not seem to get my images to upload. For some reason the working directory seems to be from my test container and not my node-chrome-debug container but even if I add the images to the test container and change the path to my test container directory with the images they do not upload either.
Here is a snippet of my code I am using for my test (it includes some stuff I wouldn't normally include, specifically the check for process.cwd() and process.env.PWD since I just wanted to see what the path was:
const {
Builder,
By,
Key,
until,
webdriver,
action
} = require('selenium-webdriver');
const mocha = require('mocha');
const chai = require("chai");
const chaiAsPromised = require("chai-as-promised");
const {
makeUtilityBelt
} = require('./util')
chai.use(chaiAsPromised);
const fs = require('fs');
const expect = require('chai').expect;
const ciPassword = require('./envData').ciPassword;
const campManagerMail = 'jdrzymala+companycreator#influential.co';
const campManagerName = 'companycreator';
const campManagerUsername = 'companycreator';
const legacy = "http://node-web-client";
const companyImage = '/opt/test/images/backgroundmario.jpg';
var currentDir = process.cwd();
var appFolder = process.env.PWD;
const {
createLegacyAdmin,
createLegacyResellerCompany,
createLegacyBrandCompany,
createLegacyAgencyCompany,
createLegacyCampManager,
createLegacyClient,
createLegacyInfluencer
} = require('./legacyCreationQueries');
const {
getEmailId,
getUserEmailId,
getRandom,
verifyRandom,
accountSetup
} = require('./sqlutil');
describe('Creates a Company of Each Type via the Legacy Dashboard', function () {
this.timeout(1200000);
this.slow(20000);
let driver;
let util;
before(async function () {
driver = new Builder().forBrowser('chrome').usingServer('http://selenium_hub:4444/wd/hub').build();
util = makeUtilityBelt(driver);
await createLegacyCampManager(campManagerName, campManagerUsername, campManagerMail);
});
afterEach(function () {
let testCaseName = this.currentTest.title;
let testCaseStatus = this.currentTest.state;
if (testCaseStatus === 'failed') {
driver.takeScreenshot().then((data) => {
let screenshotPath = `./results/${testCaseName}.png`;
console.log(`Saving Screenshot as: ${screenshotPath}`);
fs.writeFileSync(screenshotPath, data, 'base64');
});
}
});
after(function () {
driver.quit();
});
describe('Load Legacy Corporate Site and Login to Legacy Dashboard', function () {
it('Loads into the Legacy Dashboard Successfully', async function () {
await driver.get(legacy);
await driver.wait(until.elementLocated(By.xpath("//p[contains(text(),'Sign In')]"), 10000));
await driver.sleep(3000);
const emailElem = await driver.findElement(By.xpath("//input[#id='email']"));
await util.sendKeys(emailElem, campManagerMail);
const pwElem = await driver.findElement(By.xpath("//input[#id='password']"));
await util.sendKeys(pwElem, ciPassword);
await driver.findElement(By.xpath("//button[contains(text(),'Sign In')]")).click();
await driver.wait(until.elementLocated(By.xpath("//div/ul[contains(#class, 'campaign-search-list')]"), 10000));
await driver.wait(until.elementLocated(By.xpath("//ul[#class='menu']/li/a/span[contains(text(),'User Management')]"), 10000));
await driver.sleep(5000);
await console.log("Below is the Current Working Directory");
await console.log(currentDir);
await driver.sleep(3000);
await console.log(appFolder);
await driver.sleep(3000);
await console.log("The above is the app folder");
await driver.sleep(2000);
const loginSuccessElem = await driver.findElement(By.xpath("//ul[#class='menu']/li/a/span[contains(text(),'User Management')]"));
let loginSuccess = await loginSuccessElem.isDisplayed();
await driver.sleep(3000);
await expect(loginSuccess, 'Legacy Login Failed').to.be.true;
});
});
describe('Creates a Reseller Company', function(){
const companyName = 'Reseller Test Company';
it('Navigates to Company Management and Begins Company Creation Process', async function(){
await driver.wait(until.elementLocated(By.xpath("//ul[#class='menu']/li/a/span[contains(text(),'Company Management')]"), 10000));
await driver.findElement(By.xpath("//ul[#class='menu']/li/a/span[contains(text(),'Company Management')]")).click();
await driver.sleep(8000);
await driver.wait(until.elementLocated(By.xpath("//h3[contains(text(),'Search Companies')]"), 10000));
await driver.wait(until.elementLocated(By.xpath("//a[contains(text(),'+ Create Company')]"), 10000));
await driver.sleep(8000);
await driver.findElement(By.xpath("//a[contains(text(),'+ Create Company')]")).click();
await driver.wait(until.elementLocated(By.xpath("//h3[contains(text(),'Create Company')]"), 10000));
const companyCreationPageElem = await driver.findElement(By.xpath("//h3[contains(text(),'Create Company')]"));
let companyCreationPage = await companyCreationPageElem.isDisplayed();
await expect(companyCreationPage, 'Company Creation Page failed to Load').to.be.true;
});
it('Fills in the required fields and creates New Reseller Company', async function(){
const companyDescription = 'This is a test description for a random test company blah blah blah';
const companyAddress = '777 Lucky Lane';
const companyCity = 'Las Vegas';
const companyState = 'Nevada';
const companyZip = '89104';
const companyNameElem = await driver.findElement(By.xpath("//input[#label='Company Name']"));
await util.sendKeys(companyNameElem, companyName);
await driver.sleep(1000);
const companyDescriptionElem = await driver.findElement(By.xpath("//textarea[#label='Company Description']"));
await util.sendKeys(companyDescriptionElem, companyDescription);
await driver.sleep(1000);
const companyTypeElem = await driver.findElement(By.xpath("//select"));
await companyTypeElem.click();
await driver.wait(until.elementLocated(By.xpath("//select/option"), 10000));
const companyTypeSelectElem = await driver.findElement(By.xpath("//select/option[#value='1']"));
await companyTypeSelectElem.click();
await driver.sleep(1000);
const addressElem = await driver.findElement(By.xpath("//input[#label='Address']"));
await util.sendKeys(addressElem, companyAddress);
await driver.sleep(1000);
const cityElem = await driver.findElement(By.xpath("//input[#label='City']"));
await util.sendKeys(cityElem, companyCity);
await driver.sleep(1000);
const stateElem = await driver.findElement(By.xpath("//input[#label='State']"));
await util.sendKeys(stateElem, companyState);
await driver.sleep(1000);
const zipElem = await driver.findElement(By.xpath("//input[#label='Zip Code']"));
await util.sendKeys(zipElem, companyZip);
await driver.sleep(1000);
await driver.findElement(By.xpath("//input[#type='file']")).sendKeys(companyImage);
await driver.sleep(1000);
await driver.wait(until.elementLocated(By.xpath("//img[#class='image-preview']"), 10000));
await driver.sleep(1000);
const submitButtonElem = await driver.findElement(By.xpath("//button[contains(text(),'Submit')]"));
await submitButtonElem.click();
await driver.wait(until.elementLocated(By.xpath("//h3[contains(text(),'Company Actions')]"), 10000));
await driver.wait(until.elementLocated(By.xpath("//p[#class='company-name']"), 10000));
const companySuccessElem = await driver.findElement(By.xpath("//p[#class='company-name'][contains(text(),'"+companyName+"')]"));
let companySuccess = await companySuccessElem.isDisplayed();
await expect(companySuccess, 'Failed to Create New Company').to.be.true;
});
});
});
This is the last thing stopping me from integrating my large number of test files with our CI/CD process but a huge number of my tests involve uploading files so it is a major issue. I am extremely thankful for any guidance anyone could provide me. Thank you in advance!
Despite not receiving any guidance here, with a bit of additional research, the assistance of a coworker and some experimentation I was able to solve my problem.
So there are several aspects that are important. First of all, you must make sure that the images you want to upload are mounted to the container with your browser (in my case, selenium/node-chrome-debug). Then you must make some additions to your test Selenium test file.
You must add the following lines:
var path = require('path');
var remote = require('selenium-webdriver/remote');
You can use var or let although I've been told that let is a better standard practice.
Then, after the line
driver = new Builder().forBrowser('chrome').usingServer('http://selenium_hub:4444/wd/hub').build();
Add this line of code
driver.setFileDetector(new remote.FileDetector());
For your the file you wish to upload, you must set the path to that of the file system on the browser container (selenium/node-chrome-debug in my case). So your file variable would be something like:
const companyImage = process.cwd()+'/images/backgroundmario.jpg';
Then, when you want to upload the file, you find the respective element using whichever form of identification you like and add a little extra to the sendKeys function in comparison to how you would do it were you simply running the script on your local file system rather than a docker container. So the code would look like this:
await driver.findElement(By.xpath("//input[#type='file']")).sendKeys(path.resolve(__dirname, companyImage));
Maybe there is a slightly cleaner way to code it (for example, I generally declare the elements I am interacting with as variables) but the example I have provided will work. It took me a lot of time and effort to find this solution so I hope this eventually saves someone else the amount of pain I experienced trying to get this to work.