Mink icon indicating copy to clipboard operation
Mink copied to clipboard

Support for alert(), confirm() and prompt() boxes?

Open sigmaris opened this issue 13 years ago • 31 comments
trafficstars

Is there any way using the Mink API to, for example, click on the OK or Cancel buttons on a Javascript-generated confirm dialog box or fill in text in a dialog generated from prompt()? We're having trouble testing steps that require the user to confirm certain actions such as deleting objects, since the confirmation doesn't seem to succeed. FWIW we're using Mink 1.3.2 with the ZombieDriver.

sigmaris avatar Jan 02 '12 16:01 sigmaris

I need this too. I use Selenium2Driver.

oleksiizozulenko avatar Feb 21 '12 14:02 oleksiizozulenko

For a test I implemented Selenium WebDriver "acceptAlert" method support here: ef339382e41053d64660c1422b0afd7260bd542e. But I'm not sure that WebDriver-like interface will fit other drivers. With some help from Mink developers in this question I can try to implement alert support at least for WebDriver.

mekras avatar Apr 05 '12 13:04 mekras

Looking forward for this issue resolution.

With php-SeleniumWebDriver I tried: $this->getSession()->accept_alert(); returned error: PHP Fatal error: Call to undefined method Behat\Mink\Session::accept_alert() in <path to FeatureContext.php>

damehta avatar Apr 11 '12 04:04 damehta

@damehta getSession() will not give you the selenium class. It gives you the Behat\Mink\Session class.

stof avatar Apr 11 '12 07:04 stof

@mekras Do you know ETA for this issue resolution in the release? I hope ef33938 becomes available in upcoming release. Thanks so much for the fix.

damehta avatar Apr 23 '12 17:04 damehta

@damehta there is no pull request for this commit, so it has never been integrated as part of the official repo

stof avatar Apr 23 '12 18:04 stof

@stof I can create a pull request but I'm not sure if my solution fits Mink architecture.

mekras avatar May 10 '12 11:05 mekras

Hi, I find a solution to use alert(), confirm() and prompt() in Selenium2Driver Link to my gist

blazarecki avatar Jun 11 '12 16:06 blazarecki

As per discussion on support forum (https://groups.google.com/forum/?fromgroups#!topic/behat/Cc6OcObVWnE), we are looking for a solution to this same issue, using Sahi.

Sahi itself supresses the browser's modal dialogs and simply replaces them with a JS function that returns the expected value.

The default return value for alert() and confirm() is true; if you want to test a negative response to confirm() then Sahi needs to be told about this prior to the confirm() being called. This is discussed here: http://sahi.co.in/w/_expectconfirm

This method of working means you can't simply write a script that says "click the cancel button"; you need to say "when the next confirm box appears, I want the cancel button to be clicked". This may not be the same way that other drivers would do it, but may affect the architecture decisions for writing a Mink method to support it.

Spudley avatar Jun 12 '12 10:06 Spudley

A reincornation of this issue can be found in this PR: https://github.com/Behat/Mink/pull/356

aik099 avatar Aug 19 '13 16:08 aik099

Thinking more about a subject I think we might need to implement interface for setting expectations on upcoming alert/confirm/prompt dialogs similar to Sahi implementation. I know, that Selenium is much more powerful in that aspect, that it can click on alert/confirm/prompt even after it has appeared, but Sahi isn't.

Placing following Mink code right before triggering an action, that would cause alert/confirm/prompt to appear might solve the problem:

$session->expectDialog(Session::ALERT_DIALOG)->withText('dialog text here')->thenPressOK();
$session->expectDialog(Session::CONFIRM_DIALOG)->withText('dialog text here')->thenPressOK();
$session->expectDialog(Session::CONFIRM_DIALOG)->withText('dialog text here')->thenPressCancel();
$session->expectDialog(Session::PROMPT_DIALOG)->withText('dialog text here')->typeText('some text here')->thenPressOK();
$session->expectDialog(Session::PROMPT_DIALOG)->withText('dialog text here')->typeText('some text here')->thenPressCancel();

The withText and typeText methods are optional. This way you can set less/more detailed expectation for upcoming dialog. I suppose, that there can only be 1 dialog expectation at one time.

Problems with Selenium:

With Selenium we can't really set expectations on a dialog and we need to check if expectation is fulfilled after each driver command, which isn't very good. Also if somebody does this $session->getDriver()->getWDSession()->click(....), then we can't process dialog expectation.

Problems with Sahi

It seems, that with Sahi you can't really skip matching on dialog text part (see http://sahi.co.in/w/_expectConfirm), but maybe I'm wrong.

Overall problems

When dialog appears after an AJAX call we can't easily catch that in Selenium/Sahi, because test execution doesn't wait for ajax call to finish (unless we do a conditional wait call with check for an element, that would appear after ajax call has finished).

Any thoughts?

aik099 avatar Oct 30 '13 08:10 aik099

@stof , @everzet any thoughts about https://github.com/Behat/Mink/issues/158#issuecomment-27373068 ?

aik099 avatar Dec 15 '13 09:12 aik099

I kinda like the the expectation based API. I'm not sure how this would work for the Behat integration though. But this is a separate topic IMO. @aik099 would you have some time to investigate whether it would work with the different JS drivers ? I would like to hear what @everzet thinks about this API as well

stof avatar Dec 16 '13 14:12 stof

Zombie does it in similar way, like Sahi - through expectations (set browser.onalert, browser.onprompt, etc. event handler upfront to generate a response once a dialog opening attempt is created). See "Interaction" heading on http://zombie.labnotes.org/API. That API part haven't changed since 0.x version till 2.0 version :)

aik099 avatar Dec 16 '13 15:12 aik099

Although catchall hook for all Selenium driver commands isn't the best implementation ever, but it seems the only one possible to result in united driver api for dialog handling.

aik099 avatar Dec 16 '13 15:12 aik099

Is alert support going to be included any time soon?

J7mbo avatar May 13 '14 08:05 J7mbo

not until someone provides an API for it which can work accross our drivers (the proposal implemented in #356 has been rejected because of this reason). And I'm not working on this issue myself.

stof avatar May 13 '14 08:05 stof

@J7mbo, if we can midiate the problems described in https://github.com/Behat/Mink/issues/158#issuecomment-27373068, then proposed API indeed can be implemented.

Nowadays the these browser dialogs are considered bad UX and are replaced by nice looking modal windows (at least on websites I was doing), which already can be handled.

aik099 avatar May 13 '14 08:05 aik099

Given that Selenium2 is the most used JS driver for Mink (based on both the Packagist stats about installs and the feedback and questions I saw about Mink), the proposed API should work in an efficient way in Selenium. So an experiment should be done with https://github.com/Behat/Mink/issues/158#issuecomment-27373068 to see if it can actually work well in Selenium. And it would be good to have an example of how a usage of this feature would look like (i.e. writing also the surounding interactions, not only the new method call). This example could even become a unit test of the implementation later.

stof avatar Dec 10 '14 14:12 stof

Because of problems in determining best hooking place to verify dialog expectation in Selenium (described in same comment @stof mentioned) there might be a need to introduce method for manual dialog expectation matching (e.g. $session->verifyDialogExpectation() or something similar). Then for Sahi/Zombie it will do nothing because there you can set expectation on dialogs, but for Selenium it will verify that correct dialog was open prior to this new method call).

Because Zombie/Sahi actually replace the alert/confirm/prompt JS methods with their own code they can record past dialogs. Because of Selenium doesn't have it the verifyDialogExpectation would actually determine currently opened dialog and act on that according to previously defined expectation.

aik099 avatar Dec 10 '14 14:12 aik099

Any support for alerts yet? ;-)

J7mbo avatar Jul 05 '16 13:07 J7mbo

Nope. There can't any support until a decision is made in this task how it can be implemented in a cross-driver fashion.

aik099 avatar Jul 05 '16 13:07 aik099

@J7mbo no, because nobody has ever suggested an API which can be implemented across drivers. Until then, you will have to resort to driver-specific implementations (drivers generally expose a way to use their internal implementation. for instance, the Selenium2Driver has a getWebdriverSession method)

stof avatar Jul 05 '16 13:07 stof

I understand, fair enough guys. Would be good if we could come up with a good solution for that sometime.

For the record if there's anyone else here wondering the same thing and just wants to 'get it working', here's what I'm doing in one of my steps for Selenium2Driver as Stof suggested:

/** @var \Behat\Mink\Driver\Selenium2Driver $driver Needed because no cross-driver way yet */
$driver = $this->getSession()->getDriver();

/** Get the alert text (for your assertions) **/
$driverMessage = $driver->getWebDriverSession()->getAlert_text();

/** Accept the alert so you don't get a runtime exception when the steps try to continue **/
$driver->getWebDriverSession()->accept_alert();

J7mbo avatar Jul 05 '16 13:07 J7mbo

@J7mbo 's solution worked for me, thanks so much!

supriyarajgopal-spi avatar Apr 14 '17 08:04 supriyarajgopal-spi

@J7mbo 's approach did not work for me. I solved the problem by injecting JavaScript using the driver:

    /**
     * @Given /^I confirm the dialog$/
     * @throws \Behat\Mink\Exception\DriverException
     */
    public function iConfirmTheDialog()
    {
        $this->getSession()->getDriver()->executeScript('window.confirm = function () { return true; };');
    }

This has to be called before triggering the confirm.

mihaeu avatar Aug 28 '17 08:08 mihaeu

I suggest that instead of baking alert support directly into the DriverInterface (and thus immediately rendering all drivers inoperable), might I suggest an "optional" interface, which may be triggered for detection via capabilities?

Pseudocode follows:

interface DriverInterface {}
interface AlertSupportedDriverInterface extends DriverInterface {}
function buildDriver(array $config) {
	$driverClass = $this->getClass($config);
	if (isset($config['capabilities']['alerts'])) {
		if (! is_a($driverClass, AlertSupportedDriverInterface::class, true)) {
			throw new NotSuppportedCapabilityException('Alert capability not supported by this driver');
		}
	}
	// ...
}

Once the majority of drivers have been upgraded, this feature could be promoted to a standard DriverInterface.

tractorcow avatar Sep 12 '17 01:09 tractorcow

@mihaeu You saved my day. Thanks a ton. Your solution worked for me on behat 3.*

shwetaneelsharma avatar Jul 23 '19 06:07 shwetaneelsharma

@mihaeu You saved my day. Thanks a ton. Your solution worked for me on behat 3.*

You're most welcome.

mihaeu avatar Jul 23 '19 07:07 mihaeu

@J7mbo 's approach did not work for me. I solved the problem by injecting JavaScript using the driver:

    /**
     * @Given /^I confirm the dialog$/
     * @throws \Behat\Mink\Exception\DriverException
     */
    public function iConfirmTheDialog()
    {
        $this->getSession()->getDriver()->executeScript('window.confirm = function () { return true; };');
    }

This has to be called before triggering the confirm.

Thank you for the solution. @mihaeu Would you care to explain how is injecting the script can solve the problem?

ihsanfaisal avatar Jun 02 '21 13:06 ihsanfaisal