Skip to content

Commit 69b3307

Browse files
committed
WIP #275 - Add tests for color parsing
These are based on the tests at: https://github.com/SeleniumHQ/selenium/blob/selenium-4.40.0/java/test/org/openqa/selenium/support/ColorTest.java Although, I have reformatted them to be more terse, using NUnit syntax.
1 parent cab3697 commit 69b3307

4 files changed

Lines changed: 63 additions & 14 deletions

File tree

CSF.Screenplay.Selenium/Color.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ namespace CSF.Screenplay.Selenium
88
/// </summary>
99
/// <remarks>
1010
/// <para>
11-
/// An uninitialized instance of this type will represent "fully-transparent black".
11+
/// An uninitialized instance of this type will represent "fully-transparent black". This is equivalent to the value available at
12+
/// <see cref="Colors.TRANSPARENT"/>.
1213
/// Internally, this type stores color as three unsigned <see cref="byte"/> values (Red, Green, Blue) and a
1314
/// <see cref="double"/> representing the transparency (Alpha). The Alpha value must be between 0 (fully transparent)
1415
/// and 1 (fully opaque).
@@ -19,6 +20,13 @@ namespace CSF.Screenplay.Selenium
1920
/// See <see href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value">the MDN writeup of web color</see> for
2021
/// more information about the valid formats.
2122
/// </para>
23+
/// <para>
24+
/// When using a sufficiently high version of .NET &amp; the C# language (.NET 6 or higher), this type is a <c>readonly record struct</c>.
25+
/// This offers improved and extended functionality, such as access to
26+
/// <see href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record#nondestructive-mutation">non-destructive
27+
/// mutation using the <c>with</c> keyword</see>. In lower .NET/C# versions, this type is a plain <c>struct</c> with functionality
28+
/// supported by that language version.
29+
/// </para>
2230
/// </remarks>
2331
#if RECORD_STRUCT_SUPPORT
2432
public readonly record struct Color
@@ -129,7 +137,7 @@ public override bool Equals(object obj)
129137
/// <param name="green">The green component in the sRGB color space</param>
130138
/// <param name="blue">The blue component in the sRGB color space</param>
131139
/// <param name="alpha">The alpha (transparency) component, which must be between zero and one.</param>
132-
public Color(byte red, byte green, byte blue, double alpha)
140+
public Color(byte red, byte green, byte blue, double alpha = 1D)
133141
{
134142
Red = red;
135143
Green = green;

CSF.Screenplay.Selenium/ColorParser.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ protected static readonly string
3232
DecimalAlphaPart = @"\s*(0|1|0\.\d+)\s*",
3333
PercentDecimalPart = @"\s*(\d{1,3}|\d{1,2}\.\d+)%\s*",
3434
PercentPart = @"\s*(\d{1,3})%\s*",
35-
Hex2Part = @"\s*[1-9a-f]{2}\s*",
36-
Hex1Part = @"\s*[1-9a-f]\s*";
35+
Hex2Part = @"([0-9a-f]{2})",
36+
Hex1Part = @"([0-9a-f])";
3737

3838
/// <inheritdoc/>
3939
public virtual bool TryParseColor(string colorValue, out Color color)
@@ -80,7 +80,7 @@ internal class RgbColorParser : NumericColorParser
8080
protected override string GetParsingPattern(out bool includesAlpha)
8181
{
8282
includesAlpha = false;
83-
return @$"^\s*rgb\({BytePart},{BytePart},{BytePart}\)\s*$";
83+
return $"^\\s*rgb\\({BytePart},{BytePart},{BytePart}\\)\\s*$";
8484
}
8585
}
8686

@@ -90,7 +90,7 @@ internal class RgbaColorParser : NumericColorParser
9090
protected override string GetParsingPattern(out bool includesAlpha)
9191
{
9292
includesAlpha = true;
93-
return @$"^\s*rgba\({BytePart},{BytePart},{BytePart},{DecimalAlphaPart}\)\s*$";
93+
return $"^\\s*rgba\\({BytePart},{BytePart},{BytePart},{DecimalAlphaPart}\\)\\s*$";
9494
}
9595
}
9696

@@ -100,7 +100,7 @@ internal class RgbPercentageColorParser : NumericColorParser
100100
protected override string GetParsingPattern(out bool includesAlpha)
101101
{
102102
includesAlpha = false;
103-
return @$"^\s*rgb\({PercentDecimalPart},{PercentDecimalPart},{PercentDecimalPart}\)\s*$";
103+
return $"^\\s*rgb\\({PercentDecimalPart},{PercentDecimalPart},{PercentDecimalPart}\\)\\s*$";
104104
}
105105

106106
/// <inheritdoc/>
@@ -113,7 +113,7 @@ internal class RgbaPercentageColorParser : RgbPercentageColorParser
113113
protected override string GetParsingPattern(out bool includesAlpha)
114114
{
115115
includesAlpha = true;
116-
return @$"^\s*rgb\({PercentDecimalPart},{PercentDecimalPart},{PercentDecimalPart},{DecimalAlphaPart}\)\s*$";
116+
return $"^\\s*rgba\\({PercentDecimalPart},{PercentDecimalPart},{PercentDecimalPart},{DecimalAlphaPart}\\)\\s*$";
117117
}
118118
}
119119

@@ -123,7 +123,7 @@ internal class Hex3ColorParser : NumericColorParser
123123
protected override string GetParsingPattern(out bool includesAlpha)
124124
{
125125
includesAlpha = false;
126-
return @$"^\s*#{Hex1Part}{Hex1Part}{Hex1Part}\s*$";
126+
return $"^\\s*#{Hex1Part}{Hex1Part}{Hex1Part}\\s*$";
127127
}
128128

129129
/// <inheritdoc/>
@@ -136,7 +136,7 @@ internal class Hex6ColorParser : NumericColorParser
136136
protected override string GetParsingPattern(out bool includesAlpha)
137137
{
138138
includesAlpha = false;
139-
return @$"^\s*#{Hex2Part}{Hex2Part}{Hex2Part}\s*$";
139+
return $"^\\s*#{Hex2Part}{Hex2Part}{Hex2Part}\\s*$";
140140
}
141141

142142
/// <inheritdoc/>
@@ -163,7 +163,7 @@ public override bool TryParseColor(string colorValue, out Color color)
163163
protected override string GetParsingPattern(out bool includesAlpha)
164164
{
165165
includesAlpha = false;
166-
return @$"^\s*hsl\({BytePart},{PercentPart},{PercentPart}\s*$";
166+
return $"^\\s*hsl\\({BytePart},{PercentPart},{PercentPart}\\)\\s*$";
167167
}
168168

169169
/// <summary>
@@ -229,7 +229,7 @@ internal class HslaColorParser : HslColorParser
229229
protected override string GetParsingPattern(out bool includesAlpha)
230230
{
231231
includesAlpha = true;
232-
return @$"^\s*hsla\({BytePart},{PercentPart},{PercentPart},{DecimalAlphaPart}\s*$";
232+
return $"^\\s*hsla\\({BytePart},{PercentPart},{PercentPart},{DecimalAlphaPart}\\)\\s*$";
233233
}
234234
}
235235

CSF.Screenplay.Selenium/Colors.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ namespace CSF.Screenplay.Selenium
1313
/// Each static property of this type corresponds to a well-known pre-defined color, according to
1414
/// <see href="http://www.w3.org/TR/css3-color/#html4">the W3C HTML4 spec</see>.
1515
/// </para>
16+
/// <para>
17+
/// Alternatively, to get a <see cref="Color"/> instance which matches a string which names a color (case insensitive)
18+
/// use either <see cref="TryGetNamedColor(string, out Color)"/> or <see cref="GetNamedColor(string)"/>.
19+
/// </para>
1620
/// </remarks>
1721
public static class Colors
1822
{
@@ -213,7 +217,7 @@ public static Color GetNamedColor(string colorName)
213217
/// <returns><see langword="true"/> if <paramref name="colorName"/> is a well-known color name; <see langword="false"/> if not.</returns>
214218
public static bool TryGetNamedColor(string colorName, out Color color)
215219
{
216-
color = BLACK;
220+
color = default;
217221
if (colorName is null) return false;
218222
return namedColors.TryGetValue(colorName, out color);
219223
}
@@ -231,7 +235,7 @@ public static bool TryGetNamedColor(string colorName, out Color color)
231235
static ReadOnlyDictionary<string, Color> GetNamedColorCache()
232236
{
233237
var namedColorValues = typeof(Colors)
234-
.GetProperties(BindingFlags.Static)
238+
.GetProperties(BindingFlags.Static | BindingFlags.Public)
235239
.ToDictionary(k => k.Name, v => (Color) v.GetValue(null), StringComparer.InvariantCultureIgnoreCase);
236240
return new ReadOnlyDictionary<string, Color>(namedColorValues);
237241
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
3+
namespace CSF.Screenplay.Selenium;
4+
5+
[TestFixture, Parallelizable]
6+
public class ColorTests
7+
{
8+
[TestCase("rgb(1, 2, 3)", 1, 2, 3, 1)]
9+
[TestCase("rgb(10%, 20%, 30%)", 25, 51, 76, 1)]
10+
[TestCase("rgb(\t1, 2, 3)", 1, 2, 3, 1)]
11+
[TestCase("rgba(1, 2, 3, 0.5)", 1, 2, 3, 0.5D)]
12+
[TestCase("rgba(10%, 20%, 30%, 0.5)", 25, 51, 76, 0.5D)]
13+
[TestCase("#ff00a0", 255, 0, 160, 1)]
14+
[TestCase("#01Ff03", 1, 255, 3, 1)]
15+
[TestCase("#00FF33", 0, 255, 51, 1)]
16+
[TestCase("#0f3", 0, 255, 51, 1)]
17+
[TestCase("hsl(120, 100%, 25%)", 0, 128, 0, 1)]
18+
[TestCase("hsl(100, 0%, 50%)", 128, 128, 128, 1)]
19+
[TestCase("hsl(0, 100%, 50%)", 255, 0, 0, 1)]
20+
[TestCase("hsl(120, 100%, 50%)", 0, 255, 0, 1)]
21+
[TestCase("hsl(240, 100%, 50%)", 0, 0, 255, 1)]
22+
[TestCase("hsl(0, 0%, 100%)", 255, 255, 255, 1)]
23+
[TestCase("hsla(120, 100%, 25%, 1)", 0, 128, 0, 1)]
24+
[TestCase("hsla(100, 0%, 50%, 0.5)", 128, 128, 128, 0.5)]
25+
[TestCase("green", 0, 128, 0, 1)]
26+
[TestCase("gray", 128, 128, 128, 1)]
27+
[TestCase("transparent", 0, 0, 0, 0)]
28+
public void TryParseShouldReturnAMatchingColorObject(string colorString, byte red, byte green, byte blue, double alpha)
29+
{
30+
var result = Color.TryParse(colorString, out var color);
31+
Assert.Multiple(() =>
32+
{
33+
Assert.That(result, Is.True, "Parsing success");
34+
Assert.That(color, Is.EqualTo(new Color(red, green, blue, alpha)), "Expected color matches");
35+
});
36+
}
37+
}

0 commit comments

Comments
 (0)