Search code examples
xpathcodeception

XPath to click 2nd link on page with this class


In an acceptance test I'm trying to click the .fa-trash-o icon for a given address. To simplify things, I've been trying to select the 2nd element on the page with the given class name.

I've tried several variations of the following with no luck.

$I->click(\Codeception\Util\Locator::find('a', ['class' => 'fa fa-trash-o']).'[2]');

$I->click('//a[contains(@class,"fa-trash-o")][2]');

Here's my template:

            <div class="grid simple address-card">
                <div class="grid-title no-border">
                    <h4>{{ $address->name }}</h4>
                    <div class="tools">
                        {!! Form::open(['url' => '/account/address/'.$address->id, 'method' => 'delete']) !!}
                            <a href="/account/address/{{ $address->id }}/edit" class="fa fa-edit"></a>
                            <a onclick="$(this).closest('form').submit();" class="fa fa-trash-o"></a>
                        {!! Form::close() !!}
                    </div>
                </div>
                <div class="grid-body no-border">
                    @include('partials.address')
                </div>
            </div>

How can I click that delete button?


Solution

  • You're tripping up on a common mistake related to the way the // operator is defined. The XPath expression

    //a[contains(@class,"fa-trash-o")][2]
    

    actually means

    /descendant-or-self::node()/child::a[contains(@class,"fa-trash-o")][2]
    

    The [2] applies to the child:: step rather than the descendant search, so it will select every a element that is the second fa-trash-o link under its respective parent. This finds nothing since there is only ever one such a within any given div. If you add parentheses:

    (//a[contains(@class,"fa-trash-o")])[2]
    

    then it will select the second fa-trash-o in the whole document - the [2] now applies to the whole result of the (...) expression rather than just the final step.