I am writing automation scripts using WATIR and WATIN. Watir has something called page checkers, which are code snippets that run on each page load. Is there something similar in WATIN ? I want a piece of code to run on each page load. Generally this is used to check for page errors or page loading images.
It is not really that easy to tell when page loads. I quickly googled about that page checkers in Watir, that you mentioned and found an article about page checkers in Watir. See first comment bellow the article. AFAIK it's really similar in WatiN.
Unfortunately, I don't see any similar functionality in WatiN (no event is fired after internal call to WaitForComplete. The easiest thing you could do is to subclass eg. IE class:
class MyIE : IE
{
public MyIE(string url) : base(url) { } //TODO: add constructors
public override void WaitForComplete(int waitForCompleteTimeOut)
{
base.WaitForComplete(waitForCompleteTimeOut);
Console.WriteLine("Page has been loaded");
}
}
However, the situation will be similar to described in mentioned comment (runs a lot more regularly than just page load).
I think that better approach would be using Page class from WatiN library. It is well documented. Example for watin.org webpage:
var ie = new MyIE("http://watin.org/");
var homePage = ie.Page<HomePage>();
Console.WriteLine(homePage.FirstFeature);
homePage.DocumentationLink.Click();
var documentationPage = ie.Page<DocumentationPage>();
Console.WriteLine(documentationPage.FAQLink.Url);
To run that code you need following classes:
abstract class WatiNBasePage : Page
{
[FindBy(Id = "header")]
public Div HeaderDiv { get; set; }
public Link HomeLink { get { return HeaderDiv.Link(Find.ByText("Home")); } }
public Link DocumentationLink { get { return HeaderDiv.Link(Find.ByText("Documentation")); } }
protected override void InitializeContents()
{
base.InitializeContents();
VerifyDocumentProperties(UnverifiedDocument, errorMessage => { throw new Exception(errorMessage); }); //TODO: modify if needed
}
protected override void VerifyDocumentProperties(Document document, Page.ErrorReporter errorReporter)
{
base.VerifyDocumentProperties(document, errorReporter);
if (!HomeLink.Exists)
errorReporter("HomeLink not exists");
//TODO: more checks here
}
}
class HomePage : WatiNBasePage
{
[FindBy(Id = "features")]
public Table FeatureTable { get; set; }
public string FirstFeature { get { return FeatureTable.Span(Find.First()).Text; } }
}
class DocumentationPage : WatiNBasePage
{
[FindBy(Text = "Frequently Asked Questions")]
public Link FAQLink { get; set; }
}
Basically you need to implement VerifyDocumentProperties. Above code will check if HomeLink exists, but maybe you would like to check if DocumentationLink exists etc. The second thing is to modify call to VerifyDocumentProperties. Now, if verification fails, Exception will be thrown after calling ie.Page<T>() (where T is a subclass of WatinBaseClass).
In my opinion, even if you don't need to use "page checkers", using Page class is still really useful and clarifies the code, so I really recommend using it. I regret that I haven't discovered it when I was starting work with WatiN.
Related
I want to define a method in a groovy class that I can pass an xpath to on the fly(in order for the same method to be reusable depending on the application). The code snippet below is just a proof of concept, however I would eventually like to build a library of re-usable commands/components, which is why I would like to learn how to dynamically define page content.
If I try this:
import geb.Page;
class oneStepDefMethodClass extends Page {
static url = 'http://www.google.com'
static content = {
queryInput { $("input", id: "gbqfq") }
queryButton { $("button",name: "btnG") }
//songLink { $("span._BZ")}
}
....
void assertSongInResults2(String xpathOfSongLink){
println "Waiting on video link "+ xpathOfSongLink
songLink { $(xpathOfSongLink)}
waitFor {
songLink.displayed
}
}
}
I get this error :groovy.lang.MissingMethodException: No signature of method: geb.navigator.NonEmptyNavigator.songLink() is applicable for argument types: (oneStepDefMethodClass$_assertSongInResults2_closure3) values: [oneStepDefMethodClass$_assertSongInResults2_closure3#7c455e96]
If I throw a
content={songLink {$(xpathOfSongLink)}
}
block in the assertSongInResults2 method, I get this error:
geb.error.UnresolvablePropertyException: Unable to resolve songLink as content for oneStepDefMethodClass, or as a property on its Navigator context. Is songLink a class you forgot to import?
So, yeah is there a way to dynamically define page content like that? The program executes fine if I define it statically up top with the rest of the content , but that is not the point, I want to create re-usable resources instead of redefining the wheel every time I want to use geb.
Solved as I was writing the question, but thought I would post in case anyone else has a similar problem
static String someXpath
static content = {
queryInput { $("input", id: "gbqfq") } //
queryButton { $("button",name: "btnG") } //
songLink { $(someXpath) } //syntax element.className
}
....
void assertSongInResults2(String xpathOfSongLink){
println "Waiting on video link "+ xpathOfSongLink
someXpath=xpathOfSongLink
waitFor {
songLink.displayed
}
}
I have a MVC.Net4 Application in which i have Longrunning backround operations which is why i use the System.Threading.Tasks.Task Class.
I start the Tasks after the User clicked a certain Button on the GUI, from that Task im going to use async methods from a intern API which i need to await. This is all working.
public ActionResult DoAsyncAction()
{
//ReturnValue that needs to be further populated by the async action in productive environment
var arv = new AsyncReturnValue
{
ProgressBar = new ProgressBar {Action = "SomeAction", User = "SomeUser"}
};
var t = new Task<AsyncReturnValue>(DoAction, arv);
//Add a Progressbar before Task starts so i can visualize the process on the view
HttpContext.GetSession().ProgressBars.Add(arv.ProgressBar);
//from my understanding this is similar to an event that gets triggered when my DoAction Method finished so i need to remove
//the progressbar there again since the process will be finished in that case
t.ContinueWith(DoActionkComplete);
t.Start();
//Returns the User to the Index Page while the Task is processing
return View("Index");
}
Now what i really want to do is visualizing the operation. I use jQuery Progressbars on the GUI and my Own ProgressBar Object in the Session for this. I have a List of ProgressBars on my Session and a PartialView strongly Typed to a List of those ProgressBars.
ProgressBar Class:
public class ProgressBar
{
public string Action { get; set; }
public string User { get; set; }
}
PartialView:
#using AsyncProj.Models
#model List<AsyncProj.Models.ProgressBar>
#{
ViewBag.Title = "ProgressPartial";
}
#{
var foo = (MySessionObject) HttpContext.Current.Session["__MySessionObject"];
foreach (var pb in foo.ProgressBars)
{
<div style="border: 1px solid black">
<p>#pb.Action</p>
<div id="progressbar">This will be turned into a ProgressBar via jQuery.</div >
</div>
}
}
And then the Object i have in my Session:
public class MySessionObject
{
public List<ProgressBar> ProgressBars { get; set; }
public string User { get; set; }}
Whenever i start a new Task i will add another ProgressBar to that List, which works just fine.
No where i get into Troubles is when i want to Remove the ProgressBars from Session again.
In the DoActionkComplete Method which i set in Task.ContinueWith() i want to Remove the ProgressBar corresponding to the finished action. I have the ProgressBar Ready there, its stored in my AsyncReturnValue Class which i have in the Task.Result at this point:
public class AsyncReturnValue
{
public ProgressBar ProgressBar { get; set; }
}
In this Method i would like to remove the Progressbar from the Session with
HttpContext.GetSession().ProgressBars.Remove(pbToRemove). But the problem with that im still operating on a different Thread so i have no valid HttpContext there and my SessionObject is null on that Thread.
This is what my DoActionComplete Method looks right now:
public void DoActionkComplete(Task<AsyncReturnValue> t)
{
//i set the user hardcode because its only a demo
DeleteProgress.Add(new ProgressBarDeleteObject {ProgressBar = t.Result.ProgressBar, User = "Asd123"});
}
I created a Workaround where i have a static List of Progressbars on my Controller. In the DoActionComplete Method i add the ProgressBars i want to delete to that List. I need to use polling (with jQuery $.get() and setinterval) in order to delete them.
I have a custom Class for the DeleteList on which i can set a Username so i know who is the Owner of that ProgressBar and only show it to him, else everyone would see it because its Static.
public class ProgressBarDeleteObject
{
public ProgressBar ProgressBar { get; set; }
public string User { get; set; }
}
Dont get me wrong, my workaround works just fine but i want to know the clean way. From what i know static Lists on Controllers could technically grow very big and slow the site down. Such Lists also lose its entries when the ApplicationPool restarts the Application.
So my Actual Question would be how can i access a HttpContext SessionObject from a different Thread like i'm using? And if its not possible, what would be the proper Way to achieve what i want?
So my Actual Question would be how can i access a HttpContext SessionObject from a different Thread like i'm using?
That's not possible.
And if its not possible, what would be the proper Way to achieve what i want?
First, let's back up to the original scenario. The problem is here:
I have a MVC.Net4 Application in which i have Longrunning backround operations which is why i use the System.Threading.Tasks.Task Class.
That's the wrong solution for that scenario. The proper solution is to have a reliable queue (Azure queue / MSMQ) with an independent background process (Azure webjob / Win32 service) doing the processing. This is a more reliable solution because any work you toss onto the ASP.NET thread pool may be lost (especially if you don't inform ASP.NET about that work).
Once you have this architecture set up, then you can use SignalR to communicate from your web server to your clients. SignalR will use polling if it has to, but it can also use more efficient methods (such as websockets).
You can specify the SynchroniztionContext that the ContinueWith task continues on and then you should be able to access the progress bars. Try changing your t.ContinueWith(DoActionkComplete); call to
t.ContinueWith(DoActionkComplete, TaskScheduler.FromCurrentSynchronizationContext());
If you are using .NET 4.5 you can rewrite your method with async\await
public async Task<ActionResult> DoAsyncAction()
{
//ReturnValue that needs to be further populated by the async action in productive environment
var arv = new AsyncReturnValue
{
ProgressBar = new ProgressBar {Action = "SomeAction", User = "SomeUser"}
};
//Add a Progressbar before Task starts so i can visualize the process on the view
HttpContext.GetSession().ProgressBars.Add(arv.ProgressBar);
var result = await Task.Run(DoAction());
//i set the user hardcode because its only a demo
DeleteProgress.Add(new ProgressBarDeleteObject {ProgressBar = result.ProgressBar, User = "Asd123"});
//Returns the User to the Index Page while the Task is processing
return View("Index");
}
And if you make the DoAction method async as well, you can remove the Task.Run part as that uses up a thread from the thread pool.
I need to recreate new page instance on every page load (also when user pressed Back button).
So I overrided OnBackKeyPress method:
protected override void OnBackKeyPress(CancelEventArgs e)
{
base.OnBackKeyPress(e);
if (NavigationService.CanGoBack) {
e.Cancel = true;
var j = NavigationService.RemoveBackEntry();
NavigationService.Navigate(j.Source);
NavigationService.RemoveBackEntry();
}
}
The problem is that I can't handle case when user press back button to close CustomMessageBox dialog. How can I check it? Or is there any way to force recreation of page instance when going back through history state?
Why do you need to recreate the page instance? If you are simply trying to re-read the data to be displayed, why not put the data loading logic into OnNavigatedTo()?
Assuming that is what you are actually trying to achieve, try something like this...
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// don't do your data loading here. This will only be called on page creation.
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
LoadData();
base.OnNavigatedTo(e);
}
MyViewModel model;
async void LoadData()
{
model = new MyViewModel();
await model.LoadDataAsync();
}
}
If you also have specific logic that you need to run on first construction of the page vs. on a back key navigation, check the NavigationMode property of the NavigationEventArgs object that gets passed to OnNavigatedTo.
if(e.NavigationMode == NavigationMode.New)
{
//do what you need to do specifically for a new page instance
}
if (e.NavigationMode == NavigationMode.Back)
{
// do anything specific for back navigation here.
}
Ha, in the near thread, i have opposite question :)
What about MessageBox - it depends, which one are you using. It can be custom message box, for example. Anyway, try to check MessageBox.IsOpened (or alternative for your MessageBox) in your OnBackKeyPress().
Another solution is to use OnNavigatedTo() of the page you want to be new each time.
Third solution: in case you works with Mvvm Light, add some unique id in ViewModel getter, like
public MyViewModel MyViewModel
{
get
{
return ServiceLocator.Current.GetInstance<MyViewModel>((++Uid).ToString());
}
}
This would force to recreate new ViewModel each time, so you'd have different instance of VM, so you would have another data on the View.
I have a list of items that goes to another page, That page is hooked up to a view model. In the constructor of this view model I have code that grabs data from the server for that particular item.
What I found is that when I hit the back button and choose another item fromt hat list and it goes to the other page the constructor does not get hit.
I think it is because the VM is now created and thinks it does not need a new one. I am wondering how do I force a cleanup so that a fresh one is always grabbed when I select from my list?
I faced the same issue, that's how i solved it.
Have a BaseView class, override OnNavigatedTo
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (NavigatedToCommand != null && NavigatedToCommand.CanExecute(null))
NavigatedToCommand.Execute(null);
}
add DependencyProperty.
public static readonly DependencyProperty NavigatedToCommandProperty =
DependencyProperty.Register("NavigatedToCommand", typeof(ICommand), typeof(BaseView), null);
public ICommand NavigatedToCommand
{
get { return (ICommand)GetValue(NavigatedToCommandProperty); }
set { SetValue(NavigatedToCommandProperty, value); }
}
On the necessary pages, add to xaml (and, of course, inherit BaseView )
NavigatedToCommand="{Binding OnNavigatedToCommand}"
In the ViewModel, make command itself
public RelayCommand OnNavigatedToCommand
{ get { return new RelayCommand(OnNavigatedTo); } }
and implement method you want to call to update list
public async void OnNavigatedTo()
{
var result = await myDataService.UpdateMyList();
if (result.Status == OK)
MyList = result.List;
}
So, now, every time you navigate to page with list, inside of overriden OnNavigatedTo(), a NavigatedToCommand would be executed, which would execute OnNavigatedToCommand (which you set in xaml), which would call OnNavigatedTo, which would update your list.
A bit messy, but MVVM :)
EDIT: What about cleanings, they can be done in OnNavigatedFrom(), which works the same. Or OnNavigatingFrom(), which also can be useful in some cases.
I use codenameone to develop my mobile application. In this application I implement some classes and codes manually for instance create all forms by hard coding not using codenameone designer for some reason.
By the way I wanted to navigate in forms like what codenameone use, so I use one variable from type of Form called it prevForm and when I want to open a form I set it to current form and then I show new form.
Ok, that is main scenario. In this application I wanna implement internationalization too, so I create my own hashtable (Farsi and English) for this application.
This is my problem:
How can I set or change language and apply it to forms that I opened?
Is my method for navigate between forms are good?
Here is my code:
public class BaseForm extends Form implements ActionListener {
public BaseForm(){
this.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
}
Command exit, ok, back;
Form prevForm;
protected void initForm(){
}
protected void showForm(){
}
protected void showForm(final Form prevForm){
//String name = this.getName();
//if("Reminder".equals(name) || "3Transaction".equals(name))
{
this.prevForm = prevForm;
Form f = this;
back = new Command("Back");
//ok = new Command("Ok");
//delete = new Command("Delete");;
Button button = new Button("Button");
f.addCommand(back);
//f.addCommand(ok);
//f.addCommand(delete);
//f.addComponent(button);
f.addCommandListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if (ae.getCommand().equals(back)) {
//Do Exit command code
System.out.println("Back pressed");
prevForm.showBack();
} else if (ae.getCommand().equals(ok)) {
//Do Start command code
System.out.println("Ok pressed");
}
}
});
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
//Do button code
System.out.println("Action performed");
}
});
}
showForm();
}}
for open nested form I use this code:
LanguageUI lang = new LanguageUI();
lang.showForm(this);
change language [form]:
protected boolean onBtnSave() {
if(isRbFarsiSelected()){
UIManager.getInstance().setResourceBundle(new CommonSettings().getFarsi());
}
else {
UIManager.getInstance().setResourceBundle(new CommonSettings().getEnglish());
}
return false;
}
I also hard code my UI on lwuit, and i have a variable parentForm on every class so i can easily show previous form. For language change i know there is Localization in the resource editor that you can make use of. Below is how you can access it. I guess the trick is how to set the content of the L10N in the res file in code? On the other hand you can create your own helper classes that mirror the methods below.
Resources theme = Resources.open("/theme.res");
theme.getL10N(id, locale);
theme.getL10NResourceNames();
theme.isL10N(name);
theme.listL10NLocales(id)