5

I would like to create a behat definition to authenticate a user using a cookie.

It works with the Behat BrowserKitDriver, when there is no @javascript tag on the behat scenario. But it did not work with the Behat Selenium2Driver, when there is the @javascript tag like here.

I used the symfony-demo application for demonstrate my tests.

What's wrong in my definition ?

/**
 * @Given I am logged in as :username
 */
public function iAmLoggedInAs($username)
{
    $driver = $this->getSession()->getDriver();
    $session = $this->kernel->getContainer()->get('session');
    $user = $this->kernel->getContainer()->get('security.user.provider.concrete.database_users')->loadUserByUsername($username);
    $providerKey = 'secured_area';

    $token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
    $session->set('_security_'.$providerKey, serialize($token));
    $session->save();

    if ($driver instanceof BrowserKitDriver) {
        $client = $driver->getClient();
        $cookie = new Cookie($session->getName(), $session->getId());
        $client->getCookieJar()->set($cookie);
    } else if ($driver instanceof Selenium2Driver) {
        $this->visitPath('/');
    } else {
        throw new \Exception('Unsupported Driver');
    }

    $this->getSession()->setCookie($session->getName(), $session->getId());
}

I just want that my last behat test works.
I don't know if I'm clear... ask if not.

If you can do a Pull Request with a fix it will be perfect.

mykiwi
  • 1,671
  • 1
  • 19
  • 35
  • @javascript enables javascript and BrowserKitDriver cannot evaluate javascript. Do you receive any error? – lauda Jun 09 '16 at 21:59
  • When I set `@javascript`, the driver is not `BrowserKitDriver` but `Selenium2Driver`. There is no error, javascript is correctly executed but I'm not logged to the application. – mykiwi Jun 09 '16 at 22:22
  • Have you tried an echo for cookies before and after trying to set the cookie.Also make sure the page is loaded before trying to set cookie. Have you tried to both @insulated and @javascript? Does your cookie is a HttpOnly cookie? If the cookie has the HttpOnly flag you may be forbidden to set cookies via JavaScript as a protection against XSS attacks. – lauda Jun 13 '16 at 19:53
  • Have you tried setting the cookie before you visit a page? – Jakub Zalas Jun 14 '16 at 09:58
  • @lauda I tried to display the cookie: in FeatureContext.php with `var_dump($driver->getWebDriverSession()->getAllCookies());` but also in [twig](https://github.com/romqin/behat-optimized-for-symfony/blob/soluce-lauda/app/Resources/views/base.html.twig#L32-L34): [the result](http://pastebin.com/raw/d8uCsa8Q). It's odd that there are 2 cookies: PHPSESSID & MOCKSESSID. The cookie is not httpOnly (see the result page). – mykiwi Jun 14 '16 at 11:45
  • @jakub-zalas yes, same result. – mykiwi Jun 14 '16 at 11:51
  • I see that you have for both drivers setCookie so you could try to use the same methods, also i see that for BrowserKitDriver this method already does what you are doing with cookieJar. You can use visit for both drivers and after that you can try $driver->setCookie. When running behat also use -vvv option to see if any other error is displayed.Cookie should be set after going to the homepage, so visit and after set cookie.Adding @insulated to your scenario will start a clean session each time, like in incognito. – lauda Jun 14 '16 at 14:00
  • @lauda I tried, same result :( – mykiwi Jun 16 '16 at 06:23

2 Answers2

4

You have a few ways

Option 1. Just Use Mink

  • If you use Mink, your Context will extend RawWebContext or WebContext so you can access to the Mink Session with getSession().

  • Then, use setCookie.

As you can see, luckily for us, and this is the point with working with Mink, Cookie Manipulation is supported by many drivers

// ...
$this->visitPath('/');
$this->getSession()->setCookie(
    $session->getName(),
    $session->getId()
);

Note 1: Mind you, the following are not the same thing:

Note 2: Want to see your cookies?

var_dump($driver->getClient()->getCookieJar());

Option 2. Access Selenium2 Session

Don't hesitate to dive in the code to see how Selenium2 WebDriver Session works.

You will surely find absolute joy and peace of mind.

else if ($driver instanceof Selenium2Driver) {
      $this->visitPath('/');
      $cookie =  array(
           "domain" => "", <----- You can even add your domain here 
           "name" => $session->getName(), 
           "value" => $session->getId(),
           "path" => "/",
           "secure" => False
      );
      $driver->getWebDriverSession()->setCookie($cookie);
} 

Note 3: Want to see your cookies?

var_dump($driver->getWebDriverSession()->getAllCookies());
Mick
  • 30,759
  • 16
  • 111
  • 130
  • 2
    fancy answer but it does not work... I can't believe that there is no single solution for Behat tests using Selenium driver and Symfony, this is not a rare setup, it's actually very common one... – undefinedman Jul 19 '19 at 13:47
-1

Here you are the code I am using for automatically authenticate as a USER. This allows me saving a lot of time (login test should be made anyway in another feature file):

Sample Scenario into a YOUR_FILE.feature example file:

  Scenario: List Backend Products
    Given I auto authenticate as "YOUR_USER_NAME"
    Then  I go to "http://YOUR_SITE/backend/products"
    Then  I should see "Backend Products Management"

 

 /**
 * @Given /^I auto authenticate as "([^"]*)"$/
 */
public function iAutoAuthenticateAs($userName)
{
    $user = $this->getContainer()->get('fos_user.user_manager')->findUserByUsername($userName);
    $providerKey = $this->getContainer()->getParameter('fos_user.firewall_name');
    $token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());

    $context = $this->getContainer()->get('security.token_storage');
    $session = $this->getContainer()->get('session');

    $context->setToken($token);
    $session->set('_security_'.$providerKey, serialize($token));
    $session->save();

    $this->getSession()->setCookie($session->getName(), $session->getId());
}
Samuel Vicent
  • 991
  • 10
  • 16