Behat / MinkExtension

Mink extension (tight integration and configuration) for Behat
MIT License
636 stars 277 forks source link

fillField function is always losing the focus of an element when triggered #257

Closed kwoxer closed 8 years ago

kwoxer commented 8 years ago

Versions:

    "cweagans/composer-patches": "~1.0",
    "drupal/drupal-extension": "3.2.2",
    "behat/mink-selenium2-driver": "1.3.1",
    "jarnaiz/behat-junit-formatter": "1.3.2",
    "emuse/behat-html-formatter": "0.1.0"

I was reading https://github.com/Behat/Behat/issues/96 and would like to have those:

  When I bring the focus to input field "<id>"
  When I type "<text>" in input field "<id>"

But they do not exists and any way to try to create them on my own failed.

So my current state of my own function:

/**
   * @Given I give focus to the css selector :selector
   */
  public function iGiveFocusToTheCssSelector($css_selector)
  {
    $element = $this->getSession()->getPage()->find("css", $css_selector);
    if ($css_selector === NULL) {
      throw new \Exception(sprintf('Could not evaluate CSS Selector: "%s"', $element, $this->getSession()->getCurrentUrl()));
    }

    $element->click();
    $element->fillField("some-field","some text...");
  }

It works. It selects the field and types in the text. But it is always loosing focus of that field. Also If I directly try to press enter directly afterwards with:

    $script = "jQuery.event.trigger({ type : 'keypress', which : '13' });";
    $this->getSession()->evaluateScript($script);

it has still looses the focus and nothing happens anymore.

Is there a way to stop Behat/Mink from loosing the focus on fields? Why is it loosing the focus? It does not really makes sense to me.

And the reason why it is so important to me, is because I'm waiting for the suggesting box that pops out just when the focus of the field is given.

Also found this one https://www.drupal.org/node/2112797 but it's already some years ago and I have a different issue.

spolischook commented 8 years ago

@spolischook, I already have that patch with the Key::TAB active. My issue is more that the "fillField" is filling the field properly but losing focus of the field afterwards.

So that method do that you describe above - remove all previous chapters, fill with new value and press Tab. What patch you mean? This method setValue used in fillValue, see the method chain.

kwoxer commented 8 years ago

I have this patch (https://github.com/minkphp/MinkSelenium2Driver/pull/244/commits/73dd93838e651d133aa8c77e3c4e6324e3768573) active. Which is the one you meaned.

But I don't think my issue is correspondating with that patch :)

Could you maybe help me out with me issue. I need to type something in a testfield and leave the focus on that field. How am I able to manage that?

As I said "fillField" is leaving the textfield after typing in that text.

aik099 commented 8 years ago

This is what's happening currently: the fillField internally calls setValue, which internally:

  1. removes contents of the field
  2. puts new content in the field
  3. also puts TAB symbol in the field to trigger onchange JS event

The last item is a nightmare, when you want to test auto-complete dropdowns (e.g. type and see a suggestions) because these auto-complete dropdowns usually close on blur/change.

If TAB symbol isn't send at the end then, onchange JS event is never triggered and if it's sent, then auto-complete dropdowns can't be tested.

I have no idea how to make both work without conflicts and without breaking a BC.

aik099 commented 8 years ago

Maybe https://github.com/minkphp/MinkSelenium2Driver/pull/244 patch (at least for Selenium2 driver) will solve this. Does it work for you @kwoxer ?

spolischook commented 8 years ago

For just set value

$this->getSession()
   ->getDriver()
   ->getWebDriverSession()
   ->element('xpath', $field->getXpath())
   ->postValue(['value' => [$value]]);

For type value

$existingValueLength = strlen($$field->getValue());
$value = str_repeat(Key::BACKSPACE . Key::DELETE, $existingValueLength) . $value;
$this->getSession()
   ->getDriver()
   ->getWebDriverSession()
   ->element('xpath', $field->getXpath())
   ->postValue(['value' => [$value]]);
kwoxer commented 8 years ago

No @aik099, I don't think the patch has something to do with this one here. The patch overall is working properly.

@spolischook, well I tried out:

$this->getSession()
      ->getDriver()
      ->getWebDriverSession()
      ->element('xpath', $element->getXpath())
      ->postValue(['value' => ["testtest"]]);

but getting this one:

unknown error: cannot focus element (Session info: chrome=53.0.2785.101) (Driver info: chromedriver=2.23.409687 (c46e862757edc04c06b1bd88724d15a5807b84d1),platform=Linux 4.4.0-38-generic x86_64) (WARNING: The server did not provide any stacktrace information) Command duration or timeout: 34 milliseconds Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 17:00:58' System info: host: 'curtis-ThinkPad-S3-S440', ip: '127.0.1.1', os.name: 'Linux', os.arch: 'amd64', os.version: '4.4.0-38-generic', java.version: '1.8.0_101' Driver info: org.openqa.selenium.chrome.ChromeDriver Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, networkConnectionEnabled=false, chrome={chromedriverVersion=2.23.409687 (c46e862757edc04c06b1bd88724d15a5807b84d1), userDataDir=/tmp/.org.chromium.Chromium.GMjWps}, takesHeapSnapshot=true, databaseEnabled=false, handlesAlerts=true, hasTouchScreen=false, version=53.0.2785.101, platform=LINUX, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true}] Session ID: 4dd2103c7183b7c0233e9fef41d0f97d

spolischook commented 8 years ago

be sure that field is visible, and field is input or textarea

kwoxer commented 8 years ago

Okay so I now did it in the way:

  /**
   * @Given I give focus to the :field
   */
  public function iGiveFocusToTheCssSelector($field)
  {
    $session = $this->getSession();
    $element = $session->getPage()->find(
      'xpath',
      $session->getSelectorsHandler()->selectorToXpath('xpath', $field)
    );
    $this->getSession()
      ->getDriver()
      ->getWebDriverSession()
      ->element('xpath', $element)
      ->postValue(['value' => ["testtest"]]);
  }

but getting:

Cannot find elements when the XPath expression is null.

The field (it's an input) exists. And I'm using it via:

And I give focus to the "//*[@class='search_api_views_fulltext']"

spolischook commented 8 years ago

->element('xpath', $element) you must set xpath as second argument but you push NodeElement instead. try ->element('xpath', $field) however rename $field variable to $xpathSelector FYI: use selectors in scenarios definition is a bad practice

kwoxer commented 8 years ago

Great it works! :) Thank you very much.

I now have

 /**
   * @Given I write :text into :field
   */
  public function iWriteTextIntoField($text, $field)
  {
    $this->getSession()
      ->getDriver()
      ->getWebDriverSession()
      ->element('xpath', $field)
      ->postValue(['value' => [$text]]);
  }

with

And I write "brüche" into "//*[@name='search_api_views_fulltext']"

And you said it's bad practice. How could I improve that? I'm open for all :)

spolischook commented 8 years ago

Step definition must be readable for business person, ideally it need to wrote someone that not programmer. In very simple case you can wrote something like

I type in Search field Some Text

and then in your context:

public function typeInField($fieldName, $text)
{
  $field = $this->getPage()->findField($fieldName);
  // do something with field
}

But in more complex tests you need smart elements and PageObject Pattern. See already implemented behat extensions, or write it by yourself.

kwoxer commented 8 years ago

And maybe another question here. I need to press Enter into the textfield. But somehow it does not happen anything. Also no error is thrown.

I'm using:

And I press the "enter" key in the "search_api_views_fulltext" field

aik099 commented 8 years ago

https://github.com/Behat/MinkExtension/issues/257#issuecomment-248910054

@kwoxer in code sample from above comment replace ->element('xpath', $field) with ->element('xpath', $session->getSelectorsHandler()->selectorToXpath('field', $field))

and then your step definition will be: And I write "brüche" into "search_api_views_fulltext"

At the end with all that code you're just emulating fixed fillField behavior.

aik099 commented 8 years ago

https://github.com/Behat/MinkExtension/issues/257#issuecomment-248919192

@spolischook , if you need PageObject and are using Mink (Behat does use Mink), then try out https://github.com/qa-tools/qa-tools .

spolischook commented 8 years ago

@aik099 Thanks, I remember about your repository and look at it time to time.

kwoxer commented 8 years ago

Hey @aik099 I have this now:

/**
   * @Given I write :text into :field
   */
  public function iWriteTextIntoField($text, $field)
  {
    $this->getSession()
      ->getDriver()
      ->getWebDriverSession()
      ->element('xpath', $this->getSession()->getSelectorsHandler()->selectorToXpath('field', $field))
      ->postValue(['value' => [$text]]);
  }

but getting

Selector "field" is not registered.

Do I have a mistake in there or what am I missing? I change for sure to " And I write "brüche" into "search_api_views_fulltext""

And by the way I have still no idea how I can hit the Enter key in the textfield :)

aik099 commented 8 years ago

Ah, that was my mistake trying to write the code without checking back with Mink source code. The ...->selectorToXpath('field', $field) should be ...->selectorToXpath('named_exact', array('field', $field)). Sorry for the confusion.

kwoxer commented 8 years ago

Thanks, that works perfectly.

And the other issue with hitting Enter? :)

aik099 commented 8 years ago

Thanks, that works perfectly.

Great.

And the other issue with hitting Enter? :)

Doesn't sending \r\n into ->postValue(... work?

kwoxer commented 8 years ago

And I write "\r\n" into "search_api_views_fulltext" did not work for me.

Well but I did not really understand why

And I press the "enter" key in the "search_api_views_fulltext" field

is not working. The code looks like sending a key that should work properly.

aik099 commented 8 years ago

I press the "..." key in the "..." field

I see no such step in MinkContext class.

kwoxer commented 8 years ago

Well I have this in the file .../vendor/drupal/drupal-extension/src/Drupal/DrupalExtension/Context/MinkContext.php

Line 140+

 /**
   * @Given I press the :char key in the :field field
   *
   * @param mixed $char could be either char ('b') or char-code (98)
   * @throws \Exception
   */
  public function pressKey($char, $field) {
...
aik099 commented 8 years ago

That you should be submitting to https://github.com/jhedstrom/drupalextension repo instead. Looking at step code (https://github.com/jhedstrom/drupalextension/blob/master/src/Drupal/DrupalExtension/Context/MinkContext.php#L145-L192) it should work. However I would rather use postValue call to send special symbols (e.g. TAB, ENTER, etc.) to input instead of doing keydown/keyup events are they do.

kwoxer commented 8 years ago

Ok gonna post issue there.

But maybe you can get it running for me somehow with postValue?

So what do I have to write here as value in to get it running and simulating the Enter key? ->postValue(...);

aik099 commented 8 years ago

just "\r\n" I guess in double quotes.

kwoxer commented 8 years ago

->postValue("\r\n");

leads to

Curl error thrown for http POST to http://localhost:4444/wd/hub/session/35c9f3b4-cb6e-4e76-a3c2-cc8ed2c9e419/element/1/value/

Illegal characters found in URL

kwoxer commented 8 years ago

Ahh you meaned

->postValue(['value' => ["\r\n"]]);

yes. That works! Thank you. Awesome. :)

aik099 commented 8 years ago

Exactly. Keep in mind, that ->postValue doesn't replace value in the input, but just appending given text to where cursor is in that field.

kwoxer commented 8 years ago

Yeah already noticed that. But that's no problem for my task. Can be closed here now :) Thank you my friend.

aik099 commented 8 years ago

I don't have admin rights on this repo, so you'll need to close the issue yourself.

jnicola commented 1 year ago

For anyone interested in a final working version, this worked for me:

`/**