Skip to content

Commit ebe571c

Browse files
committed
Add text-trimming functionality
Fixes #276 This now means that by default, when reading text from the browser, leading and trailing whitespace will be trimmed. This will help us out in Safari, where unwanted whitespace gets returned by the browser.
1 parent ecc0417 commit ebe571c

3 files changed

Lines changed: 179 additions & 1 deletion

File tree

CSF.Screenplay.Selenium/Builders/QueryPredicatePrototypeBuilder.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,20 @@ public QueryPredicatePrototype<Size> Size(Size value)
230230
/// <summary>
231231
/// Creates a query predicate based on the element's text content.
232232
/// </summary>
233+
/// <remarks>
234+
/// <para>
235+
/// When reading text from the web browser, this predicate will trim leading/trailing whitespace from that text before comparing it.
236+
/// This is because some browsers (Safari)
237+
/// include whitespace at the beginning/end of text read from the browser, which isn't visible to the end user. This is typically the
238+
/// space which is inherent in the markup, but which browsers ignore when actually displaying content.
239+
/// </para>
240+
/// <para>
241+
/// Trimming it by default ensures that Screenplay reproduces functionality reliably cross-browser.
242+
/// If this causes an issue and you would like the leading/trailing whitespace included the use
243+
/// <see cref="TextWithoutTrimmingWhitespace(string)"/> instead.
244+
/// Note that you may see different results in browsers which include leading/trailing whitespace anyway.
245+
/// </para>
246+
/// </remarks>
233247
/// <param name="predicate">The predicate to apply to the element's text.</param>
234248
/// <returns>A <see cref="QueryPredicatePrototype{String}"/>, which may be converted to a full predicate.</returns>
235249
public QueryPredicatePrototype<string> Text(Func<string,bool> predicate)
@@ -238,11 +252,55 @@ public QueryPredicatePrototype<string> Text(Func<string,bool> predicate)
238252
/// <summary>
239253
/// Creates a query predicate based on the element's text content.
240254
/// </summary>
255+
/// <remarks>
256+
/// <para>
257+
/// When reading text from the web browser, this predicate will trim leading/trailing whitespace from that text before comparing it.
258+
/// This is because some browsers (Safari)
259+
/// include whitespace at the beginning/end of text read from the browser, which isn't visible to the end user. This is typically the
260+
/// space which is inherent in the markup, but which browsers ignore when actually displaying content.
261+
/// </para>
262+
/// <para>
263+
/// Trimming it by default ensures that Screenplay reproduces functionality reliably cross-browser.
264+
/// If this causes an issue and you would like the leading/trailing whitespace included the use
265+
/// <see cref="TextWithoutTrimmingWhitespace(string)"/> instead.
266+
/// Note that you may see different results in browsers which include leading/trailing whitespace anyway.
267+
/// </para>
268+
/// </remarks>
241269
/// <param name="value">The value to compare against the element's text.</param>
242270
/// <returns>A <see cref="QueryPredicatePrototype{String}"/>, which may be converted to a full predicate.</returns>
243271
public QueryPredicatePrototype<string> Text(string value)
244272
=> CreatePrototype(new TextQuery(), Spec.Func<string>(x => x == value), t => $"{t.Name} has text equal to '{value}'");
245273

274+
/// <summary>
275+
/// Creates a query predicate based on the element's text content.
276+
/// </summary>
277+
/// <remarks>
278+
/// <para>
279+
/// When reading text from the web browser, this predicate will leave any leading/trailing whitespace in the text
280+
/// without trimming it.
281+
/// Note that you may see different results in browsers which include leading/trailing whitespace anyway.
282+
/// </para>
283+
/// </remarks>
284+
/// <param name="predicate">The predicate to apply to the element's text.</param>
285+
/// <returns>A <see cref="QueryPredicatePrototype{String}"/>, which may be converted to a full predicate.</returns>
286+
public QueryPredicatePrototype<string> TextWithoutTrimmingWhitespace(Func<string,bool> predicate)
287+
=> CreatePrototype(new TextQuery(false), Spec.Func(predicate), t => $"{t.Name} has text matching a predicate");
288+
289+
/// <summary>
290+
/// Creates a query predicate based on the element's text content, without trimming leading/trailing whitespace.
291+
/// </summary>
292+
/// <remarks>
293+
/// <para>
294+
/// When reading text from the web browser, this predicate will leave any leading/trailing whitespace in the text
295+
/// without trimming it.
296+
/// Note that you may see different results in browsers which include leading/trailing whitespace anyway.
297+
/// </para>
298+
/// </remarks>
299+
/// <param name="value">The value to compare against the element's text.</param>
300+
/// <returns>A <see cref="QueryPredicatePrototype{String}"/>, which may be converted to a full predicate.</returns>
301+
public QueryPredicatePrototype<string> TextWithoutTrimmingWhitespace(string value)
302+
=> CreatePrototype(new TextQuery(false), Spec.Func<string>(x => x == value), t => $"{t.Name} has text equal to '{value}'");
303+
246304
/// <summary>
247305
/// Creates a query predicate based on the element's DOM <c>value</c>.
248306
/// </summary>
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System.Collections.Generic;
2+
using CSF.Screenplay.Performables;
3+
using CSF.Screenplay.Selenium.Elements;
4+
using CSF.Screenplay.Selenium.Queries;
5+
using CSF.Screenplay.Selenium.Questions;
6+
7+
namespace CSF.Screenplay.Selenium.Builders
8+
{
9+
/// <summary>
10+
/// Builder type for a performable that gets a collection of text values.
11+
/// </summary>
12+
/// <remarks>
13+
/// <para>
14+
/// The purpose of this builder is to enable or disable the trimming of whitespace characters at the beginning or end of the returned text.
15+
/// </para>
16+
/// </remarks>
17+
public class TextMultiQueryBuilder : IGetsPerformableWithResult<IReadOnlyList<string>>
18+
{
19+
readonly ITarget elements;
20+
bool trimWhitespace = true;
21+
22+
/// <summary>
23+
/// Configures the performable to disable the trimming of whitespace characters at the beginning or end of the returned text.
24+
/// </summary>
25+
/// <remarks>
26+
/// <para>
27+
/// Trimming of whitespace at the beginning/end of text is enabled by default. This is because some browsers (Safari)
28+
/// include whitespace at the beginning/end of text read from the browser, which isn't visible to the end user. This is typically the
29+
/// space which is inherent in the markup, but which browsers ignore when actually displaying content.
30+
/// </para>
31+
/// <para>
32+
/// Trimming it by default ensures that Screenplay reproduces functionality reliably cross-browser.
33+
/// If this causes an issue and you would like the leading/trailing whitespace included in the result then use this method.
34+
/// Note that you may see different results in browsers which include leading/trailing whitespace anyway.
35+
/// </para>
36+
/// </remarks>
37+
/// <returns>The current instance of the builder, so calls may be chained.</returns>
38+
public TextMultiQueryBuilder WithoutTrimmingWhitespace()
39+
{
40+
trimWhitespace = false;
41+
return this;
42+
}
43+
44+
IPerformableWithResult<IReadOnlyList<string>> IGetsPerformableWithResult<IReadOnlyList<string>>.GetPerformable()
45+
=> ElementCollectionPerformableWithResultAdapter.From(ElementCollectionQuery.From(new TextQuery(trimWhitespace)), elements);
46+
47+
/// <summary>
48+
/// Initializes a new instance of the <see cref="TextMultiQueryBuilder"/> class.
49+
/// </summary>
50+
/// <param name="elements">The target elements to query.</param>
51+
public TextMultiQueryBuilder(ITarget elements)
52+
{
53+
this.elements = elements ?? throw new System.ArgumentNullException(nameof(elements));
54+
}
55+
}
56+
57+
/// <summary>
58+
/// Builder type for a performable that gets a single text value.
59+
/// </summary>
60+
/// <remarks>
61+
/// <para>
62+
/// The purpose of this builder is to enable or disable the trimming of whitespace characters at the beginning or end of the returned text.
63+
/// </para>
64+
/// </remarks>
65+
public class TextQueryBuilder : IGetsPerformableWithResult<string>
66+
{
67+
readonly ITarget element;
68+
bool trimWhitespace = true;
69+
70+
/// <summary>
71+
/// Configures the performable to disable the trimming of whitespace characters at the beginning or end of the returned text.
72+
/// </summary>
73+
/// <remarks>
74+
/// <para>
75+
/// Trimming of whitespace at the beginning/end of text is enabled by default. This is because some browsers (Safari)
76+
/// include whitespace at the beginning/end of text read from the browser, which isn't visible to the end user. This is typically the
77+
/// space which is inherent in the markup, but which browsers ignore when actually displaying content.
78+
/// </para>
79+
/// <para>
80+
/// Trimming it by default ensures that Screenplay reproduces functionality reliably cross-browser.
81+
/// If this causes an issue and you would like the leading/trailing whitespace included in the result then use this method.
82+
/// Note that you may see different results in browsers which include leading/trailing whitespace anyway.
83+
/// </para>
84+
/// </remarks>
85+
/// <returns>The current instance of the builder, so calls may be chained.</returns>
86+
public TextQueryBuilder WithoutTrimmingWhitespace()
87+
{
88+
trimWhitespace = false;
89+
return this;
90+
}
91+
92+
IPerformableWithResult<string> IGetsPerformableWithResult<string>.GetPerformable()
93+
=> SingleElementPerformableWithResultAdapter.From(SingleElementQuery.From(new TextQuery(trimWhitespace)), element);
94+
95+
/// <summary>
96+
/// Initializes a new instance of the <see cref="TextQueryBuilder"/> class.
97+
/// </summary>
98+
/// <param name="element">The target element to query.</param>
99+
public TextQueryBuilder(ITarget element)
100+
{
101+
this.element = element ?? throw new System.ArgumentNullException(nameof(element));
102+
}
103+
}
104+
}

CSF.Screenplay.Selenium/Queries/TextQuery.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,26 @@ namespace CSF.Screenplay.Selenium.Queries
77
/// </summary>
88
public class TextQuery : IQuery<string>
99
{
10+
readonly bool trimWhitespace;
11+
1012
/// <inheritdoc/>
1113
public string Name => $"the contained text";
1214

1315
/// <inheritdoc/>
14-
public string GetValue(SeleniumElement element) => element.WebElement.Text;
16+
public string GetValue(SeleniumElement element)
17+
{
18+
var text = element.WebElement.Text;
19+
return trimWhitespace ? text?.Trim() : text;
20+
}
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="TextQuery"/> class.
24+
/// </summary>
25+
/// <param name="trimWhitespace">If <see langword="true"/>, leading &amp; trailing whitespace will be trimmed from
26+
/// the returned text content; if <see langword="true"/> it will not.</param>
27+
public TextQuery(bool trimWhitespace = true)
28+
{
29+
this.trimWhitespace = trimWhitespace;
30+
}
1531
}
1632
}

0 commit comments

Comments
 (0)