Skip to content

Commit

Permalink
Merge pull request #109 from MADE-Apps/feature/web-table
Browse files Browse the repository at this point in the history
#93 - Added web Table element wrapper
  • Loading branch information
jamesmcroft authored Jan 12, 2022
2 parents 7af4228 + 5375239 commit a5e569d
Show file tree
Hide file tree
Showing 6 changed files with 360 additions and 6 deletions.
167 changes: 167 additions & 0 deletions samples/W3SchoolsWebTests/Tests/TableTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
namespace W3SchoolsWebTests.Tests
{
using System.Collections.Generic;
using System.Linq;
using Legerity;
using Legerity.Extensions;
using Legerity.Web.Elements.Core;
using NUnit.Framework;
using OpenQA.Selenium;
using Shouldly;

[TestFixture]
public class TableTests : BaseTestClass
{
public override string Url => "https://www.w3schools.com/html/tryit.asp?filename=tryhtml_table_intro";

[Test]
public void ShouldGetHeaders()
{
// Arrange
Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));

// Act
var headers = table.Headers.ToList();

// Assert
headers.ShouldBeEquivalentTo(new List<string> { "Company", "Contact", "Country" });
}

[Test]
public void ShouldGetRows()
{
// Arrange
var expectedRowValues = new List<IEnumerable<string>>
{
new List<string> {"Company", "Contact", "Country"},
new List<string> {"Alfreds Futterkiste", "Maria Anders", "Germany"},
new List<string> {"Centro comercial Moctezuma", "Francisco Chang", "Mexico"},
new List<string> {"Ernst Handel", "Roland Mendel", "Austria"},
new List<string> {"Island Trading", "Helen Bennett", "UK"},
new List<string> {"Laughing Bacchus Winecellars", "Yoshi Tannamuri", "Canada"},
new List<string> {"Magazzini Alimentari Riuniti", "Giovanni Rovelli", "Italy"},
};

Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));

// Act
var rows = table.Rows.ToList();

// Assert
for (int index = 0; index < rows.Count; index++)
{
TableRow row = rows[index];
row.Values.ToList().ShouldBeEquivalentTo(expectedRowValues[index]);
}
}

[Test]
public void ShouldGetDataRows()
{
// Arrange
var expectedRowValues = new List<IEnumerable<string>>
{
new List<string> {"Alfreds Futterkiste", "Maria Anders", "Germany"},
new List<string> {"Centro comercial Moctezuma", "Francisco Chang", "Mexico"},
new List<string> {"Ernst Handel", "Roland Mendel", "Austria"},
new List<string> {"Island Trading", "Helen Bennett", "UK"},
new List<string> {"Laughing Bacchus Winecellars", "Yoshi Tannamuri", "Canada"},
new List<string> {"Magazzini Alimentari Riuniti", "Giovanni Rovelli", "Italy"},
};

Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));

// Act
var rows = table.DataRows.ToList();

// Assert
rows.Count.ShouldBe(expectedRowValues.Count);

for (int index = 0; index < rows.Count; index++)
{
TableRow row = rows[index];
row.Values.ToList().ShouldBeEquivalentTo(expectedRowValues[index]);
}
}

[Test]
public void ShouldGetRowValuesByRowIndex()
{
// Arrange
var expectedRowValues = new Dictionary<string, string>
{
{"Company", "Alfreds Futterkiste"}, {"Contact", "Maria Anders"}, {"Country", "Germany"}
};

Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));

// Act
IReadOnlyDictionary<string, string> rowData = table.GetRowDataByIndex(0);

// Assert
rowData.Count.ShouldBe(expectedRowValues.Count);

foreach (KeyValuePair<string, string> row in rowData)
{
expectedRowValues.ShouldContainKeyAndValue(row.Key, row.Value);
}
}


[Test]
public void ShouldGetColumnValuesByColumnHeader()
{
// Arrange
var expectedColumnValues = new List<string>
{
"Alfreds Futterkiste",
"Centro comercial Moctezuma",
"Ernst Handel",
"Island Trading",
"Laughing Bacchus Winecellars",
"Magazzini Alimentari Riuniti"
};

Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));

// Act
var columnData = table.GetColumnDataByHeader("Company").ToList();

// Assert
columnData.Count.ShouldBe(expectedColumnValues.Count);

foreach (string columnValue in columnData)
{
expectedColumnValues.ShouldContain(columnValue);
}
}

[Test]
public void ShouldGetColumnValuesByColumnIndex()
{
// Arrange
var expectedColumnValues = new List<string>
{
"Alfreds Futterkiste",
"Centro comercial Moctezuma",
"Ernst Handel",
"Island Trading",
"Laughing Bacchus Winecellars",
"Magazzini Alimentari Riuniti"
};

Table table = AppManager.WebApp.FindWebElement(By.TagName("table"));

// Act
var columnData = table.GetColumnDataByIndex(0).ToList();

// Assert
columnData.Count.ShouldBe(expectedColumnValues.Count);

foreach (string columnValue in columnData)
{
expectedColumnValues.ShouldContain(columnValue);
}
}
}
}
10 changes: 10 additions & 0 deletions src/Legerity.Core/Extensions/DriverExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ public static ReadOnlyCollection<IWebElement> GetAllElements(this RemoteWebDrive
return driver.FindElements(By.XPath("//*"));
}

/// <summary>
/// Retrieves all child elements that can be located by the driver in the page.
/// </summary>
/// <param name="driver">The remote web driver.</param>
/// <returns>A readonly collection of <see cref="IWebElement"/>.</returns>
public static ReadOnlyCollection<IWebElement> GetAllChildElements(this RemoteWebDriver driver)
{
return driver.FindElements(By.XPath(".//*"));
}

/// <summary>
/// Waits until a specified driver condition is met, with an optional timeout.
/// </summary>
Expand Down
6 changes: 3 additions & 3 deletions src/Legerity.Core/Extensions/ElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ public static ReadOnlyCollection<IWebElement> FindElementsByPartialText(this IWe
}

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

/// <summary>
Expand Down
6 changes: 3 additions & 3 deletions src/Legerity.Core/Extensions/ElementWrapperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,17 @@ public static ReadOnlyCollection<IWebElement> FindElementsByPartialText<TElement
}

/// <summary>
/// Retrieves all elements that can be located by the driver for the given element.
/// Retrieves all child elements that can be located by the driver for the given element.
/// </summary>
/// <typeparam name="TElement">
/// The type of <see cref="IWebElement"/>.
/// </typeparam>
/// <param name="element">The remote web driver.</param>
/// <returns>A readonly collection of <see cref="IWebElement"/>.</returns>
public static ReadOnlyCollection<IWebElement> GetAllElements<TElement>(this IElementWrapper<TElement> element)
public static ReadOnlyCollection<IWebElement> GetAllChildElements<TElement>(this IElementWrapper<TElement> element)
where TElement : IWebElement
{
return element.Element.GetAllElements();
return element.Element.GetAllChildElements();
}

/// <summary>
Expand Down
135 changes: 135 additions & 0 deletions src/Legerity.Web/Elements/Core/Table.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
namespace Legerity.Web.Elements.Core
{
using System.Collections.Generic;
using System.Linq;
using Legerity.Extensions;
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;

/// <summary>
/// Defines a <see cref="IWebElement"/> wrapper for the core web Table control.
/// </summary>
public class Table : WebElementWrapper
{
/// <summary>
/// Initializes a new instance of the <see cref="Table"/> class.
/// </summary>
/// <param name="element">
/// The <see cref="IWebElement"/> reference.
/// </param>
public Table(IWebElement element)
: this(element as RemoteWebElement)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="Table"/> class.
/// </summary>
/// <param name="element">
/// The <see cref="RemoteWebElement"/> reference.
/// </param>
public Table(RemoteWebElement element)
: base(element)
{
}

/// <summary>
/// Gets or sets a value indicating whether the first row in the table is a header row.
/// </summary>
public bool IsFirstRowHeaders { get; set; } = true;

/// <summary>
/// Gets the header names for the table.
/// </summary>
public IEnumerable<string> Headers => this.IsFirstRowHeaders ? this.Rows.FirstOrDefault().GetAllChildElements().Select(x => x.Text) : this.Element.FindElements(By.TagName("th")).Select(x => x.Text);

/// <summary>
/// Gets the collection of rows associated with the table. If a header row exists, that will be included.
/// </summary>
public IEnumerable<TableRow> Rows => this.Element.FindElements(By.TagName("tr")).Select(e => new TableRow(e));

/// <summary>
/// Gets the collection of rows associated with the table only containing the data values (not headers).
/// </summary>
public IEnumerable<TableRow> DataRows => this.Rows.Where(x => x.FindElements(By.TagName("th")).Count == 0);

/// <summary>
/// Allows conversion of a <see cref="IWebElement"/> to the <see cref="Table"/> without direct casting.
/// </summary>
/// <param name="element">
/// The <see cref="IWebElement"/>.
/// </param>
/// <returns>
/// The <see cref="Table"/>.
/// </returns>
public static implicit operator Table(RemoteWebElement element)
{
return new Table(element);
}

/// <summary>
/// Retrieves the column names and associated column values for a specific row in the table by index.
/// </summary>
/// <param name="idx">The specified row data index to retrieve data for.</param>
/// <returns>A dictionary of key-value pairs containing the column header and the column value for the row.</returns>
public IReadOnlyDictionary<string, string> GetRowDataByIndex(int idx)
{
void AddRowData(IDictionary<string, string> dictionary, string header, TableRow tableRow, int valueIdx)
{
dictionary.Add(header, tableRow.Values.ElementAt(valueIdx));
}

var headers = this.Headers.ToList();

int dataRowIdx = this.IsFirstRowHeaders ? idx + 1 : idx;
TableRow dataRow = this.Rows.ElementAt(dataRowIdx);

var rowValues = new Dictionary<string, string>();

if (headers.Any())
{
for (int i = 0; i < headers.Count; i++)
{
AddRowData(rowValues, headers.ElementAt(i), dataRow, i);
}
}
else
{
for (int i = 0; i < dataRow.Values.Count(); i++)
{
AddRowData(rowValues, $"Col{i}", dataRow, i);
}
}

return rowValues;
}

/// <summary>
/// Retrieves the column values for a specific column by header name.
/// </summary>
/// <param name="header">The column header name.</param>
/// <returns>A collection of values for each row in the column.</returns>
public IEnumerable<string> GetColumnDataByHeader(string header)
{
var headers = this.Headers.ToList();
int idx = headers.IndexOf(header);
return this.GetColumnDataByIndex(idx);
}

/// <summary>
/// Retrieves the column values for a specific column by index.
/// </summary>
/// <param name="idx">The specified column index of data to retrieve.</param>
/// <returns>A collection of values for each row in the column.</returns>
public IEnumerable<string> GetColumnDataByIndex(int idx)
{
if (idx == -1)
{
return null;
}

var dataRows = this.DataRows.ToList();
return dataRows.Select(row => row.Values.ElementAt(idx)).ToList();
}
}
}
Loading

0 comments on commit a5e569d

Please sign in to comment.