Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use better technique to determine whether user is logged in #131

Closed
wants to merge 7 commits into from
Closed

Use better technique to determine whether user is logged in #131

wants to merge 7 commits into from

Conversation

axe312ger
Copy link

Not all websites have the logout link on the front page, in my case, Chrome with Selenium was simply to fast, admin-menu showed up after the check for the link happened.

Since there is no real 100% reliable method in D7, I've implemented two more methods to determine if a user is logged in. (D8 has the user information in the global Drupal Javascript object)

First test: Check for a "logged-in" class on the body element, most themes support this. (Mothership for example doesn't)
Second test: Visit "/user/login", if there is no login form, the user should be logged in.

I also had the Idea to simple check "/user/login" for a 403 status code, but this is not supported by Selenium because it is out of scope. (https://code.google.com/p/selenium/issues/detail?id=141)

// Look for the logged-in class on the body tag.
// This should work with almost any theme.
$body = $page->find('css', 'body');
if ($body->hasClass('logged-in')) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make the logged-in class configurable, the same way the log out text is (see getDrupalText() usage below for example). (Same goes for the below form#user-login.)

@jhedstrom
Copy link
Owner

I like this direction. This is a very-often-requested feature!

@axe312ger
Copy link
Author

Here we go. The two checks can now be altered in the selector section of the yml file. Please have a look :)

@zuernBernhard
Copy link

I'd like to help on this. How can I run the travis-build locally (ubuntu linux) to debug the problem ?

@jhedstrom
Copy link
Owner

I've never had much luck getting travis to run locally (there are various online blogs about it, but they get deprecated every 6 months or so).

However, you should be able to get the tests up and running locally by duplicating what is done in the .travis.yml file (particullary in the before_script and script sections).

Essentially:

  • Get a Drupal 7 site up and running, and served by drush runserver
  • Run the behat tests.

@zuernBernhard
Copy link

Yes you are right. Hope I get the same problems like Travis does ;). Will try to accomplish that.

@axe312ger
Copy link
Author

I just wrapped the first check for the css class in a try {} statement. When using the Drupal api driver, there was no page loaded at this point and this caused the login process to break.

@axe312ger
Copy link
Author

I found no other way to check if the driver already loaded a page. Does anyone have a better approach?

@zuernBernhard
Copy link

So we still need a logout-link or css to determine if there is a user logged in ?

if ($page->has('css', $this->getDrupalSelector('logged_in_selector'))) {
return TRUE;
}
} catch (\Behat\Mink\Exception\DriverException $e) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a use statement instead of having the full namespace here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course we can :)

@MKorostoff
Copy link

Pretty sure you'd want to look up the session ID and then check to see if there's a cookie named that, e.g.

$session_id = session_id();
$is_logged_in = $this->getSession->getCookie($session_id)

Rather than testing for something that may or may not be on the page. I'm pretty sure that session_id() would be the wrong function there, just trying to show the general principle.

Relevant docs

@jhedstrom
Copy link
Owner

That makes a ton of sense. Thanks @MKorostoff 👍

@recrit
Copy link

recrit commented May 14, 2015

If anyone runs into this issue and needs a work around before a fix is committed, then the following assertions can be created in your FeatureContext that implement a customized loggedIn() check that can be changed as needed for your site. The assertion code is a direct copy and paste from DrupalContext, and the text has the same beginning with an addition of " on this site".

class FeatureContext extends RawDrupalContext implements SnippetAcceptingContext {
  /**
   * Check logged in status.
   *
   * Overrides RawDrupalContext::loggedIn().
   * @see https://github.com/jhedstrom/drupalextension/pull/131.
   */
  public function loggedIn() {
    $session = $this->getSession();
    $page = $session->getPage();

    // Body class check from pull/131.
    $body = $page->find('css', 'body');
    return $body->hasClass('logged-in');
  }

  /**
   * RawDrupalContext::assertAnonymousUser() with better logged in check.
   *
   * @Given I am an anonymous user on this site
   * @Given I am not logged in on this site
   */
  public function assertAnonymousUserOnThisSite() {
    // Verify the user is logged out.
    if ($this->loggedIn()) {
      $this->logout();
    }
  }

  /**
   * Creates and authenticates a user with the given role(s).
   *
   * RawDrupalContext::assertAuthenticatedByRole() with better logged in check.
   *
   * @Given I am logged in as a user with the :role role(s) on this site
   */
  public function assertAuthenticatedByRoleOnThisSite($role) {
    // Check if a user with this role is already logged in.
    if (!$this->loggedInWithRole($role)) {
      // Create user (and project)
      $user = (object) array(
        'name' => $this->getRandom()->name(8),
        'pass' => $this->getRandom()->name(16),
        'role' => $role,
      );
      $user->mail = "{$user->name}@example.com";

      $this->userCreate($user);

      $roles = explode(',', $role);
      $roles = array_map('trim', $roles);
      foreach ($roles as $role) {
        if (!in_array(strtolower($role), array('authenticated', 'authenticated user'))) {
          // Only add roles other than 'authenticated user'.
          $this->getDriver()->userAddRole($user, $role);
        }
      }

      // Login.
      $this->login();
    }
  }
}

@axe312ger
Copy link
Author

Just checked it out, it looks like the session cookie is only set if you are logged in. For some reason, I had in mind that this cookie is even set when you are logged out. I try to update this pull request asap :)

@jhedstrom jhedstrom added this to the 3.2.0 release milestone Sep 2, 2015
@jhedstrom
Copy link
Owner

Setting this as a target for version 3.2.0.

@jhedstrom jhedstrom mentioned this pull request Sep 2, 2015
@natgateway
Copy link

I am experiencing an issue that may be related to this one: I'm creating a custom step where I need the UID of the current user to check certain permissions, but it always returns as 0 (anonymous) even though my test generates a user with a particular role. However I'm using a background to generate the user for multiple scenarios.

  │     [uid] => 0
  │     [hostname] => 127.0.0.1
  │     [roles] => Array
  │         (
  │             [1] => anonymous user
  │         )
  │ 
  │     [cache] => 0

@recrit
Thanks for posting that code snippet. I was wondering if that snippet could be modified to work where a user is created via background for multiple scenarios in the same feature file. I received a "Unable to access the request before visiting a page (Behat\Mink\Exception\DriverException)" error when the user is being created during the background stage.

My test.feature looks something like this:

Background:

Given I am logged in as a user with the "editorial" role on this site

Scenario: When editorial navigates to node/add they should have access to the add/content page, but only only see creation links to content they have custom OG permission bypasses to

When I visit "/node/add"
Then I should not see "access denied"
Then I should see the heading "Add Content"
Then "custom step here"

The test fails during the background stage. I'm wondering if that error is being triggered in the loggedIn() function where $session->getPage(); is called.

My workaround for the time being is to run these tests against user that currently exist in the database although it's not ideal for repeat-ability.

I'm very new at behat/drupal testing so I very well may have missed something obvious, I'd appreciate any pointers on what to look for.

@natgateway
Copy link

It turns out my problem was solved after getting some tips on this thread: #173.

@LionsAd
Copy link
Contributor

LionsAd commented Dec 12, 2015

I just ran into this with Drupal 8 and minimal profile, which does not have a logout link.

Interestingly it just started failing when using some Drupal Context tests.

I am not sure if it is possible or wanted to distinguish versions, but on Drupal 8 a logged in user can be easily identified by:

drupalSettings.user.uid being set to the user id of the user.

@LionsAd
Copy link
Contributor

LionsAd commented Dec 13, 2015

#237 is a replacement for this using the same code, but cleaned up, etc.

I propose to close this PR in favor of #237.

@jhedstrom
Copy link
Owner

Agreed. Let's pursue this in #237.

@jhedstrom jhedstrom closed this Dec 14, 2015
jhedstrom added a commit that referenced this pull request Dec 14, 2015
Use better technique to determine whether user is logged in #131
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants