Using playwright I am trying to do a simple login operation on a website
The steps are simple :
click on a Login button
Try to enter an email (Playwrights fails here)
I can see that the selector is getting to the correct field but fails to enter the username into the input (and times out instead)
async def login(url, username, password):
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto(url)
print(await page.title())
# Click login
await page.click('xpath=/html/body/nav[2]/div/ul/li[4]/a')
await page.pause()
# Wait for email popup
# await page.type('[placeholder="Enter your email address and hit enter"]', username)
await page.type('//input[#type="email" and not(#disabled)]', username, delay = 10) # fails here
await page.click('xpath=/html/body/app-main/app-widget/screen-layout/main/current-screen/div/screen-login/p[2]/button')
asyncio.run(login(url, username, password))
The error that I get is a timeout :
return await self.inner_send(method, params, False)
File "/Users/work/Documents/Code/venv/lib/python3.9/site-packages/playwright/_impl/_connection.py", line 63, in inner_send
result = next(iter(done)).result()
playwright._impl._api_types.TimeoutError: Timeout 30000ms exceeded.
=========================== logs ===========================
waiting for selector "//input[#type="email" and not(#disabled)]"
============================================================
This essentially means playwright was waiting for this element and no luck finding it, so it explodes after the default 30 seconds
I tried using
different xpaths
CSS selectors
Text selectors
I also tried using selenium and strangely the behavior is the same.
The website in question: https://epaper.thehindu.com/
That particular element on the website that I am trying to type into
<input _ngcontent-ttw-c171="" fieldloginemail="" type="email" placeholder="Enter your email address and hit enter" id="email" class="sub margin-0 empty" autocorrect="off" autocapitalize="off">
The question is : why playwright cannot find this input? (Playwright Inspector can clearly see it in broad daylight)
That whole element is iFrame. Get that iFrame first then type.
https://playwright.dev/docs/release-notes#frame-locators
As #senaid already pointed out, the element you want to access is inside an iframe. So you can use the iframe locator as given in the link provided by senaid and then use it to login.
loc = await page.frameLocator('#piano-id-u4ajR').locator('#email');
await loc.type("username");
Check for if any iFrame is there. If iframe is not there then you can try this.
const element = page.locator("//input[#type="email" and not(#disabled)]").first();
await element.focus();
await element.click();
await page.locator("//input[#type="email" and not(#disabled)]").first().fill(textTobeEntered)
Related
Using this plugin as a reference, I have Flex configured to be able to send a call to a Twilio Studio IVR, after an agent has accepted a call.
I'd like to be able to send an incoming call back to Studio when an agent rejects a call (i.e. as soon as they click the reject button). I'm trying to do this by adding a listener to the plugin's init method:
flex.Actions.addListener("afterRejectTask", async (payload, abortFunction) => {
let url: string = payload.task.attributes.transferToIvrUrl;
let menu: string = 'hangup';
await request(url, { CallSid: payload.sid, menu });
});
See here for the full context -- I'm pretty much using that exact code, with the addition of this listener.
I'm getting this error message, and the call is not transferred anywhere.
twilio-flex.unbundled-react.min.js:1574 Error on afterRejectTask: SyntaxError: Unexpected token < in JSON at position 0
Here's additional context from the console, if that's helpful:
Additional info:
The url being requested is a Twilio function, which successfully returns a response like this:
<Response>
<Enqueue workflowSid="WWcc1a650e4175089538d754a6c2e15a98">
<Task>{"transferToIvrUrl": "https://my-twilio-function-service.twil.io/studio-flex-transfer-helper"}</Task>
</Enqueue>
</Response>
Any advice would be appreciated.
Ah, ok, so looking at that plugin I found the example Twilio Function that works with it. From what I can tell, this function is intended to be used in two places, either in Studio to transfer the call to Flex (though I'm not sure it's needed for that) or from Flex to transfer the call back to Studio. The thing that triggers the different response is whether you pass an argument called transferToIVRMenu with the request.
Your current request is not passing that argument, you currently have:
await request(url, { CallSid: payload.sid, menu });
which looks similar to the original plugin's request:
await request(transferToIvrUrl, { CallSid: call_sid, transferToIVRMenu });
The difference is in the second property in the object. When you just pass the name of the variable in an object, it expands to call the property the same name as the variable and set the value to the value within the variable. So the original request expands out to:
await request(transferToIvrUrl, { CallSid: call_sid, transferToIVRMenu: transferToIVRMenu });
but your request only expands to:
await request(url, { CallSid: payload.sid, menu: menu });
So you are passing a parameter called menu not transferToIVRMenu and that triggers the Function on the back end to return TwiML and not to update the call.
To fix this, you can update your plugin code to send the transferToIVRMenu parameter, like:
flex.Actions.addListener("afterRejectTask", async (payload, abortFunction) => {
let url: string = payload.task.attributes.transferToIvrUrl;
let menu: string = 'hangup';
await request(url, { CallSid: payload.sid, transferToIVRMenu: menu });
});
How to get value using grabAttributeFrom in codecept. It returns only object,promise. How to get the attribute value from it.
let userid_xpath = await I.grabAttributeFrom("//div[#class='mat-form-field-infix']/input[contains(#id,'mat-input')]","id");
var userid = "//*[#id='"+userid_xpath+"']";
I.fillField(userid,"username")
If i use like above and execute the test, i dont get any error. but i saw the debug panel displays like below
Emitted | step.after (I grab attribute from "//mat-label[contains(text(),'UserId')]//ancestor::label//ancestor::span//ancest...
Emitted | step.before (I fill field "//*[#id='[object Promise]']", "username")
How to get the attribute value and use it in a string. If i pass the userid_xpath variable in assert ; it works. but my requirement is to pass it in a string and then use it.
Look carefully in documentation
It returns what it said to return.
Returns Promise<string> attribute value.
Communication between codeceptjs and browser asynchronous.
Codeceptjs looks for synchronisation of actions sent to browser. But it can't look for browser response, so you should manually tell for codeceptjs to wait for result.
In CodeceptJS such implementation is made by Promises
To get value from Promise you should await it by let userid_xpath = await I.grabAttributeFrom(someSelector);
But awaiting of Promise will end up execution due to inner implementation of Codeceptjs. So, as said in docs:
Resumes test execution, so should be used inside async with await operator.
mark you test function as async:
Scenario("should log viewability point, when time and percent are reached", async (I) => {
I.amOnPage(testPage);
const userid_xpath = await I.grabAttributeFrom("//div[#class='mat-form-field-infix']/input[contains(#id,'mat-input')]","id");
const userid = "//*[#id='" + userid_xpath + "']";
I.fillField(userid, "username")
});
When I submit an auth call to google to I get the popup from the google window, then when I submit my credentials and press submit it forwards on to something like
https://auth.firebase.com/v2/FIREBASEREF/auth/google/callback?state= etc
And then all I get is a blank (blue background) screen.
$('#loginButton').click(function() {
myFirebaseRef.authWithOAuthPopup("google", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
alert("Login Failed, please try again.")
} else {
console.log("Authenticated successfully with payload:", authData);
myUserID = authData.uid;
}
});
});
The same is also happening when trying to auth with Github, I can press "submit" or "login" etc. and then it just loads a blank page at auth.firebase.com
Any ideas?
Thanks
FWIW if anyone ends up on this issue: I was getting the exact same thing... the oauth popup was blank and not doing anything. I fixed it by removing the async/await from my form handler:
<form onSubmit={this.onSubmit}>
<button type="submit">Sign In with Google</button>
</form>
...
onSubmit = async event => {
await this.auth.signInWithPopup(this.googleProvider)
.then(....
Sometimes this should be an permissions issue related with the domain where you're trying to do the request like localhost or 127.0.0.1.
In that case, Go to Firebase Console -> Authentication -> Sign-In Method then scroll down to Authorized domains and add your current domain from you're trying to sign in e.g. 127.0.0.1.
Note: This is just for dev purposes, in case you want to deploy your app to Prod, you shouldn't have this configuration. For multiples environments you should check here
Another Note: If you're working with Javascript, you might add an event.preventDefault() before to call firebase.authWithOAuthPopup(...) to be able to obtain the result of the promise that return the authWithOAuthPopup function.
My solution was adding inAppBrowser from cordova and it no longer returned blank page.
ASP.NET MVC's AntiForgeryToken mechanism is based on the current HttpContext.User. It uses that value to construct the token when you call Html.AntiForgeryToken(). Basically it is OK (see an explanation in the last paragraph here) but a problem arises when you log in through an Ajax call.
In my code, when a user logs in, the credentials are sent as a Json object in Ajax (the AntiForgeryToken hidden field value is also sent inside the Json), the server authenticates the user, applies FormsAuthentication.SetAuthCookie(), and returns a Json result which contains some user-specific data. In that way, I can avoid full page refresh upon login.
The problem is that every subsequent Ajax request to the server now fails upon ValidateAntiForgeryTokenAttribute, because it now expects an anti-forgery token that is incompatible with the anti-forgery cookie.
How can I get a valid anti-forgery token to put in the client's hidden field so every Json request after login will succeed?
I tried to get a new hidden-field token manually (using AntiForgery.GetHtml() on the action, extracting the token string itself, returning it to the client in Json and placing it in the anti-forgery hidden field manually in JavaScript) but it does not work - a subsequent Ajax call fails on the ValidateAntiForgeryTokenAttribute on the server.
In fact, every call to AntiForgery.GetHtml() (which is essentially what Html.AntiForgeryToken() helper does) produces a different token, which invalidates the previous one.
I also tried to set HttpContext.User = new GenericPrincipal(new GenericIdentity(email), null); as detailed here, but it doesn't work.
Note: This solution doesn't work for me, because of my specific situation: An Ajax login which changes the user identity on the server and hence every token that was generated before the login is invalid; this solution also doesn't apply because it addresses a different problem.
You will need to clear and redo any existing form token you have upon login. This means your login code will have to either refresh the current page (kinda kills the ajax portion of it eh), your own token implementation, or you will need to refresh your token. It is possible to request a partial view, extract the token, and update your form. You could actually have a restful url which returns nothing but a token to an authenticated user. One may argue this is a security issue, but I don't believe so because it is simply an easier way to get a token rather than requesting any view -partial or otherwise.
You should be able to easily get the token instances to replace via:
var token = $('input[name=""__RequestVerificationToken""]');
EDIT
After re-reading a few more times - I question
Why would you have a token on the form if the user isn't logged in. You allow the same form to be 'operated' while not logged in and logged in? Most sites on the net even in this case will redirect for a login. Am I understanding this correctly? If so, you may want to consider skipping the token here or use a second type of token for unauthenticated users. You I believe are saying an unauthenticated user can already submit something in the application - again if I understand this correctly - without being authenticated.
Ok, what I did was combine the answer from here: jQuery Ajax calls and the Html.AntiForgeryToken() with a partial. I'm using knockout but for those of you not familiar with it you should still be able to follow along pretty easily.
First my html:
<form id="__AjaxAntiForgeryForm" action="#" method="post">#{Html.RenderPartial("AntiForgeryToken");}</form>
<div id="loginTestView">
<button data-bind="visible: signedIn() == false,click: signIn">Sign In</button>
<button data-bind="visible: signedIn, click: signOut">Sign Out</button>
<form>
<button data-bind="click: testToken">Test Token</button>
</form>
</div>
The main difference being that instead of #Html.AntiForgeryToken() I have a AntiForgeryToken partial that contain #Html.AntiForgeryToken().
So to really clarify I now have a AntiForgeryToken.cshtml file with just:
#Html.AntiForgeryToken()
Now when you sign in/out you need to update the token so the javascript/jquery looks like:
$(document).ready(function () {
AddAntiForgeryToken = function (data) {
data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
return data;
};
var viewmodel = function () {
var vm = this;
vm.signedIn = ko.observable(false);
vm.signIn = function () {
$.post('Home/SignIn', function () {
vm.signedIn(true);
$.get('Home/GetAuthToken', function (newToken) {
$('#__AjaxAntiForgeryForm').html(newToken);
});
});
};
vm.signOut = function () {
$.post('Home/SignOut', function () {
vm.signedIn(false);
$.get('Home/GetAuthToken', function (newToken) {
$('#__AjaxAntiForgeryForm').html(newToken);
});
});
};
vm.testToken = function () {
$.post('Home/TestToken', AddAntiForgeryToken({ stuff: 'stuff' }));
};
};
ko.applyBindings(new viewmodel(), $('#loginTestView')[0]);
});
The main thing to pay attention to here is that the $.get needs to happen after the $.post to signIn/Out. This code could be cleaned up a bit, but that's the main take away. If you don't then since the requests are asynchronous the $.get could (and probably will) come back before you are actually signed in.
That should do it. I haven't run into any other times when the token is updated but it would just require just another call to update the partial.
I'm trying to get user profile information upon logging in with google-oauth2. User successfully logs in and i can get the access_token and can refresh the token when needed.
Though i could not manage to get any information about the user despite reading the docs and trying for hours.
From "Retrieving profiles" section of developers guide :
https://www.google.com/m8/feeds/profiles/domain/domainName/full
should be enough. i've tried with "gmail.com", "google.com", "gmail", "google", "orkut", "orkut.com" , myregisteredappsdomainname (and .com) as domainName. i've also tried it with
https://www.google.com/m8/feeds/profiles/domain/domainName/full?access_token=access_token_for_user
all i managed to get was 401 error, where it says "That’s an error.". Regarding 401 error, I've refreshed the token and tried again with new token, but kept getting 401s.
How can i get profile information and image address for user upon logging in?
The scope you're looking for is:
https://www.googleapis.com/oauth2/v1/userinfo
This has been already answered here
I was getting similar errors requesting profiles even after correctly defining the scope and getting access tokens etc.. The trick for me was to include the API version on my requests. See here for more info http://code.google.com/googleapps/domain/profiles/developers_guide.html#Versioning
Maybe little late yet could this be helpful to someone. Below is the working code I wrote to get gplus user profile
In HTML below markup will display goolge signIn button
<span id="signinButton">
<span
class="g-signin"
data-callback="signinCallback"
data-clientid="YOUR GPLUS CLIENT ID"
data-cookiepolicy="single_host_origin"
data-scope="email">
</span>
</span>
Below is the java script
var access_token;
/**
* Called when the Google+ client library reports authorization status.
*/
function signinCallback(authResult) {
access_token = authResult.access_token;
gapi.client.load('plus', 'v1', function () {
gapi.client.plus.people.get({ userId: 'me' }).execute(printProfile);
});
}
/**
* Response callback for when the API client receives a response.
*
* #param resp The API response object with the user email and profile information.
*/
function printProfile(resp) {
if (resp.code != 403) {
console.log('name:' + access_token.givenname);
console.log('last name:' + access_token.lastname);
console.log('email:' + access_token.emails[0]);
console.log('gender:' + access_token.gender);
console.log('profile image url:' + access_token.image.url);
}
}
Please make sure that you load google api javascript asynchronously within the body tag as below
<script type="text/javascript">
(function () {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/platform.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
</script>
To handle logout refer to the answer I provide in below link, you will need to store access_token in backend so that during logout call this to be used, in my case I have stored in session and getting through ajax call
gapi.auth.signOut(); not working I'm lost
Hey why don't you look at the code given at:
http://www.codeproject.com/KB/aspnet/OAuth4Client.aspx
It definitely helps you. The project is actually an oauth playground to send correct oauth header to correct endpoints.