I'm trying to get my script to go to a new page after successfully logging in, however, it attempts to go to the next page before login is complete.
const page = await browser.newPage();
page.goto('https://website/login');
page.once('load', async () => {
console.log('Page loaded!');
// Login script here
});
I can't figure out how to go to a new link after logging in though, my original solution was to just do;
// go to stats
await page.goto('https://www.website/stats');
However, since the page is already defined, this doesn't really wait for anything.
Is it possible to have a callback in the .click() function to go to a new page after?
The correct way to navigate via a click and wait for page load is to use Promise.all() and page.waitForNavigation(), like this:
await Promise.all([
page.waitForNavigation(),
page.click('#some-link')
]);
However, when you are navigating via page.goto(), the above is not necessary, because page.goto() waits for page load automatically.
In both cases, you can customize which event it waits for, using the waitUntil option, which defaults to the load event.
page.goto(url[, options])
url URL to navigate page to. The url should include scheme, e.g. https://.
options Navigation parameters which might have the following properties:
timeout Maximum navigation time in milliseconds, defaults to 30 seconds, pass 0 to disable timeout. The default value can be changed by using the page.setDefaultNavigationTimeout(timeout) method.
waitUntil When to consider navigation succeeded, defaults to load. Given an array of event strings, navigation is considered to be successful after all events have been fired. ...
Putting this together, an example of logging in would be:
const page = await browser.newPage();
await page.goto('https://website/login');
await page.type('input[type="email"]', 'foo#example.com');
await page.type('input[type="password"]', 'pass1234');
await Promise.all([
page.waitForNavigation(),
page.click('button')
]);
// Now we are on the home page (or wherever we end up after logging in)
Some other notes:
You may need to use page.waitForSelector() or other forms of waiting if the page load event fires before the input field is ready.
You could alternatively submit the login form by pressing the Enter key with the keyboard. This would still require the Promise.all() pattern, but means you don't need a selector for the submit button. You may need to .click() one of the input fields depending on how the page is implemented (e.g. if the page does not use autofocus).
Same as the solution below, however if i've understood correctly, you should have the click event first and then wait for navigation.
await Promise.all([
page.click('button'),
page.waitForNavigation(),
]);
Related
I'm trying to log the network calls in the browser. For my use case I require the browser to be in an open state and should be closed only with the scripts. I am currently using the page.pause() function to prevent the browser from automatically closing. Is there any other way to prevent the browser from closing automatically.
test('Verify home page Load event',async({page})=>{
//const browser = await chromium.launchPersistentContext("",{headless:false});
await page.goto("https://samplesite.com")
await page.on('request',req=>{
const requestUrl = req.url();
if(requestUrl.indexOf("google-analytics.com/collect")>-1){
console.log("Intercepted:->"+requestUrl);
}else{
req.continue
}
})
await page.pause();
})
I tried checking out this [link] (How to keep browser opening by the end of the code running with playwright-python?) for python but could not apply it to JS.
Similar to what was described in the answer to the python question, you need to keep your script alive somehow.
This answer describes a couple of ways to do that.
However, page.pause() is definitely the recommended approach- it exists precisely for this kind of situation where you need to inspect the browser while your script is executing. Your script also has some problems- as it stands when you encounter your target request you are logging something but not calling request.continue() (note that this a method, not a property). That will cause all requests to hang indefinitely until it is continued or aborted.
You probably want to do something like this:
await page.route('**/*', (route, request) => {
const rurl = request.url();
if (rurl.includes('google-analytics.com/collect')) {
console.log(`Intercepted request to ${rurl}`);
// Do other stuff?
}
route.continue();
});
It's not clear what you are trying to accomplish from your snippet- if you just need to wait for a particular request to fire, you can use either:
page.waitForRequest or page.waitForResponse, and do away with worrying about keeping the browser open.
I tried to use await page.pause() and it doesn't work for me, but I found the tricky way, and it works well, just put at the end of your test:
await new Promise(() => {})
Reference on the link.
You can try await Task.Delay(-1)
i'm using puppeteer and i need to keep refreshing the page until the requested element is live, "button" is the element i need.
i tried with the wait until but it is not working and gives me this error:
Error: Unknown value for options.waitUntil: JSHandle#node
This is what i tried
const [button] = await page.$x("//a[contains(., 'Denim')]");
if (button) {
await button.click();
}
await page.reload({ waitUntil: ["networkidle0", "domcontentloaded", button] });
The error is pretty explicit in this case. You are telling puppeteer to wait for these 3 things: ["networkidle0", "domcontentloaded", button]. The first 2 are acceptable options. The 3rd is not. button is a reference to a DOM element which you can use in your puppeteer code. (Or a JSHandle#node). As per the docs, this is not a viable option.
Just another point here: you don't share all your code so we must assume that this is happening in some sort of loop with some sort of timeout between calls. As is, this code will check for this button, click the button if found, and then reload the page exactly 1 time. Reload does not reload the page multiple times while searching for some feedback. It just reloads once. The waitUntil option just defines when the returned promise should resolve.
Good luck!
TestCafe 1.8.0,
Firefox 76.0 (any will do),
macOS 10.15.4.
My TestCafe tests (steps after useRole) are trying to execute even before useRole is completely done. Example:
import { Role } from 'testcafe';
const role = Role('http://example.com/login', async t => {
await t
.typeText('#login', 'username')
.typeText('#password', 'password')
.click('#sign-in'); // Redirects to http://example.com/session
});
fixture `My Fixture`;
test('My test', async t => {
await t
.navigateTo('http://example.com/')
.useRole(role);
.click()
.typeText('#search', 'query')
// Further tests.
.......
Role is being used -> user is redirected to: http://example.com/session at the end.
Once role execution is finished -> TestCafe goes back to My test AND it reloads http://example.com/ again.
This is a huge problem because between this one page reload for just a moment 'page is ready' and since TestCafe is working rapidly .click() is executed. Now page reloads so test execution stops. Once page loaded test brakes because it is trying to .typeText(...) without a .click().
Tried this solutions:
Wait until will work only first time. Second time when useRole used (from cache) code will execute even before this second page reload. .expect(getUrl()).eql('desiredurl', { timeout: 10000 })
As we all know hard wait .wait(3000) or slowing down tests .setTestSpeed(0.7) will work but it is not a good solution from code perspective. Tests still might fail from time to time and I need and want stability here.
With { preserveUrl: true } it would just reload http://example.com/session so it doesn't matter if this option is used. Reload is still happening.
Any ideas?
How can I let my test know to wait for exactly the same page reload without using any hardcoded waits, code sleep?
As far as I understand, the main issue is that the click action is executed on the wrong page for some reason. It does not wait until the page is completely reloaded. This behavior is unexpected. We would really appreciate it if you create a reproducible sample.
I agree that the use of wait or setTestSpeed is not a suitable solution.
I see that you tried to use assertions: .expect(getUrl()).eql('desiredurl', { timeout: 10000 }). I think this approach should work, but I cannot be sure because I couldn't reproduce the issue.
You can define your role with preserveUrl: true. Then, extract the useRole method as follows:
async function useRole (t, role) {
await t.useRole(role);
await t.expect(getUrl()).eql('http://example.com/session', {timeout: 10000 });
}
Now, you can use the new useRole(t, role) method, which will wait until the page is completely loaded.
I am using protractor to do e2e tests in an Angular 8 app and I am currently having trouble making an 'each' loop work as I need.
In my page object I have created a method that returns an ElementArrayFinder.
public getCards(): ElementArrayFinder {
return element.all(
by.css(
`${this.baseSelector} .board-body cs-medication-card`,
),
);
}
For each of the elements returned I want to perform a set of actions that consists in clicking in a button that opens a menu list (while the menu is open there is an overlay element over all the view except the menu list) and pick one of the options. In my test I have this:
await page.board
.getCards()
.each(async (el: ElementFinder) => {
await until.invisibilityOf(await page.getOverlay());
await el
.element(by.css('.card-header .actions'))
.getWebElement()
.click();
await expect(page.isItemInMenu('X')).toBeTruthy();
await page
.getMenuItemByLabel('X')
.getWebElement()
.click();
});
I was expecting that for each card it would click the actions button, check if the option is in the list and click in the option.
What is happening is that it seems that protractor is trying to do everything at same time, since it says it cannot click on the actions button because the overlay is over it. The only way to the overlay be over the button is if the action in the previous iteration is not complete. I have already threw in the ExpectedCondition to wait for the overlay to be invisible but no luck. If only one element is returned by the ElementArrayFinder it does what is supposed.
I am out of ideas, any help would be much appreciated.
Protractor's each is asynchronous.
It's fine when you need to perform some action over an array of element (e.g get text or count) but it's not the best choice when you need to perform some kind of scenario.
I'm suggesting to use any other way like for loop.
Another workaround (which again might not work because of async nature of .each) is FIRST to wait for overlay to appear and the wait for it to disappear.
The each function is almost of no use. It even doesn't wait for the promise returned by each Function.
await $$('...').each((ele, index) => {
return ele.$('.fake_class').click().then(() => {
console.log(`the item ${index} is clicked.`)
return browser.wait(5000)
});
});
I think there are some issues in the function of each.
getWebElement() is unnecessary
await expect(page.isItemInMenu('X')).toBeTruthy(); seems wrong
await until.invisibilityOf(await page.getOverlay()); I'm not sure it work well.
Please try following code:
await page.board
.getCards()
.each(async (el: ElementFinder) => {
// await until.invisibilityOf(await page.getOverlay());
browser.sleep(10*1000)
await el
.element(by.css('.card-header .actions'))
.click();
// expect(await page.isItemInMenu('X')).toBeTruthy();
await page
.getMenuItemByLabel('X')
.click();
})
.catch((err)->{ // use catch() to capture any runtime error in previous each
console.log('err: ' + err)
})
If above code worked, you can revert back the expect and run again, then remove browser.sleep() and revert back your await until.invisibilityOf(....) and run again.
Protractor hangs completely when trying to get any element property after logging in (idk if it's related to logging in or related just to switching pages).
it("Should get location of main container", async function() {
await LoginPage.validLogin();
// Works and logs in the dashboard
await browser.sleep(3000);
// Get the main container by class name
const container = await element(by.css(".main-container"));
// Logs properly the element functions (as expected)
console.log(container);
console.log(await container.getLocation()); // Hangs here
});
In this case, I'm trying to get the location of the main container element on the page. The first console.log fires and shows properly, while the second hangs completely, so I get the script timeout. Increasing the timeout time doesn't help at all...
I found online that misusing $timeout in AngularJS instead of using $interval may lead to this strange behaviour, but I really can't skim through the entire (very big!) project's codebase to change everything hoping that it just works, not to talk about the external libraries using $timeout.
I have SELENIUM_PROMISE_MANAGER = false; in my Protractor config so I disabled the built-in Control Flow in order to manually manage the promises using async/await, but even if I use the built-in Control Flow without using async/await I get the very same behaviour and error.
I'm using Jasmine as testing framework.
Maybe I'm missing something? Any help would be much appreciated, thanks!
This is caused by the fact that angular is not stable. Have a look at the link below. I found my answer there. When the page you are trying to test is open go to the browser dev tools and type in the console getAllAngularTestabilities(). There are a few properties here that indicate whether angular is ready to be tested. hasPendingMicrotasts needs to be false. hasPendingMacroTasks needs to be false. isStable needs to be true. I put a screenshot below. In my screenshot hasPendingMacrotasks is true and it must be false. So the page I looked at was not ready to be tested.
Failed: script timeout: result was not received in 11 seconds From: Task: Protractor.waitForAngular() - Locator: By(css selector, #my-btn)
Try something like this:
it("Should get location of main container", async function() {
await LoginPage.validLogin();
const container = await element(by.css(".main-container"));
await browser.wait(protractor.ExpectedConditions.presenceOf(container), 5000, 'Element taking too long to appear in the DOM');
await console.log(await container.getLocation());
});
I don't think that getLocation() exists in the Javascript bindings for selenium. I couldn't find it in the source code anyway. So that promise will never return which is why it hangs. But I the you can achieve basically the same thing with getRect():
it("Should get location of main container", async function() {
await LoginPage.validLogin();
const container = await element(by.css(".main-container"));
await browser.wait(protractor.ExpectedConditions.presenceOf(container), 5000, 'Element taking too long to appear in the DOM');
await console.log(await container.getRect());
});