Skip to content

Instantly share code, notes, and snippets.

@Patabugen
Last active July 27, 2022 14:42
Show Gist options
  • Save Patabugen/da53bca6ed46cadf7e36eefed0f21ab1 to your computer and use it in GitHub Desktop.
Save Patabugen/da53bca6ed46cadf7e36eefed0f21ab1 to your computer and use it in GitHub Desktop.
Laravel Dusk "waitForAjax" Macro (and an old CodeCeption one)
<?php
/**
* $browser->waitForAjax() macro for Laravel Dusk. See comment below for details.
**/
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Laravel\Dusk\Browser;
class DuskServiceProvider extends ServiceProvider
{
public function boot()
{
/**
* Adds $browser->waitForAjax() to simply wait and let the browser finish any
* outstanding AJAX requests. The goals it provide a simple method call which
* is really reliable, even if it may not always be the most efficient way.
*
* There's no straightforward API to know whether the browser is currently making
* any ajax requests, so we have to use site specific ways to figure it out.
*
* In my current example I only need to check for the jQuery BlockUI plugin's overlay
* but other techniques include adding hook to axios or checking for the presence of
* indicators.
*
* See AcceptanceHelper.php for a more comprehensive example from an older project.
*
* The Repeat parameter lets you simply run all the checks again, either by default or
* by passing in a value appropriate to your own test - this is especially useful
* if you've got a lot going on or repeated calls.
*/
Browser::macro(
'waitForAjax',
function($seconds = null, $repeat = 0) {
/** @var Browser $this */
$this->waitUntilMissing('.blockOverlay', $seconds);
if ($repeat-- > 0) {
$this->waitForAjax($seconds, $repeat);
}
return $this;
}
);
}
}
<?php
/**
* This is an older version of the same idea I've been using in CodeCeption since around 2017
* and which has been working well (albeit sometimes because of the brute force nature of
* the method itself!).
*
**/
namespace Codeception\Module;
class AcceptanceHelper extends \Codeception\Module
{
/**
* Wait for all the Ajax requests to be completed
*
* @param \AcceptanceTester $I The CodeCeption tester we're usig
*
* @return \AcceptanceTester
*/
public function waitForAjax(\AcceptanceTester $I, $timeout = 60, $repeat = true)
{
// Check that our JS Counter is 0
$I->waitForJS("return (jQuery('#ajax-counter').text() * 1) == 0;", $timeout);
// Check that our JS Loader icon is gone
$I->waitForJS("return jQuery('#ajax-loader.ajax-active:visible').length == 0;", $timeout);
// Check that the overlay isn't visible
$I->waitForJS("return jQuery('#overlay:visible').length == 0;", $timeout);
// Then check that jQuery thinks it's done with Ajax too
$I->waitForJS("return $.active == 0;", $timeout);
$I->waitForElementNotVisible("#overlay");
// Now lets be reallly sure
$I->wait(1);
// Now lets be really, really sure (handy for things which trigger Ajax which
// then trigger another set of Ajax and the above may clear out between the two
// - Confirming a SupplierBooking on ViewBooking page does this)
if ($repeat) {
$this->waitForAjax($I, $timeout, false);
}
return $I;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment