Skip to content

Commit 72bf57b

Browse files
authored
Merge pull request #328 from csf-dev/craigfowler/issue326
Resolve #326 - LSP violation
2 parents dd3c5dc + 2394632 commit 72bf57b

17 files changed

Lines changed: 229 additions & 331 deletions

CSF.Screenplay.Selenium/Builders/FindElementBuilder.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace CSF.Screenplay.Selenium.Builders
1919
public class FindElementBuilder : IGetsPerformableWithResult<SeleniumElement>
2020
{
2121
readonly ITarget target;
22+
readonly IHasSearchContext searchContext;
2223
Locator locator;
2324
string name;
2425

@@ -46,7 +47,9 @@ public FindElementBuilder AndNameIt(string name)
4647

4748
IPerformableWithResult<SeleniumElement> IGetsPerformableWithResult<SeleniumElement>.GetPerformable()
4849
{
49-
return SingleElementPerformableWithResultAdapter.From(new FindElement(name, locator), target);
50+
return target != null
51+
? new FindElement(target, name, locator)
52+
: new FindElement(searchContext, name, locator);
5053
}
5154

5255
/// <summary>
@@ -59,13 +62,12 @@ public FindElementBuilder(ITarget target)
5962
}
6063

6164
/// <summary>
62-
/// Converts a <see cref="FindElementsBuilder"/> to a <see cref="SingleElementPerformableWithResultAdapter{SeleniumElement}"/>.
65+
/// Initializes a new instance of the <see cref="FindElementsBuilder"/> class with the specified target.
6366
/// </summary>
64-
/// <param name="builder">The <see cref="FindElementBuilder"/> instance to convert.</param>
65-
/// <returns>A <see cref="SingleElementPerformableWithResultAdapter{SeleniumElement}"/> instance.</returns>
66-
public static implicit operator SingleElementPerformableWithResultAdapter<SeleniumElement>(FindElementBuilder builder)
67+
/// <param name="searchContext">The target within which elements will be found.</param>
68+
public FindElementBuilder(IHasSearchContext searchContext)
6769
{
68-
return SingleElementPerformableWithResultAdapter.From(new FindElement(builder.name, builder.locator), builder.target);
70+
this.searchContext = searchContext ?? throw new System.ArgumentNullException(nameof(searchContext));
6971
}
7072
}
7173
}

CSF.Screenplay.Selenium/Builders/FindElementsBuilder.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace CSF.Screenplay.Selenium.Builders
1919
public class FindElementsBuilder : IGetsPerformableWithResult<SeleniumElementCollection>
2020
{
2121
readonly ITarget target;
22+
readonly IHasSearchContext searchContext;
2223
Locator locator;
2324
string name;
2425

@@ -46,7 +47,9 @@ public FindElementsBuilder AndNameThem(string name)
4647

4748
IPerformableWithResult<SeleniumElementCollection> IGetsPerformableWithResult<SeleniumElementCollection>.GetPerformable()
4849
{
49-
return SingleElementPerformableWithResultAdapter.From(new FindElements(name, locator), target);
50+
return target != null
51+
? new FindElements(target, name, locator)
52+
: new FindElements(searchContext, name, locator);
5053
}
5154

5255
/// <summary>
@@ -59,13 +62,12 @@ public FindElementsBuilder(ITarget target)
5962
}
6063

6164
/// <summary>
62-
/// Converts a <see cref="FindElementsBuilder"/> to a <see cref="SingleElementPerformableWithResultAdapter{SeleniumElementCollection}"/>.
65+
/// Initializes a new instance of the <see cref="FindElementsBuilder"/> class with the specified target.
6366
/// </summary>
64-
/// <param name="builder">The <see cref="FindElementsBuilder"/> instance to convert.</param>
65-
/// <returns>A <see cref="SingleElementPerformableWithResultAdapter{SeleniumElementCollection}"/> instance.</returns>
66-
public static implicit operator SingleElementPerformableWithResultAdapter<SeleniumElementCollection>(FindElementsBuilder builder)
67+
/// <param name="searchContext">The target within which elements will be found.</param>
68+
public FindElementsBuilder(IHasSearchContext searchContext)
6769
{
68-
return SingleElementPerformableWithResultAdapter.From(new FindElements(builder.name, builder.locator), builder.target);
70+
this.searchContext = searchContext ?? throw new System.ArgumentNullException(nameof(searchContext));
6971
}
7072
}
7173
}

CSF.Screenplay.Selenium/Elements/SeleniumElement.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ namespace CSF.Screenplay.Selenium.Elements
1212
/// a <see cref="Name"/>. This optional, but recommended, technique facilitates human-readable reporting.
1313
/// </para>
1414
/// </remarks>
15-
public class SeleniumElement : ITarget
15+
public class SeleniumElement : ITarget, IHasWebElement, IHasSearchContext
1616
{
1717
const string unknownNameFormat = "an HTML {0} element";
1818

1919
/// <inheritdoc/>
2020
public string Name { get; }
2121

22-
/// <summary>
23-
/// Gets the native Selenium web element.
24-
/// </summary>
22+
/// <inheritdoc/>
2523
public IWebElement WebElement { get; }
2624

25+
ISearchContext IHasSearchContext.SearchContext => WebElement;
26+
2727
SeleniumElementCollection ITarget.GetElements(IWebDriver driver) => new SeleniumElementCollection(new[] { this }, Name);
2828

2929
SeleniumElement ITarget.GetElement(IWebDriver driver) => this;
@@ -42,4 +42,26 @@ public SeleniumElement(IWebElement webElement, string name = null)
4242
Name = name ?? string.Format(unknownNameFormat, webElement.TagName);
4343
}
4444
}
45+
46+
/// <summary>
47+
/// An object which exposes a Selenium web element.
48+
/// </summary>
49+
public interface IHasWebElement : IHasName
50+
{
51+
/// <summary>
52+
/// Gets the native Selenium web element.
53+
/// </summary>
54+
IWebElement WebElement { get; }
55+
}
56+
57+
/// <summary>
58+
/// An object which exposes a Selenium search context.
59+
/// </summary>
60+
public interface IHasSearchContext : IHasName
61+
{
62+
/// <summary>
63+
/// Gets the native Selenium search context.
64+
/// </summary>
65+
ISearchContext SearchContext { get; }
66+
}
4567
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System;
2+
using OpenQA.Selenium;
3+
4+
namespace CSF.Screenplay.Selenium.Elements
5+
{
6+
/// <summary>
7+
/// Implementation of <see cref="IHasSearchContext"/> which represents an HTML shadow-root node.
8+
/// </summary>
9+
/// <seealso cref="Questions.GetShadowRootNatively"/>
10+
/// <seealso cref="Questions.GetShadowRootWithJavaScript"/>
11+
/// <seealso cref="Tasks.GetShadowRoot"/>
12+
public class ShadowRoot : IHasSearchContext
13+
{
14+
readonly ISearchContext shadowRoot;
15+
16+
/// <inheritdoc/>
17+
public ISearchContext SearchContext => shadowRoot;
18+
19+
/// <inheritdoc/>
20+
public string Name { get; }
21+
22+
/// <summary>
23+
/// Initializes a new instance of <see cref="ShadowRoot"/>.
24+
/// </summary>
25+
/// <param name="shadowRoot">The wrapped shadow root element</param>
26+
/// <param name="name">The name of this Shadow Root object</param>
27+
/// <exception cref="ArgumentNullException">If <paramref name="shadowRoot"/> is <see langword="null"/></exception>
28+
public ShadowRoot(ISearchContext shadowRoot, string name = null)
29+
{
30+
this.shadowRoot = shadowRoot ?? throw new ArgumentNullException(nameof(shadowRoot));
31+
Name = name ?? "Shadow root";
32+
}
33+
}
34+
}

CSF.Screenplay.Selenium/Elements/ShadowRootAdapter.cs

Lines changed: 0 additions & 123 deletions
This file was deleted.

0 commit comments

Comments
 (0)