Skip to content

Commit a5e569d

Browse files
authored
Merge pull request #109 from MADE-Apps/feature/web-table
#93 - Added web Table element wrapper
2 parents 7af4228 + 5375239 commit a5e569d

File tree

6 files changed

+360
-6
lines changed

6 files changed

+360
-6
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
namespace W3SchoolsWebTests.Tests
2+
{
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using Legerity;
6+
using Legerity.Extensions;
7+
using Legerity.Web.Elements.Core;
8+
using NUnit.Framework;
9+
using OpenQA.Selenium;
10+
using Shouldly;
11+
12+
[TestFixture]
13+
public class TableTests : BaseTestClass
14+
{
15+
public override string Url => "https://www.w3schools.com/html/tryit.asp?filename=tryhtml_table_intro";
16+
17+
[Test]
18+
public void ShouldGetHeaders()
19+
{
20+
// Arrange
21+
Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));
22+
23+
// Act
24+
var headers = table.Headers.ToList();
25+
26+
// Assert
27+
headers.ShouldBeEquivalentTo(new List<string> { "Company", "Contact", "Country" });
28+
}
29+
30+
[Test]
31+
public void ShouldGetRows()
32+
{
33+
// Arrange
34+
var expectedRowValues = new List<IEnumerable<string>>
35+
{
36+
new List<string> {"Company", "Contact", "Country"},
37+
new List<string> {"Alfreds Futterkiste", "Maria Anders", "Germany"},
38+
new List<string> {"Centro comercial Moctezuma", "Francisco Chang", "Mexico"},
39+
new List<string> {"Ernst Handel", "Roland Mendel", "Austria"},
40+
new List<string> {"Island Trading", "Helen Bennett", "UK"},
41+
new List<string> {"Laughing Bacchus Winecellars", "Yoshi Tannamuri", "Canada"},
42+
new List<string> {"Magazzini Alimentari Riuniti", "Giovanni Rovelli", "Italy"},
43+
};
44+
45+
Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));
46+
47+
// Act
48+
var rows = table.Rows.ToList();
49+
50+
// Assert
51+
for (int index = 0; index < rows.Count; index++)
52+
{
53+
TableRow row = rows[index];
54+
row.Values.ToList().ShouldBeEquivalentTo(expectedRowValues[index]);
55+
}
56+
}
57+
58+
[Test]
59+
public void ShouldGetDataRows()
60+
{
61+
// Arrange
62+
var expectedRowValues = new List<IEnumerable<string>>
63+
{
64+
new List<string> {"Alfreds Futterkiste", "Maria Anders", "Germany"},
65+
new List<string> {"Centro comercial Moctezuma", "Francisco Chang", "Mexico"},
66+
new List<string> {"Ernst Handel", "Roland Mendel", "Austria"},
67+
new List<string> {"Island Trading", "Helen Bennett", "UK"},
68+
new List<string> {"Laughing Bacchus Winecellars", "Yoshi Tannamuri", "Canada"},
69+
new List<string> {"Magazzini Alimentari Riuniti", "Giovanni Rovelli", "Italy"},
70+
};
71+
72+
Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));
73+
74+
// Act
75+
var rows = table.DataRows.ToList();
76+
77+
// Assert
78+
rows.Count.ShouldBe(expectedRowValues.Count);
79+
80+
for (int index = 0; index < rows.Count; index++)
81+
{
82+
TableRow row = rows[index];
83+
row.Values.ToList().ShouldBeEquivalentTo(expectedRowValues[index]);
84+
}
85+
}
86+
87+
[Test]
88+
public void ShouldGetRowValuesByRowIndex()
89+
{
90+
// Arrange
91+
var expectedRowValues = new Dictionary<string, string>
92+
{
93+
{"Company", "Alfreds Futterkiste"}, {"Contact", "Maria Anders"}, {"Country", "Germany"}
94+
};
95+
96+
Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));
97+
98+
// Act
99+
IReadOnlyDictionary<string, string> rowData = table.GetRowDataByIndex(0);
100+
101+
// Assert
102+
rowData.Count.ShouldBe(expectedRowValues.Count);
103+
104+
foreach (KeyValuePair<string, string> row in rowData)
105+
{
106+
expectedRowValues.ShouldContainKeyAndValue(row.Key, row.Value);
107+
}
108+
}
109+
110+
111+
[Test]
112+
public void ShouldGetColumnValuesByColumnHeader()
113+
{
114+
// Arrange
115+
var expectedColumnValues = new List<string>
116+
{
117+
"Alfreds Futterkiste",
118+
"Centro comercial Moctezuma",
119+
"Ernst Handel",
120+
"Island Trading",
121+
"Laughing Bacchus Winecellars",
122+
"Magazzini Alimentari Riuniti"
123+
};
124+
125+
Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));
126+
127+
// Act
128+
var columnData = table.GetColumnDataByHeader("Company").ToList();
129+
130+
// Assert
131+
columnData.Count.ShouldBe(expectedColumnValues.Count);
132+
133+
foreach (string columnValue in columnData)
134+
{
135+
expectedColumnValues.ShouldContain(columnValue);
136+
}
137+
}
138+
139+
[Test]
140+
public void ShouldGetColumnValuesByColumnIndex()
141+
{
142+
// Arrange
143+
var expectedColumnValues = new List<string>
144+
{
145+
"Alfreds Futterkiste",
146+
"Centro comercial Moctezuma",
147+
"Ernst Handel",
148+
"Island Trading",
149+
"Laughing Bacchus Winecellars",
150+
"Magazzini Alimentari Riuniti"
151+
};
152+
153+
Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));
154+
155+
// Act
156+
var columnData = table.GetColumnDataByIndex(0).ToList();
157+
158+
// Assert
159+
columnData.Count.ShouldBe(expectedColumnValues.Count);
160+
161+
foreach (string columnValue in columnData)
162+
{
163+
expectedColumnValues.ShouldContain(columnValue);
164+
}
165+
}
166+
}
167+
}

src/Legerity.Core/Extensions/DriverExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ public static ReadOnlyCollection<IWebElement> GetAllElements(this RemoteWebDrive
8888
return driver.FindElements(By.XPath("//*"));
8989
}
9090

91+
/// <summary>
92+
/// Retrieves all child elements that can be located by the driver in the page.
93+
/// </summary>
94+
/// <param name="driver">The remote web driver.</param>
95+
/// <returns>A readonly collection of <see cref="IWebElement"/>.</returns>
96+
public static ReadOnlyCollection<IWebElement> GetAllChildElements(this RemoteWebDriver driver)
97+
{
98+
return driver.FindElements(By.XPath(".//*"));
99+
}
100+
91101
/// <summary>
92102
/// Waits until a specified driver condition is met, with an optional timeout.
93103
/// </summary>

src/Legerity.Core/Extensions/ElementExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,13 @@ public static ReadOnlyCollection<IWebElement> FindElementsByPartialText(this IWe
9292
}
9393

9494
/// <summary>
95-
/// Retrieves all elements that can be located by the driver for the given element.
95+
/// Retrieves all child elements that can be located by the driver for the given element.
9696
/// </summary>
9797
/// <param name="element">The remote web driver.</param>
9898
/// <returns>A readonly collection of <see cref="IWebElement"/>.</returns>
99-
public static ReadOnlyCollection<IWebElement> GetAllElements(this IWebElement element)
99+
public static ReadOnlyCollection<IWebElement> GetAllChildElements(this IWebElement element)
100100
{
101-
return element.FindElements(By.XPath("//*"));
101+
return element.FindElements(By.XPath(".//*"));
102102
}
103103

104104
/// <summary>

src/Legerity.Core/Extensions/ElementWrapperExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,17 @@ public static ReadOnlyCollection<IWebElement> FindElementsByPartialText<TElement
117117
}
118118

119119
/// <summary>
120-
/// Retrieves all elements that can be located by the driver for the given element.
120+
/// Retrieves all child elements that can be located by the driver for the given element.
121121
/// </summary>
122122
/// <typeparam name="TElement">
123123
/// The type of <see cref="IWebElement"/>.
124124
/// </typeparam>
125125
/// <param name="element">The remote web driver.</param>
126126
/// <returns>A readonly collection of <see cref="IWebElement"/>.</returns>
127-
public static ReadOnlyCollection<IWebElement> GetAllElements<TElement>(this IElementWrapper<TElement> element)
127+
public static ReadOnlyCollection<IWebElement> GetAllChildElements<TElement>(this IElementWrapper<TElement> element)
128128
where TElement : IWebElement
129129
{
130-
return element.Element.GetAllElements();
130+
return element.Element.GetAllChildElements();
131131
}
132132

133133
/// <summary>
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
namespace Legerity.Web.Elements.Core
2+
{
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using Legerity.Extensions;
6+
using OpenQA.Selenium;
7+
using OpenQA.Selenium.Remote;
8+
9+
/// <summary>
10+
/// Defines a <see cref="IWebElement"/> wrapper for the core web Table control.
11+
/// </summary>
12+
public class Table : WebElementWrapper
13+
{
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="Table"/> class.
16+
/// </summary>
17+
/// <param name="element">
18+
/// The <see cref="IWebElement"/> reference.
19+
/// </param>
20+
public Table(IWebElement element)
21+
: this(element as RemoteWebElement)
22+
{
23+
}
24+
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="Table"/> class.
27+
/// </summary>
28+
/// <param name="element">
29+
/// The <see cref="RemoteWebElement"/> reference.
30+
/// </param>
31+
public Table(RemoteWebElement element)
32+
: base(element)
33+
{
34+
}
35+
36+
/// <summary>
37+
/// Gets or sets a value indicating whether the first row in the table is a header row.
38+
/// </summary>
39+
public bool IsFirstRowHeaders { get; set; } = true;
40+
41+
/// <summary>
42+
/// Gets the header names for the table.
43+
/// </summary>
44+
public IEnumerable<string> Headers => this.IsFirstRowHeaders ? this.Rows.FirstOrDefault().GetAllChildElements().Select(x => x.Text) : this.Element.FindElements(By.TagName("th")).Select(x => x.Text);
45+
46+
/// <summary>
47+
/// Gets the collection of rows associated with the table. If a header row exists, that will be included.
48+
/// </summary>
49+
public IEnumerable<TableRow> Rows => this.Element.FindElements(By.TagName("tr")).Select(e => new TableRow(e));
50+
51+
/// <summary>
52+
/// Gets the collection of rows associated with the table only containing the data values (not headers).
53+
/// </summary>
54+
public IEnumerable<TableRow> DataRows => this.Rows.Where(x => x.FindElements(By.TagName("th")).Count == 0);
55+
56+
/// <summary>
57+
/// Allows conversion of a <see cref="IWebElement"/> to the <see cref="Table"/> without direct casting.
58+
/// </summary>
59+
/// <param name="element">
60+
/// The <see cref="IWebElement"/>.
61+
/// </param>
62+
/// <returns>
63+
/// The <see cref="Table"/>.
64+
/// </returns>
65+
public static implicit operator Table(RemoteWebElement element)
66+
{
67+
return new Table(element);
68+
}
69+
70+
/// <summary>
71+
/// Retrieves the column names and associated column values for a specific row in the table by index.
72+
/// </summary>
73+
/// <param name="idx">The specified row data index to retrieve data for.</param>
74+
/// <returns>A dictionary of key-value pairs containing the column header and the column value for the row.</returns>
75+
public IReadOnlyDictionary<string, string> GetRowDataByIndex(int idx)
76+
{
77+
void AddRowData(IDictionary<string, string> dictionary, string header, TableRow tableRow, int valueIdx)
78+
{
79+
dictionary.Add(header, tableRow.Values.ElementAt(valueIdx));
80+
}
81+
82+
var headers = this.Headers.ToList();
83+
84+
int dataRowIdx = this.IsFirstRowHeaders ? idx + 1 : idx;
85+
TableRow dataRow = this.Rows.ElementAt(dataRowIdx);
86+
87+
var rowValues = new Dictionary<string, string>();
88+
89+
if (headers.Any())
90+
{
91+
for (int i = 0; i < headers.Count; i++)
92+
{
93+
AddRowData(rowValues, headers.ElementAt(i), dataRow, i);
94+
}
95+
}
96+
else
97+
{
98+
for (int i = 0; i < dataRow.Values.Count(); i++)
99+
{
100+
AddRowData(rowValues, $"Col{i}", dataRow, i);
101+
}
102+
}
103+
104+
return rowValues;
105+
}
106+
107+
/// <summary>
108+
/// Retrieves the column values for a specific column by header name.
109+
/// </summary>
110+
/// <param name="header">The column header name.</param>
111+
/// <returns>A collection of values for each row in the column.</returns>
112+
public IEnumerable<string> GetColumnDataByHeader(string header)
113+
{
114+
var headers = this.Headers.ToList();
115+
int idx = headers.IndexOf(header);
116+
return this.GetColumnDataByIndex(idx);
117+
}
118+
119+
/// <summary>
120+
/// Retrieves the column values for a specific column by index.
121+
/// </summary>
122+
/// <param name="idx">The specified column index of data to retrieve.</param>
123+
/// <returns>A collection of values for each row in the column.</returns>
124+
public IEnumerable<string> GetColumnDataByIndex(int idx)
125+
{
126+
if (idx == -1)
127+
{
128+
return null;
129+
}
130+
131+
var dataRows = this.DataRows.ToList();
132+
return dataRows.Select(row => row.Values.ElementAt(idx)).ToList();
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)