I am not able to locate MatSnackBar element using protractor.
This is how I display snack bar.
const snackBarRef = this.snackBar.open('Book added.', 'Undo', {
duration: 300000
});
This is e2e test.
const snackBar = element(by.tagName('simple-snack-bar'));
browser.wait(ExpectedConditions.visibilityOf(snackBar), 30000);
element(by.tagName('simple-snack-bar')).getText().then(function (val) {
console.log(val);
expect(val).toEqual('Book added');
});
Test code fails with this error.
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Thanks for your message, #dplavcic.
The reason that my code didn't work is that snackbar was out of angular root. So I have to use browser.driver.findElement() instead of element(), and finally have solved the issue.
Something like:
public getSnackBar(): promise.Promise<string> {
const snackbar = browser.driver.wait(until.elementLocated(By.tagName('simple-snack-bar')), 10000);
return snackbar.getText();
}```
The time out is from the browser.wait of 30 second, so the application cannot find the elemnt with in that time . so increase the time out as :
add below property to your config file:
jasmineNodeOpts: {defaultTimeoutInterval: 40000}
About element not found
make sure elemnt is not inside iframe or shadown , if so , then switch to parent before accessing it
use promise chaining properly or use await instead
it('should find an element by text input model', async function() {
await browser.get('app/index.html#/form');
var username =element(by.model('username'));
await username.clear();
await username.sendKeys('Jane Doe');
var name = element(by.binding('username'));
expect(await name.getText()).toEqual('Jane Doe');
});
I am using puppeteer to scrape a website that is being live updated, to report the latest item elsewhere.
Currently the way I was thinking accomplishing this is to run a setInterval call on my async scrape and to compare if the last item has changed, checking every 30 seconds. I assume there has to be a better way of doing this then that.
Here is my current code:
const puppeteer = require('puppeteer');
playtracker = async () => {
console.log('loading');
const browser = await puppeteer.launch({});
const page = await browser.newPage();
await page.goto('URL-Being-Scraped');
await page.waitForSelector('.playlist-tracklist-view');
let html = await page.$$eval('.playlist-tracklist-view > .playlist-track', tracks => {
tracks = tracks.filter(track => track.querySelector('.playlist-trackname').textContent);
tracks = tracks.map(el => el.querySelector('.playlist-trackname').textContent);
return tracks;
});
console.log('logging', html[html.length-1]);
};
setInterval(playtracker, 30000)
There is an api called "MutationObserver". You can check that out on MDN. Here's the link https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
What it is doing is basically do whatever you want to do when the spesific element has changed. Lets say you have a list you want to listen. What you would do is
const listElement = document.querySelector( [list element] );
const callbackFunc = funcion foo () {
//do something
}
const yourMutationObserver = new MutationObserver(callbackFunc)
yourMutationObserver.observe(listElement)
You can disconnect your mutation observer with yourMutationObserver.disconnect() method whenever you want.
This could help too if you confused about how to implement it https://stackoverflow.com/a/48145840/14138428
I'm using WebDriverIO (v5.18.7) and I'm trying to write something the can go to each URL, and scroll down in increments until you reached the bottom, then move on the next URL. The issue I'm having is when it comes to the scrolling portion of the script, it might scroll once before it goes to the next URL.
From what I'm understanding, the documentation for WebDriverIO says the commands are send in asynchronous, which is handled in the framework behind the scenes. So I tried sticking with the framework and tried browser.execute / browser.executeAsync but wasn't able to get it working.
Here's what I have that seems close to what I want. Any guidance would be appreciated!
const { doesNotMatch } = require('assert');
const assert = require('assert');
const { Driver } = require('selenium-webdriver/chrome');
// variable for list of URLS
const arr = browser.config.urls
describe('Getting URL and scrolling', () => {
it('Get URL and scroll', async () => {
// let i = 0;
for (const value of arr) {
await browser.url(value);
await browser.execute(() => {
const elem = document.querySelector('.info-section');
// scroll until this reaches the end.
// add another for loop with a counter?
elem.scrollIntoView(); //Using this for now as a place holder
});
// i += 1;
}
})
})
Short anwer $('.info-section').scrollIntoView()
See https://webdriver.io/docs/api/element/scrollIntoView.html
WebdriverIO suppors sync and async modes, see https://webdriver.io/docs/sync-vs-async.html
I am interacting with a webpage whereby some of the deeper nested elements within sub-frames load even after the document is ready - i.e. they rely on pending ajax/api requests that are yet to be completed when the document is loaded.
My intention is to wait until these elements exist prior to doing anything with them. I can do this using setTimeout and wait for an arbitrary amount of time before doing anything e.g.
setTimeout(function() {
$("#better_content .binder__toc").append(
"<h4>Experiments</h4><ul><li>This</li><li>is</li><li>a</li><li>test</li></ul>"
);
}, 5000);
However, it would be nice in a recursive and asynchronous (non-blocking) manner to keep checking for said element ("#better_content .binder__toc") until undefined or null is not returned i.e. the element exists. I have tried to do this using Promises. A simple example with a counter is as follows:
static waitForElement = counter => {
counter++
return new Promise((res, rej) => {
if (counter < 5) {
this.waitForElement(counter)
.then(function() {
res("complete");
})
.catch(rej);
}
res("complete");
});
};
this.waitForElement(counter)
.then(a => console.log(a))
.catch(a => console.log(a));
The above resolves successfully and appears to be non-blocking. However, if I replace the counter for the element selector as below:
static waitForElement = selector => {
let found = $(document).find(selector)[0];
console.log(found);
return new Promise((res, rej) => {
if (found === undefined) {
this.waitForElement(selector)
.then(function() {
res("found");
})
.catch(rej);
}
res("found");
});
};
this.waitForElement("#better_content .binder__toc")
.then(a => {
console.log(a);
$("#better_content .binder__toc").append(
"<h4>Experiments</h4><ul><li>This</li><li>is</li><li>a</li><li>test</li></ul>"
);
})
.catch(a => console.log(a));
Then this never seems to resolve successfully and really slows down the webpage - I assume as it is blocking the main thread still.
Any suggestions on how I can correct or understand what is going on here would be more than welcome.
You can check for changes in an element, or children of an element, with the Mutation Observer API. The API fires a callback in which you can assert your logic to act when a certain change in the element is happened. In your case you'll want to listen on the container where the element is appended to with the fetch request.
It is possible to listen for certain changes, like attribute changes, or in your case changes in the children. In the configuration add childList: true which will indicate that you want to do something whenever a child is added or removed.
This way you don't have to check in if the element exists.
Check out the example below to see your example in action.
const target = document.getElementById('container');
const config = { childList: true };
const observer = new MutationObserver((mutations, observer) => {
/**
* Loop through all changes
*/
mutations.forEach(mutation => {
/**
* Only act if it is a change in the children.
*/
if (mutation.type !== 'childList') {
return;
}
/**
* Loop through the added elements and check if
* it is the correct element. Then add HTML to
* the newly added element.
*/
mutation.addedNodes.forEach(node => {
if (node.id === 'content') {
const html = `
<h4>Experiments</h4>
<ul>
<li>This</li>
<li>is</li>
<li>a</li
><li>test</li>
</ul>`;
node.innerHTML = html;
}
});
// Stop observing.
observer.disconnect();
});
});
// Observe the children in container.
observer.observe(target, config);
// Add content element after 2 seconds.
setTimeout(function() {
const content = document.createElement('div');
content.id = 'content';
target.append(content);
}, 2000);
<div id="container"></div>
I'm new on protractor, and I'm trying to implement an e2e test.
I don't know if this is the right way to do this, but...
The page that I want to test is not a full angular page based, so... I'm having some trouble.
On my first spec I have:
describe('should open contact page', function() {
var ptor = protractor.getInstance();
beforeEach(function(){
var Login = require('./util/Login');
new Login(ptor);
});
I have created this Login class, but after login I want to open the contact page, but protractor immediately try to find element before the page is fully loaded.
I've tried to use:
browser.driver.wait(function() {
expect(browser.findElement(by.xpath("//a[#href='#/contacts']")).isDisplayed());
ptor.findElement(by.xpath("//a[#href='#/contacts']")).click();
});
But it doesn't work... it always try to find the element before the page loads.
I tried this one too:
browser.driver.wait(function() {
expect(ptor.isElementPresent(by.xpath("//a[#href='#/contacts']")));
ptor.findElement(by.xpath("//a[#href='#/contacts']")).click();
});
I'm able to do that using browser.sleep(); but I don't think that is a good option. Any idea? On my login class I have:
ptor.ignoreSynchronization = true;
How can I wait for this #href='#/contacts before protractor tries to click on it?
Protractor 1.7.0 has also introduced a new feature: Expected Conditions.
There are several predefined conditions to explicitly wait for. In case you want to wait for an element to become present:
var EC = protractor.ExpectedConditions;
var e = element(by.id('xyz'));
browser.wait(EC.presenceOf(e), 10000);
expect(e.isPresent()).toBeTruthy();
See also:
Expected conditions in protractor
I finally find out...
var waitLoading = by.css('#loading.loader-state-hidden');
browser.wait(function() {
return ptor.isElementPresent(waitLoading);
}, 8000);
expect(ptor.isElementPresent(waitLoading)).toBeTruthy();
var openContact = by.xpath("//a[#href='#/contacts']");
element(openContact).click();
With this protractor could wait for that element until it loading page disappears.
Thanks for those who tried to help XD.
I had the same problem you were having for the longest time while using protractor. In my e2e test I start in a non angular app, then get into an angular portion, then get back out to a non angular portion. Made things tricky. The key is to understand promises and how they work. Here's some examples of my real world code in a functioning e2e test. Hoping this gives you an idea of how to structure your tests. Probably some bad practice in this code, please feel free to improve upon this, but I know that it works, maybe not the best way.
To get to angular I use
var ptor;
var events = require('events');
var eventEmitter = new events.EventEmitter();
var secondClick = require('./second-click');
beforeEach(function () {
browser.driver.get('http://localhost:8080/');
},10000);
it("should start the test", function () {
describe("starting", function () {
it("should find the link and start the test", function(){
var elementToFind = by.linkText('Start'); //what element we are looking for
browser.driver.isElementPresent(elementToFind).then(function(isPresent){
expect(isPresent).toBe(true); //the test, kind of redundant but it helps pass or fail
browser.driver.findElement(elementToFind).then(function(start){
start.click().then(function(){ //once we've found the element and its on the page click it!! :)
ptor = protractor.getInstance(); //pass down protractor and the events to other files so we can emit events
secondClick(eventEmitter, ptor); //this is your callback to keep going on to other actions or test in another file
});
});
});
});
});
},60000);
While in angular this code works
describe("type in a message ", function(){
it("should find and type in a random message", function(){
var elementToFind = by.css('form textarea.limited');
browser.driver.isElementPresent(elementToFind).then(function(isPresent){
element(elementToFind).sendKeys(randomSentence).then(function(){
console.log("typed in random message");
continueOn();
});
});
});
},15000);
After exiting angular
browser.driver.wait(function(){
console.log("polling for a firstName to appear");
return browser.driver.isElementPresent(by.name('firstName')).then(function(el){
return el === true;
});
}).
then(function(){
somefunctionToExecute()
});
Hope that gives some guidance and helps you out!
browser.driver.wait(function() {
return browser.driver.isElementPresent(by.xpath("//a[#href='#/contacts']"));
});
This works for me too (without the timeout param)..
for more information, see http://angular.github.io/protractor/#/api?view=webdriver.WebDriver.prototype.wait
Thanks to answers above, this was my simplified and updated usage
function waitFor (selector) {
return browser.wait(function () {
return browser.isElementPresent(by.css(selector));
}, 50000);
}
Have you tried putting the ng-app in the <html> tag (assuming this part of code is under your control)? This solved a lot of initialization timing problems for me.
Best way to use wait conditions in protractor that helps to show proper error message to particular element if test case failed
const EC = ExpectedConditions;
const ele = element(by.xpath(your xpath));
return browser.wait(EC.visibilityOf(ele),9000,'element not found').then(() => {
ele.click();
});
I'm surprised that nobody has added this solution. Basically, if you are using modal dialogues you often get an element visible and available to click but not being clickable due to the modal dialogue being in front of it. This happens because protractor moves faster than angular and is ready to click the next element while angular is still closing the modal.
I suggest using
public async clickElementBug(elementLocator: Locator) {
const elem = await element(elementLocator);
await browser.wait(
async function() {
try {
await elem.click();
return true;
} catch (error) {
return false;
}
},
this.TIMEOUT_MILLIS,
'Clicking of element failed: ' + elem
);
}
browser.wait may sound too ordinary, but it's not!
browser.wait is the way to go. Just pass a function to it that would have a condition which to wait for. For example wait until there is no loading animation on the page
let $animation = $$('.loading');
await browser.wait(
async () => (await animation.count()) === 0, // function; if returns true it stops waiting; can wait for anything in the world if you get creative with it
5000, // timeout
`message on timeout` // comment on error
);
Make sure to use await
You can also use existing library called ExpectedConditions that has lots of predefined conditions to wait for
You can't imagine what you can do with it...
A few of my favorite ones:
wait until the number of browser's tab's is 2
// wait until the number of browser's tab's is 2
await browser.wait(
async () => {
let tabCount = await browser.getAllWindowHandles();
return tabCount.length === 2;
},
5000,
'the url didnt open in a new window'
);
wait until the loading animation is gone for at last 750ms
// wait until the loading animation is gone for at last 750ms
await browser.wait(
async () => (await this.$$loadAnimations.count()) === 0 && !(await browser.sleep(750)) && (await this.$$loadAnimations.count()) === 0,
5000,
`waiting timeout`
);
wait for ANY number of elements to be present
// wait for any number of elements to be present
async waitForElements($elem, timeout = 120000, start = +new Date()) {
let conditions = [];
for (let i = 0; i < $elem.length; i++) {
conditions.push(ExpectedConditions.presenceOf($elem[i]));
}
await browser.wait(
ExpectedConditions.and(...conditions),
remainingTimeout(timeout, start),
`wait for all elements`
);
}
// and use
await waitForElements([
$usernameField,
$passwordFiend,
$submitButton
])