How to handle confirm popup with phantomjs + behat + mink - symfony

In my tests I use this step to confirm a javascript confirm popup:
/**
* #when /^(?:|I )confirm the popup$/
*/
public function confirmPopup()
{
$this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
}
This step work fine with selenium2 and chrome/firefox, but doesn't work with phantomjs.
How can I handle a confirm popup with phantomjs ?
for informations:
symfony: 2.0.23
behat: 2.4.6
mink: 1.5.0
Symfony2Extension: 1.0.2
MinkExtension: 1.1.4
MinkBrowserKitDriver: 1.1.0
MinkSelenium2Driver: 1.1.0
phamtomjs 1.9.1
behat.yml
default:
extensions:
Behat\Symfony2Extension\Extension:
mink_driver: true
Behat\MinkExtension\Extension:
base_url: "http://localhost:8000/app_test.php"
default_session: selenium2
selenium2:
wd_host: "http://localhost:9876/wd/hub"
Thanks!
PS: Here the gist : https://gist.github.com/blazarecki/2888851

I updated my "Selenium2Driver.php" with the following:
public function acceptAlert()
{
$this->wdSession->accept_alert();
}
This makes the accept_alert() available for the driver.
So in the script, you could do something line this to accept the alert.
$this->getSession()->getDriver()->acceptAlert();
Note that I'm using the RawMinkContext not the native MinkContext

phantomjs is a headless browser, therefore all dialogs are not show and cannot be interacted with. A solution is to rewrite widnow.confirm and window.alert with your own functions that return pre-defined values.
Since a scenario runs within the same driver, it is perfectly safe to overwrite native methods with pre-defined return values (you will not have a situation where you really need to see a window within the same scenario). Moreover, it is safe to call these step definitions multiple times within a single scenario to flip returned value.
/**
* #When I accept confirmation dialogs
*/
public function acceptConfirmation() {
$this->getSession()->getDriver()->executeScript('window.confirm = function(){return true;}');
}
/**
* #When I do not accept confirmation dialogs
*/
public function acceptNotConfirmation() {
$this->getSession()->getDriver()->executeScript('window.confirm = function(){return false;}');
}
Scenario example:
Scenario: Removal of something with confirmation dialog
Given I accept confirmation dialogs
And I click a ".mylink" element
And I wait for AJAX to finish
And I should not see a ".some-removed-element" element

Related

Initialising a driver instance with callSingle does not work for automated UI tests [duplicate]

I was trying to find a way to launch all features in Karate testing through maven using an external variable to set up the browser (with a local webdriver or using a Selenium grid).
So something like:
mvn test -Dbrowser=chrome (or firefox, safari, etc)
or using a Selenium grid:
mvn test -Dbrowser=chrome (or firefox, safari, etc) -Dgrid="grid url"
With Cucumber and Java this was quite simple using a singleton for setting up a global webdriver that was then used in all tests. In this way I could run the tests with different local or remote webdrivers.
In Karate I tried different solution, the last was to:
define the Karate config file a variable "browser"
use the variable "browser" in a single feature "X" in which I set up only the Karate driver
from all the other features with callonce to re-call the feature "X" for using that driver
but it didn't work and to be honest it doesn't seem to me to be the right approach.
Probably being able to set the Karate driver from a Javascript function inside the features is the right way but I was not able to find a solution of that.
Another problem I found with karate is differentiating the behavior using a local or a remote webdriver as in the features files they're set in different ways.
So does anyone had my same needs and how can I solve it?
With the suggestions of Peter Thomas I used this karate-config.js
function fn() {
// browser settings, if not set it takes chrome
var browser = karate.properties['browser'] || 'chrome';
karate.log('the browser set is: ' + browser + ', default: "chrome"');
// grid flag, if not set it takes false. The grid url is in this format http://localhost:4444/wd/hub
var grid_url = karate.properties['grid_url'] || false;
karate.log('the grid url set is: ' + grid_url + ', default: false');
// configurations.
var config = {
host: 'http://httpstat.us/'
};
if (browser == 'chrome') {
if (!grid_url) {
karate.configure('driver', { type: 'chromedriver', executable: 'chromedriver' });
karate.log("Selected Chrome");
} else {
karate.configure('driver', { type: 'chromedriver', start: false, webDriverUrl: grid_url });
karate.log("Selected Chrome in grid");
}
} else if (browser == 'firefox') {
if (!grid_url) {
karate.configure('driver', { type: 'geckodriver', executable: 'geckodriver' });
karate.log("Selected Firefox");
} else {
karate.configure('driver', { type: 'geckodriver', start: false, webDriverUrl: grid_url });
karate.log("Selected Firefox in grid");
}
}
return config;
}
In this way I was able to call the the test suite specifying the browser to use directly from the command line (to be used in a Jenkins pipeline):
mvn clean test -Dbrowser=firefox -Dgrid_url=http://localhost:4444/wd/hub
Here are a couple of principles. Karate is responsible for starting the driver (the equivalent of the Selenium WebDriver). All you need to do is set up the configure driver as described here: https://github.com/intuit/karate/tree/master/karate-core#configure-driver
Finally, depending on your environment, just switch the driver config. This can easily be done in karate-config.js actually (globally) instead of in each feature file:
function fn() {
var config = {
baseUrl: 'https://qa.mycompany.com'
};
if (karate.env == 'chrome') {
karate.configure('driver', { type: 'chromedriver', start: false, webDriverUrl: 'http://somehost:9515/wd/hub' });
}
return config;
}
And on the command-line:
mvn test -Dkarate.env=chrome
I suggest you get familiar with Karate's configuration: https://github.com/intuit/karate#configuration - it actually ends up being simpler than typical Java / Maven projects.
Another way is to set variables in the karate-config.js and then use them in feature files.
* configure driver = { type: '#(myVariableFromConfig)' }
Keep these principles in mind:
Any driver instances created by a "top level" feature will be available to "called" features
You can even call a "common" feature, create the driver there, and it will be set in the "calling" feature
Any driver created will be closed when the "top level" feature ends
You don't need any other patterns.
EDIT: there's some more details in the documentation: https://github.com/intuit/karate/tree/develop/karate-core#code-reuse
And for parallel execution or trying to re-use a single browser for all tests, refer: https://stackoverflow.com/a/60387907/143475

Symfony QueryParam with empty value

I'm trying to use QueryParam on my symfony controller.
The problem that i dont want to accept the empty params in the query. But when i define the QueryParam with
#REST\QueryParam(name="test" , description="The test", requirements="\w+", strict=true, nullable=false)
and i try to call the web-service with mysite.com/test= it's still working.
What i have to do to refuse empty param in the request without testing in the code if the param is empty or not
Thank you
When testing weirdly enough the validation did not seem to work with the configuration param_fetcher_listener simply on true, but when I used force, everything suddenly worked:
# config.yml
fos_rest:
param_fetcher_listener: force
And in the controller:
/**
* #QueryParam(name="test" , description="The test", requirements="[a-zA-Z]+", strict=true, nullable=false, allowBlank=false)
*/
public function indexAction($test)
{
// replace this example code with whatever you need
return new JsonResponse($test);
}
Note that in my example there's multiple validations and only ?test=a will work. No test, ?test= and ?test=1 will all not work for specified reasons.
If anyone knows how to get it to work with param_fetcher_listener: true I'm curious :)
Also see the documentation (https://symfony.com/doc/master/bundles/FOSRestBundle/param_fetcher_listener.html) for the implications of using force

function ipress in behat is not works

i have make test with behat in symfony but is not working.
this is my feature:
Feature: dossiertest
#javascript
Scenario: List 2 files in a directory
Given I am on the homepage
Then I should see "hello"
When I go to "/ajoutermed"
Then I should see "ajouter"
And I wait 2 seconds
And I fill in "test_medbundle_med_section" with "tes nom"
And I fill in "test_medbundle_med_info" with "tes nom"
And I press "Ajouter Med"
Then I should see "ajouter"
this is AjouterContext.php:
<?php
namespace test\MedBundle\Behat\Context;
use Behat\Behat\Context\Context;
use Behat\Behat\Context\BehatContext;
use Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;
use Behat\MinkExtension\Context\MinkContext;
use test\MedBundle\Entity\Apps;
use test\MedBundle\Entity\Product;
/**
* Defines application features from the specific context.
*/
class AjouterContext extends MinkContext
{
/**
* #When I press :arg1
*/
public function iPress($arg1)
{
self::$driver->findElement(WebDriverBy::cssSelector($arg1))->click();
//$element = $page->find('css',"input[value='$arg1']");
}
/**
* #Then I wait :arg1 seconds
*/
public function iWaitSeconds($arg1)
{
$this->getSession()->wait($arg1 * 1000);
}
}
behat.yml:
default:
suites:
default:
bundle: MedBundle
contexts:
- test\MedBundle\Behat\Context\FeatureContext
- test\MedBundle\Behat\Context\AjouterContext
extensions:
Behat\Symfony2Extension: ~
Behat\MinkExtension:
base_url: http://localhost/test/web/app_dev.php
goutte: ~
javascript_session: selenium2
selenium2:
wd_host: http://localhost:4444/wd/hub
sessions:
default:
symfony2: ~
and this is my problem when i launche test:
enter image description here
help me please and thank you
regarding your error message, there is an ambigous "#When I press" action. If you change your function iPress by
/**
* #When I click on input :arg1
*/
public function iClickOnInput($arg1)
{
self::$driver->findElement(WebDriverBy::cssSelector($arg1))->click();
//$element = $page->find('css',"input[value='$arg1']");
}
It should work.
If is a button element and it has Ajouter Med as one of the following id|name|title|alt|value then it should work with I press "Ajouter Med"
If this is a link then you should use I follow "Ajouter Med"
Make sure you try to search the appropriate method to use, try using an IDE editor with autocomplete for Behat/Gherkin steps and for php methods.
Avoid writing your own custom base methods/steps, do this only if you have no alternative, you might end with 2-3 steps that are doing the same thing.

Testing Symfony2 emails with Behat 3

I followed the Behat 2.5 docs to test mails. After a few tweaks to match Behat 3 I have ended with the following code (I have removed non-relevant parts):
public function getSymfonyProfile()
{
$driver = $this->mink->getSession()->getDriver();
if (!$driver instanceof KernelDriver) {
// Throw exception
}
$profile = $driver->getClient()->getProfile();
if (false === $profile) {
// Throw exception
}
return $profile;
}
/**
* #Then I should get an email with subject :subject on :email
*/
public function iShouldGetAnEmail($subject, $email)
{
$profile = $this->getSymfonyProfile();
$collector = $profile->getCollector('swiftmailer');
foreach ($collector->getMessages() as $message) {
// Assert email
}
// Throw an error if something went wrong
}
When I run this test, it throws the following error:
exception 'LogicException' with message 'Missing default data in Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector' in vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DataCollector/MessageDataCollector.php:93
Stack trace:
#0 vendor/symfony/swiftmailer-bundle/Symfony/Bundle/SwiftmailerBundle/DataCollector/MessageDataCollector.php(122): Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector->getMailerData('default')
#1 features/bootstrap/FeatureContext.php(107): Symfony\Bundle\SwiftmailerBundle\DataCollector\MessageDataCollector->getMessages()
My profiler is configured as follows:
# app/config/config_test.yml
framework:
test: ~
profiler:
enabled: true
collect: true
It seems that the Profile is correctly loaded and the MessageDataCollector from Swiftmailer does exist, but it is not doing its work as expected. Any clue to solve this?
Maybe the issue you have has been fixed as I do not have this anymore (I'm using Behat v3.0.15, BrowserKit driver 1.3.* and Symfony v2.6.6).
I managed to reproduce your error but only when I forgot to enable profiler data collecting:
profiler:
collect: false
Once this problem solved (the configuration you provided solving the problem for me) I managed to check emails in my Behat tests.
Two solutions for this:
Solution #1: Intercepting redirects globally
If it does not break all your other tests you can do so by configuring your web profiler as follows:
web_profiler:
intercept_redirects: true
Solution #2: Preventing client to follow redirections temporarily
For my part, intercepting redirections globally in the configuration broke most of my other functional tests. I therefore use this method instead.
As preventing redirections allows mainly to check data in the data collectors I decided to use a tag #collect on each scenario requiring redirect interception. I then used #BeforeScenario and #AfterScenario to enable this behaviour only for those scenarios:
/**
* Follow client redirection once
*
* #Then /^(?:|I )follow the redirection$/
*/
public function followRedirect()
{
$this->getDriver()->getClient()->followRedirect();
}
/**
* Restore the automatic following of redirections
*
* #param BeforeScenarioScope $scope
*
* #BeforeScenario #collect
*/
public static function disableFollowRedirects(BeforeScenarioScope $scope)
{
$context = $scope->getEnvironment()->getContext(get_class());
$context->getDriver()->getClient()->followRedirects(false);
}
/**
* Restore the automatic following of redirections
*
* #param AfterScenarioScope $scope
*
* #AfterScenario #collect
*/
public static function restoreFollowRedirects(AfterScenarioScope $scope)
{
$context = $scope->getEnvironment()->getContext(get_class());
$context->getDriver()->getClient()->followRedirects(true);
}
It's not the answer your are looking for, but I'm pretty sure it will suits your needs (perhaps more).
If I can suggest, try using Mailcatcher with this bundle: https://packagist.org/packages/alexandresalome/mailcatcher
You'll be able to easily tests if emails are sent, what's their subject, follow a link in the body, and so on...
Many steps are included with this bundle.

Behat+symfony2 access container parameters set in custom extension

(I'm going to explain situation, incase someone knows of a better way to accomplice what I want to do).
Using Symfony2 + Behat + Symfony2Extension + Mink +
We have an application with multiple urls that will be visited during scenarios.
I do understand that you use the parameters sent in from the FeatureContext __construct method, but what I'm trying to do is set up the urls in the behat.yml file so that we can use them in our custom Context to visit the urls.
Looking at how the extensions work I have setup the dependency injection as follows:
class Extension implements ExtensionInterface
{
/**
* Loads a specific configuration.
*
* #param array $config Extension configuration hash (from behat.yml)
* #param ContainerBuilder $container ContainerBuilder instance
*/
public function load(array $config, ContainerBuilder $container)
{
$container->setParameter('url_one', $config['url_one']);
$container->setParameter('url_two', $config['url_two']);
}
/**
* Setups configuration for current extension.
*
* #param ArrayNodeDefinition $builder
*/
public function getConfig(ArrayNodeDefinition $builder)
{
$builder->
children()->
scalarNode('one_url')->
isRequired()->
end()->
scalarNode('two_url')->
isRequired()->
end()->
end();
}
code continues....
And my behat.yml looks like this:
default:
extensions:
Behat\MinkExtension\Extension:
goutte: ~
selenium2: ~
Behat\Symfony2Extension\Extension: ~
Acme\AcmeExtension\Extension:
url_one: 'http://example1.com'
url_two: 'http:/example2.com'
Now in my FeatureContext.php class I would like to do the following:
$url = $kernel->getContainer()->getParameter('url_one');
But this is not working, it is returning parameters from my Symfony2 application, which is expected since I have symfony2extension enabled. But I can not access the parameters or services from the extension class.
(Please note that if I'm in the Extension class in the load method and I call the parameter I just set it returns it, so I know it is set, but it must be set to a different container?)
First off is this possible? And if so what should I do to make it work.
Many thanks for any help.
Obviously, $kernel->getContainer() returns you container of the Symfony2 app kernel. Those kernel and container are not shared with Behat. Behat has its own container instance, which it uses to manage own services. Which means that extension is setting parameters inside Behat container, but you are attempting to access your app kernel container. That's why you have different results.
Now, the question is, how would you pass some value from your extension to context class. Answer is context initialiser. Check out:
https://github.com/Behat/MinkExtension/blob/master/src/Behat/MinkExtension/services/core.xml#L43-L47
https://github.com/Behat/MinkExtension/blob/master/src/Behat/MinkExtension/Context/Initializer/MinkAwareInitializer.php#L26-L77

Resources