I'm writing some function tests and wrapping a Microsoft Playwright (it's similar to Selenium for those unfamiliar) IPage
with a page object containing some helper functions to simplify the test code.
The following function reads a table on the HTML page and makes the data available in a format that is easier for test assertions (where the column headers are the rows' dictionary keys). I'm wondering if there are some simpler ways to assembly this data for the desired output format. Thoughts?
public async Task<IEnumerable<IDictionary<string, string>>> GetTableData()
{
var headerCells = (await _page.QuerySelectorAllAsync("#results th"))
.Select(async c => await c.TextContentAsync())
.Select((t, i) => new
{
Text = t.Result?.Trim() ?? string.Empty,
ColumnIndex = i
})
.ToList();
var dataRows = (await _page.QuerySelectorAllAsync("#results td"))
.Select(async c => await c.TextContentAsync())
.Select((t, i) => new
{
Text = t.Result?.Trim() ?? string.Empty,
Index = i,
ColumnIndex = i % headerCells.Count
})
.GroupBy(o => o.Index / headerCells.Count)
.ToList();
var result = dataRows.Select(g => g
.Zip(headerCells, (dr, hr) => new KeyValuePair<string, string>(hr.Text, dr.Text))
.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Key)) // <- drop columns with no headers (e.g. Edit button column)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value))
.ToList();
return result;
}
```
async c => await c.TextContentAsync()
is equal toc => c.TextContentAsync()
but without generating yet another state machine. Or.Select.Select
can be always merged into single.Select
. \$\endgroup\$IAsyncEnumerable
usage. Then .ToArray a bit mre efficient than .ToList if you don't need add/remove the list items after creation. And finally, dictionary is overkill, too many copies of the keys. Better to useDataTable
here. \$\endgroup\$