Open robertgtaylor1 opened 9 years ago
Could you share code snippet (page object, how you use them) to try reproduce exception?
I got the latest source of castle.core and added it to the selenol proj and that seemed to cure the problem.
Looks like I may have an old version installed, however l did notice a timing issue where it was trying to parse the page before it was fully loaded. I added a call to FindElement and that seemed to cure that problem. It's just possible that the two issues are the same.
-----Original Message----- From: "Pavel Bakshy" notifications@github.com Sent: 20/02/2015 15:12 To: "pbakshy/Selenol" Selenol@noreply.github.com Cc: "Rob Taylor" rgt1@rocketmail.com Subject: Re: [Selenol] Null Argument exception inIsPropertyWithSelectorAttribute (#2)
Could you share code snippet (page object, how you use them) to try reproduce exception? — Reply to this email directly or view it on GitHub.
I sent the previous email from my phone on my way home, but I think the issue was resolved by the 'newer’ version of Castle.Core.
The 'timing' issue seems to be a more general problem with Selenium testing, but it would be mice to have an easier way to delay the ‘DOM search' until it's been loaded then FindElement(By).
The application I am developing is an ASP MVC app that uses (mostly) Razor to render the Html. We've started to build acceptance tests into the project using Specflow and I have looked at both Selenol & Seleno as tools to aid that task.
Seleno looked good as it's tied into MVC Views & Controllers and I have to say I was backing it, but I took a breath and decided to take a look at Selenol as well. It seems to me that your library isn’t tied to Razor and could be used in a more general Html/Ajax environment. In particular we are discussing the option of using a javascript, Mvc framework possibly based on AngularJs for some parts of the application and your library would seem to be better in that case.
The problem I'm having at the moment is deciding how best to process a table. I can use the TableElement but processing the rows and the footer are not that easy. In particular the footer contains a 'Pager control’ which is a 'ul’ and I want to access the last-1 ‘li’. Have you get any clue about how I can do this?
Sent from Surface
From: Pavel Bakshy Sent: Friday, 20 February 2015 15:12 To: pbakshy/Selenol Cc: Rob Taylor
Could you share code snippet (page object, how you use them) to try reproduce exception?
— Reply to this email directly or view it on GitHub.
I'm planning to add helpers for waiting page/control loads and update Castle.Core for last version.
The problem I'm having at the moment is deciding how best to process a table. I can use the >TableElement but processing the rows and the footer are not that easy. In particular the footer >contains a 'Pager control’ which is a 'ul’ and I want to access the last-1 ‘li’. Have you get any clue >about how I can do this?
I think you should use hierarchy of Controls instead of TableElement. Something link this:
public class CustomTableControl : Control {
public CustomTableControl(IWebElement webElement)
: base(webElement) { }
[Class("rows")]
public IEnumerable<CustomRowControl> Rows { get; private set; }
[Class("footer")]
public FooterControl Footer { get; private set; }
}
I tried something like this but couldn't get the results I expected. I'll give it another look tomorrow.
One thing I wasn't sure about was the attributes.
The table has an Id of 'Grid' which is fine as it's unique. The header, body and footer can all be identified by their tag. Is the relative to the outer/parent rather than the whole page?
Sent from Surface
From: Pavel Bakshy Sent: Sunday, 22 February 2015 11:19 To: pbakshy/Selenol Cc: Rob Taylor
I'm planning to add helpers for waiting page/control loads and update Castle.Core for last version.
The problem I'm having at the moment is deciding how best to process a table. I can use the >TableElement but processing the rows and the footer are not that easy. In particular the footer >contains a 'Pager control’ which is a 'ul’ and I want to access the last-1 ‘li’. Have you get any clue >about how I can do this?
I think you should use hierarchy of Controls instead of TableElement. Something link this:
public class CustomTableControl : Control {
public CustomTableControl(IWebElement webElement)
: base(webElement) { }
public IEnumerable<CustomRowControl> Rows { get; private set; }
public FooterControl Footer { get; private set; }
}
— Reply to this email directly or view it on GitHub.
Yes, selector attributes search for inner components/elements related to root component search context. So use TagNameAttribute for not collection property if it's unique in this context, otherwise it will bind first found element.
OK, I have now got this working, but there's one minor issue...
I'm trying to get the text in the colum of one of the rows in the table.
I have the following 2 classes defined...
///
/// The row ///
public class SearchResult : Control
{
public SearchResult(IWebElement webElement)
: base(webElement)
{ }
///
/// All columns ///
[TagName(HtmlElements.TableCell)]
public virtual IEnumerable
///
/// The column ///
public class ResultDetail : Control { public ResultDetail(IWebElement webElement) : base(webElement) { Detail = webElement; }
public IWebElement Detail { get; set; } }...and access the required info var details = product.ResultDetails.ToList(); var code = details[1].Detail.Text;I'm not too sure if using IWebElement in this way is realy the way to do this but it's the only way I can make it work.I tried to use ContainerElement but that throws a missing Validation exception. Robert Taylor
On Monday, February 23, 2015 5:35 AM, Pavel Bakshy <notifications@github.com> wrote:
Yes, selector attributes search for inner components/elements related to root component search context. So use TagNameAttribute for not collection property if it's unique in this context, otherwise it will bind first found element.— Reply to this email directly or view it on GitHub.
It depends on how you want map cells. If just as TableCells, you can do it like this:
public class SearchResult : Control
{
public SearchResult(IWebElement webElement)
: base(webElement)
{ }
[TagName(HtmlElements.TableCell)]
public virtual IEnumerable<TableCellElement> Cells { get; private set; }
}
Or if you interpret cells as controls, you can mix behavior:
public class SearchResult : Control
{
public SearchResult(IWebElement webElement)
: base(webElement)
{ }
[TagName(HtmlElements.TableCell)]
public virtual IEnumerable<ResultDetail> ResultDetails { get; private set; }
}
public class ResultDetail : Control
{
public ResultDetail(IWebElement webElement)
: base(webElement)
{
}
public TableCellELement Cell { get { this.As<TableCellElement>(); } }
}
...
searchResult.ResultDetails.First().Cell.Text
I think it would be nice to add ability inherit from generic Control based on Element type. Something like class MyCustomCell : Control
Tried the 2nd option and it throws an exception "'tbody' tag does not match to 'table' tag"
public TElement As
try { return (TElement)constructor.Invoke(new object[] { this.WebElement }); } catch (TargetInvocationException ex) { if (ex.InnerException != null) { throw ex.InnerException; <<< Here }
throw; } } Robert Taylor
On Monday, February 23, 2015 4:58 PM, Pavel Bakshy <notifications@github.com> wrote:
It depends on how you want map cells. If just as TableCells, you can do it like this:public class SearchResult : Control { public SearchResult(IWebElement webElement) : base(webElement) { }
[TagName(HtmlElements.TableCell)]
public virtual IEnumerable<TableCellElement> Cells { get; private set; }
}Or if you interpret cells as controls, you can mix behavior:public class SearchResult : Control { public SearchResult(IWebElement webElement) : base(webElement) { }
[TagName(HtmlElements.TableCell)]
public virtual IEnumerable<ResultDetail> ResultDetails { get; private set; }
}
public class ResultDetail : Control { public ResultDetail(IWebElement webElement) : base(webElement) { }
public TableCellELement Cell { get { this.As<TableCellElement>(); } }
} ... searchResult.ResultDetails.First().Cell.TextI think it would be nice to add ability inherit from generic Control based on Element type. Something like class MyCustomCell : Control, but as workaround you can use example bellow— Reply to this email directly or view it on GitHub.
Please give more details: html and page object/controls which you try to map. It looks like you mapped tbody as control and convert it as a TableElement.
This is the set of classes that define the 'table' namespace Selenol.Sample { public class SearchResults : Control { public SearchResults(IWebElement webElement) : base(webElement) { }
[Css("#Grid table")] public virtual SearchResultsTable SearchResultsTable { get; set; } }
public class SearchResultsTable : Control { public SearchResultsTable(IWebElement webElement) : base(webElement) { }
[TagName(HtmlElements.TableHead)] public virtual SearchResultsHeader Header { get; set; }
[TagName(HtmlElements.TableBody)] public virtual SearchResultsBody Body { get; set; }
[TagName(HtmlElements.TableFooter)] public virtual SearchResultsFooter Footer { get; set; }
}
//
// Header
//
public class SearchResultsHeader : Control
{
public SearchResultsHeader(IWebElement webElement)
: base(webElement)
{ }
}
//
// Body
//
public class SearchResultsBody : Control
{
public SearchResultsBody(IWebElement webElement)
: base(webElement)
{ }
///
/// All rows ///
[TagName(HtmlElements.TableRow)]
public virtual IEnumerable
///
/// The row ///
public class SearchResult : Control { public SearchResult(IWebElement webElement) : base(webElement) { }
///
/// All columns ///
[TagName(HtmlElements.TableCell)]
public virtual IEnumerable
///
/// The column ///
public class ResultDetail : Control { public ResultDetail(IWebElement webElement) : base(webElement) { }
public TableCellElement Cell { get { return this.As
//
// Footer
//
public class SearchResultsFooter : Control
{
public SearchResultsFooter(IWebElement webElement)
: base(webElement)
{ }
///
/// The page control (<ul class="pagination">) ///
[Class("pagination")] public virtual PagerDetails Pager { get; set; } }
///
/// All elements of the page control (an unordered list) ///
public class PagerDetails : Control { public PagerDetails(IWebElement webElement) : base(webElement) { }
[Css("li:nth-last-child(2) span")] public virtual ContainerElement Records { get; set; }
[Css("li:nth-last-child(3) span")] public virtual ContainerElement Pages { get; set; }
public string RecordCount { get { return this.Records.Text; } } public string PageCount { get { return this.Pages.Text; } } } } Robert Taylor
On Tuesday, February 24, 2015 10:21 AM, Pavel Bakshy <notifications@github.com> wrote:
Please give more details: html and page object/controls which you try to map. It looks like you mapped tbody as control and convert it as a TableElement.— Reply to this email directly or view it on GitHub.
I can't see where you use TableElement. Please show full exception stack trace.
Sounds like I'm misunderstanding something. This is the page class [Url("/")] public class ProductSearchPage : BasePage { [Id("Filter")] public virtual SearchFormControl SearchForm { get; set; }
[Id("Grid")] public virtual SearchResults Results { get; set; } }In an earlier version I used TableElement to 'define' the 'Grid' but following your coment I decided to break it down into a number of classes, so I don't use 'TableElement' any more. This is a screen shot of the Html.
Robert Taylor
On Tuesday, February 24, 2015 12:14 PM, Pavel Bakshy <notifications@github.com> wrote:
I can't see where you use TableElement. Please show full exception stack trace.— Reply to this email directly or view it on GitHub.
Html screenshot wasn't attached. I think it may not work with email commenting. [Css("#Grid table")] public virtual SearchResultsTable SearchResultsTable { get; set; } This selector could be wrong here. Try to replace it with [TagName("table")] public virtual SearchResultsTable SearchResultsTable { get; set; }
And try to copy your html here
HTML
Removing '#Grid' didn't change anything. It seems to be walking up the DOM tree and finding 'tbody' and expecting 'table'. There isn't an element for 'table' although the SearchResultsTable in my code equates to it. ...in fact I debugged thro' this and the line throwing the exception is line 51 in BaseHtmlElement.cs ..
if (!validators.Any(x => x.Validate(this))) { var message = validators.Select(x => x.GetErrorMessage(this)).Join(" or "); throw new WrongElementException(message, this.WebElement); }
The call stack at the point the exception is thrown is...
Selenol.dll!Selenol.Elements.BaseHtmlElement.BaseHtmlElement(OpenQA.Selenium.IWebElement webElement) Line 51 Selenol.dll!Selenol.Elements.ContainerElement.ContainerElement(OpenQA.Selenium.IWebElement webElement) Line 17 Selenol.dll!Selenol.Elements.TableElement.TableElement(OpenQA.Selenium.IWebElement webElement) Line 24 [Native to Managed Transition] Selenol.dll!Selenol.Elements.BaseHtmlElement.As
Is a [SkipValidation] missing from TableElement? Robert Taylor
On Tuesday, February 24, 2015 2:06 PM, Robert Taylor <rgt1@rocketmail.com> wrote:
Removing '#Grid' didn't change anything. It seems to be walking up the DOM tree and finding 'tbody' and expecting 'table'. There isn't an element for 'table' although the SearchResultsTable in my code equates to it. ...in fact I debugged thro' this and the line throwing the exception is line 51 in BaseHtmlElement.cs ..
if (!validators.Any(x => x.Validate(this)))
{
var message = validators.Select(x => x.GetErrorMessage(this)).Join(" or ");
throw new WrongElementException(message, this.WebElement);
}
The call stack at the point the exception is thrown is... Selenol.dll!Selenol.Elements.BaseHtmlElement.BaseHtmlElement(OpenQA.Selenium.IWebElement webElement) Line 51 Selenol.dll!Selenol.Elements.ContainerElement.ContainerElement(OpenQA.Selenium.IWebElement webElement) Line 17 Selenol.dll!Selenol.Elements.TableElement.TableElement(OpenQA.Selenium.IWebElement webElement) Line 24 [Native to Managed Transition] Selenol.dll!Selenol.Elements.BaseHtmlElement.As
Robert Taylor
On Tuesday, February 24, 2015 1:25 PM, Robert Taylor <rgt1@rocketmail.com> wrote:
HTML
I found that the 3.3.3 version of Castle.Core didn't work with Selenol so I downloaded the source and used that.I didn't check this at the time but i seems that I downloaded the source for
[assembly: System.Runtime.InteropServices.ComVisible(false)] [assembly: System.CLSCompliant(true)] [assembly: System.Reflection.AssemblyTitle("Castle.Core for .NETFramework v4.0 Client Profile")] [assembly: System.Reflection.AssemblyProduct("Castle.Core")] [assembly: System.Reflection.AssemblyVersion("3.0.0.0")] [assembly: System.Reflection.AssemblyFileVersion("3.0.0.0")] [assembly: System.Reflection.AssemblyInformationalVersion("3.0.0 beta 1")]
I've now gone back and retried the nuget packages and found that 3.3.3 still doesn't work and neither does 3.1.0 but that 3.0.0.4001 does. Robert Taylor
On Tuesday, February 24, 2015 2:20 PM, Robert Taylor <rgt1@rocketmail.com> wrote:
Is a [SkipValidation] missing from TableElement? Robert Taylor
On Tuesday, February 24, 2015 2:06 PM, Robert Taylor <rgt1@rocketmail.com> wrote:
Removing '#Grid' didn't change anything. It seems to be walking up the DOM tree and finding 'tbody' and expecting 'table'. There isn't an element for 'table' although the SearchResultsTable in my code equates to it. ...in fact I debugged thro' this and the line throwing the exception is line 51 in BaseHtmlElement.cs ..
if (!validators.Any(x => x.Validate(this)))
{
var message = validators.Select(x => x.GetErrorMessage(this)).Join(" or ");
throw new WrongElementException(message, this.WebElement);
}
The call stack at the point the exception is thrown is... Selenol.dll!Selenol.Elements.BaseHtmlElement.BaseHtmlElement(OpenQA.Selenium.IWebElement webElement) Line 51 Selenol.dll!Selenol.Elements.ContainerElement.ContainerElement(OpenQA.Selenium.IWebElement webElement) Line 17 Selenol.dll!Selenol.Elements.TableElement.TableElement(OpenQA.Selenium.IWebElement webElement) Line 24 [Native to Managed Transition] Selenol.dll!Selenol.Elements.BaseHtmlElement.As
Robert Taylor
On Tuesday, February 24, 2015 1:25 PM, Robert Taylor <rgt1@rocketmail.com> wrote:
HTML
The bug in TableRowElement constructor. We should do
this.ParentTable = new TableElement(this.WebElement.FindElement(By.XPath("./ancestor::table[1]")));
Instead of
this.ParentTable = this.Parent.As<TableElement>();
Ok, that seems to fix it. Thanks. My main issue now is errors caused by slow page loads. I've looked at various 'solutions' but none of them appear to work 100% and I'm having to resort to Thread.Sleep(1000) - which is ok until it starts to impact the testing and slows things down too much :-(
Robert Taylor
On Tuesday, February 24, 2015 4:28 PM, Pavel Bakshy <notifications@github.com> wrote:
The bug in TableRowElement constructor.
We should do this.ParentTable = new TableElement(this.WebElement.FindElement(By.XPath("./ancestor::table[1]")));Instead of this.ParentTable = this.Parent.As
The best way is actively wait for element existence. I'm planning to add some helpers for it. But not earlier than next week, a lot of work currently.
I added a FindElememt call and that appeared to work but the page/bridge reloads so I think the FindElement found the old one not the reloaded one and the test crashed
-----Original Message----- From: "Pavel Bakshy" notifications@github.com Sent: 24/02/2015 17:07 To: "pbakshy/Selenol" Selenol@noreply.github.com Cc: "Rob Taylor" rgt1@rocketmail.com Subject: Re: [Selenol] Null Argument exception inIsPropertyWithSelectorAttribute (#2)
The best way is actively wait for element existence. I'm planning to add some helpers for it. But not earlier than next week, a lot of work currently. — Reply to this email directly or view it on GitHub.
If page reloads you should use new instance of page object. If DOM part recreated after some ajax request then you could try get new element by additional call of FindElememt.
Unfortunately the partial view doesn't have any different elements 😢
-----Original Message----- From: "Pavel Bakshy" notifications@github.com Sent: 24/02/2015 19:34 To: "pbakshy/Selenol" Selenol@noreply.github.com Cc: "Rob Taylor" rgt1@rocketmail.com Subject: Re: [Selenol] Null Argument exception inIsPropertyWithSelectorAttribute (#2)
If page reloads you should use new instance of page object. If DOM part recreated after some ajax request then you could try get new element by additional call of FindElememt. — Reply to this email directly or view it on GitHub.
I'm getting a NullArg exception in IsPropertyWithSelectorAttribute which is being called from SelectInterceptors in your Sample app.
The call on line 26 var propertyInfo = type.GetProperty(method); is returnimg null. (the 'method' value is {Selenol.Elements.TextboxElement get_SearchBox()} and 'type is {Name = "RuntimeType" FullName = "System.RuntimeType"} )
This (in turn) is being called from DynamicProxyGenAssembly2!Castle.Proxies.MainPageProxy.SearchBox.get()
I'm not sure what's going on here but I ran this at home and it was OK but in work it fails. I have problems with the FirefoxDriver (firewall issues) so I'm using the ChromeDriver (but I also see the same problem with IE)
Any idea what's wrong. Nothing in the call stack helps. I would guess that the interceptor code isn't working but I'm not used 'Castle' so I'm at a bit of a loss.