I have an application that uses WebBrowser control to go to a website, and try to log in. I found a few questions that seem similar but are not the same, and their answers do not help.
The website prompts for the information in steps, first prompts for the username, then prompts for the challenge question, then prompts for the password. To debug, I've created three buttons, one for each step above, so that I can separate to see what works and what doesn't. (In the end this will be fully automated, without any user interaction).
The first button navigates to the site and enters the username and submits -- that works fine. The second button is supposed to enter the answer to the challenge question, but the site returns saying that the entry is invalid. The actual HTML looks like this:
<input name="IDToken1" tabindex="1" id="IDToken1" style="padding: 2px; border: 2px solid rgb(240, 0, 0); border-image: none; width: 226px; color: black;" type="password" size="40" maxlength="40" value="" autocomplete="off">
The code to enter the challenge question looks like this:
var browser = (WebBrowser)_interactor.WinForm.Controls["browser"];
var txt = browser.Document.GetElementById("IDToken1");
if (txt != null)
{
txt.SetAttribute("Value", "answer-to-question");
// set event handler on click
//btnContinue.AttachEventHandler("onclick", (sender, args) => OnElementClicked(txt2, EventArgs.Empty));
var btnLogin = browser.Document.GetElementById("signIn");
btnLogin.InvokeMember("click");
}
I verified that the value is properly set by adding an event handler to the submit button, then checking the input value at the point of the form submission. The value looks to be set at that point, but when the form is submitted it returns an error, stating that the value is not valid.
I've also tried entering it by submitting the actual form, like so:
browser.Document.Forms["challengequestion"].Children["IDToken1"].SetAttribute("Value", "answer-to-question");
browser.Document.Forms["challengequestion"].InvokeMember("submit");
I get the same response.
I've also tried doing the cache clear as suggested here, which does not seem to help.
Finally, I've tried manually entering the answer and clicking the submit button, via the WebBrowser control on the application.
What am I missing? If I check the value of the input element on form submit and it has the proper value, how is it that the site is not able to see it? I enter the same value manually on the control and it works fine. Any ideas?
Related
I'm using Selenium from C# to try and periodically do a search on a website and pull in updated data. Unfortunately the website needs a login I can't make public so I can't actually give any kind of reproducible test case.
Using the Selenium IDE I've made a script that successfully logs in and pulls the data, so the task is possible. However when exporting that script to C#, there's a failure when trying to use an iframe that appears during the process.
By reading around I've discovered I need to add code to explicitly find and switch to the iframe, which I've done;
driver.SwitchTo().Frame(driver.FindElement(By.Id("popup_iframe")));
Inside that frame is an area to the left with items to click. Each item when clicked changes the visible controls to the right. When I break at this point and click manually, everything works as expected. If I call the .Click on the IWebElement representing the object, nothing happens so the next step in the program fails because it gets another object, tries to click it and an exception's thrown because the object isn't visible.
I'm leaving 5s delays between operations in case delays are needed.
The code on the website in this area is;
<div id="ctl00_popup_workarea_left_container" class="popup-workarea-left-container" style="overflow: auto; height: 337px; display: block;">
<a id="LBTN1" class="navbutton navbutton_Disabled_Selected">Basic Search</a>
<a id="LBTN2" class="navbutton">Advanced Search</a>
<a id="LBTN3" class="navbutton">Permit Holder Search</a>
<a id="LBTN4" class="navbutton">Assessor Search</a>
</div>
The code I'm using to get and click the IWebElement is
driver.FindElement(By.Id("LBTN3")).Click();
I've also tried
Actions act = new Actions(driver);
act.MoveToElement(driver.FindElement(By.Id("LBTN3"))).Click().Perform();
Aside from the fact these don't throw exceptions, I've confirmed I'm getting an IWebElement interactively;
driver.FindElement(By.Id("LBTN3"))
{Element (id = f42435b5-139c-4334-be12-4a1f9f48221f)}
Coordinates: {OpenQA.Selenium.Remote.RemoteCoordinates}
Displayed: true
Enabled: true
Id: "f42435b5-139c-4334-be12-4a1f9f48221f"
Location: {X = 0 Y = 112}
LocationOnScreenOnceScrolledIntoView: {X = 0 Y = 112}
Selected: false
Size: {Width = 200 Height = 29}
TagName: "a"
Text: "Permit Holder Search"
WrappedDriver: {OpenQA.Selenium.Firefox.FirefoxDriver}
So I'm getting the element. The HTML appears to show that it really is the element- I'm not accidentally using the ID of something containing it. The original script created in the Selenium IDE works fine- I just can't automate it so it kicks off unattended in the early hours. I know this is vague because I can't give people access to play to reproduce it, but does anyone have an idea of what could be going wrong and how to fix?
For completeness I thought I'd better put an answer here.
I never identified why none of the attempts I made worked, but I discovered that injecting Javascript into the page to click the element I wanted to click did work. So assuming .FindElement() is able to get the element to click, the following snippet worked for me in a number of places where IWebElement.Click() proved uncooperative.
IWebElement ibtn3 = driver.FindElement(By.Id("LBTN3"));
IJavaScriptExecutor executor = (IJavaScriptExecutor)driver;
executor.ExecuteScript("arguments[0].click();", ibtn3);
I have the following label in vb.net:
<div style="height: 20px; vertical-align: text-bottom;">
<div style="float: left; width: 86%; text-align: right">
<b>Sub-Total</b> (a) through (e) above:
</div>
<div style="float: right; padding-right: 10px">
<asp:Label runat="server" ID="lblRateableEmployeesSubtotal" CssClass="lblRateableEmployeesSubtotalCls" Text="0"></asp:Label>
</div>
</div>
And I am dynamically adjusting this value in from a javascript function:
$('.lblRateableEmployeesSubtotalCls').text(numberWithCommas(subtotal));
This function is called whenever certain textboxes are changed. numberWithCommas just formats the text into a number format (x,xxx).
When I am trying to save these values in my codebehind, for some reason the labels' text is still showing as "0"! Even though it is clearly updated on the screen.
If lblRateableEmployeesSubtotal.Text > "" Then .TotalEmployees = CInt(lblRateableEmployeesSubtotal.Text)
Any idea why this could be happening?
ASP doesn't bother to include this information when it performs a postback, specifically because the whole idea of a label isn't to accept input from the client. When posting back ASP doesn't send the entire DOM; it only sends the information for fields specifically designed to accept input from the client.
In this case, the appropriate tool to use here is a Hidden control. Add an asp:HiddenField control to the page, set that control's value in your JavaScript code (you can set the label too, of course) and then inspect the value of the hidden field on the server side.
Only Input-type controls are posted back to the server. One possible solution in your case is simulate label with read-only flat-styled TextBox control.
That jQuery function only changes the text on the client, server doesn't know anything about any changes to the page until you send a POST request, but in that request by default only values of the INPUT elements are sent.
If you want to alter some things on the server using javascript on the client you're looking for the AJAX technology. If not, consider obtaining the value on the server from an input control like text box or make some hidden fields which will be updated in the same time as your label.
Sorry for my english.
I've been stuck trying to do some automating tests with AOL mail service (sending text messages). I want to insert text into a textbox and submit it but it fails all the time. In AOL web interface, I have to type the message, then hit "Enter" to submit or send it.
Here's how the textbox looks like:
<div id="dijit__Widget_66" class="inputContainer" layoutalign="bottom" data-dojo-type="ws/widget/Pane" widgetid="dijit__Widget_66">
<textarea class="wsInput" tabindex="201" data-dojo-attach-event="onkeyup: onKeyUp, onkeypress: onKeyPress, onblur: onBlurTextarea" data-dojo-attach-point="messageInput" style="width: 316px;"></textarea></div>
My code of selecting and inserting text into textbox area works fine:
IWebElement ele = driver.FindElement(By.CssSelector("div[id*='dijit__Widget'].inputContainer>textarea.wsInput"));
ele.Clear();
ele.SendKeys("Hello");
But thing turns to be complex when I want to submit the text, neither of these work:
ele.Submit();
ele.SendKeys(Keys.Enter);
SendKeys(Keys.Enter) only adds a new line into the textbox instead of submitting and sending the message.
I'm desperately looking for help!
**I've discovered that the command Keypress - Value: "013" (Enter key) in Selenium IDE works. But it only works with Selenium IDE, when exporting to Webdriver, I receive this error:
// ERROR: Caught exception [ERROR: Unsupported command [keyPress | css=div[id*='dijit__Widget'].inputContainer>textarea.wsInput | 013]]
Way too late for this post but for anyone else looking to solve this. Check and see if there is a function or event that is triggered when you submit text to that specific field. If there is, see if you can remove it by executing a script on the IDE.
It will look something like the image below:
Here's how you can remove events using JS:
document.getElementsByTagName('input')[input_index].attributes.removeNamedItem("eventname");
Have you tried using TAB, not sure if this will work but you can give it a try :
ele.SendKeys(Keys.Tab);
I have found this great class for sending POST request to a webpage. Now I tried it on a test page with single form and it works. But now I would like to do a POST request to a page, that has multiple forms on the same page. Is it possible to select specific form, if it is defined with name?
For example, like this:
<form method="post" action="index.php" target="_parent">
...
</form>
<form method="post" action="index.php" name="login" target="_top" class="login">
// This is the form that I want to post data
...
<input value="Go" type="submit" id="input_go" />
</form>
EDIT:
This solution provided by Anfurny does not work:
post.PostItems.Add("login", "input_go");
I have updated code, the button that submits has only id defined.
EDIT2:
I have found a good example of a page with two forms. phpMyAdmin demo, it has two forms, one is for selecting different language, the other is for login. This demo has:
username = root
password is empty
Now I have tried to login programmatically with this code (it uses class that I have posted the link on start of the page):
// First we create an instance of a class PostSubmitter.
PostSubmitter post = new PostSubmitter();
// We set the URL to which the POST should go.
post.Url = "http://demo.phpmyadmin.net/STABLE/index.php";
// We add items (password and username).
post.PostItems.Add("pma_username", "root");
post.PostItems.Add("pma_password", "");
// Also tried to add to which login form should post and the
// value set to id of the submit button, no luck.
//post.PostItems.Add("login_form", "input_go");
// Here we set the method.
post.Type = PostSubmitter.PostTypeEnum.Post;
// Getting back the result.
string result = post.Post();
// Writting it to the file.
TextWriter tw = new StreamWriter("C:/response.txt");
tw.WriteLine(result);
tw.Close();
The response.txt file has the same content as the login page, where it should have the code of the welcome page. Now how can I change code, to successfully login?
Usually when you have multiple forms posting to the same place, that you would differentiate them with the button that was clicked. Can you not do the same and add a virtual button (action) to your post list of values?
Have you tried doing something like this:
post.PostItems.Add("FORM_NAME","SUBMIT_BUTTON_NAME ");
?
Where FORM_NAME is the name of the form you wish to submit, and SUBMIT_BUTTON_NAME is the name of the submit button you wish to pretend was pressed (if more than one is present).
I have a button which right now is set as a type "submit". This calls the controller, execute some code and returns back to the view. When I use jquery to hide the button, I see that when I click on the button, what I have hides the button but as soon as the view is returned, the button is not hidden no more. Whereas with type "button", when I click the button, this hides the button but doesnt execute the code in the controller. Is there a way to hide the type "submit" button so when the view returns, the button is still hidden?
$('#btnAdd').click(function() {
$('#btnAdd').hide();
});
<input type='submit'> creates a button that submits a form to a server and triggers your server code. If you want the button hidden when the page comes back, you need to add logic to your page to do that. How you do this will depend on your server technology (php, .net, etc.).
The reason the behavior with <button> is different is that <button>s don't submit the form (unless you add more code to make them do that)...so the above mentioned stuff never happens. It's not so much that a <button> stays hidden as much as the page never changes/reloads. If you added code to the <button> to make it refresh the page, it'd reappear, too.
The button is shown because the page is newly displayed after submiting the form. Your "old" page, where clicked and hid the button is history.
What do you want?
Pressing a button, do something on serverside, do not change your current page:
Use a button of type button, use ajax to call the server side.
Or use a button of type submit and do what Pablo said http://en.wikipedia.org/wiki/Post/Redirect/Get on serverside.
Pressing a button, do something on serverside, give user feedback:
Use <form method="post" to markup your form. Use a submit button to call the server side. On serverside hide the submit button, if it is called by method post (calling a page with link or typing it into the address field is calling it with method = get).
What is the difference between type submit and type button?
A submit button works without javascript to send some input to serverside. The surrounding form is send to the server and the response is rendered in browser.
A button button needs a javascript onclick handler, a javascript function. The onclick handler is called when the user pressed the button.
Since the page will be reloaded upon pressing the submit button, the button will reappear. One quick and dirty to get what you want is...
First, create a hidden field
<input type="hidden" id="hidden" value="" />
Then, when you press submit, in a click event for submit button, do something like this..
$('#submit').click(function() {
$('#hiddenField').val("1");
$('#form').submit();
return false;
});
Now in your controller, use the value of hiddenField of pass some variable to the view which can be used like this...
<?php if($hidden == "1"): ?>
<input type="submit" id="submit" value="Submit" />
<?php endif; ?>
As far as the button not submitting the form is concerned, it won't submit the form, until you submit the form yourself on the click event of button. Something like this...
$('button').click(function() {
$('#form').submit();
});
Of course, as I mentioned this is a quick and dirty way to implement the function you want, there are better ways - using AJAX, also the implementation can change depending on what server side language you use (I used php over here).
When your form is submitted and your controller process the data, if certain criteria is met, you can set a temporary session variable or a cookie in server side code. So, basically the page will check for this variable on every page load. Example in PHP:
if( empty($_SESSION['temp']['hideSubmitButton']) ) {
$submitButton = '<button type="submit">Normal Button</button>';
} else {
$submitButton = '<button type="submit" disabled="disabled">Disabled Button</button>';
// or $submitButton = '';
}
But then you have to decide when to unset() the $_SESSION['temp'] or $_COOKIE['temp'] variable.