0

In my angular protractor e2e tests, I want to do assertions on a html fragment like this:

<table>
    <thead>
        <tr>
            <th>Name</th>
            <th>Age</th>
            <th>Gender</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Joe</td>
            <td>23</td>
            <td>M</td>
        </tr>
        <tr>
            <td>Mary</td>
            <td>26</td>
            <td>W</td>
        </tr>
        ...
    </tbody>
</table>

How can I convert that into a JavaScript object like this?

[
    {Name: 'Joe', Age: 23, Gender: 'M'},
    {Name: 'Mary', Age: 25, Gender: 'W'},
    ...
]

I tried this, but it gives me one dimensional array:

const row = element.all(by.css('tr'));
const cells = row.all(by.tagName('td'));
return cells.map(function (elm) {
  return elm.getText();
});
slartidan
  • 20,403
  • 15
  • 83
  • 131
  • https://stackoverflow.com/questions/29501976/protractor-read-table-contents/36570127 seems not to work for me, because I did not create the table using ng-repeater. – slartidan Mar 02 '18 at 12:34
  • you don't need a repeater for that, you can get lists of elements based on other selectors too.. – toskv Mar 02 '18 at 12:52
  • what you had is correct, you just need to create objects instead of just mapping on the columns – toskv Mar 02 '18 at 12:53

1 Answers1

3

For your specific example, you can convert the HTML into a simple array of objects that match your needs:

function isHTMLTableElement(elem: HTMLTableElement | HTMLElement) : elem is HTMLTableElement {
    return elem.tagName === 'TABLE';
}

const table = document.getElementById('test');
const result: {name: string, age: number, gender: string}[] = [];

if (isHTMLTableElement(table)) {
    const rows: HTMLElement[] = [].slice.call(table.querySelectorAll('tbody tr'));
    for (const row of rows) {
        const cells: HTMLElement[] = [].slice.call(row.querySelectorAll('td'));
        const [name, age, gender] = cells.map(c => c.innerText);
        result.push({
            name: name,
            age: +age,
            gender: gender
        });
    }

    console.log(JSON.stringify(result));
}

You could collapse this further by replacing for loops with array methods, but I left it like this as it is probably more understandable.

Fenton
  • 241,084
  • 71
  • 387
  • 401
  • Thanks a lot! However, currently protractor complains: `Failed: document is not defined` – slartidan Mar 02 '18 at 13:01
  • If possible, I would prefer a solution, that uses the `th` elements to produce the names. However, I can give it a try to figure it out by myself. The type safety (age as number) is not a priority for me. – slartidan Mar 02 '18 at 13:02
  • You can do a similar trick on the `thead > tr > th` elements to get the names. – Fenton Mar 02 '18 at 17:42
  • Indeed it helped me, But How can we make it dynamic, like the name,age,gender etc. columns should come dynamically – Rohit Borude Aug 16 '20 at 06:11