Mink
Mink copied to clipboard
Support for alert(), confirm() and prompt() boxes?
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.
I need this too. I use Selenium2Driver.
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.
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 getSession() will not give you the selenium class. It gives you the Behat\Mink\Session class.
@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 there is no pull request for this commit, so it has never been integrated as part of the official repo
@stof I can create a pull request but I'm not sure if my solution fits Mink architecture.
Hi, I find a solution to use alert(), confirm() and prompt() in Selenium2Driver Link to my gist
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.
A reincornation of this issue can be found in this PR: https://github.com/Behat/Mink/pull/356
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?
@stof , @everzet any thoughts about https://github.com/Behat/Mink/issues/158#issuecomment-27373068 ?
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
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 :)
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.
Is alert support going to be included any time soon?
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.
@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.
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.
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.
Any support for alerts yet? ;-)
Nope. There can't any support until a decision is made in this task how it can be implemented in a cross-driver fashion.
@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)
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 's solution worked for me, thanks so much!
@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.
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.
@mihaeu You saved my day. Thanks a ton. Your solution worked for me on behat 3.*
@mihaeu You saved my day. Thanks a ton. Your solution worked for me on behat 3.*
You're most welcome.
@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?