Codeception: [Feature request] Executes the given callback within the context of a node

Created on 19 Jan 2017  路  7Comments  路  Source: Codeception/Codeception

Hi folks,

What are you trying to achieve?

Executes the given callback within the context of a node/element.
-- Capybara doc

Sometimes you may wish to perform several operations while scoping all of the operations within a given selector. For example, you may wish to assert that some text exists only within a table and then click a button within that table.
-- Laravel Dusk doc

Here a sample:

$I->amWithin('.table', function ($table) {
    $table->see('Hello World');
    $table->click('Delete');
});

See:

What do you get instead?

I didn't find any Codeception actions to achieve this feature.

Cheers,
Tortue Torche

Discuss ENHANCEMENT WebDriver

Most helpful comment

The callback way looks the most natural to me.

All 7 comments

Yep, we have it in CodeceptJS btw :)

This feature can be implemented but requires a deep look into the WebDriver module. Unlike Dusk we have lots of methods and many lines of code and everything needs to be taken into account when doing a feature like narrowing the search scope.

Let me think on it

To be honest it is not hard to implement that technically, but there are problems in displaying the results for such execution. You'd expect the output to look like:

within #form
  I click 'xx'
  I fill field 'yyy'
  I see 'zzz'

however, that doesn't fit into lots of cases: printing HTML report, using it inside Gherkin scenarios, using it inside pageobjects, etc. This feature can be easily implemented if this would be considered as one step:

I execute in context '#form', function

but I don't know how valuable would be that output information.
So the question is to implement it partly (in a bad way) or redesign the output to support this one feature.

Hi Michael,

Thank you for your response, I understand your concern about the output information issue.
Maybe, as a first try, the $I->amWithin()/within() feature could be an extension or an hidden API method like _findElements()?

Have a good day,
Tortue Torche

hi @tortuetorche , hey team, I have some ideas with the cleanest implementation.

$I->performOn('#xxxx', ['see' => 'xxx', 'seeElement' => 'zzzz']);
$I->performOn('xxxx', ElementActions::build()->see('xxxx')->click('zzzz'));
$I->performOn('xxxx', (new ElementActions())->click('zzzz'));
// in all this cases second parameter will be array of parameters

$I->performOn('xxx', function(ElementActions $I) {
  $I->see('xxxx');
  $I->click('zzzz');
});
// similar to previous, but a bit uglier output

This produces following output:

I perfrom on 'xxx', {see: 'xxx', seeElement: 'zzzz'}

if second parameter is function, it will be more like (I perfrom on 'xxx', lambda funciton)

This feature won't introduce new changes in reporters, steps, into the Codeception core.
This why I like it for simplicity. For WebDriver it is also possible to set a timeout (wait for element xxx to appear on page before running additional actions).

Right now I think this feature can be implemented for WebDriver.
This will improve performance for calls like

$I->waitForElement('#xxxx');
$I->click('#xxxx');

Not sure if it should be extended to PhpBrowser/Frameworks.
Also it is questionable which actions should be allowed inside an element.

Implementation of this method can be the following:

public function performOn($element, $actions) {
  $actionsArray = $this->elementActionsToArray($actions);
  $this->_setBaseElement($element);
  foreach ($action => $value) {
    call_user_func([$this, $action], $value);
  }
  $this->_setBaseElement(null);
}

So what do you think?

The callback way looks the most natural to me.

Agree with @Naktibalda

The callback way looks the most natural to me.

The problem with callback that you can't get a function body inside an output, so it probably won't be as nice. But if a scenario inside a function is long then it probably shouldn't even be included in output.

The array syntax would be good for cases described above:

$I->performOn('#modal', ['see' => 'Are you sure you want to delete this?']);

which will wait for modal to appear and then check the text inside it.

Also I'm not sure performOn is the best name for the function, but I can't find better. Suggestions are welcome

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sasha-x picture sasha-x  路  3Comments

allen0817 picture allen0817  路  3Comments

m4rcinkowski picture m4rcinkowski  路  4Comments

simara-svatopluk picture simara-svatopluk  路  3Comments

gimler picture gimler  路  3Comments