Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iWaitForAjaxToFinish() fails #557

Open
brooke-heaton opened this issue Oct 22, 2019 · 7 comments
Open

iWaitForAjaxToFinish() fails #557

brooke-heaton opened this issue Oct 22, 2019 · 7 comments

Comments

@brooke-heaton
Copy link

I'm consistently getting null $result on drupal-extension/src/Drupal/DrupalExtension/Context/MinkContext.php:156 I'm not clear on where the failure is but this essentially makes any @javascript Feature impossible. I'm on Drupal v 8.7.8 using Lando with the selenium/standalone-chrome:3.141.59-oxygen chromedriver on PHP 7.2

Full error output for reference:

[Testing\Behat] Running behat  --format pretty /app/tests/behat --colors --no-interaction --stop-on-failure --strict --config /app/tests/behat/naswa.behat.yml --profile naswa --tags @conference -v
@naswa @javascript @api @release @fields @conference
Feature: Conference
  In order to verify that the conference has the correct fields and form display
  As an administrator
  I should be able to go to the conference create page and all fields

  Scenario: Create a conference page with all fields                      # features/naswa/release/ContentEditing/ConferenceFields.feature:7
    Given I am logged in as user "[email protected]"                      # Drupal\FeatureContext::iAmLoggedInAsUser()
    Given I am on "/node/add/conference"                                  # Drupal\DrupalExtension\Context\MinkContext::visit()
    ┌─ @BeforeStep # Drupal\DrupalExtension\Context\MinkContext::beforeJavascriptStep()
    │
    ╳  RuntimeException: Unable to complete AJAX request. {"name":"step.before","feature":"Conference","step":"I click the \"Primary Info\" tab","suite":"default"} in /app/vendor/drupal/drupal-extension/src/Drupal/DrupalExtension/Context/MinkContext.php:173
    ╳  Stack trace:
    ╳  #0 /app/vendor/drupal/drupal-extension/src/Drupal/DrupalExtension/Context/MinkContext.php(107): Drupal\DrupalExtension\Context\MinkContext->iWaitForAjaxToFinish(Object(Behat\Behat\Hook\Scope\BeforeStepScope))
    ╳  #1 [internal function]: Drupal\DrupalExtension\Context\MinkContext->beforeJavascriptStep(Object(Behat\Behat\Hook\Scope\BeforeStepScope))
    ╳  #2 /app/vendor/behat/behat/src/Behat/Testwork/Call/Handler/RuntimeCallHandler.php(109): call_user_func_array(Array, Array)
    ╳  #3 /app/vendor/behat/behat/src/Behat/Testwork/Call/Handler/RuntimeCallHandler.php(64): Behat\Testwork\Call\Handler\RuntimeCallHandler->executeCall(Object(Behat\Testwork\Hook\Call\HookCall))
    ╳  #4 /app/vendor/behat/behat/src/Behat/Testwork/Call/CallCenter.php(140): Behat\Testwork\Call\Handler\RuntimeCallHandler->handleCall(Object(Behat\Testwork\Hook\Call\HookCall))
    ╳  #5 /app/vendor/behat/behat/src/Behat/Testwork/Call/CallCenter.php(96): Behat\Testwork\Call\CallCenter->handleCall(Object(Behat\Testwork\Hook\Call\HookCall))
    ╳  #6 /app/vendor/behat/behat/src/Behat/Testwork/Hook/HookDispatcher.php(74): Behat\Testwork\Call\CallCenter->makeCall(Object(Behat\Testwork\Hook\Call\HookCall))
    ╳  #7 /app/vendor/behat/behat/src/Behat/Testwork/Hook/HookDispatcher.php(58): Behat\Testwork\Hook\HookDispatcher->dispatchHook(Object(Behat\Behat\Hook\Scope\BeforeStepScope), Object(Behat\Behat\Hook\Call\BeforeStep))
    ╳  #8 /app/vendor/behat/behat/src/Behat/Behat/Hook/Tester/HookableStepTester.php(64): Behat\Testwork\Hook\HookDispatcher->dispatchScopeHooks(Object(Behat\Behat\Hook\Scope\BeforeStepScope))
    ╳  #9 /app/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/TickingStepTester.php(49): Behat\Behat\Hook\Tester\HookableStepTester->setUp(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\StepNode), false)
    ╳  #10 /app/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingStepTester.php(60): Behat\Behat\EventDispatcher\Tester\TickingStepTester->setUp(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\StepNode), false)
    ╳  #11 /app/vendor/behat/behat/src/Behat/Behat/Tester/StepContainerTester.php(56): Behat\Behat\EventDispatcher\Tester\EventDispatchingStepTester->setUp(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\StepNode), false)
    ╳  #12 /app/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeScenarioTester.php(76): Behat\Behat\Tester\StepContainerTester->test(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\ScenarioNode), false)
    ╳  #13 /app/vendor/behat/behat/src/Behat/Behat/Hook/Tester/HookableScenarioTester.php(74): Behat\Behat\Tester\Runtime\RuntimeScenarioTester->test(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\ScenarioNode), false)
    ╳  #14 /app/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingScenarioTester.php(103): Behat\Behat\Hook\Tester\HookableScenarioTester->test(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\ScenarioNode), false)
    ╳  #15 /app/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/IsolatingScenarioTester.php(69): Behat\Behat\EventDispatcher\Tester\EventDispatchingScenarioTester->test(Object(Behat\Behat\Context\Environment\InitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\ScenarioNode), false)
    ╳  #16 /app/vendor/behat/behat/src/Behat/Behat/Tester/Runtime/RuntimeFeatureTester.php(84): Behat\Behat\Tester\Runtime\IsolatingScenarioTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), Object(Behat\Gherkin\Node\ScenarioNode), false)
    ╳  #17 /app/vendor/behat/behat/src/Behat/Behat/Hook/Tester/HookableFeatureTester.php(72): Behat\Behat\Tester\Runtime\RuntimeFeatureTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), false)
    ╳  #18 /app/vendor/behat/behat/src/Behat/Behat/EventDispatcher/Tester/EventDispatchingFeatureTester.php(71): Behat\Behat\Hook\Tester\HookableFeatureTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), false)
    ╳  #19 /app/vendor/behat/behat/src/Behat/Testwork/Tester/Runtime/RuntimeSuiteTester.php(63): Behat\Behat\EventDispatcher\Tester\EventDispatchingFeatureTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Gherkin\Node\FeatureNode), false)
    ╳  #20 /app/vendor/behat/behat/src/Behat/Testwork/Hook/Tester/HookableSuiteTester.php(73): Behat\Testwork\Tester\Runtime\RuntimeSuiteTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Testwork\Specification\GroupedSpecificationIterator), false)
    ╳  #21 /app/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingSuiteTester.php(72): Behat\Testwork\Hook\Tester\HookableSuiteTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Testwork\Specification\GroupedSpecificationIterator), false)
    ╳  #22 /app/vendor/behat/behat/src/Behat/Testwork/Tester/Runtime/RuntimeExercise.php(71): Behat\Testwork\EventDispatcher\Tester\EventDispatchingSuiteTester->test(Object(Behat\Behat\Context\Environment\UninitializedContextEnvironment), Object(Behat\Testwork\Specification\GroupedSpecificationIterator), false)
    ╳  #23 /app/vendor/behat/behat/src/Behat/Testwork/EventDispatcher/Tester/EventDispatchingExercise.php(70): Behat\Testwork\Tester\Runtime\RuntimeExercise->test(Array, false)
    ╳  #24 /app/vendor/behat/behat/src/Behat/Testwork/Ordering/OrderedExercise.php(80): Behat\Testwork\EventDispatcher\Tester\EventDispatchingExercise->test(Array, false)
    ╳  #25 /app/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/ExerciseController.php(149): Behat\Testwork\Ordering\OrderedExercise->test(Array, false)
    ╳  #26 /app/vendor/behat/behat/src/Behat/Testwork/Tester/Cli/ExerciseController.php(108): Behat\Testwork\Tester\Cli\ExerciseController->testSpecifications(Object(Symfony\Component\Console\Input\ArgvInput), Array)
    ╳  #27 /app/vendor/behat/behat/src/Behat/Testwork/Cli/Command.php(63): Behat\Testwork\Tester\Cli\ExerciseController->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    ╳  #28 /app/vendor/symfony/console/Command/Command.php(255): Behat\Testwork\Cli\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    ╳  #29 /app/vendor/symfony/console/Application.php(982): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    ╳  #30 /app/vendor/symfony/console/Application.php(255): Symfony\Component\Console\Application->doRunCommand(Object(Behat\Testwork\Cli\Command), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    ╳  #31 /app/vendor/behat/behat/src/Behat/Testwork/Cli/Application.php(124): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    ╳  #32 /app/vendor/symfony/console/Application.php(148): Behat\Testwork\Cli\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    ╳  #33 /app/vendor/behat/behat/bin/behat(34): Symfony\Component\Console\Application->run()
    ╳  #34 {main}
    │
    And I click the "Primary Info" tab                                    # Drupal\FeatureContext::iClickTheTab()
    And I click the ".js-form-item-field-date-range-0-value-date" element # Drupal\FeatureContext::iClickTheElement()
    And I choose "January" for "Month" in the "Start date" Datepicker     # Drupal\FeatureContext::iChooseForInTheDatepicker()
@ptmkenny
Copy link

ptmkenny commented Jun 25, 2020

iWaitForAjaxtoFinish() is also failing for me.

I'm using Drupal Extension 4.1.0 on Drupal 8.9.1.

I have my site set up locally using lando; when I run the tests there, "I wait for AJAX to finish" works as expected.

Lando is using the selenium/standalone-chrome-debug docker image.

However, when I push my changes to github and a build is triggered via CircleCI on Pantheon, the "I wait for AJAX to finish" step gives me the following error:

Pantheon CircleCI uses DMore\Chromedriver instead of selenium standalone chrome:

# Dynamically set Behat configuration parameters
export BEHAT_PARAMS='{"extensions" : {"Behat\\MinkExtension" : {"base_url" : "https://'$TERMINUS_ENV'-'$TERMINUS_SITE'.pantheonsite.io/"}, "Drupal\\DrupalExtension" : {"drush" :   {  "alias":  "@pantheon.'$TERMINUS_SITE'.'$TERMINUS_ENV'" }, { "root": "/app/web" }}}}'

# Start headless Chrome
echo "\n Starting Chrome in headless mode ..."
google-chrome --disable-gpu --headless --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 --no-sandbox </dev/null &>/dev/null &

      Browser crashed (Behat\Mink\Exception\DriverException)
 
    │
 
    ╳  Fatal error: Call to a member function call() on null (Behat\Testwork\Call\Exception\FatalThrowableError)
 
    │
 
    └─ @AfterStep # FailAid\Context\FailureContext::gatherStateFactsAfterFailedStep()
 
      Internal error
 

I also get the same browser crash with code like this:

  /**
   * Wait for a element.
   *
   * https://stackoverflow.com/a/49117368/1209486
   *
   * @When (I )wait :count second(s) until I see the :element element
   */
  public function iWaitSecondsForElement($seconds, $element) {
    $timeout = $seconds * 1000;
    $page = $this->getSession()->getPage();

    $page->waitFor("$timeout",
      function () use ($page, $element) {
        return $page->find('css', "$element");
      }
    );
  }

In both cases, this error only occurs when testing the Pantheon site via CircleCI; it does not occur locally in lando.

@shagel7984
Copy link

Any news here?

@michaellenahan
Copy link
Contributor

michaellenahan commented Jan 7, 2022

Using drupal/drupal-extension v4.1.0 with Drupal 9.2.9

iWaitForAjaxtoFinish() is failing for us on our local VMs, but, strangely, not on our gitlab-ci.

On the VM, we get many "Unable to complete AJAX request" errors.

Taking a look at MinkContext.php ...

https://github.com/jhedstrom/drupalextension/blob/master/src/Drupal/DrupalExtension/Context/MinkContext.php#L147

... we see that iWaitForAjaxToFinish() contains a javascript function which returns false if Ajax is still running, and returns true if Ajax is no longer running.

The problem I am having is that the jQuery.active property is not always available, and, in the cases where jQuery.active is not available, jQuery.active === 0 is evaluating to false. Therefore, the javascript function is returning false, and the $this->getSession()->wait() times out.

Here is the pull request:
#603

In the PR I made this change to the javascript function, to add the check to see if the jQuery.active property was available: jQuery.hasOwnProperty('active') === false (admittedly, this is verbose ... it does not have to stay that way ... I did it like this for my own clarity while debugging).

      return (
        // Assert no AJAX request is running (via jQuery or Drupal) and no
        // animation is running.
        (typeof jQuery === 'undefined' || jQuery.hasOwnProperty('active') === false || (jQuery.active === 0 && jQuery(':animated').length === 0)) &&
        d7_not_ajaxing && d8_not_ajaxing
      );

Here is some debug code, this helped me investigate what jQuery.active was doing.

    public function iWaitForAjaxToFinish($event = null)
    {
        // DEBUG
        $active = <<<JS
    (function() {
        if (typeof jQuery === 'undefined') {
            return "jQuery === 'undefined'";
        }
        if (jQuery.hasOwnProperty('active') === false) {
            return 'jQuery.hasOwnProperty(\'active\') === false';
        }
        return jQuery.active;
    }());
JS;
        $active_result = $this->getSession()->evaluateScript($active);
        echo $active_result;
        // END DEBUG

Running the behat command I now get output like this:

Sometimes, jQuery.active returns an integer (0).
Sometimes, jQuery itself is undefined.
Sometimes, jQuery is defined but it has no 'active' property <-- this is the case which was not handled properly and was causing the error.

$ ${BEHAT} --config ${CODEBASE_DIR}/tests/behat/behat.gitlab.yml ${BEHAT_OPTIONS} --tags=comments -vvv
@javascript @api @comments @coretest
Feature: Test comments.

  Scenario: I disable Google Recaptcha for testing comments.          # features/comments.feature:7
    Given I am logged in as a user with the "administrator" role      # FeatureContext::assertAuthenticatedByRole()
    And I am on "/admin/config/services/vue_comments"                 # Drupal\DrupalExtension\Context\MinkContext::visit()
    Then I uncheck the box "Enable Google Captcha"                    # Drupal\DrupalExtension\Context\MinkContext::assertUncheckBox()
    ┌─ @BeforeStep # Drupal\DrupalExtension\Context\MinkContext::beforeJavascriptStep()
    │
    │  0
    │
    Then I press "edit-submit"                                        # Drupal\DrupalExtension\Context\MinkContext::pressButton()
    │
    │  0
    │
    └─ @AfterStep # Drupal\DrupalExtension\Context\MinkContext::afterJavascriptStep()
    Then I should see "Die Konfigurationsoptionen wurden gespeichert" # Drupal\DrupalExtension\Context\MinkContext::assertPageContainsText()

  Scenario: I comment on a recipe.                                                 # features/comments.feature:14
    Given I am on "/rezepte/rocher-torte-rezept-mit-nutella"                       # Drupal\DrupalExtension\Context\MinkContext::visit()
    And I wait for "Kommentare zu"                                                 # Devinci\DevinciExtension\Context\JavascriptContext::iWaitFor()
    And I wait for "Kommentare lesen oder abgeben"                                 # Devinci\DevinciExtension\Context\JavascriptContext::iWaitFor()
    ┌─ @BeforeStep # Drupal\DrupalExtension\Context\MinkContext::beforeJavascriptStep()
    │
    │  jQuery === 'undefined'
    │
    Then I click "Kommentare lesen oder abgeben"                                   # Drupal\DrupalExtension\Context\MinkContext::assertClick()
    │
    │  jQuery === 'undefined'
    │
    └─ @AfterStep # Drupal\DrupalExtension\Context\MinkContext::afterJavascriptStep()
    And I fill in "comment-name" with "Kommentar Name Behat 1"                     # Drupal\DrupalExtension\Context\MinkContext::fillField()
    And I fill in "comment-text" with "Kommentar Text Behat 1"                     # Drupal\DrupalExtension\Context\MinkContext::fillField()
    ┌─ @BeforeStep # Drupal\DrupalExtension\Context\MinkContext::beforeJavascriptStep()
    │
    │  jQuery.hasOwnProperty('active') === false
    │
    And I click on ".lupus-comments__top__rating .lupus-rating-star:first-of-type" # ContentContext::clickElement()
    │
    │  jQuery.hasOwnProperty('active') === false
    │
    └─ @AfterStep # Drupal\DrupalExtension\Context\MinkContext::afterJavascriptStep()

@jhedstrom
Copy link
Owner

This looks great! Thanks for diving into that. I can merge the PR as it looks good to me.

@Natshah
Copy link

Natshah commented Oct 25, 2022

Facing the same issue

@Aporie
Copy link

Aporie commented Dec 2, 2022

I'm still facing the same issue even with michaellenahan comment.

In my case, the error happens when I return a 403 or a 500 on purpose. I'd like Behat not to stop there, but to actually validate that a message is displayed to the user.

I did a quick digging around but couldn't figure it out. I actually don't really understand how this test works, basically we wait until the result is false and then throw an error. How does it work when the AJAX callback is actually returning a 200? What is stopping the execution of this function not to throw the \RuntimeException?

From a rough guess, I think what triggers my error is that all Drupal.ajax.instances are done but Behat didn't get a 200 response so continue the execution of iWaitForAjaxToFinish which throws the error and break the scenario execution ...

AliagaDev added a commit to AliagaDev/drupalextension that referenced this issue Apr 17, 2024
jQuery.active internal value may have negative value in some cases
causing inconsistent test results, as jquery supposedly increments the
value when an ajax request starts and decrements it when it finishes, it
should not influence the identification of active ajax requests
@AliagaDev
Copy link

Created pull request

jQuery.active internal value may have negative value in some cases causing inconsistent test results, as jquery supposedly increments the value when an ajax request starts and decrements it when it finishes, it should not influence the identification of active ajax requests

Hope it helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants