Search code examples
testingautomationautomated-testse2e-testingtestcafe

TestCafe: Selector within Selector


I work on a set of helpers for my page model.

This is how the DOM may look like:

    <div id="parentA">
        <div class="child yes">hello</div>
        <div class="child">world</div>
    </div>
    
    <div id="parentB">
        <div class="child no">hello</div>
        <div class="child">world</div>
    </div>

Now I want to inspect one of the.child elements within #parentA or #parentB.

import { Selector } from "testcafe";

fixture `children`
    .page `http://localhost:8080/index.html`;

// an example of what I expect.
// this is not how i want to write tests.
test("hard-coded: child in A has class 'yes'", async (t) => {
    const yesChild = Selector("#parentA .child").withText("hello");
    t.expect((await yesChild.classNames).includes("yes"));
});

// helper function for the page model (in a shared module later)
function getParent(name: string) {
    return Selector(`#parent${name}`);
}
// helper function for the page model (in a shared module later)
function getChild() {
    return Selector(".child");
}

// this is how I want to write tests.
test("parametric-find: child in A has class 'yes'", async (t) => {
    const parent = getParent("A");
    const child = getChild().withText("hello");
    const yesChild = parent.find(child); // there is no overload for find that takes another Selector.
    t.expect((await yesChild.classNames).includes("yes"));
});

I think one solution could be a function like this:

async function withinParent(child: Selector, parent: Selector): Selector {
   // how should I implement this?
}

Another solution could be a higher order function that creates the filterFunction:

test("parametric-find-descendantChild: child in A has class 'yes'", async (t) => {
    const parent = getParent("A");
    const child = getChild().withText("hello");
    const yesChild = parent.find(descendantChild(child));
    
    t.expect((await yesChild.classNames).includes("yes"));
});
function descendantChild(child: Selector): (node: Element) => boolean {
    // how should I implement this?
}

but all the approaches I can think of lead to dead-ends.

  • parent and child could match multiple elements
  • there is no easy way to access an element selected by a selector in order to compare it to another selector
  • how do I compare Selectors?
  • functions/callbacks that operate on the Element level are executed in the browser. How cold I pass a Selector or the Elements matched by a selector into a browser function?

Well, should I write a feature request, or is there a smart way to do this?


Solution

  • You can chain Selector methods to achieve this.

    function getParent(name) {
        return Selector(`#parent${name}`);
    }
    
    function getChildren(selector) {
        return selector.child('.child');
    }
    
    test(`parametric-find: child in A has class 'yes'`, async (t) => {
        const parent = getParent('A');
        const child  = getChildren(parent).withText('hello');
    
        await t.expect(child.classNames).contains('yes');
    });