Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -0,0 +1,64 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Tests\views_ui\Functional\UITestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Tests views role access plugin UI.
*
* @group user
* @see \Drupal\user\Plugin\views\access\Role
*/
class AccessRoleUITest extends UITestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_access_role'];
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user', 'user_test_views'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
}
/**
* Tests the role access plugin UI.
*/
public function testAccessRoleUI() {
$entity_manager = $this->container->get('entity.manager');
$entity_manager->getStorage('user_role')->create(['id' => 'custom_role', 'label' => 'Custom role'])->save();
$access_url = "admin/structure/views/nojs/display/test_access_role/default/access_options";
$this->drupalPostForm($access_url, ['access_options[role][custom_role]' => 1], t('Apply'));
$this->assertResponse(200);
$this->drupalPostForm(NULL, [], t('Save'));
$view = $entity_manager->getStorage('view')->load('test_access_role');
$display = $view->getDisplay('default');
$this->assertEqual($display['display_options']['access']['options']['role'], ['custom_role' => 'custom_role']);
// Test changing access plugin from role to none.
$this->drupalPostForm('admin/structure/views/nojs/display/test_access_role/default/access', ['access[type]' => 'none'], t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
// Verify that role option is not set.
$view = $entity_manager->getStorage('view')->load('test_access_role');
$display = $view->getDisplay('default');
$this->assertFalse(isset($display['display_options']['access']['options']['role']));
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Drupal\Tests\user\Functional\Hal;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\user\Functional\Rest\RoleResourceTestBase;
/**
* @group hal
*/
class RoleHalJsonAnonTest extends RoleResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\user\Functional\Hal;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\user\Functional\Rest\RoleResourceTestBase;
/**
* @group hal
*/
class RoleHalJsonBasicAuthTest extends RoleResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal', 'basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\Tests\user\Functional\Hal;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\user\Functional\Rest\RoleResourceTestBase;
/**
* @group hal
*/
class RoleHalJsonCookieTest extends RoleResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,65 @@
<?php
namespace Drupal\Tests\user\Functional\Hal;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\user\Functional\Rest\UserResourceTestBase;
/**
* @group hal
*/
class UserHalJsonAnonTest extends UserResourceTestBase {
use HalEntityNormalizationTrait;
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['hal'];
/**
* {@inheritdoc}
*/
protected static $format = 'hal_json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/hal+json';
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$default_normalization = parent::getExpectedNormalizedEntity();
$normalization = $this->applyHalFieldNormalization($default_normalization);
return $normalization + [
'_links' => [
'self' => [
'href' => $this->baseUrl . '/user/3?_format=hal_json',
],
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return parent::getNormalizedPostEntity() + [
'_links' => [
'type' => [
'href' => $this->baseUrl . '/rest/type/user/user',
],
],
];
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\user\Functional\Hal;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group hal
*/
class UserHalJsonBasicAuthTest extends UserHalJsonAnonTest {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,19 @@
<?php
namespace Drupal\Tests\user\Functional\Hal;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group hal
*/
class UserHalJsonCookieTest extends UserHalJsonAnonTest {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class RoleJsonAnonTest extends RoleResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class RoleJsonBasicAuthTest extends RoleResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class RoleJsonCookieTest extends RoleResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\Role;
abstract class RoleResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['user'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'user_role';
/**
* @var \Drupal\user\RoleInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer permissions']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$role = Role::create([
'id' => 'llama',
'name' => $this->randomString(),
]);
$role->save();
return $role;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'uuid' => $this->entity->uuid(),
'weight' => 2,
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [],
'id' => 'llama',
'label' => NULL,
'is_admin' => NULL,
'permissions' => [],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class RoleXmlAnonTest extends RoleResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class RoleXmlBasicAuthTest extends RoleResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,31 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class RoleXmlCookieTest extends RoleResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,24 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class UserJsonAnonTest extends UserResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
/**
* @group rest
*/
class UserJsonBasicAuthTest extends UserResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class UserJsonCookieTest extends UserResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,329 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Core\Url;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
use GuzzleHttp\RequestOptions;
abstract class UserResourceTestBase extends EntityResourceTestBase {
use BcTimestampNormalizerUnixTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['user'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'user';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'changed' => NULL,
];
/**
* @var \Drupal\user\UserInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected static $labelFieldName = 'name';
/**
* {@inheritdoc}
*/
protected static $firstCreatedEntityId = 4;
/**
* {@inheritdoc}
*/
protected static $secondCreatedEntityId = 5;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['access user profiles']);
break;
case 'POST':
case 'PATCH':
case 'DELETE':
$this->grantPermissionsToTestedRole(['administer users']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Llama" user.
$user = User::create(['created' => 123456789]);
$user->setUsername('Llama')
->setChangedTime(123456789)
->activate()
->save();
return $user;
}
/**
* {@inheritdoc}
*/
protected function createAnotherEntity() {
/** @var \Drupal\user\UserInterface $user */
$user = $this->entity->createDuplicate();
$user->setUsername($user->label() . '_dupe');
$user->save();
return $user;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'uid' => [
['value' => 3],
],
'uuid' => [
['value' => $this->entity->uuid()],
],
'langcode' => [
[
'value' => 'en',
],
],
'name' => [
[
'value' => 'Llama',
],
],
'created' => [
$this->formatExpectedTimestampItemValues(123456789),
],
'changed' => [
$this->formatExpectedTimestampItemValues($this->entity->getChangedTime()),
],
'default_langcode' => [
[
'value' => TRUE,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'name' => [
[
'value' => 'Dramallama',
],
],
];
}
/**
* Tests PATCHing security-sensitive base fields of the logged in account.
*/
public function testPatchDxForSecuritySensitiveBaseFields() {
// The anonymous user is never allowed to modify itself.
if (!static::$auth) {
$this->markTestSkipped();
}
$this->initAuthentication();
$this->provisionEntityResource();
/** @var \Drupal\user\UserInterface $user */
$user = static::$auth ? $this->account : User::load(0);
// @todo Remove the array_diff_key() call in https://www.drupal.org/node/2821077.
$original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['created' => TRUE, 'changed' => TRUE, 'name' => TRUE]);
// Since this test must be performed by the user that is being modified,
// we cannot use $this->getUrl().
$url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
$request_options = [
RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
];
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
// Test case 1: changing email.
$normalization = $original_normalization;
$normalization['mail'] = [['value' => 'new-email@example.com']];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 422 when changing email without providing the password.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n", $response, FALSE, FALSE, FALSE, FALSE);
$normalization['pass'] = [['existing' => 'wrong']];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 422 when changing email while providing a wrong password.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n", $response, FALSE, FALSE, FALSE, FALSE);
$normalization['pass'] = [['existing' => $this->account->passRaw]];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// 200 for well-formed request.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
// Test case 2: changing password.
$normalization = $original_normalization;
$new_password = $this->randomString();
$normalization['pass'] = [['value' => $new_password]];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 422 when changing password without providing the current password.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the Password.\n", $response, FALSE, FALSE, FALSE, FALSE);
$normalization['pass'][0]['existing'] = $this->account->pass_raw;
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// 200 for well-formed request.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
// Verify that we can log in with the new password.
$this->assertRpcLogin($user->getAccountName(), $new_password);
// Update password in $this->account, prepare for future requests.
$this->account->passRaw = $new_password;
$this->initAuthentication();
$request_options = [
RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
];
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
// Test case 3: changing name.
$normalization = $original_normalization;
$normalization['name'] = [['value' => 'Cooler Llama']];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 403 when modifying username without required permission.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceErrorResponse(403, "Access denied on updating field 'name'.", $response);
$this->grantPermissionsToTestedRole(['change own username']);
// 200 for well-formed request.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
// Verify that we can log in with the new username.
$this->assertRpcLogin('Cooler Llama', $new_password);
}
/**
* Verifies that logging in with the given username and password works.
*
* @param string $username
* The username to log in with.
* @param string $password
* The password to log in with.
*/
protected function assertRpcLogin($username, $password) {
$request_body = [
'name' => $username,
'pass' => $password,
];
$request_options = [
RequestOptions::HEADERS => [],
RequestOptions::BODY => $this->serializer->encode($request_body, 'json'),
];
$response = $this->request('POST', Url::fromRoute('user.login.http')->setRouteParameter('_format', 'json'), $request_options);
$this->assertSame(200, $response->getStatusCode());
}
/**
* Tests PATCHing security-sensitive base fields to change other users.
*/
public function testPatchSecurityOtherUser() {
// The anonymous user is never allowed to modify other users.
if (!static::$auth) {
$this->markTestSkipped();
}
$this->initAuthentication();
$this->provisionEntityResource();
/** @var \Drupal\user\UserInterface $user */
$user = $this->account;
$original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['changed' => TRUE]);
// Since this test must be performed by the user that is being modified,
// we cannot use $this->getUrl().
$url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
$request_options = [
RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
];
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
$normalization = $original_normalization;
$normalization['mail'] = [['value' => 'new-email@example.com']];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// Try changing user 1's email.
$user1 = [
'mail' => [['value' => 'another_email_address@example.com']],
'uid' => [['value' => 1]],
'name' => [['value' => 'another_user_name']],
'pass' => [['existing' => $this->account->passRaw]],
'uuid' => [['value' => '2e9403a4-d8af-4096-a116-624710140be0']],
] + $original_normalization;
$request_options[RequestOptions::BODY] = $this->serializer->encode($user1, static::$format);
$response = $this->request('PATCH', $url, $request_options);
// Ensure the email address has not changed.
$this->assertEquals('admin@example.com', $this->entityStorage->loadUnchanged(1)->getEmail());
$this->assertResourceErrorResponse(403, "Access denied on updating field 'uid'. The entity ID cannot be changed.", $response);
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessMessage($method) {
if ($this->config('rest.settings')->get('bc_entity_resource_permissions')) {
return parent::getExpectedUnauthorizedAccessMessage($method);
}
switch ($method) {
case 'GET':
return "The 'access user profiles' permission is required and the user must be active.";
case 'PATCH':
return "You are not authorized to update this user entity.";
case 'DELETE':
return 'You are not authorized to delete this user entity.';
default:
return parent::getExpectedUnauthorizedAccessMessage($method);
}
}
/**
* {@inheritdoc}
*/
protected function getExpectedUnauthorizedAccessCacheability() {
// @see \Drupal\user\UserAccessControlHandler::checkAccess()
return parent::getExpectedUnauthorizedAccessCacheability()
->addCacheTags(['user:3']);
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class UserXmlAnonTest extends UserResourceTestBase {
use AnonResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
public function testPatchDxForSecuritySensitiveBaseFields() {
// Deserialization of the XML format is not supported.
$this->markTestSkipped();
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class UserXmlBasicAuthTest extends UserResourceTestBase {
use BasicAuthResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
/**
* {@inheritdoc}
*/
public function testPatchDxForSecuritySensitiveBaseFields() {
// Deserialization of the XML format is not supported.
$this->markTestSkipped();
}
/**
* {@inheritdoc}
*/
public function testPatchSecurityOtherUser() {
// Deserialization of the XML format is not supported.
$this->markTestSkipped();
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Drupal\Tests\user\Functional\Rest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
/**
* @group rest
*/
class UserXmlCookieTest extends UserResourceTestBase {
use CookieResourceTestTrait;
use XmlEntityNormalizationQuirksTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'xml';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'text/xml; charset=UTF-8';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
/**
* {@inheritdoc}
*/
public function testPatchDxForSecuritySensitiveBaseFields() {
// Deserialization of the XML format is not supported.
$this->markTestSkipped();
}
/**
* {@inheritdoc}
*/
public function testPatchSecurityOtherUser() {
// Deserialization of the XML format is not supported.
$this->markTestSkipped();
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\user\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests user email token upgrade path.
*
* @group Update
* @group legacy
*/
class UserUpdateEmailToken extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/drupal-8.user-email-token-2587275.php',
];
}
/**
* Tests that email token in status_blocked of user.mail is updated.
*/
public function testEmailToken() {
$mail = \Drupal::config('user.mail')->get('status_blocked');
$this->assertTrue(strpos($mail['body'], '[site:account-name]'));
$this->runUpdates();
$mail = \Drupal::config('user.mail')->get('status_blocked');
$this->assertFalse(strpos($mail['body'], '[site:account-name]'));
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace Drupal\Tests\user\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
/**
* Tests user permissions sort upgrade path.
*
* @group Update
* @group legacy
*/
class UserUpdateOrderPermissionsTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
];
}
/**
* Tests that permissions are ordered by machine name.
*/
public function testPermissionsOrder() {
$authenticated = \Drupal::config('user.role.authenticated');
$permissions = $authenticated->get('permissions');
sort($permissions);
$this->assertNotSame($permissions, $authenticated->get('permissions'));
$this->runUpdates();
$authenticated = \Drupal::config('user.role.authenticated');
$this->assertSame($permissions, $authenticated->get('permissions'));
}
}

View file

@ -0,0 +1,136 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests user-account links.
*
* @group user
*/
class UserAccountLinksTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['menu_ui', 'block', 'test_page_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:account');
// Make test-page default.
$this->config('system.site')->set('page.front', '/test-page')->save();
}
/**
* Tests the secondary menu.
*/
public function testSecondaryMenu() {
// Create a regular user.
$user = $this->drupalCreateUser([]);
// Log in and get the homepage.
$this->drupalLogin($user);
$this->drupalGet('<front>');
// For a logged-in user, expect the secondary menu to have links for "My
// account" and "Log out".
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
':menu_class' => 'menu',
':href' => 'user',
':text' => 'My account',
]);
$this->assertEqual(count($link), 1, 'My account link is in secondary menu.');
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
':menu_class' => 'menu',
':href' => 'user/logout',
':text' => 'Log out',
]);
$this->assertEqual(count($link), 1, 'Log out link is in secondary menu.');
// Log out and get the homepage.
$this->drupalLogout();
$this->drupalGet('<front>');
// For a logged-out user, expect the secondary menu to have a "Log in" link.
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
':menu_class' => 'menu',
':href' => 'user/login',
':text' => 'Log in',
]);
$this->assertEqual(count($link), 1, 'Log in link is in secondary menu.');
}
/**
* Tests disabling the 'My account' link.
*/
public function testDisabledAccountLink() {
// Create an admin user and log in.
$this->drupalLogin($this->drupalCreateUser(['access administration pages', 'administer menu']));
// Verify that the 'My account' link exists before we check for its
// disappearance.
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
':menu_class' => 'menu',
':href' => 'user',
':text' => 'My account',
]);
$this->assertEqual(count($link), 1, 'My account link is in the secondary menu.');
// Verify that the 'My account' link is enabled. Do not assume the value of
// auto-increment is 1. Use XPath to obtain input element id and name using
// the consistent label text.
$this->drupalGet('admin/structure/menu/manage/account');
$label = $this->xpath('//label[contains(.,:text)]/@for', [':text' => 'Enable My account menu link']);
$this->assertFieldChecked($label[0]->getText(), "The 'My account' link is enabled by default.");
// Disable the 'My account' link.
$edit['links[menu_plugin_id:user.page][enabled]'] = FALSE;
$this->drupalPostForm('admin/structure/menu/manage/account', $edit, t('Save'));
// Get the homepage.
$this->drupalGet('<front>');
// Verify that the 'My account' link does not appear when disabled.
$link = $this->xpath('//ul[@class=:menu_class]/li/a[contains(@href, :href) and text()=:text]', [
':menu_class' => 'menu',
':href' => 'user',
':text' => 'My account',
]);
$this->assertEqual(count($link), 0, 'My account link is not in the secondary menu.');
}
/**
* Tests page title is set correctly on user account tabs.
*/
public function testAccountPageTitles() {
// Default page titles are suffixed with the site name - Drupal.
$title_suffix = ' | Drupal';
$this->drupalGet('user');
$this->assertTitle('Log in' . $title_suffix, "Page title of /user is 'Log in'");
$this->drupalGet('user/login');
$this->assertTitle('Log in' . $title_suffix, "Page title of /user/login is 'Log in'");
$this->drupalGet('user/register');
$this->assertTitle('Create new account' . $title_suffix, "Page title of /user/register is 'Create new account' for anonymous users.");
$this->drupalGet('user/password');
$this->assertTitle('Reset your password' . $title_suffix, "Page title of /user/register is 'Reset your password' for anonymous users.");
// Check the page title for registered users is "My Account" in menus.
$this->drupalLogin($this->drupalCreateUser());
// After login, the client is redirected to /user.
$this->assertLink(t('My account'), 0, "Page title of /user is 'My Account' in menus for registered users");
$this->assertLinkByHref(\Drupal::urlGenerator()->generate('user.page'), 0);
}
}

View file

@ -0,0 +1,184 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Tests\BrowserTestBase;
/**
* Tests users' ability to change their own administration language.
*
* @group user
*/
class UserAdminLanguageTest extends BrowserTestBase {
/**
* A user with permission to access admin pages and administer languages.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* A non-administrator user for this test.
*
* @var \Drupal\user\UserInterface
*/
protected $regularUser;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user', 'language', 'language_test'];
protected function setUp() {
parent::setUp();
// User to add and remove language.
$this->adminUser = $this->drupalCreateUser(['administer languages', 'access administration pages']);
// User to check non-admin access.
$this->regularUser = $this->drupalCreateUser();
}
/**
* Tests that admin language is not configurable in single language sites.
*/
public function testUserAdminLanguageConfigurationNotAvailableWithOnlyOneLanguage() {
$this->drupalLogin($this->adminUser);
$this->setLanguageNegotiation();
$path = 'user/' . $this->adminUser->id() . '/edit';
$this->drupalGet($path);
// Ensure administration pages language settings widget is not available.
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available.');
}
/**
* Tests that admin language negotiation is configurable only if enabled.
*/
public function testUserAdminLanguageConfigurationAvailableWithAdminLanguageNegotiation() {
$this->drupalLogin($this->adminUser);
$this->addCustomLanguage();
$path = 'user/' . $this->adminUser->id() . '/edit';
// Checks with user administration pages language negotiation disabled.
$this->drupalGet($path);
// Ensure administration pages language settings widget is not available.
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available.');
// Checks with user administration pages language negotiation enabled.
$this->setLanguageNegotiation();
$this->drupalGet($path);
// Ensure administration pages language settings widget is available.
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector is available.');
}
/**
* Tests that the admin language is configurable only for administrators.
*
* If a user has the permission "access administration pages", they should
* be able to see the setting to pick the language they want those pages in.
*
* If a user does not have that permission, it would confusing for them to
* have a setting for pages they cannot access, so they should not be able to
* set a language for those pages.
*/
public function testUserAdminLanguageConfigurationAvailableIfAdminLanguageNegotiationIsEnabled() {
$this->drupalLogin($this->adminUser);
// Adds a new language, because with only one language, setting won't show.
$this->addCustomLanguage();
$this->setLanguageNegotiation();
$path = 'user/' . $this->adminUser->id() . '/edit';
$this->drupalGet($path);
// Ensure administration pages language setting is visible for admin.
$this->assertFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector available for admins.');
// Ensure administration pages language setting is hidden for non-admins.
$this->drupalLogin($this->regularUser);
$path = 'user/' . $this->regularUser->id() . '/edit';
$this->drupalGet($path);
$this->assertNoFieldByXPath($this->constructFieldXpath('id', 'edit-preferred-admin-langcode'), NULL, 'Administration pages language selector not available for regular user.');
}
/**
* Tests the actual language negotiation.
*/
public function testActualNegotiation() {
$this->drupalLogin($this->adminUser);
$this->addCustomLanguage();
$this->setLanguageNegotiation();
// Even though we have admin language negotiation, so long as the user has
// no preference set, negotiation will fall back further.
$path = 'user/' . $this->adminUser->id() . '/edit';
$this->drupalGet($path);
$this->assertText('Language negotiation method: language-default');
$this->drupalGet('xx/' . $path);
$this->assertText('Language negotiation method: language-url');
// Set a preferred language code for the user.
$edit = [];
$edit['preferred_admin_langcode'] = 'xx';
$this->drupalPostForm($path, $edit, t('Save'));
// Test negotiation with the URL method first. The admin method will only
// be used if the URL method did not match.
$this->drupalGet($path);
$this->assertText('Language negotiation method: language-user-admin');
$this->drupalGet('xx/' . $path);
$this->assertText('Language negotiation method: language-url');
// Test negotiation with the admin language method first. The admin method
// will be used at all times.
$this->setLanguageNegotiation(TRUE);
$this->drupalGet($path);
$this->assertText('Language negotiation method: language-user-admin');
$this->drupalGet('xx/' . $path);
$this->assertText('Language negotiation method: language-user-admin');
// Unset the preferred language code for the user.
$edit = [];
$edit['preferred_admin_langcode'] = '';
$this->drupalPostForm($path, $edit, t('Save'));
$this->drupalGet($path);
$this->assertText('Language negotiation method: language-default');
$this->drupalGet('xx/' . $path);
$this->assertText('Language negotiation method: language-url');
}
/**
* Sets the User interface negotiation detection method.
*
* Enables the "Account preference for administration pages" language
* detection method for the User interface language negotiation type.
*
* @param bool $admin_first
* Whether the admin negotiation should be first.
*/
public function setLanguageNegotiation($admin_first = FALSE) {
$edit = [
'language_interface[enabled][language-user-admin]' => TRUE,
'language_interface[enabled][language-url]' => TRUE,
'language_interface[weight][language-user-admin]' => ($admin_first ? -12 : -8),
'language_interface[weight][language-url]' => -10,
];
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
}
/**
* Helper method for adding a custom language.
*/
public function addCustomLanguage() {
$langcode = 'xx';
// The English name for the language.
$name = $this->randomMachineName(16);
$edit = [
'predefined_langcode' => 'custom',
'langcode' => $langcode,
'label' => $name,
'direction' => LanguageInterface::DIRECTION_LTR,
];
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\User;
/**
* Tests the user admin listing if views is not enabled.
*
* @group user
* @see user_admin_account()
*/
class UserAdminListingTest extends BrowserTestBase {
/**
* Tests the listing.
*/
public function testUserListing() {
$this->drupalGet('admin/people');
$this->assertResponse(403, 'Anonymous user does not have access to the user admin listing.');
// Create a bunch of users.
$accounts = [];
for ($i = 0; $i < 3; $i++) {
$account = $this->drupalCreateUser();
$accounts[$account->label()] = $account;
}
// Create a blocked user.
$account = $this->drupalCreateUser();
$account->block();
$account->save();
$accounts[$account->label()] = $account;
// Create a user at a certain timestamp.
$account = $this->drupalCreateUser();
$account->created = 1363219200;
$account->save();
$accounts[$account->label()] = $account;
$timestamp_user = $account->label();
$rid_1 = $this->drupalCreateRole([], 'custom_role_1', 'custom_role_1');
$rid_2 = $this->drupalCreateRole([], 'custom_role_2', 'custom_role_2');
$account = $this->drupalCreateUser();
$account->addRole($rid_1);
$account->addRole($rid_2);
$account->save();
$accounts[$account->label()] = $account;
$role_account_name = $account->label();
// Create an admin user and look at the listing.
$admin_user = $this->drupalCreateUser(['administer users']);
$accounts[$admin_user->label()] = $admin_user;
$accounts['admin'] = User::load(1);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/people');
$this->assertResponse(200, 'The admin user has access to the user admin listing.');
$result = $this->xpath('//table[contains(@class, "responsive-enabled")]/tbody/tr');
$result_accounts = [];
foreach ($result as $account) {
$account_columns = $account->findAll('css', 'td');
$name = $account_columns[0]->getText();
$roles = [];
$account_roles = $account_columns[2]->findAll('css', 'td div ul li');
if (!empty($account_roles)) {
foreach ($account_roles as $element) {
$roles[] = $element->getText();
}
}
$result_accounts[$name] = [
'name' => $name,
'status' => $account_columns[1]->getText(),
'roles' => $roles,
'member_for' => $account_columns[3]->getText(),
'last_access' => $account_columns[4]->getText(),
];
}
$this->assertFalse(array_keys(array_diff_key($result_accounts, $accounts)), 'Ensure all accounts are listed.');
foreach ($result_accounts as $name => $values) {
$this->assertEqual($values['status'] == t('active'), $accounts[$name]->status->value, 'Ensure the status is displayed properly.');
}
$expected_roles = ['custom_role_1', 'custom_role_2'];
$this->assertEqual($result_accounts[$role_account_name]['roles'], $expected_roles, 'Ensure roles are listed properly.');
$this->assertEqual($result_accounts[$timestamp_user]['member_for'], \Drupal::service('date.formatter')->formatTimeDiffSince($accounts[$timestamp_user]->created->value), 'Ensure the right member time is displayed.');
$this->assertEqual($result_accounts[$timestamp_user]['last_access'], 'never', 'Ensure the last access time is "never".');
}
}

View file

@ -0,0 +1,205 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\RoleInterface;
/**
* Tests user administration page functionality.
*
* @group user
*/
class UserAdminTest extends BrowserTestBase {
use AssertMailTrait {
getMails as drupalGetMails;
}
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['taxonomy', 'views'];
/**
* Registers a user and deletes it.
*/
public function testUserAdmin() {
$config = $this->config('user.settings');
$user_a = $this->drupalCreateUser();
$user_a->name = 'User A';
$user_a->mail = $this->randomMachineName() . '@example.com';
$user_a->save();
$user_b = $this->drupalCreateUser(['administer taxonomy']);
$user_b->name = 'User B';
$user_b->save();
$user_c = $this->drupalCreateUser(['administer taxonomy']);
$user_c->name = 'User C';
$user_c->save();
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create admin user to delete registered user.
$admin_user = $this->drupalCreateUser(['administer users']);
// Use a predictable name so that we can reliably order the user admin page
// by name.
$admin_user->name = 'Admin user';
$admin_user->save();
$this->drupalLogin($admin_user);
$this->drupalGet('admin/people');
$this->assertText($user_a->getUsername(), 'Found user A on admin users page');
$this->assertText($user_b->getUsername(), 'Found user B on admin users page');
$this->assertText($user_c->getUsername(), 'Found user C on admin users page');
$this->assertText($admin_user->getUsername(), 'Found Admin user on admin users page');
// Test for existence of edit link in table.
$link = $user_a->link(t('Edit'), 'edit-form', ['query' => ['destination' => $user_a->url('collection')]]);
$this->assertRaw($link, 'Found user A edit link on admin users page');
// Test exposed filter elements.
foreach (['user', 'role', 'permission', 'status'] as $field) {
$this->assertField("edit-$field", "$field exposed filter found.");
}
// Make sure the reduce duplicates element from the ManyToOneHelper is not
// displayed.
$this->assertNoField('edit-reduce-duplicates', 'Reduce duplicates form element not found in exposed filters.');
// Filter the users by name/email.
$this->drupalGet('admin/people', ['query' => ['user' => $user_a->getUsername()]]);
$result = $this->xpath('//table/tbody/tr');
$this->assertEqual(1, count($result), 'Filter by username returned the right amount.');
$this->assertEqual($user_a->getUsername(), $result[0]->find('xpath', '/td[2]/span')->getText(), 'Filter by username returned the right user.');
$this->drupalGet('admin/people', ['query' => ['user' => $user_a->getEmail()]]);
$result = $this->xpath('//table/tbody/tr');
$this->assertEqual(1, count($result), 'Filter by username returned the right amount.');
$this->assertEqual($user_a->getUsername(), $result[0]->find('xpath', '/td[2]/span')->getText(), 'Filter by username returned the right user.');
// Filter the users by permission 'administer taxonomy'.
$this->drupalGet('admin/people', ['query' => ['permission' => 'administer taxonomy']]);
// Check if the correct users show up.
$this->assertNoText($user_a->getUsername(), 'User A not on filtered by perm admin users page');
$this->assertText($user_b->getUsername(), 'Found user B on filtered by perm admin users page');
$this->assertText($user_c->getUsername(), 'Found user C on filtered by perm admin users page');
// Filter the users by role. Grab the system-generated role name for User C.
$roles = $user_c->getRoles();
unset($roles[array_search(RoleInterface::AUTHENTICATED_ID, $roles)]);
$this->drupalGet('admin/people', ['query' => ['role' => reset($roles)]]);
// Check if the correct users show up when filtered by role.
$this->assertNoText($user_a->getUsername(), 'User A not on filtered by role on admin users page');
$this->assertNoText($user_b->getUsername(), 'User B not on filtered by role on admin users page');
$this->assertText($user_c->getUsername(), 'User C on filtered by role on admin users page');
// Test blocking of a user.
$account = $user_storage->load($user_c->id());
$this->assertTrue($account->isActive(), 'User C not blocked');
$edit = [];
$edit['action'] = 'user_block_user_action';
$edit['user_bulk_form[4]'] = TRUE;
$config
->set('notify.status_blocked', TRUE)
->save();
$this->drupalPostForm('admin/people', $edit, t('Apply to selected items'), [
// Sort the table by username so that we know reliably which user will be
// targeted with the blocking action.
'query' => ['order' => 'name', 'sort' => 'asc'],
]);
$site_name = $this->config('system.site')->get('name');
$this->assertMailString('body', 'Your account on ' . $site_name . ' has been blocked.', 1, 'Blocked message found in the mail sent to user C.');
$user_storage->resetCache([$user_c->id()]);
$account = $user_storage->load($user_c->id());
$this->assertTrue($account->isBlocked(), 'User C blocked');
// Test filtering on admin page for blocked users
$this->drupalGet('admin/people', ['query' => ['status' => 2]]);
$this->assertNoText($user_a->getUsername(), 'User A not on filtered by status on admin users page');
$this->assertNoText($user_b->getUsername(), 'User B not on filtered by status on admin users page');
$this->assertText($user_c->getUsername(), 'User C on filtered by status on admin users page');
// Test unblocking of a user from /admin/people page and sending of activation mail
$editunblock = [];
$editunblock['action'] = 'user_unblock_user_action';
$editunblock['user_bulk_form[4]'] = TRUE;
$this->drupalPostForm('admin/people', $editunblock, t('Apply to selected items'), [
// Sort the table by username so that we know reliably which user will be
// targeted with the blocking action.
'query' => ['order' => 'name', 'sort' => 'asc'],
]);
$user_storage->resetCache([$user_c->id()]);
$account = $user_storage->load($user_c->id());
$this->assertTrue($account->isActive(), 'User C unblocked');
$this->assertMail("to", $account->getEmail(), "Activation mail sent to user C");
// Test blocking and unblocking another user from /user/[uid]/edit form and sending of activation mail
$user_d = $this->drupalCreateUser([]);
$user_storage->resetCache([$user_d->id()]);
$account1 = $user_storage->load($user_d->id());
$this->drupalPostForm('user/' . $account1->id() . '/edit', ['status' => 0], t('Save'));
$user_storage->resetCache([$user_d->id()]);
$account1 = $user_storage->load($user_d->id());
$this->assertTrue($account1->isBlocked(), 'User D blocked');
$this->drupalPostForm('user/' . $account1->id() . '/edit', ['status' => TRUE], t('Save'));
$user_storage->resetCache([$user_d->id()]);
$account1 = $user_storage->load($user_d->id());
$this->assertTrue($account1->isActive(), 'User D unblocked');
$this->assertMail("to", $account1->getEmail(), "Activation mail sent to user D");
}
/**
* Tests the alternate notification email address for user mails.
*/
public function testNotificationEmailAddress() {
// Test that the Notification Email address field is on the config page.
$admin_user = $this->drupalCreateUser(['administer users', 'administer account settings']);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/config/people/accounts');
$this->assertRaw('id="edit-mail-notification-address"', 'Notification Email address field exists');
$this->drupalLogout();
// Test custom user registration approval email address(es).
$config = $this->config('user.settings');
// Allow users to register with admin approval.
$config
->set('verify_mail', TRUE)
->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)
->save();
// Set the site and notification email addresses.
$system = $this->config('system.site');
$server_address = $this->randomMachineName() . '@example.com';
$notify_address = $this->randomMachineName() . '@example.com';
$system
->set('mail', $server_address)
->set('mail_notification', $notify_address)
->save();
// Register a new user account.
$edit = [];
$edit['name'] = $this->randomMachineName();
$edit['mail'] = $edit['name'] . '@example.com';
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$subject = 'Account details for ' . $edit['name'] . ' at ' . $system->get('name') . ' (pending admin approval)';
// Ensure that admin notification mail is sent to the configured
// Notification Email address.
$admin_mail = $this->drupalGetMails([
'to' => $notify_address,
'from' => $server_address,
'subject' => $subject,
]);
$this->assertTrue(count($admin_mail), 'New user mail to admin is sent to configured Notification Email address');
// Ensure that user notification mail is sent from the configured
// Notification Email address.
$user_mail = $this->drupalGetMails([
'to' => $edit['mail'],
'from' => $server_address,
'reply-to' => $notify_address,
'subject' => $subject,
]);
$this->assertTrue(count($user_mail), 'New user mail to user is sent from configured Notification Email address');
}
}

View file

@ -0,0 +1,134 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\dynamic_page_cache\EventSubscriber\DynamicPageCacheSubscriber;
use Drupal\Tests\BrowserTestBase;
/**
* Tests user blocks.
*
* @group user
*/
class UserBlocksTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'views'];
/**
* A user with the 'administer blocks' permission.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(['administer blocks']);
$this->drupalLogin($this->adminUser);
$this->drupalPlaceBlock('user_login_block');
$this->drupalLogout($this->adminUser);
}
/**
* Tests that user login block is hidden from user/login.
*/
public function testUserLoginBlockVisibility() {
// Array keyed list where key being the URL address and value being expected
// visibility as boolean type.
$paths = [
'node' => TRUE,
'user/login' => FALSE,
'user/register' => TRUE,
'user/password' => TRUE,
];
foreach ($paths as $path => $expected_visibility) {
$this->drupalGet($path);
$elements = $this->xpath('//div[contains(@class,"block-user-login-block") and @role="form"]');
if ($expected_visibility) {
$this->assertTrue(!empty($elements), 'User login block in path "' . $path . '" should be visible');
}
else {
$this->assertTrue(empty($elements), 'User login block in path "' . $path . '" should not be visible');
}
}
}
/**
* Test the user login block.
*/
public function testUserLoginBlock() {
// Create a user with some permission that anonymous users lack.
$user = $this->drupalCreateUser(['administer permissions']);
// Log in using the block.
$edit = [];
$edit['name'] = $user->getUsername();
$edit['pass'] = $user->passRaw;
$this->drupalPostForm('admin/people/permissions', $edit, t('Log in'));
$this->assertNoText(t('User login'), 'Logged in.');
// Check that we are still on the same page.
$this->assertUrl(\Drupal::url('user.admin_permissions', [], ['absolute' => TRUE]), [], 'Still on the same page after login for access denied page');
// Now, log out and repeat with a non-403 page.
$this->drupalLogout();
$this->drupalGet('filter/tips');
$this->assertEqual('MISS', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
$this->drupalPostForm(NULL, $edit, t('Log in'));
$this->assertNoText(t('User login'), 'Logged in.');
$this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
// Log out again and repeat with a non-403 page including query arguments.
$this->drupalLogout();
$this->drupalGet('filter/tips', ['query' => ['foo' => 'bar']]);
$this->assertEqual('HIT', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
$this->drupalPostForm(NULL, $edit, t('Log in'));
$this->assertNoText(t('User login'), 'Logged in.');
$this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
$this->assertTrue(strpos($this->getUrl(), '/filter/tips?foo=bar') !== FALSE, 'Correct query arguments are displayed after login');
// Repeat with different query arguments.
$this->drupalLogout();
$this->drupalGet('filter/tips', ['query' => ['foo' => 'baz']]);
$this->assertEqual('HIT', $this->drupalGetHeader(DynamicPageCacheSubscriber::HEADER));
$this->drupalPostForm(NULL, $edit, t('Log in'));
$this->assertNoText(t('User login'), 'Logged in.');
$this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
$this->assertTrue(strpos($this->getUrl(), '/filter/tips?foo=baz') !== FALSE, 'Correct query arguments are displayed after login');
// Check that the user login block is not vulnerable to information
// disclosure to third party sites.
$this->drupalLogout();
$this->drupalPostForm('http://example.com/', $edit, t('Log in'), ['external' => FALSE]);
// Check that we remain on the site after login.
$this->assertUrl($user->url('canonical', ['absolute' => TRUE]), [], 'Redirected to user profile page after login from the frontpage');
// Verify that form validation errors are displayed immediately for forms
// in blocks and not on subsequent page requests.
$this->drupalLogout();
$edit = [];
$edit['name'] = 'foo';
$edit['pass'] = 'invalid password';
$this->drupalPostForm('filter/tips', $edit, t('Log in'));
$this->assertText(t('Unrecognized username or password. Forgot your password?'));
$this->drupalGet('filter/tips');
$this->assertNoText(t('Unrecognized username or password. Forgot your password?'));
}
/**
* Updates the access column for a user.
*/
private function updateAccess($uid, $access = REQUEST_TIME) {
db_update('users_field_data')
->condition('uid', $uid)
->fields(['access' => $access])
->execute();
}
}

View file

@ -2,7 +2,7 @@
namespace Drupal\Tests\user\Functional;
use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase;
use Drupal\Tests\system\Functional\Entity\EntityWithUriCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;

View file

@ -99,23 +99,6 @@ class UserCancelTest extends BrowserTestBase {
\Drupal::service('module_installer')->install(['views']);
\Drupal::service('router.builder')->rebuild();
// Update uid 1's name and password to we know it.
$password = user_password();
$account = [
'name' => 'user1',
'pass' => $this->container->get('password')->hash(trim($password)),
];
// We cannot use $account->save() here, because this would result in the
// password being hashed again.
db_update('users_field_data')
->fields($account)
->condition('uid', 1)
->execute();
// Reload and log in uid 1.
$user_storage->resetCache([1]);
$user1 = $user_storage->load(1);
$user1->pass_raw = $password;
// Try to cancel uid 1's account with a different user.
$admin_user = $this->drupalCreateUser(['administer users']);
@ -268,6 +251,12 @@ class UserCancelTest extends BrowserTestBase {
// Confirm account cancellation request.
$this->drupalGet("user/" . $account->id() . "/cancel/confirm/$timestamp/" . user_pass_rehash($account, $timestamp));
// Confirm that the user was redirected to the front page.
$this->assertSession()->addressEquals('');
$this->assertSession()->statusCodeEquals(200);
// Confirm that the confirmation message made it through to the end user.
$this->assertRaw(t('%name has been disabled.', ['%name' => $account->getUsername()]), "Confirmation message displayed to user.");
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertTrue($account->isBlocked(), 'User has been blocked.');
@ -283,9 +272,6 @@ class UserCancelTest extends BrowserTestBase {
$storage->resetCache([$comment->id()]);
$comment = $storage->load($comment->id());
$this->assertFalse($comment->isPublished(), 'Comment of the user has been unpublished.');
// Confirm that the confirmation message made it through to the end user.
$this->assertRaw(t('%name has been disabled.', ['%name' => $account->getUsername()]), "Confirmation message displayed to user.");
}
/**
@ -328,7 +314,8 @@ class UserCancelTest extends BrowserTestBase {
$revision = $revision_node->getRevisionId();
$settings = get_object_vars($revision_node);
$settings['revision'] = 1;
$settings['uid'] = 1; // Set new/current revision to someone else.
// Set new/current revision to someone else.
$settings['uid'] = 1;
$revision_node = $this->drupalCreateNode($settings);
// Attempt to cancel account.
@ -454,7 +441,8 @@ class UserCancelTest extends BrowserTestBase {
$revision = $revision_node->getRevisionId();
$settings = get_object_vars($revision_node);
$settings['revision'] = 1;
$settings['uid'] = 1; // Set new/current revision to someone else.
// Set new/current revision to someone else.
$settings['uid'] = 1;
$revision_node = $this->drupalCreateNode($settings);
// Attempt to cancel account.

View file

@ -0,0 +1,135 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the create user administration page.
*
* @group user
*/
class UserCreateTest extends BrowserTestBase {
use AssertMailTrait {
getMails as drupalGetMails;
}
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['image'];
/**
* Create a user through the administration interface and ensure that it
* displays in the user list.
*/
public function testUserAdd() {
$user = $this->drupalCreateUser(['administer users']);
$this->drupalLogin($user);
$this->assertEqual($user->getCreatedTime(), REQUEST_TIME, 'Creating a user sets default "created" timestamp.');
$this->assertEqual($user->getChangedTime(), REQUEST_TIME, 'Creating a user sets default "changed" timestamp.');
// Create a field.
$field_name = 'test_field';
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'user',
'module' => 'image',
'type' => 'image',
'cardinality' => 1,
'locked' => FALSE,
'indexes' => ['target_id' => ['target_id']],
'settings' => [
'uri_scheme' => 'public',
],
])->save();
FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'user',
'label' => 'Picture',
'bundle' => 'user',
'description' => t('Your virtual face or picture.'),
'required' => FALSE,
'settings' => [
'file_extensions' => 'png gif jpg jpeg',
'file_directory' => 'pictures',
'max_filesize' => '30 KB',
'alt_field' => 0,
'title_field' => 0,
'max_resolution' => '85x85',
'min_resolution' => '',
],
])->save();
// Test user creation page for valid fields.
$this->drupalGet('admin/people/create');
$this->assertFieldbyId('edit-status-0', 0, 'The user status option Blocked exists.', 'User login');
$this->assertFieldbyId('edit-status-1', 1, 'The user status option Active exists.', 'User login');
$this->assertFieldByXPath('//input[@type="radio" and @id="edit-status-1" and @checked="checked"]', NULL, 'Default setting for user status is active.');
// Test that browser autocomplete behavior does not occur.
$this->assertNoRaw('data-user-info-from-browser', 'Ensure form attribute, data-user-info-from-browser, does not exist.');
// Test that the password strength indicator displays.
$config = $this->config('user.settings');
$config->set('password_strength', TRUE)->save();
$this->drupalGet('admin/people/create');
$this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.');
$config->set('password_strength', FALSE)->save();
$this->drupalGet('admin/people/create');
$this->assertNoRaw(t('Password strength:'), 'The password strength indicator is not displayed.');
// We create two users, notifying one and not notifying the other, to
// ensure that the tests work in both cases.
foreach ([FALSE, TRUE] as $notify) {
$name = $this->randomMachineName();
$edit = [
'name' => $name,
'mail' => $this->randomMachineName() . '@example.com',
'pass[pass1]' => $pass = $this->randomString(),
'pass[pass2]' => $pass,
'notify' => $notify,
];
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
if ($notify) {
$this->assertText(t('A welcome message with further instructions has been emailed to the new user @name.', ['@name' => $edit['name']]), 'User created');
$this->assertEqual(count($this->drupalGetMails()), 1, 'Notification email sent');
}
else {
$this->assertText(t('Created a new user account for @name. No email has been sent.', ['@name' => $edit['name']]), 'User created');
$this->assertEqual(count($this->drupalGetMails()), 0, 'Notification email not sent');
}
$this->drupalGet('admin/people');
$this->assertText($edit['name'], 'User found in list of users');
$user = user_load_by_name($name);
$this->assertTrue($user->isActive(), 'User is not blocked');
}
// Test that the password '0' is considered a password.
// @see https://www.drupal.org/node/2563751.
$name = $this->randomMachineName();
$edit = [
'name' => $name,
'mail' => $this->randomMachineName() . '@example.com',
'pass[pass1]' => 0,
'pass[pass2]' => 0,
'notify' => FALSE,
];
$this->drupalPostForm('admin/people/create', $edit, t('Create new account'));
$this->assertText("Created a new user account for $name. No email has been sent");
$this->assertNoText('Password field is required');
}
}

View file

@ -0,0 +1,154 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\BrowserTestBase;
/**
* Tests user edit page.
*
* @group user
*/
class UserEditTest extends BrowserTestBase {
/**
* Test user edit page.
*/
public function testUserEdit() {
// Test user edit functionality.
$user1 = $this->drupalCreateUser(['change own username']);
$user2 = $this->drupalCreateUser([]);
$this->drupalLogin($user1);
// Test that error message appears when attempting to use a non-unique user name.
$edit['name'] = $user2->getUsername();
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t('The username %name is already taken.', ['%name' => $edit['name']]));
// Check that the default value in user name field
// is the raw value and not a formatted one.
\Drupal::state()->set('user_hooks_test_user_format_name_alter', TRUE);
\Drupal::service('module_installer')->install(['user_hooks_test']);
Cache::invalidateTags(['rendered']);
$this->drupalGet('user/' . $user1->id() . '/edit');
$this->assertFieldByName('name', $user1->getAccountName());
// Ensure the formatted name is displayed when expected.
$this->drupalGet('user/' . $user1->id());
$this->assertSession()->responseContains($user1->getDisplayName());
$this->assertSession()->titleEquals(strip_tags($user1->getDisplayName()) . ' | Drupal');
// Check that filling out a single password field does not validate.
$edit = [];
$edit['pass[pass1]'] = '';
$edit['pass[pass2]'] = $this->randomMachineName();
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
$edit['pass[pass1]'] = $this->randomMachineName();
$edit['pass[pass2]'] = '';
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
// Test that the error message appears when attempting to change the mail or
// pass without the current password.
$edit = [];
$edit['mail'] = $this->randomMachineName() . '@new.example.com';
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", ['%name' => t('Email')]));
$edit['current_pass'] = $user1->passRaw;
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("The changes have been saved."));
// Test that the user must enter current password before changing passwords.
$edit = [];
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
$edit['pass[pass2]'] = $new_pass;
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", ['%name' => t('Password')]));
// Try again with the current password.
$edit['current_pass'] = $user1->passRaw;
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("The changes have been saved."));
// Make sure the changed timestamp is updated.
$this->assertEqual($user1->getChangedTime(), REQUEST_TIME, 'Changing a user sets "changed" timestamp.');
// Make sure the user can log in with their new password.
$this->drupalLogout();
$user1->passRaw = $new_pass;
$this->drupalLogin($user1);
$this->drupalLogout();
// Test that the password strength indicator displays.
$config = $this->config('user.settings');
$this->drupalLogin($user1);
$config->set('password_strength', TRUE)->save();
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t('Password strength:'), 'The password strength indicator is displayed.');
$config->set('password_strength', FALSE)->save();
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertNoRaw(t('Password strength:'), 'The password strength indicator is not displayed.');
// Check that the user status field has the correct value and that it is
// properly displayed.
$admin_user = $this->drupalCreateUser(['administer users']);
$this->drupalLogin($admin_user);
$this->drupalGet('user/' . $user1->id() . '/edit');
$this->assertNoFieldChecked('edit-status-0');
$this->assertFieldChecked('edit-status-1');
$edit = ['status' => 0];
$this->drupalPostForm('user/' . $user1->id() . '/edit', $edit, t('Save'));
$this->assertText(t('The changes have been saved.'));
$this->assertFieldChecked('edit-status-0');
$this->assertNoFieldChecked('edit-status-1');
$edit = ['status' => 1];
$this->drupalPostForm('user/' . $user1->id() . '/edit', $edit, t('Save'));
$this->assertText(t('The changes have been saved.'));
$this->assertNoFieldChecked('edit-status-0');
$this->assertFieldChecked('edit-status-1');
}
/**
* Tests setting the password to "0".
*
* We discovered in https://www.drupal.org/node/2563751 that logging in with a
* password that is literally "0" was not possible. This test ensures that
* this regression can't happen again.
*/
public function testUserWith0Password() {
$admin = $this->drupalCreateUser(['administer users']);
$this->drupalLogin($admin);
// Create a regular user.
$user1 = $this->drupalCreateUser([]);
$edit = ['pass[pass1]' => '0', 'pass[pass2]' => '0'];
$this->drupalPostForm("user/" . $user1->id() . "/edit", $edit, t('Save'));
$this->assertRaw(t("The changes have been saved."));
}
/**
* Tests editing of a user account without an email address.
*/
public function testUserWithoutEmailEdit() {
// Test that an admin can edit users without an email address.
$admin = $this->drupalCreateUser(['administer users']);
$this->drupalLogin($admin);
// Create a regular user.
$user1 = $this->drupalCreateUser([]);
// This user has no email address.
$user1->mail = '';
$user1->save();
$this->drupalPostForm("user/" . $user1->id() . "/edit", ['mail' => ''], t('Save'));
$this->assertRaw(t("The changes have been saved."));
}
}

View file

@ -11,13 +11,6 @@ use Drupal\Tests\BrowserTestBase;
*/
class UserEditedOwnAccountTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user_form_test'];
public function testUserEditedOwnAccount() {
// Change account setting 'Who can register accounts?' to Administrators
// only.

View file

@ -0,0 +1,103 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Tests whether proper language is stored for new users and access to language
* selector.
*
* @group user
*/
class UserLanguageCreationTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user', 'language'];
/**
* Functional test for language handling during user creation.
*/
public function testLocalUserCreation() {
// User to add and remove language and create new users.
$admin_user = $this->drupalCreateUser(['administer languages', 'access administration pages', 'administer users']);
$this->drupalLogin($admin_user);
// Add predefined language.
$langcode = 'fr';
ConfigurableLanguage::createFromLangcode($langcode)->save();
// Set language negotiation.
$edit = [
'language_interface[enabled][language-url]' => TRUE,
];
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
$this->assertText(t('Language detection configuration saved.'), 'Set language negotiation.');
// Check if the language selector is available on admin/people/create and
// set to the currently active language.
$this->drupalGet($langcode . '/admin/people/create');
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Global language set in the language selector.');
// Create a user with the admin/people/create form and check if the correct
// language is set.
$username = $this->randomMachineName(10);
$edit = [
'name' => $username,
'mail' => $this->randomMachineName(4) . '@example.com',
'pass[pass1]' => $username,
'pass[pass2]' => $username,
];
$this->drupalPostForm($langcode . '/admin/people/create', $edit, t('Create new account'));
$user = user_load_by_name($username);
$this->assertEqual($user->getPreferredLangcode(), $langcode, 'New user has correct preferred language set.');
$this->assertEqual($user->language()->getId(), $langcode, 'New user has correct profile language set.');
// Register a new user and check if the language selector is hidden.
$this->drupalLogout();
$this->drupalGet($langcode . '/user/register');
$this->assertNoFieldByName('language[fr]', 'Language selector is not accessible.');
$username = $this->randomMachineName(10);
$edit = [
'name' => $username,
'mail' => $this->randomMachineName(4) . '@example.com',
];
$this->drupalPostForm($langcode . '/user/register', $edit, t('Create new account'));
$user = user_load_by_name($username);
$this->assertEqual($user->getPreferredLangcode(), $langcode, 'New user has correct preferred language set.');
$this->assertEqual($user->language()->getId(), $langcode, 'New user has correct profile language set.');
// Test if the admin can use the language selector and if the
// correct language is was saved.
$user_edit = $langcode . '/user/' . $user->id() . '/edit';
$this->drupalLogin($admin_user);
$this->drupalGet($user_edit);
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Language selector is accessible and correct language is selected.');
// Set passRaw so we can log in the new user.
$user->passRaw = $this->randomMachineName(10);
$edit = [
'pass[pass1]' => $user->passRaw,
'pass[pass2]' => $user->passRaw,
];
$this->drupalPostForm($user_edit, $edit, t('Save'));
$this->drupalLogin($user);
$this->drupalGet($user_edit);
$this->assertOptionSelected("edit-preferred-langcode", $langcode, 'Language selector is accessible and correct language is selected.');
}
}

View file

@ -3,6 +3,7 @@
namespace Drupal\Tests\user\Functional;
use Drupal\Core\Flood\DatabaseBackend;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Controller\UserAuthenticationController;
@ -14,12 +15,16 @@ use Drupal\hal\Encoder\JsonEncoder as HALJsonEncoder;
use Symfony\Component\Serializer\Serializer;
/**
* Tests login via direct HTTP.
* Tests login and password reset via direct HTTP.
*
* @group user
*/
class UserLoginHttpTest extends BrowserTestBase {
use AssertMailTrait {
getMails as drupalGetMails;
}
/**
* Modules to install.
*
@ -61,7 +66,7 @@ class UserLoginHttpTest extends BrowserTestBase {
* @param string $format
* The format to use to make the request.
*
* @return \Psr\Http\Message\ResponseInterface The HTTP response.
* @return \Psr\Http\Message\ResponseInterface
* The HTTP response.
*/
protected function loginRequest($name, $pass, $format = 'json') {
@ -92,99 +97,153 @@ class UserLoginHttpTest extends BrowserTestBase {
* Tests user session life cycle.
*/
public function testLogin() {
// Without the serialization module only JSON is supported.
$this->doTestLogin('json');
// Enable serialization so we have access to additional formats.
$this->container->get('module_installer')->install(['serialization']);
$this->doTestLogin('json');
$this->doTestLogin('xml');
$this->doTestLogin('hal_json');
}
/**
* Do login testing for a given serialization format.
*
* @param string $format
* Serialization format.
*/
protected function doTestLogin($format) {
$client = \Drupal::httpClient();
foreach ([FALSE, TRUE] as $serialization_enabled_option) {
if ($serialization_enabled_option) {
/** @var \Drupal\Core\Extension\ModuleInstaller $module_installer */
$module_installer = $this->container->get('module_installer');
$module_installer->install(['serialization']);
$formats = ['json', 'xml', 'hal_json'];
}
else {
// Without the serialization module only JSON is supported.
$formats = ['json'];
}
foreach ($formats as $format) {
// Create new user for each iteration to reset flood.
// Grant the user administer users permissions to they can see the
// 'roles' field.
$account = $this->drupalCreateUser(['administer users']);
$name = $account->getUsername();
$pass = $account->passRaw;
// Create new user for each iteration to reset flood.
// Grant the user administer users permissions to they can see the
// 'roles' field.
$account = $this->drupalCreateUser(['administer users']);
$name = $account->getUsername();
$pass = $account->passRaw;
$login_status_url = $this->getLoginStatusUrlString($format);
$response = $client->get($login_status_url);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
$login_status_url = $this->getLoginStatusUrlString($format);
$response = $client->get($login_status_url);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
// Flooded.
$this->config('user.flood')
->set('user_limit', 3)
->save();
// Flooded.
$this->config('user.flood')
->set('user_limit', 3)
->save();
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 403, 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.', $format);
$response = $this->loginRequest($name, 'wrong-pass', $format);
$this->assertHttpResponseWithMessage($response, 403, 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.', $format);
// After testing the flood control we can increase the limit.
$this->config('user.flood')
->set('user_limit', 100)
->save();
// After testing the flood control we can increase the limit.
$this->config('user.flood')
->set('user_limit', 100)
->save();
$response = $this->loginRequest(NULL, NULL, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.', $format);
$response = $this->loginRequest(NULL, NULL, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.', $format);
$response = $this->loginRequest(NULL, $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.name.', $format);
$response = $this->loginRequest(NULL, $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.name.', $format);
$response = $this->loginRequest($name, NULL, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.pass.', $format);
$response = $this->loginRequest($name, NULL, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.pass.', $format);
// Blocked.
$account
->block()
->save();
// Blocked.
$account
->block()
->save();
$response = $this->loginRequest($name, $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'The user has not been activated or is blocked.', $format);
$response = $this->loginRequest($name, $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'The user has not been activated or is blocked.', $format);
$account
->activate()
->save();
$account
->activate()
->save();
$response = $this->loginRequest($name, 'garbage', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, 'garbage', $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest('garbage', $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest('garbage', $pass, $format);
$this->assertHttpResponseWithMessage($response, 400, 'Sorry, unrecognized username or password.', $format);
$response = $this->loginRequest($name, $pass, $format);
$this->assertEquals(200, $response->getStatusCode());
$result_data = $this->serializer->decode($response->getBody(), $format);
$this->assertEquals($name, $result_data['current_user']['name']);
$this->assertEquals($account->id(), $result_data['current_user']['uid']);
$this->assertEquals($account->getRoles(), $result_data['current_user']['roles']);
$logout_token = $result_data['logout_token'];
$response = $this->loginRequest($name, $pass, $format);
$this->assertEquals(200, $response->getStatusCode());
$result_data = $this->serializer->decode($response->getBody(), $format);
$this->assertEquals($name, $result_data['current_user']['name']);
$this->assertEquals($account->id(), $result_data['current_user']['uid']);
$this->assertEquals($account->getRoles(), $result_data['current_user']['roles']);
$logout_token = $result_data['logout_token'];
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_IN);
// Logging in while already logged in results in a 403 with helpful message.
$response = $this->loginRequest($name, $pass, $format);
$this->assertSame(403, $response->getStatusCode());
$this->assertSame(['message' => 'This route can only be accessed by anonymous users.'], $this->serializer->decode($response->getBody(), $format));
$response = $this->logoutRequest($format, $logout_token);
$this->assertEquals(204, $response->getStatusCode());
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_IN);
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
$response = $this->logoutRequest($format, $logout_token);
$this->assertEquals(204, $response->getStatusCode());
$this->resetFlood();
}
}
$response = $client->get($login_status_url, ['cookies' => $this->cookies]);
$this->assertHttpResponse($response, 200, UserAuthenticationController::LOGGED_OUT);
$this->resetFlood();
}
/**
* Executes a password HTTP request.
*
* @param array $request_body
* The request body.
* @param string $format
* The format to use to make the request.
*
* @return \Psr\Http\Message\ResponseInterface
* The HTTP response.
*/
protected function passwordRequest(array $request_body, $format = 'json') {
$password_reset_url = Url::fromRoute('user.pass.http')
->setRouteParameter('_format', $format)
->setAbsolute();
$result = \Drupal::httpClient()->post($password_reset_url->toString(), [
'body' => $this->serializer->encode($request_body, $format),
'headers' => [
'Accept' => "application/$format",
],
'http_errors' => FALSE,
'cookies' => $this->cookies,
]);
return $result;
}
/**
* Tests user password reset.
*/
public function testPasswordReset() {
// Create a user account.
$account = $this->drupalCreateUser();
// Without the serialization module only JSON is supported.
$this->doTestPasswordReset('json', $account);
// Enable serialization so we have access to additional formats.
$this->container->get('module_installer')->install(['serialization']);
$this->doTestPasswordReset('json', $account);
$this->doTestPasswordReset('xml', $account);
$this->doTestPasswordReset('hal_json', $account);
}
/**
@ -343,7 +402,7 @@ class UserLoginHttpTest extends BrowserTestBase {
* @param string $logout_token
* The csrf token for user logout.
*
* @return \Psr\Http\Message\ResponseInterface The HTTP response.
* @return \Psr\Http\Message\ResponseInterface
* The HTTP response.
*/
protected function logoutRequest($format = 'json', $logout_token = '') {
@ -426,4 +485,60 @@ class UserLoginHttpTest extends BrowserTestBase {
return $user_login_status_url->toString();
}
/**
* Do password reset testing for given format and account.
*
* @param string $format
* Serialization format.
* @param \Drupal\user\UserInterface $account
* Test account.
*/
protected function doTestPasswordReset($format, $account) {
$response = $this->passwordRequest([], $format);
$this->assertHttpResponseWithMessage($response, 400, 'Missing credentials.name or credentials.mail', $format);
$response = $this->passwordRequest(['name' => 'dramallama'], $format);
$this->assertHttpResponseWithMessage($response, 400, 'Unrecognized username or email address.', $format);
$response = $this->passwordRequest(['mail' => 'llama@drupal.org'], $format);
$this->assertHttpResponseWithMessage($response, 400, 'Unrecognized username or email address.', $format);
$account
->block()
->save();
$response = $this->passwordRequest(['name' => $account->getAccountName()], $format);
$this->assertHttpResponseWithMessage($response, 400, 'The user has not been activated or is blocked.', $format);
$response = $this->passwordRequest(['mail' => $account->getEmail()], $format);
$this->assertHttpResponseWithMessage($response, 400, 'The user has not been activated or is blocked.', $format);
$account
->activate()
->save();
$response = $this->passwordRequest(['name' => $account->getAccountName()], $format);
$this->assertEquals(200, $response->getStatusCode());
$this->loginFromResetEmail();
$this->drupalLogout();
$response = $this->passwordRequest(['mail' => $account->getEmail()], $format);
$this->assertEquals(200, $response->getStatusCode());
$this->loginFromResetEmail();
$this->drupalLogout();
}
/**
* Login from reset password email.
*/
protected function loginFromResetEmail() {
$_emails = $this->drupalGetMails();
$email = end($_emails);
$urls = [];
preg_match('#.+user/reset/.+#', $email['body'], $urls);
$resetURL = $urls[0];
$this->drupalGet($resetURL);
$this->drupalPostForm(NULL, NULL, 'Log in');
}
}

View file

@ -0,0 +1,178 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\User;
/**
* Ensure that login works as expected.
*
* @group user
*/
class UserLoginTest extends BrowserTestBase {
/**
* Tests login with destination.
*/
public function testLoginCacheTagsAndDestination() {
$this->drupalGet('user/login');
// The user login form says "Enter your <site name> username.", hence it
// depends on config:system.site, and its cache tags should be present.
$this->assertCacheTag('config:system.site');
$user = $this->drupalCreateUser([]);
$this->drupalGet('user/login', ['query' => ['destination' => 'foo']]);
$edit = ['name' => $user->getUserName(), 'pass' => $user->passRaw];
$this->drupalPostForm(NULL, $edit, t('Log in'));
$this->assertUrl('foo', [], 'Redirected to the correct URL');
}
/**
* Test the global login flood control.
*/
public function testGlobalLoginFloodControl() {
$this->config('user.flood')
->set('ip_limit', 10)
// Set a high per-user limit out so that it is not relevant in the test.
->set('user_limit', 4000)
->save();
$user1 = $this->drupalCreateUser([]);
$incorrect_user1 = clone $user1;
$incorrect_user1->passRaw .= 'incorrect';
// Try 2 failed logins.
for ($i = 0; $i < 2; $i++) {
$this->assertFailedLogin($incorrect_user1);
}
// A successful login will not reset the IP-based flood control count.
$this->drupalLogin($user1);
$this->drupalLogout();
// Try 8 more failed logins, they should not trigger the flood control
// mechanism.
for ($i = 0; $i < 8; $i++) {
$this->assertFailedLogin($incorrect_user1);
}
// The next login trial should result in an IP-based flood error message.
$this->assertFailedLogin($incorrect_user1, 'ip');
// A login with the correct password should also result in a flood error
// message.
$this->assertFailedLogin($user1, 'ip');
}
/**
* Test the per-user login flood control.
*/
public function testPerUserLoginFloodControl() {
$this->config('user.flood')
// Set a high global limit out so that it is not relevant in the test.
->set('ip_limit', 4000)
->set('user_limit', 3)
->save();
$user1 = $this->drupalCreateUser([]);
$incorrect_user1 = clone $user1;
$incorrect_user1->passRaw .= 'incorrect';
$user2 = $this->drupalCreateUser([]);
// Try 2 failed logins.
for ($i = 0; $i < 2; $i++) {
$this->assertFailedLogin($incorrect_user1);
}
// A successful login will reset the per-user flood control count.
$this->drupalLogin($user1);
$this->drupalLogout();
// Try 3 failed logins for user 1, they will not trigger flood control.
for ($i = 0; $i < 3; $i++) {
$this->assertFailedLogin($incorrect_user1);
}
// Try one successful attempt for user 2, it should not trigger any
// flood control.
$this->drupalLogin($user2);
$this->drupalLogout();
// Try one more attempt for user 1, it should be rejected, even if the
// correct password has been used.
$this->assertFailedLogin($user1, 'user');
}
/**
* Test that user password is re-hashed upon login after changing $count_log2.
*/
public function testPasswordRehashOnLogin() {
// Determine default log2 for phpass hashing algorithm
$default_count_log2 = 16;
// Retrieve instance of password hashing algorithm
$password_hasher = $this->container->get('password');
// Create a new user and authenticate.
$account = $this->drupalCreateUser([]);
$password = $account->passRaw;
$this->drupalLogin($account);
$this->drupalLogout();
// Load the stored user. The password hash should reflect $default_count_log2.
$user_storage = $this->container->get('entity.manager')->getStorage('user');
$account = User::load($account->id());
$this->assertIdentical($password_hasher->getCountLog2($account->getPassword()), $default_count_log2);
// Change the required number of iterations by loading a test-module
// containing the necessary container builder code and then verify that the
// users password gets rehashed during the login.
$overridden_count_log2 = 19;
\Drupal::service('module_installer')->install(['user_custom_phpass_params_test']);
$this->resetAll();
$account->passRaw = $password;
$this->drupalLogin($account);
// Load the stored user, which should have a different password hash now.
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertIdentical($password_hasher->getCountLog2($account->getPassword()), $overridden_count_log2);
$this->assertTrue($password_hasher->check($password, $account->getPassword()));
}
/**
* Make an unsuccessful login attempt.
*
* @param \Drupal\user\Entity\User $account
* A user object with name and passRaw attributes for the login attempt.
* @param mixed $flood_trigger
* (optional) Whether or not to expect that the flood control mechanism
* will be triggered. Defaults to NULL.
* - Set to 'user' to expect a 'too many failed logins error.
* - Set to any value to expect an error for too many failed logins per IP
* .
* - Set to NULL to expect a failed login.
*/
public function assertFailedLogin($account, $flood_trigger = NULL) {
$edit = [
'name' => $account->getUsername(),
'pass' => $account->passRaw,
];
$this->drupalPostForm('user/login', $edit, t('Log in'));
$this->assertNoFieldByXPath("//input[@name='pass' and @value!='']", NULL, 'Password value attribute is blank.');
if (isset($flood_trigger)) {
if ($flood_trigger == 'user') {
$this->assertRaw(\Drupal::translation()->formatPlural($this->config('user.flood')->get('user_limit'), 'There has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', 'There have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', [':url' => \Drupal::url('user.pass')]));
}
else {
// No uid, so the limit is IP-based.
$this->assertRaw(t('Too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href=":url">request a new password</a>.', [':url' => \Drupal::url('user.pass')]));
}
}
else {
$this->assertText(t('Unrecognized username or password. Forgot your password?'));
}
}
}

View file

@ -30,7 +30,7 @@ class UserMailNotifyTest extends EntityKernelTestBase {
['status_canceled', ['status_canceled']],
['register_admin_created', ['register_admin_created']],
['register_no_approval_required', ['register_no_approval_required']],
['register_pending_approval', ['register_pending_approval', 'register_pending_approval_admin']]
['register_pending_approval', ['register_pending_approval', 'register_pending_approval_admin']],
];
}

View file

@ -0,0 +1,326 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\Core\Url;
use Drupal\Tests\system\Functional\Cache\PageCacheTagsTestBase;
use Drupal\user\Entity\User;
/**
* Ensure that password reset methods work as expected.
*
* @group user
*/
class UserPasswordResetTest extends PageCacheTagsTestBase {
use AssertMailTrait {
getMails as drupalGetMails;
}
/**
* The user object to test password resetting.
*
* @var \Drupal\user\UserInterface
*/
protected $account;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:account');
// Create a user.
$account = $this->drupalCreateUser();
// Activate user by logging in.
$this->drupalLogin($account);
$this->account = User::load($account->id());
$this->account->passRaw = $account->passRaw;
$this->drupalLogout();
// Set the last login time that is used to generate the one-time link so
// that it is definitely over a second ago.
$account->login = REQUEST_TIME - mt_rand(10, 100000);
db_update('users_field_data')
->fields(['login' => $account->getLastLoginTime()])
->condition('uid', $account->id())
->execute();
}
/**
* Tests password reset functionality.
*/
public function testUserPasswordReset() {
// Verify that accessing the password reset form without having the session
// variables set results in an access denied message.
$this->drupalGet(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
$this->assertResponse(403);
// Try to reset the password for an invalid account.
$this->drupalGet('user/password');
$edit = ['name' => $this->randomMachineName(32)];
$this->drupalPostForm(NULL, $edit, t('Submit'));
$this->assertText(t('@name is not recognized as a username or an email address.', ['@name' => $edit['name']]), 'Validation error message shown when trying to request password for invalid account.');
$this->assertEqual(count($this->drupalGetMails(['id' => 'user_password_reset'])), 0, 'No email was sent when requesting a password for an invalid account.');
// Reset the password by username via the password reset page.
$edit['name'] = $this->account->getUsername();
$this->drupalPostForm(NULL, $edit, t('Submit'));
// Verify that the user was sent an email.
$this->assertMail('to', $this->account->getEmail(), 'Password email sent to user.');
$subject = t('Replacement login information for @username at @site', ['@username' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]);
$this->assertMail('subject', $subject, 'Password reset email subject is correct.');
$resetURL = $this->getResetURL();
$this->drupalGet($resetURL);
// Ensure that the current url does not contain the hash and timestamp.
$this->assertUrl(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'));
// Ensure the password reset URL is not cached.
$this->drupalGet($resetURL);
$this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'));
// Check the one-time login page.
$this->assertText($this->account->getUsername(), 'One-time login page contains the correct username.');
$this->assertText(t('This login can be used only once.'), 'Found warning about one-time login.');
$this->assertTitle(t('Reset password | Drupal'), 'Page title is "Reset password".');
// Check successful login.
$this->drupalPostForm(NULL, NULL, t('Log in'));
$this->assertLink(t('Log out'));
$this->assertTitle(t('@name | @site', ['@name' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]), 'Logged in using password reset link.');
// Change the forgotten password.
$password = user_password();
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertText(t('The changes have been saved.'), 'Forgotten password changed.');
// Verify that the password reset session has been destroyed.
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertText(t("Your current password is missing or incorrect; it's required to change the Password."), 'Password needed to make profile changes.');
// Log out, and try to log in again using the same one-time link.
$this->drupalLogout();
$this->drupalGet($resetURL);
$this->drupalPostForm(NULL, NULL, t('Log in'));
$this->assertText(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'One-time link is no longer valid.');
// Request a new password again, this time using the email address.
$this->drupalGet('user/password');
// Count email messages before to compare with after.
$before = count($this->drupalGetMails(['id' => 'user_password_reset']));
$edit = ['name' => $this->account->getEmail()];
$this->drupalPostForm(NULL, $edit, t('Submit'));
$this->assertTrue(count($this->drupalGetMails(['id' => 'user_password_reset'])) === $before + 1, 'Email sent when requesting password reset using email address.');
// Visit the user edit page without pass-reset-token and make sure it does
// not cause an error.
$resetURL = $this->getResetURL();
$this->drupalGet($resetURL);
$this->drupalPostForm(NULL, NULL, t('Log in'));
$this->drupalGet('user/' . $this->account->id() . '/edit');
$this->assertNoText('Expected user_string to be a string, NULL given');
$this->drupalLogout();
// Create a password reset link as if the request time was 60 seconds older than the allowed limit.
$timeout = $this->config('user.settings')->get('password_reset_timeout');
$bogus_timestamp = REQUEST_TIME - $timeout - 60;
$_uid = $this->account->id();
$this->drupalGet("user/reset/$_uid/$bogus_timestamp/" . user_pass_rehash($this->account, $bogus_timestamp));
$this->drupalPostForm(NULL, NULL, t('Log in'));
$this->assertText(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.');
// Create a user, block the account, and verify that a login link is denied.
$timestamp = REQUEST_TIME - 1;
$blocked_account = $this->drupalCreateUser()->block();
$blocked_account->save();
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp));
$this->assertResponse(403);
// Verify a blocked user can not request a new password.
$this->drupalGet('user/password');
// Count email messages before to compare with after.
$before = count($this->drupalGetMails(['id' => 'user_password_reset']));
$edit = ['name' => $blocked_account->getUsername()];
$this->drupalPostForm(NULL, $edit, t('Submit'));
$this->assertRaw(t('%name is blocked or has not been activated yet.', ['%name' => $blocked_account->getUsername()]), 'Notified user blocked accounts can not request a new password');
$this->assertTrue(count($this->drupalGetMails(['id' => 'user_password_reset'])) === $before, 'No email was sent when requesting password reset for a blocked account');
// Verify a password reset link is invalidated when the user's email address changes.
$this->drupalGet('user/password');
$edit = ['name' => $this->account->getUsername()];
$this->drupalPostForm(NULL, $edit, t('Submit'));
$old_email_reset_link = $this->getResetURL();
$this->account->setEmail("1" . $this->account->getEmail());
$this->account->save();
$this->drupalGet($old_email_reset_link);
$this->drupalPostForm(NULL, NULL, t('Log in'));
$this->assertText(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'One-time link is no longer valid.');
// Verify a password reset link will automatically log a user when /login is
// appended.
$this->drupalGet('user/password');
$edit = ['name' => $this->account->getUsername()];
$this->drupalPostForm(NULL, $edit, t('Submit'));
$reset_url = $this->getResetURL();
$this->drupalGet($reset_url . '/login');
$this->assertLink(t('Log out'));
$this->assertTitle(t('@name | @site', ['@name' => $this->account->getUsername(), '@site' => $this->config('system.site')->get('name')]), 'Logged in using password reset link.');
// Ensure blocked and deleted accounts can't access the user.reset.login
// route.
$this->drupalLogout();
$timestamp = REQUEST_TIME - 1;
$blocked_account = $this->drupalCreateUser()->block();
$blocked_account->save();
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp) . '/login');
$this->assertResponse(403);
$blocked_account->delete();
$this->drupalGet("user/reset/" . $blocked_account->id() . "/$timestamp/" . user_pass_rehash($blocked_account, $timestamp) . '/login');
$this->assertResponse(403);
}
/**
* Retrieves password reset email and extracts the login link.
*/
public function getResetURL() {
// Assume the most recent email.
$_emails = $this->drupalGetMails();
$email = end($_emails);
$urls = [];
preg_match('#.+user/reset/.+#', $email['body'], $urls);
return $urls[0];
}
/**
* Test user password reset while logged in.
*/
public function testUserPasswordResetLoggedIn() {
$another_account = $this->drupalCreateUser();
$this->drupalLogin($another_account);
$this->drupalGet('user/password');
$this->drupalPostForm(NULL, NULL, t('Submit'));
// Click the reset URL while logged and change our password.
$resetURL = $this->getResetURL();
// Log in as a different user.
$this->drupalLogin($this->account);
$this->drupalGet($resetURL);
$this->assertRaw(new FormattableMarkup(
'Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href=":logout">log out</a> and try using the link again.',
['%other_user' => $this->account->getUsername(), '%resetting_user' => $another_account->getUsername(), ':logout' => Url::fromRoute('user.logout')->toString()]
));
$another_account->delete();
$this->drupalGet($resetURL);
$this->assertText('The one-time login link you clicked is invalid.');
// Log in.
$this->drupalLogin($this->account);
// Reset the password by username via the password reset page.
$this->drupalGet('user/password');
$this->drupalPostForm(NULL, NULL, t('Submit'));
// Click the reset URL while logged and change our password.
$resetURL = $this->getResetURL();
$this->drupalGet($resetURL);
$this->drupalPostForm(NULL, NULL, t('Log in'));
// Change the password.
$password = user_password();
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
$this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertText(t('The changes have been saved.'), 'Password changed.');
// Logged in users should not be able to access the user.reset.login or the
// user.reset.form routes.
$timestamp = REQUEST_TIME - 1;
$this->drupalGet("user/reset/" . $this->account->id() . "/$timestamp/" . user_pass_rehash($this->account, $timestamp) . '/login');
$this->assertResponse(403);
$this->drupalGet("user/reset/" . $this->account->id());
$this->assertResponse(403);
}
/**
* Prefill the text box on incorrect login via link to password reset page.
*/
public function testUserResetPasswordTextboxFilled() {
$this->drupalGet('user/login');
$edit = [
'name' => $this->randomMachineName(),
'pass' => $this->randomMachineName(),
];
$this->drupalPostForm('user/login', $edit, t('Log in'));
$this->assertRaw(t('Unrecognized username or password. <a href=":password">Forgot your password?</a>',
[':password' => \Drupal::url('user.pass', [], ['query' => ['name' => $edit['name']]])]));
unset($edit['pass']);
$this->drupalGet('user/password', ['query' => ['name' => $edit['name']]]);
$this->assertFieldByName('name', $edit['name'], 'User name found.');
// Ensure the name field value is not cached.
$this->drupalGet('user/password');
$this->assertNoFieldByName('name', $edit['name'], 'User name not found.');
}
/**
* Make sure that users cannot forge password reset URLs of other users.
*/
public function testResetImpersonation() {
// Create two identical user accounts except for the user name. They must
// have the same empty password, so we can't use $this->drupalCreateUser().
$edit = [];
$edit['name'] = $this->randomMachineName();
$edit['mail'] = $edit['name'] . '@example.com';
$edit['status'] = 1;
$user1 = User::create($edit);
$user1->save();
$edit['name'] = $this->randomMachineName();
$user2 = User::create($edit);
$user2->save();
// Unique password hashes are automatically generated, the only way to
// change that is to update it directly in the database.
db_update('users_field_data')
->fields(['pass' => NULL])
->condition('uid', [$user1->id(), $user2->id()], 'IN')
->execute();
\Drupal::entityManager()->getStorage('user')->resetCache();
$user1 = User::load($user1->id());
$user2 = User::load($user2->id());
$this->assertEqual($user1->getPassword(), $user2->getPassword(), 'Both users have the same password hash.');
// The password reset URL must not be valid for the second user when only
// the user ID is changed in the URL.
$reset_url = user_pass_reset_url($user1);
$attack_reset_url = str_replace("user/reset/{$user1->id()}", "user/reset/{$user2->id()}", $reset_url);
$this->drupalGet($attack_reset_url);
$this->drupalPostForm(NULL, NULL, t('Log in'));
$this->assertNoText($user2->getUsername(), 'The invalid password reset page does not show the user name.');
$this->assertUrl('user/password', [], 'The user is redirected to the password reset request page.');
$this->assertText('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.');
}
}

View file

@ -0,0 +1,188 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\RoleInterface;
use Drupal\user\Entity\Role;
/**
* Verify that role permissions can be added and removed via the permissions
* page.
*
* @group user
*/
class UserPermissionsTest extends BrowserTestBase {
/**
* User with admin privileges.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* User's role ID.
*
* @var string
*/
protected $rid;
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(['administer permissions', 'access user profiles', 'administer site configuration', 'administer modules', 'administer account settings']);
// Find the new role ID.
$all_rids = $this->adminUser->getRoles();
unset($all_rids[array_search(RoleInterface::AUTHENTICATED_ID, $all_rids)]);
$this->rid = reset($all_rids);
}
/**
* Test changing user permissions through the permissions page.
*/
public function testUserPermissionChanges() {
$permissions_hash_generator = $this->container->get('user_permissions_hash_generator');
$storage = $this->container->get('entity.manager')->getStorage('user_role');
// Create an additional role and mark it as admin role.
Role::create(['is_admin' => TRUE, 'id' => 'administrator', 'label' => 'Administrator'])->save();
$storage->resetCache();
$this->drupalLogin($this->adminUser);
$rid = $this->rid;
$account = $this->adminUser;
$previous_permissions_hash = $permissions_hash_generator->generate($account);
$this->assertIdentical($previous_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
// Add a permission.
$this->assertFalse($account->hasPermission('administer users'), 'User does not have "administer users" permission.');
$edit = [];
$edit[$rid . '[administer users]'] = TRUE;
$this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
$this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
$storage->resetCache();
$this->assertTrue($account->hasPermission('administer users'), 'User now has "administer users" permission.');
$current_permissions_hash = $permissions_hash_generator->generate($account);
$this->assertIdentical($current_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
$previous_permissions_hash = $current_permissions_hash;
// Remove a permission.
$this->assertTrue($account->hasPermission('access user profiles'), 'User has "access user profiles" permission.');
$edit = [];
$edit[$rid . '[access user profiles]'] = FALSE;
$this->drupalPostForm('admin/people/permissions', $edit, t('Save permissions'));
$this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
$storage->resetCache();
$this->assertFalse($account->hasPermission('access user profiles'), 'User no longer has "access user profiles" permission.');
$current_permissions_hash = $permissions_hash_generator->generate($account);
$this->assertIdentical($current_permissions_hash, $permissions_hash_generator->generate($this->loggedInUser));
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
// Ensure that the admin role doesn't have any checkboxes.
$this->drupalGet('admin/people/permissions');
foreach (array_keys($this->container->get('user.permissions')->getPermissions()) as $permission) {
$this->assertSession()->checkboxChecked('administrator[' . $permission . ']');
$this->assertSession()->fieldDisabled('administrator[' . $permission . ']');
}
}
/**
* Test assigning of permissions for the administrator role.
*/
public function testAdministratorRole() {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/config/people/accounts');
// Verify that the administration role is none by default.
$this->assertOptionSelected('edit-user-admin-role', '', 'Administration role defaults to none.');
$this->assertFalse(Role::load($this->rid)->isAdmin());
// Set the user's role to be the administrator role.
$edit = [];
$edit['user_admin_role'] = $this->rid;
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
\Drupal::entityManager()->getStorage('user_role')->resetCache();
$this->assertTrue(Role::load($this->rid)->isAdmin());
// Enable aggregator module and ensure the 'administer news feeds'
// permission is assigned by default.
\Drupal::service('module_installer')->install(['aggregator']);
$this->assertTrue($this->adminUser->hasPermission('administer news feeds'), 'The permission was automatically assigned to the administrator role');
// Ensure that selecting '- None -' removes the admin role.
$edit = [];
$edit['user_admin_role'] = '';
$this->drupalPostForm('admin/config/people/accounts', $edit, t('Save configuration'));
\Drupal::entityManager()->getStorage('user_role')->resetCache();
\Drupal::configFactory()->reset();
$this->assertFalse(Role::load($this->rid)->isAdmin());
// Manually create two admin roles, in that case the single select should be
// hidden.
Role::create(['id' => 'admin_role_0', 'is_admin' => TRUE, 'label' => 'Admin role 0'])->save();
Role::create(['id' => 'admin_role_1', 'is_admin' => TRUE, 'label' => 'Admin role 1'])->save();
$this->drupalGet('admin/config/people/accounts');
$this->assertNoFieldByName('user_admin_role');
}
/**
* Verify proper permission changes by user_role_change_permissions().
*/
public function testUserRoleChangePermissions() {
$permissions_hash_generator = $this->container->get('user_permissions_hash_generator');
$rid = $this->rid;
$account = $this->adminUser;
$previous_permissions_hash = $permissions_hash_generator->generate($account);
// Verify current permissions.
$this->assertFalse($account->hasPermission('administer users'), 'User does not have "administer users" permission.');
$this->assertTrue($account->hasPermission('access user profiles'), 'User has "access user profiles" permission.');
$this->assertTrue($account->hasPermission('administer site configuration'), 'User has "administer site configuration" permission.');
// Change permissions.
$permissions = [
'administer users' => 1,
'access user profiles' => 0,
];
user_role_change_permissions($rid, $permissions);
// Verify proper permission changes.
$this->assertTrue($account->hasPermission('administer users'), 'User now has "administer users" permission.');
$this->assertFalse($account->hasPermission('access user profiles'), 'User no longer has "access user profiles" permission.');
$this->assertTrue($account->hasPermission('administer site configuration'), 'User still has "administer site configuration" permission.');
// Verify the permissions hash has changed.
$current_permissions_hash = $permissions_hash_generator->generate($account);
$this->assertNotEqual($previous_permissions_hash, $current_permissions_hash, 'Permissions hash has changed.');
}
/**
* Verify 'access content' is listed in the correct location.
*/
public function testAccessContentPermission() {
$this->drupalLogin($this->adminUser);
// When Node is not installed the 'access content' permission is listed next
// to 'access site reports'.
$this->drupalGet('admin/people/permissions');
$next_row = $this->xpath('//tr[@data-drupal-selector=\'edit-permissions-access-content\']/following-sibling::tr[1]');
$this->assertEqual('edit-permissions-access-site-reports', $next_row[0]->getAttribute('data-drupal-selector'));
// When Node is installed the 'access content' permission is listed next to
// to 'view own unpublished content'.
\Drupal::service('module_installer')->install(['node']);
$this->drupalGet('admin/people/permissions');
$next_row = $this->xpath('//tr[@data-drupal-selector=\'edit-permissions-access-content\']/following-sibling::tr[1]');
$this->assertEqual('edit-permissions-view-own-unpublished-content', $next_row[0]->getAttribute('data-drupal-selector'));
}
}

View file

@ -0,0 +1,154 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\image\Entity\ImageStyle;
use Drupal\Tests\BrowserTestBase;
use Drupal\file\Entity\File;
use Drupal\Tests\TestFileCreationTrait;
/**
* Tests user picture functionality.
*
* @group user
*/
class UserPictureTest extends BrowserTestBase {
use TestFileCreationTrait {
getTestFiles as drupalGetTestFiles;
}
/**
* The profile to install as a basis for testing.
*
* Using the standard profile to test user picture config provided by the
* standard profile.
*
* @var string
*/
protected $profile = 'standard';
/**
* A regular user.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
protected function setUp() {
parent::setUp();
// This test expects unused managed files to be marked temporary and then
// cleaned up by file_cron().
$this->config('file.settings')
->set('make_unused_managed_files_temporary', TRUE)
->save();
$this->webUser = $this->drupalCreateUser([
'access content',
'access comments',
'post comments',
'skip comment approval',
]);
}
/**
* Tests creation, display, and deletion of user pictures.
*/
public function testCreateDeletePicture() {
$this->drupalLogin($this->webUser);
// Save a new picture.
$image = current($this->drupalGetTestFiles('image'));
$file = $this->saveUserPicture($image);
// Verify that the image is displayed on the user account page.
$this->drupalGet('user');
$this->assertRaw(file_uri_target($file->getFileUri()), 'User picture found on user account page.');
// Delete the picture.
$edit = [];
$this->drupalPostForm('user/' . $this->webUser->id() . '/edit', $edit, t('Remove'));
$this->drupalPostForm(NULL, [], t('Save'));
// Call file_cron() to clean up the file. Make sure the timestamp
// of the file is older than the system.file.temporary_maximum_age
// configuration value.
db_update('file_managed')
->fields([
'changed' => REQUEST_TIME - ($this->config('system.file')->get('temporary_maximum_age') + 1),
])
->condition('fid', $file->id())
->execute();
\Drupal::service('cron')->run();
// Verify that the image has been deleted.
$this->assertFalse(File::load($file->id()), 'File was removed from the database.');
// Clear out PHP's file stat cache so we see the current value.
clearstatcache(TRUE, $file->getFileUri());
$this->assertFalse(is_file($file->getFileUri()), 'File was removed from the file system.');
}
/**
* Tests embedded users on node pages.
*/
public function testPictureOnNodeComment() {
$this->drupalLogin($this->webUser);
// Save a new picture.
$image = current($this->drupalGetTestFiles('image'));
$file = $this->saveUserPicture($image);
$node = $this->drupalCreateNode(['type' => 'article']);
// Enable user pictures on nodes.
$this->config('system.theme.global')->set('features.node_user_picture', TRUE)->save();
$image_style_id = $this->config('core.entity_view_display.user.user.compact')->get('content.user_picture.settings.image_style');
$style = ImageStyle::load($image_style_id);
$image_url = file_url_transform_relative($style->buildUrl($file->getfileUri()));
$alt_text = 'Profile picture for user ' . $this->webUser->getUsername();
// Verify that the image is displayed on the node page.
$this->drupalGet('node/' . $node->id());
$elements = $this->cssSelect('.node__meta .field--name-user-picture img[alt="' . $alt_text . '"][src="' . $image_url . '"]');
$this->assertEqual(count($elements), 1, 'User picture with alt text found on node page.');
// Enable user pictures on comments, instead of nodes.
$this->config('system.theme.global')
->set('features.node_user_picture', FALSE)
->set('features.comment_user_picture', TRUE)
->save();
$edit = [
'comment_body[0][value]' => $this->randomString(),
];
$this->drupalPostForm('comment/reply/node/' . $node->id() . '/comment', $edit, t('Save'));
$elements = $this->cssSelect('.comment__meta .field--name-user-picture img[alt="' . $alt_text . '"][src="' . $image_url . '"]');
$this->assertEqual(count($elements), 1, 'User picture with alt text found on the comment.');
// Disable user pictures on comments and nodes.
$this->config('system.theme.global')
->set('features.node_user_picture', FALSE)
->set('features.comment_user_picture', FALSE)
->save();
$this->drupalGet('node/' . $node->id());
$this->assertNoRaw(file_uri_target($file->getFileUri()), 'User picture not found on node and comment.');
}
/**
* Edits the user picture for the test user.
*/
public function saveUserPicture($image) {
$edit = ['files[user_picture_0]' => \Drupal::service('file_system')->realpath($image->uri)];
$this->drupalPostForm('user/' . $this->webUser->id() . '/edit', $edit, t('Save'));
// Load actual user data from database.
$user_storage = $this->container->get('entity.manager')->getStorage('user');
$user_storage->resetCache([$this->webUser->id()]);
$account = $user_storage->load($this->webUser->id());
return File::load($account->user_picture->target_id);
}
}

View file

@ -0,0 +1,384 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\BrowserTestBase;
/**
* Tests registration of user under different configurations.
*
* @group user
*/
class UserRegistrationTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['field_test'];
public function testRegistrationWithEmailVerification() {
$config = $this->config('user.settings');
// Require email verification.
$config->set('verify_mail', TRUE)->save();
// Set registration to administrator only.
$config->set('register', USER_REGISTER_ADMINISTRATORS_ONLY)->save();
$this->drupalGet('user/register');
$this->assertResponse(403, 'Registration page is inaccessible when only administrators can create accounts.');
// Allow registration by site visitors without administrator approval.
$config->set('register', USER_REGISTER_VISITORS)->save();
$edit = [];
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertText(t('A welcome message with further instructions has been sent to your email address.'), 'User registered successfully.');
/** @var EntityStorageInterface $storage */
$storage = $this->container->get('entity_type.manager')->getStorage('user');
$accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
$new_user = reset($accounts);
$this->assertTrue($new_user->isActive(), 'New account is active after registration.');
$resetURL = user_pass_reset_url($new_user);
$this->drupalGet($resetURL);
$this->assertTitle(t('Set password | Drupal'), 'Page title is "Set password".');
// Allow registration by site visitors, but require administrator approval.
$config->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
$edit = [];
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->container->get('entity.manager')->getStorage('user')->resetCache();
$accounts = $storage->loadByProperties(['name' => $name, 'mail' => $mail]);
$new_user = reset($accounts);
$this->assertFalse($new_user->isActive(), 'New account is blocked until approved by an administrator.');
}
public function testRegistrationWithoutEmailVerification() {
$config = $this->config('user.settings');
// Don't require email verification and allow registration by site visitors
// without administrator approval.
$config
->set('verify_mail', FALSE)
->set('register', USER_REGISTER_VISITORS)
->save();
$edit = [];
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
// Try entering a mismatching password.
$edit['pass[pass1]'] = '99999.0';
$edit['pass[pass2]'] = '99999';
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertText(t('The specified passwords do not match.'), 'Typing mismatched passwords displays an error message.');
// Enter a correct password.
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
$edit['pass[pass2]'] = $new_pass;
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->container->get('entity.manager')->getStorage('user')->resetCache();
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
->loadByProperties(['name' => $name, 'mail' => $mail]);
$new_user = reset($accounts);
$this->assertNotNull($new_user, 'New account successfully created with matching passwords.');
$this->assertText(t('Registration successful. You are now logged in.'), 'Users are logged in after registering.');
$this->drupalLogout();
// Allow registration by site visitors, but require administrator approval.
$config->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL)->save();
$edit = [];
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
$edit['pass[pass1]'] = $pass = $this->randomMachineName();
$edit['pass[pass2]'] = $pass;
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertText(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'), 'Users are notified of pending approval');
// Try to log in before administrator approval.
$auth = [
'name' => $name,
'pass' => $pass,
];
$this->drupalPostForm('user/login', $auth, t('Log in'));
$this->assertText(t('The username @name has not been activated or is blocked.', ['@name' => $name]), 'User cannot log in yet.');
// Activate the new account.
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
->loadByProperties(['name' => $name, 'mail' => $mail]);
$new_user = reset($accounts);
$admin_user = $this->drupalCreateUser(['administer users']);
$this->drupalLogin($admin_user);
$edit = [
'status' => 1,
];
$this->drupalPostForm('user/' . $new_user->id() . '/edit', $edit, t('Save'));
$this->drupalLogout();
// Log in after administrator approval.
$this->drupalPostForm('user/login', $auth, t('Log in'));
$this->assertText(t('Member for'), 'User can log in after administrator approval.');
}
public function testRegistrationEmailDuplicates() {
// Don't require email verification and allow registration by site visitors
// without administrator approval.
$this->config('user.settings')
->set('verify_mail', FALSE)
->set('register', USER_REGISTER_VISITORS)
->save();
// Set up a user to check for duplicates.
$duplicate_user = $this->drupalCreateUser();
$edit = [];
$edit['name'] = $this->randomMachineName();
$edit['mail'] = $duplicate_user->getEmail();
// Attempt to create a new account using an existing email address.
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying an exact duplicate email address displays an error message');
// Attempt to bypass duplicate email registration validation by adding spaces.
$edit['mail'] = ' ' . $duplicate_user->getEmail() . ' ';
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertText(t('The email address @email is already taken.', ['@email' => $duplicate_user->getEmail()]), 'Supplying a duplicate email address with added whitespace displays an error message');
}
/**
* Tests that UUID isn't cached in form state on register form.
*
* This is a regression test for https://www.drupal.org/node/2500527 to ensure
* that the form is not cached on GET requests.
*/
public function testUuidFormState() {
\Drupal::service('module_installer')->install(['image']);
\Drupal::service('router.builder')->rebuild();
// Add a picture field in order to ensure that no form cache is written,
// which breaks registration of more than 1 user every 6 hours.
$field_storage = FieldStorageConfig::create([
'field_name' => 'user_picture',
'entity_type' => 'user',
'type' => 'image',
]);
$field_storage->save();
$field = FieldConfig::create([
'field_name' => 'user_picture',
'entity_type' => 'user',
'bundle' => 'user',
]);
$field->save();
$form_display = EntityFormDisplay::create([
'targetEntityType' => 'user',
'bundle' => 'user',
'mode' => 'default',
'status' => TRUE,
]);
$form_display->setComponent('user_picture', [
'type' => 'image_image',
]);
$form_display->save();
// Don't require email verification and allow registration by site visitors
// without administrator approval.
$this->config('user.settings')
->set('verify_mail', FALSE)
->set('register', USER_REGISTER_VISITORS)
->save();
$edit = [];
$edit['name'] = $this->randomMachineName();
$edit['mail'] = $edit['name'] . '@example.com';
$edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
// Create one account.
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertResponse(200);
$user_storage = \Drupal::entityManager()->getStorage('user');
$this->assertTrue($user_storage->loadByProperties(['name' => $edit['name']]));
$this->drupalLogout();
// Create a second account.
$edit['name'] = $this->randomMachineName();
$edit['mail'] = $edit['name'] . '@example.com';
$edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertResponse(200);
$this->assertTrue($user_storage->loadByProperties(['name' => $edit['name']]));
}
public function testRegistrationDefaultValues() {
// Don't require email verification and allow registration by site visitors
// without administrator approval.
$config_user_settings = $this->config('user.settings')
->set('verify_mail', FALSE)
->set('register', USER_REGISTER_VISITORS)
->save();
// Set the default timezone to Brussels.
$config_system_date = $this->config('system.date')
->set('timezone.user.configurable', 1)
->set('timezone.default', 'Europe/Brussels')
->save();
// Check the presence of expected cache tags.
$this->drupalGet('user/register');
$this->assertCacheTag('config:user.settings');
$edit = [];
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
$edit['pass[pass1]'] = $new_pass = $this->randomMachineName();
$edit['pass[pass2]'] = $new_pass;
$this->drupalPostForm(NULL, $edit, t('Create new account'));
// Check user fields.
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
->loadByProperties(['name' => $name, 'mail' => $mail]);
$new_user = reset($accounts);
$this->assertEqual($new_user->getUsername(), $name, 'Username matches.');
$this->assertEqual($new_user->getEmail(), $mail, 'Email address matches.');
$this->assertTrue(($new_user->getCreatedTime() > REQUEST_TIME - 20), 'Correct creation time.');
$this->assertEqual($new_user->isActive(), $config_user_settings->get('register') == USER_REGISTER_VISITORS ? 1 : 0, 'Correct status field.');
$this->assertEqual($new_user->getTimezone(), $config_system_date->get('timezone.default'), 'Correct time zone field.');
$this->assertEqual($new_user->langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct language field.');
$this->assertEqual($new_user->preferred_langcode->value, \Drupal::languageManager()->getDefaultLanguage()->getId(), 'Correct preferred language field.');
$this->assertEqual($new_user->init->value, $mail, 'Correct init field.');
}
/**
* Tests username and email field constraints on user registration.
*
* @see \Drupal\user\Plugin\Validation\Constraint\UserNameUnique
* @see \Drupal\user\Plugin\Validation\Constraint\UserMailUnique
*/
public function testUniqueFields() {
$account = $this->drupalCreateUser();
$edit = ['mail' => 'test@example.com', 'name' => $account->getUsername()];
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertRaw(new FormattableMarkup('The username %value is already taken.', ['%value' => $account->getUsername()]));
$edit = ['mail' => $account->getEmail(), 'name' => $this->randomString()];
$this->drupalPostForm('user/register', $edit, t('Create new account'));
$this->assertRaw(new FormattableMarkup('The email address %value is already taken.', ['%value' => $account->getEmail()]));
}
/**
* Tests Field API fields on user registration forms.
*/
public function testRegistrationWithUserFields() {
// Create a field on 'user' entity type.
$field_storage = FieldStorageConfig::create([
'field_name' => 'test_user_field',
'entity_type' => 'user',
'type' => 'test_field',
'cardinality' => 1,
]);
$field_storage->save();
$field = FieldConfig::create([
'field_storage' => $field_storage,
'label' => 'Some user field',
'bundle' => 'user',
'required' => TRUE,
]);
$field->save();
entity_get_form_display('user', 'user', 'default')
->setComponent('test_user_field', ['type' => 'test_field_widget'])
->save();
entity_get_form_display('user', 'user', 'register')
->save();
// Check that the field does not appear on the registration form.
$this->drupalGet('user/register');
$this->assertNoText($field->label(), 'The field does not appear on user registration form');
$this->assertCacheTag('config:core.entity_form_display.user.user.register');
$this->assertCacheTag('config:user.settings');
// Have the field appear on the registration form.
entity_get_form_display('user', 'user', 'register')
->setComponent('test_user_field', ['type' => 'test_field_widget'])
->save();
$this->drupalGet('user/register');
$this->assertText($field->label(), 'The field appears on user registration form');
$this->assertRegistrationFormCacheTagsWithUserFields();
// Check that validation errors are correctly reported.
$edit = [];
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
// Missing input in required field.
$edit['test_user_field[0][value]'] = '';
$this->drupalPostForm(NULL, $edit, t('Create new account'));
$this->assertRegistrationFormCacheTagsWithUserFields();
$this->assertRaw(t('@name field is required.', ['@name' => $field->label()]), 'Field validation error was correctly reported.');
// Invalid input.
$edit['test_user_field[0][value]'] = '-1';
$this->drupalPostForm(NULL, $edit, t('Create new account'));
$this->assertRegistrationFormCacheTagsWithUserFields();
$this->assertRaw(t('%name does not accept the value -1.', ['%name' => $field->label()]), 'Field validation error was correctly reported.');
// Submit with valid data.
$value = rand(1, 255);
$edit['test_user_field[0][value]'] = $value;
$this->drupalPostForm(NULL, $edit, t('Create new account'));
// Check user fields.
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
->loadByProperties(['name' => $name, 'mail' => $mail]);
$new_user = reset($accounts);
$this->assertEqual($new_user->test_user_field->value, $value, 'The field value was correctly saved.');
// Check that the 'add more' button works.
$field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$field_storage->save();
$this->drupalGet('user/register');
$this->assertRegistrationFormCacheTagsWithUserFields();
// Add two inputs.
$value = rand(1, 255);
$edit = [];
$edit['test_user_field[0][value]'] = $value;
$this->drupalPostForm(NULL, $edit, t('Add another item'));
$this->drupalPostForm(NULL, $edit, t('Add another item'));
// Submit with three values.
$edit['test_user_field[1][value]'] = $value + 1;
$edit['test_user_field[2][value]'] = $value + 2;
$edit['name'] = $name = $this->randomMachineName();
$edit['mail'] = $mail = $edit['name'] . '@example.com';
$this->drupalPostForm(NULL, $edit, t('Create new account'));
// Check user fields.
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
->loadByProperties(['name' => $name, 'mail' => $mail]);
$new_user = reset($accounts);
$this->assertEqual($new_user->test_user_field[0]->value, $value, 'The field value was correctly saved.');
$this->assertEqual($new_user->test_user_field[1]->value, $value + 1, 'The field value was correctly saved.');
$this->assertEqual($new_user->test_user_field[2]->value, $value + 2, 'The field value was correctly saved.');
}
/**
* Asserts the presence of cache tags on registration form with user fields.
*/
protected function assertRegistrationFormCacheTagsWithUserFields() {
$this->assertCacheTag('config:core.entity_form_display.user.user.register');
$this->assertCacheTag('config:field.field.user.user.test_user_field');
$this->assertCacheTag('config:field.storage.user.test_user_field');
$this->assertCacheTag('config:user.settings');
}
}

View file

@ -0,0 +1,132 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests adding, editing and deleting user roles and changing role weights.
*
* @group user
*/
class UserRoleAdminTest extends BrowserTestBase {
/**
* User with admin privileges.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* Modules to enable.
*
* @var string[]
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(['administer permissions', 'administer users']);
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Test adding, renaming and deleting roles.
*/
public function testRoleAdministration() {
$this->drupalLogin($this->adminUser);
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
// Test presence of tab.
$this->drupalGet('admin/people/permissions');
$tabs = $this->xpath('//ul[@class=:classes and //a[contains(., :text)]]', [
':classes' => 'tabs primary',
':text' => 'Roles',
]);
$this->assertEqual(count($tabs), 1, 'Found roles tab');
// Test adding a role. (In doing so, we use a role name that happens to
// correspond to an integer, to test that the role administration pages
// correctly distinguish between role names and IDs.)
$role_name = '123';
$edit = ['label' => $role_name, 'id' => $role_name];
$this->drupalPostForm('admin/people/roles/add', $edit, t('Save'));
$this->assertRaw(t('Role %label has been added.', ['%label' => 123]));
$role = Role::load($role_name);
$this->assertTrue(is_object($role), 'The role was successfully retrieved from the database.');
// Check that the role was created in site default language.
$this->assertEqual($role->language()->getId(), $default_langcode);
// Try adding a duplicate role.
$this->drupalPostForm('admin/people/roles/add', $edit, t('Save'));
$this->assertRaw(t('The machine-readable name is already in use. It must be unique.'), 'Duplicate role warning displayed.');
// Test renaming a role.
$role_name = '456';
$edit = ['label' => $role_name];
$this->drupalPostForm("admin/people/roles/manage/{$role->id()}", $edit, t('Save'));
$this->assertRaw(t('Role %label has been updated.', ['%label' => $role_name]));
\Drupal::entityManager()->getStorage('user_role')->resetCache([$role->id()]);
$new_role = Role::load($role->id());
$this->assertEqual($new_role->label(), $role_name, 'The role name has been successfully changed.');
// Test deleting a role.
$this->drupalGet("admin/people/roles/manage/{$role->id()}");
$this->clickLink(t('Delete'));
$this->drupalPostForm(NULL, [], t('Delete'));
$this->assertRaw(t('The role %label has been deleted.', ['%label' => $role_name]));
$this->assertNoLinkByHref("admin/people/roles/manage/{$role->id()}", 'Role edit link removed.');
\Drupal::entityManager()->getStorage('user_role')->resetCache([$role->id()]);
$this->assertFalse(Role::load($role->id()), 'A deleted role can no longer be loaded.');
// Make sure that the system-defined roles can be edited via the user
// interface.
$this->drupalGet('admin/people/roles/manage/' . RoleInterface::ANONYMOUS_ID);
$this->assertResponse(200, 'Access granted when trying to edit the built-in anonymous role.');
$this->assertNoText(t('Delete role'), 'Delete button for the anonymous role is not present.');
$this->drupalGet('admin/people/roles/manage/' . RoleInterface::AUTHENTICATED_ID);
$this->assertResponse(200, 'Access granted when trying to edit the built-in authenticated role.');
$this->assertNoText(t('Delete role'), 'Delete button for the authenticated role is not present.');
}
/**
* Test user role weight change operation and ordering.
*/
public function testRoleWeightOrdering() {
$this->drupalLogin($this->adminUser);
$roles = user_roles();
$weight = count($roles);
$new_role_weights = [];
$saved_rids = [];
// Change the role weights to make the roles in reverse order.
$edit = [];
foreach ($roles as $role) {
$edit['entities[' . $role->id() . '][weight]'] = $weight;
$new_role_weights[$role->id()] = $weight;
$saved_rids[] = $role->id();
$weight--;
}
$this->drupalPostForm('admin/people/roles', $edit, t('Save'));
$this->assertText(t('The role settings have been updated.'), 'The role settings form submitted successfully.');
// Load up the user roles with the new weights.
drupal_static_reset('user_roles');
$roles = user_roles();
$rids = [];
// Test that the role weights have been correctly saved.
foreach ($roles as $role) {
$this->assertEqual($role->getWeight(), $new_role_weights[$role->id()]);
$rids[] = $role->id();
}
// The order of the roles should be reversed.
$this->assertIdentical($rids, array_reverse($saved_rids));
}
}

View file

@ -21,7 +21,7 @@ class UserRolesAssignmentTest extends BrowserTestBase {
* Tests that a user can be assigned a role and that the role can be removed
* again.
*/
public function testAssignAndRemoveRole() {
public function testAssignAndRemoveRole() {
$rid = $this->drupalCreateRole(['administer users']);
$account = $this->drupalCreateUser();

View file

@ -0,0 +1,95 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\Tests\BrowserTestBase;
/**
* Set a user time zone and verify that dates are displayed in local time.
*
* @group user
*/
class UserTimeZoneTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'system_test'];
/**
* Tests the display of dates and time when user-configurable time zones are set.
*/
public function testUserTimeZone() {
// Setup date/time settings for Los Angeles time.
$this->config('system.date')
->set('timezone.user.configurable', 1)
->set('timezone.default', 'America/Los_Angeles')
->save();
// Load the 'medium' date format, which is the default for node creation
// time, and override it. Since we are testing time zones with Daylight
// Saving Time, and need to future proof against changes to the zoneinfo
// database, we choose the 'I' format placeholder instead of a
// human-readable zone name. With 'I', a 1 means the date is in DST, and 0
// if not.
DateFormat::load('medium')
->setPattern('Y-m-d H:i I')
->save();
// Create a user account and login.
$web_user = $this->drupalCreateUser();
$this->drupalLogin($web_user);
// Create some nodes with different authored-on dates.
// Two dates in PST (winter time):
$date1 = '2007-03-09 21:00:00 -0800';
$date2 = '2007-03-11 01:00:00 -0800';
// One date in PDT (summer time):
$date3 = '2007-03-20 21:00:00 -0700';
$this->drupalCreateContentType(['type' => 'article']);
$node1 = $this->drupalCreateNode(['created' => strtotime($date1), 'type' => 'article']);
$node2 = $this->drupalCreateNode(['created' => strtotime($date2), 'type' => 'article']);
$node3 = $this->drupalCreateNode(['created' => strtotime($date3), 'type' => 'article']);
// Confirm date format and time zone.
$this->drupalGet('node/' . $node1->id());
$this->assertText('2007-03-09 21:00 0', 'Date should be PST.');
$this->drupalGet('node/' . $node2->id());
$this->assertText('2007-03-11 01:00 0', 'Date should be PST.');
$this->drupalGet('node/' . $node3->id());
$this->assertText('2007-03-20 21:00 1', 'Date should be PDT.');
// Change user time zone to Santiago time.
$edit = [];
$edit['mail'] = $web_user->getEmail();
$edit['timezone'] = 'America/Santiago';
$this->drupalPostForm("user/" . $web_user->id() . "/edit", $edit, t('Save'));
$this->assertText(t('The changes have been saved.'), 'Time zone changed to Santiago time.');
// Confirm date format and time zone.
$this->drupalGet('node/' . $node1->id());
$this->assertText('2007-03-10 02:00 1', 'Date should be Chile summer time; five hours ahead of PST.');
$this->drupalGet('node/' . $node2->id());
$this->assertText('2007-03-11 05:00 0', 'Date should be Chile time; four hours ahead of PST');
$this->drupalGet('node/' . $node3->id());
$this->assertText('2007-03-21 00:00 0', 'Date should be Chile time; three hours ahead of PDT.');
// Ensure that anonymous users also use the default timezone.
$this->drupalLogout();
$this->drupalGet('node/' . $node1->id());
$this->assertText('2007-03-09 21:00 0', 'Date should be PST.');
$this->drupalGet('node/' . $node2->id());
$this->assertText('2007-03-11 01:00 0', 'Date should be PST.');
$this->drupalGet('node/' . $node3->id());
$this->assertText('2007-03-20 21:00 1', 'Date should be PDT.');
// Format a date without accessing the current user at all and
// ensure that it uses the default timezone.
$this->drupalGet('/system-test/date');
$this->assertText('2016-01-13 08:29 0', 'Date should be PST.');
}
}

View file

@ -0,0 +1,99 @@
<?php
namespace Drupal\Tests\user\Functional;
use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
/**
* Tests the User Translation UI.
*
* @group user
*/
class UserTranslationUITest extends ContentTranslationUITestBase {
/**
* The user name of the test user.
*
* @var string
*/
protected $name;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['language', 'content_translation', 'user', 'views'];
protected function setUp() {
$this->entityTypeId = 'user';
$this->testLanguageSelector = FALSE;
$this->name = $this->randomMachineName();
parent::setUp();
\Drupal::entityManager()->getStorage('user')->resetCache();
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
return array_merge(parent::getTranslatorPermissions(), ['administer users']);
}
/**
* {@inheritdoc}
*/
protected function getNewEntityValues($langcode) {
// User name is not translatable hence we use a fixed value.
return ['name' => $this->name] + parent::getNewEntityValues($langcode);
}
/**
* {@inheritdoc}
*/
protected function doTestTranslationEdit() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
foreach ($this->langcodes as $langcode) {
// We only want to test the title for non-english translations.
if ($langcode != 'en') {
$options = ['language' => $languages[$langcode]];
$url = $entity->urlInfo('edit-form', $options);
$this->drupalGet($url);
$title = t('@title [%language translation]', [
'@title' => $entity->getTranslation($langcode)->label(),
'%language' => $languages[$langcode]->getName(),
]);
$this->assertRaw($title);
}
}
}
/**
* Test translated user deletion.
*/
public function testTranslatedUserDeletion() {
$this->drupalLogin($this->administrator);
$entity_id = $this->createEntity($this->getNewEntityValues('en'), 'en');
$entity = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId)
->load($entity_id);
$translated_entity = $entity->addTranslation('fr');
$translated_entity->save();
$url = $entity->toUrl(
'edit-form',
['language' => $this->container->get('language_manager')->getLanguage('en')]
);
$this->drupalPostForm($url, [], t('Cancel account'));
$this->assertSession()->statusCodeEquals(200);
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\user\Plugin\views\access\Permission;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Views;
/**
* Tests views perm access plugin.
*
* @group user
* @see \Drupal\user\Plugin\views\access\Permission
*/
class AccessPermissionTest extends AccessTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_access_perm'];
/**
* Tests perm access plugin.
*/
public function testAccessPerm() {
$view = Views::getView('test_access_perm');
$view->setDisplay();
$access_plugin = $view->display_handler->getPlugin('access');
$this->assertTrue($access_plugin instanceof Permission, 'Make sure the right class got instantiated.');
$this->assertEqual($access_plugin->pluginTitle(), t('Permission'));
$this->assertFalse($view->display_handler->access($this->webUser));
$this->assertTrue($view->display_handler->access($this->normalUser));
}
/**
* Tests access on render caching.
*/
public function testRenderCaching() {
$view = Views::getView('test_access_perm');
$display = &$view->storage->getDisplay('default');
$display['display_options']['cache'] = [
'type' => 'tag',
];
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
/** @var \Drupal\Core\Session\AccountSwitcherInterface $account_switcher */
$account_switcher = \Drupal::service('account_switcher');
// First access as user without access.
$build = DisplayPluginBase::buildBasicRenderable('test_access_perm', 'default');
$account_switcher->switchTo($this->normalUser);
$result = $renderer->renderPlain($build);
$this->assertNotEqual($result, '');
// Then with access.
$build = DisplayPluginBase::buildBasicRenderable('test_access_perm', 'default');
$account_switcher->switchTo($this->webUser);
$result = $renderer->renderPlain($build);
$this->assertEqual($result, '');
}
}

View file

@ -0,0 +1,140 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\Core\Cache\Cache;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\user\Plugin\views\access\Role;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Views;
/**
* Tests views role access plugin.
*
* @group user
* @see \Drupal\user\Plugin\views\access\Role
*/
class AccessRoleTest extends AccessTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_access_role'];
/**
* Tests role access plugin.
*/
public function testAccessRole() {
/** @var \Drupal\views\ViewEntityInterface $view */
$view = \Drupal::entityManager()->getStorage('view')->load('test_access_role');
$display = &$view->getDisplay('default');
$display['display_options']['access']['options']['role'] = [
$this->normalRole => $this->normalRole,
];
$view->save();
$this->container->get('router.builder')->rebuildIfNeeded();
$expected = [
'config' => ['user.role.' . $this->normalRole],
'module' => ['user', 'views_test_data'],
];
$this->assertIdentical($expected, $view->calculateDependencies()->getDependencies());
$executable = Views::executableFactory()->get($view);
$executable->setDisplay('page_1');
$access_plugin = $executable->display_handler->getPlugin('access');
$this->assertTrue($access_plugin instanceof Role, 'Make sure the right class got instantiated.');
// Test the access() method on the access plugin.
$this->assertFalse($executable->display_handler->access($this->webUser));
$this->assertTrue($executable->display_handler->access($this->normalUser));
$this->drupalLogin($this->webUser);
$this->drupalGet('test-role');
$this->assertResponse(403);
$this->assertCacheContext('user.roles');
$this->drupalLogin($this->normalUser);
$this->drupalGet('test-role');
$this->assertResponse(200);
$this->assertCacheContext('user.roles');
// Test allowing multiple roles.
$view = Views::getView('test_access_role')->storage;
$display = &$view->getDisplay('default');
$display['display_options']['access']['options']['role'] = [
$this->normalRole => $this->normalRole,
'anonymous' => 'anonymous',
];
$view->save();
$this->container->get('router.builder')->rebuildIfNeeded();
// Ensure that the list of roles is sorted correctly, if the generated role
// ID comes before 'anonymous', see https://www.drupal.org/node/2398259.
$roles = ['user.role.anonymous', 'user.role.' . $this->normalRole];
sort($roles);
$expected = [
'config' => $roles,
'module' => ['user', 'views_test_data'],
];
$this->assertIdentical($expected, $view->calculateDependencies()->getDependencies());
$this->drupalLogin($this->webUser);
$this->drupalGet('test-role');
$this->assertResponse(403);
$this->assertCacheContext('user.roles');
$this->drupalLogout();
$this->drupalGet('test-role');
$this->assertResponse(200);
$this->assertCacheContext('user.roles');
$this->drupalLogin($this->normalUser);
$this->drupalGet('test-role');
$this->assertResponse(200);
$this->assertCacheContext('user.roles');
}
/**
* Tests access on render caching.
*/
public function testRenderCaching() {
$view = Views::getView('test_access_role');
$display = &$view->storage->getDisplay('default');
$display['display_options']['cache'] = [
'type' => 'tag',
];
$display['display_options']['access']['options']['role'] = [
$this->normalRole => $this->normalRole,
];
$view->save();
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
/** @var \Drupal\Core\Session\AccountSwitcherInterface $account_switcher */
$account_switcher = \Drupal::service('account_switcher');
// First access as user with access.
$build = DisplayPluginBase::buildBasicRenderable('test_access_role', 'default');
$account_switcher->switchTo($this->normalUser);
$result = $renderer->renderPlain($build);
$this->assertTrue(in_array('user.roles', $build['#cache']['contexts']));
$this->assertEqual(['config:views.view.test_access_role'], $build['#cache']['tags']);
$this->assertEqual(Cache::PERMANENT, $build['#cache']['max-age']);
$this->assertNotEqual($result, '');
// Then without access.
$build = DisplayPluginBase::buildBasicRenderable('test_access_role', 'default');
$account_switcher->switchTo($this->webUser);
$result = $renderer->renderPlain($build);
// @todo Fix this in https://www.drupal.org/node/2551037,
// DisplayPluginBase::applyDisplayCacheabilityMetadata() is not invoked when
// using buildBasicRenderable() and a Views access plugin returns FALSE.
// $this->assertTrue(in_array('user.roles', $build['#cache']['contexts']));
// $this->assertEqual([], $build['#cache']['tags']);
$this->assertEqual(Cache::PERMANENT, $build['#cache']['max-age']);
$this->assertEqual($result, '');
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
/**
* A common test base class for the user access plugin tests.
*/
abstract class AccessTestBase extends UserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block'];
/**
* Contains a user object that has no special permissions.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
/**
* Contains a user object that has the 'views_test_data test permission'.
*
* @var \Drupal\user\UserInterface
*/
protected $normalUser;
/**
* Contains a role ID that is used by the webUser.
*
* @var string
*/
protected $webRole;
/**
* Contains a role ID that is used by the normalUser.
*
* @var string
*/
protected $normalRole;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->drupalPlaceBlock('system_breadcrumb_block');
$this->enableViewsTestModule();
$this->webUser = $this->drupalCreateUser();
$roles = $this->webUser->getRoles();
$this->webRole = $roles[0];
$this->normalRole = $this->drupalCreateRole([]);
$this->normalUser = $this->drupalCreateUser(['views_test_data test permission']);
$this->normalUser->addRole($this->normalRole);
$this->normalUser->save();
// @todo when all the plugin information is cached make a reset function and
// call it here.
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\views\Views;
/**
* Tests views user argument default plugin.
*
* @group user
*/
class ArgumentDefaultTest extends UserTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_plugin_argument_default_current_user'];
public function test_plugin_argument_default_current_user() {
// Create a user to test.
$account = $this->drupalCreateUser();
// Switch the user.
\Drupal::service('account_switcher')->switchTo($account);
$view = Views::getView('test_plugin_argument_default_current_user');
$view->initHandlers();
$this->assertEqual($view->argument['null']->getDefaultArgument(), $account->id(), 'Uid of the current user is used.');
// Switch back.
\Drupal::service('account_switcher')->switchBack();
}
}

View file

@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\Core\Form\FormState;
use Drupal\views\Plugin\views\argument\ArgumentPluginBase;
use Drupal\views\Views;
/**
* Tests user argument validators for ID and name.
*
* @group user
*/
class ArgumentValidateTest extends UserTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_view_argument_validate_user', 'test_view_argument_validate_username'];
/**
* A user for this test.
*
* @var \Drupal\user\UserInterface
*/
protected $account;
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->account = $this->drupalCreateUser();
}
/**
* Tests the User (ID) argument validator.
*/
public function testArgumentValidateUserUid() {
$account = $this->account;
$view = Views::getView('test_view_argument_validate_user');
$this->executeView($view);
$this->assertTrue($view->argument['null']->validateArgument($account->id()));
// Reset argument validation.
$view->argument['null']->argument_validated = NULL;
// Fail for a valid numeric, but for a user that doesn't exist
$this->assertFalse($view->argument['null']->validateArgument(32));
$form = [];
$form_state = new FormState();
$view->argument['null']->buildOptionsForm($form, $form_state);
$sanitized_id = ArgumentPluginBase::encodeValidatorId('entity:user');
$this->assertTrue($form['validate']['options'][$sanitized_id]['roles']['#states']['visible'][':input[name="options[validate][options][' . $sanitized_id . '][restrict_roles]"]']['checked']);
}
/**
* Tests the UserName argument validator.
*/
public function testArgumentValidateUserName() {
$account = $this->account;
$view = Views::getView('test_view_argument_validate_username');
$this->executeView($view);
$this->assertTrue($view->argument['null']->validateArgument($account->getUsername()));
// Reset argument validation.
$view->argument['null']->argument_validated = NULL;
// Fail for a valid string, but for a user that doesn't exist
$this->assertFalse($view->argument['null']->validateArgument($this->randomMachineName()));
}
}

View file

@ -0,0 +1,132 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\user\Entity\User;
/**
* Tests if entity access is respected on a user bulk form.
*
* @group user
* @see \Drupal\user\Plugin\views\field\UserBulkForm
* @see \Drupal\user\Tests\Views\BulkFormTest
*/
class BulkFormAccessTest extends UserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user_access_test'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_user_bulk_form'];
/**
* Tests if users that may not be edited, can not be edited in bulk.
*/
public function testUserEditAccess() {
// Create an authenticated user.
$no_edit_user = $this->drupalCreateUser([], 'no_edit');
// Ensure this account is not blocked.
$this->assertFalse($no_edit_user->isBlocked(), 'The user is not blocked.');
// Log in as user admin.
$admin_user = $this->drupalCreateUser(['administer users']);
$this->drupalLogin($admin_user);
// Ensure that the account "no_edit" can not be edited.
$this->drupalGet('user/' . $no_edit_user->id() . '/edit');
$this->assertFalse($no_edit_user->access('update', $admin_user));
$this->assertResponse(403, 'The user may not be edited.');
// Test blocking the account "no_edit".
$edit = [
'user_bulk_form[' . ($no_edit_user->id() - 1) . ']' => TRUE,
'action' => 'user_block_user_action',
];
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$this->assertResponse(200);
$this->assertRaw(new FormattableMarkup('No access to execute %action on the @entity_type_label %entity_label.', [
'%action' => 'Block the selected user(s)',
'@entity_type_label' => 'User',
'%entity_label' => $no_edit_user->label(),
]));
// Re-load the account "no_edit" and ensure it is not blocked.
$no_edit_user = User::load($no_edit_user->id());
$this->assertFalse($no_edit_user->isBlocked(), 'The user is not blocked.');
// Create a normal user which can be edited by the admin user
$normal_user = $this->drupalCreateUser();
$this->assertTrue($normal_user->access('update', $admin_user));
$edit = [
'user_bulk_form[' . ($normal_user->id() - 1) . ']' => TRUE,
'action' => 'user_block_user_action',
];
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$normal_user = User::load($normal_user->id());
$this->assertTrue($normal_user->isBlocked(), 'The user is blocked.');
// Log in as user without the 'administer users' permission.
$this->drupalLogin($this->drupalCreateUser());
$edit = [
'user_bulk_form[' . ($normal_user->id() - 1) . ']' => TRUE,
'action' => 'user_unblock_user_action',
];
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
// Re-load the normal user and ensure it is still blocked.
$normal_user = User::load($normal_user->id());
$this->assertTrue($normal_user->isBlocked(), 'The user is still blocked.');
}
/**
* Tests if users that may not be deleted, can not be deleted in bulk.
*/
public function testUserDeleteAccess() {
// Create two authenticated users.
$account = $this->drupalCreateUser([], 'no_delete');
$account2 = $this->drupalCreateUser([], 'may_delete');
// Log in as user admin.
$this->drupalLogin($this->drupalCreateUser(['administer users']));
// Ensure that the account "no_delete" can not be deleted.
$this->drupalGet('user/' . $account->id() . '/cancel');
$this->assertResponse(403, 'The user "no_delete" may not be deleted.');
// Ensure that the account "may_delete" *can* be deleted.
$this->drupalGet('user/' . $account2->id() . '/cancel');
$this->assertResponse(200, 'The user "may_delete" may be deleted.');
// Test deleting the accounts "no_delete" and "may_delete".
$edit = [
'user_bulk_form[' . ($account->id() - 1) . ']' => TRUE,
'user_bulk_form[' . ($account2->id() - 1) . ']' => TRUE,
'action' => 'user_cancel_user_action',
];
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$edit = [
'user_cancel_method' => 'user_cancel_delete',
];
$this->drupalPostForm(NULL, $edit, t('Cancel accounts'));
// Ensure the account "no_delete" still exists.
$account = User::load($account->id());
$this->assertNotNull($account, 'The user "no_delete" is not deleted.');
// Ensure the account "may_delete" no longer exists.
$account = User::load($account2->id());
$this->assertNull($account, 'The user "may_delete" is deleted.');
}
}

View file

@ -0,0 +1,140 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
use Drupal\views\Views;
/**
* Tests a user bulk form.
*
* @group user
* @see \Drupal\user\Plugin\views\field\UserBulkForm
*/
class BulkFormTest extends UserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['views_ui'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_user_bulk_form', 'test_user_bulk_form_combine_filter'];
/**
* Tests the user bulk form.
*/
public function testBulkForm() {
// Log in as a user without 'administer users'.
$this->drupalLogin($this->drupalCreateUser(['administer permissions']));
$user_storage = $this->container->get('entity.manager')->getStorage('user');
// Create an user which actually can change users.
$this->drupalLogin($this->drupalCreateUser(['administer users']));
$this->drupalGet('test-user-bulk-form');
$result = $this->cssSelect('#edit-action option');
$this->assertTrue(count($result) > 0);
// Test submitting the page with no selection.
$edit = [
'action' => 'user_block_user_action',
];
$this->drupalPostForm('test-user-bulk-form', $edit, t('Apply to selected items'));
$this->assertText(t('No users selected.'));
// Assign a role to a user.
$account = $user_storage->load($this->users[0]->id());
$roles = user_role_names(TRUE);
unset($roles[RoleInterface::AUTHENTICATED_ID]);
$role = key($roles);
$this->assertFalse($account->hasRole($role), 'The user currently does not have a custom role.');
$edit = [
'user_bulk_form[1]' => TRUE,
'action' => 'user_add_role_action.' . $role,
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
// Re-load the user and check their roles.
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertTrue($account->hasRole($role), 'The user now has the custom role.');
$edit = [
'user_bulk_form[1]' => TRUE,
'action' => 'user_remove_role_action.' . $role,
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
// Re-load the user and check their roles.
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertFalse($account->hasRole($role), 'The user no longer has the custom role.');
// Block a user using the bulk form.
$this->assertTrue($account->isActive(), 'The user is not blocked.');
$this->assertRaw($account->label(), 'The user is found in the table.');
$edit = [
'user_bulk_form[1]' => TRUE,
'action' => 'user_block_user_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
// Re-load the user and check their status.
$user_storage->resetCache([$account->id()]);
$account = $user_storage->load($account->id());
$this->assertTrue($account->isBlocked(), 'The user is blocked.');
$this->assertNoRaw($account->label(), 'The user is not found in the table.');
// Remove the user status filter from the view.
$view = Views::getView('test_user_bulk_form');
$view->removeHandler('default', 'filter', 'status');
$view->storage->save();
// Ensure the anonymous user is found.
$this->drupalGet('test-user-bulk-form');
$this->assertText($this->config('user.settings')->get('anonymous'));
// Attempt to block the anonymous user.
$edit = [
'user_bulk_form[0]' => TRUE,
'action' => 'user_block_user_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$anonymous_account = $user_storage->load(0);
$this->assertTrue($anonymous_account->isBlocked(), 'Ensure the anonymous user got blocked.');
// Test the list of available actions with a value that contains a dot.
$this->drupalLogin($this->drupalCreateUser(['administer permissions', 'administer views', 'administer users']));
$action_id = 'user_add_role_action.' . $role;
$edit = [
'options[include_exclude]' => 'exclude',
"options[selected_actions][$action_id]" => $action_id,
];
$this->drupalPostForm('admin/structure/views/nojs/handler/test_user_bulk_form/default/field/user_bulk_form', $edit, t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
$this->drupalGet('test-user-bulk-form');
$this->assertNoOption('edit-action', $action_id);
$edit['options[include_exclude]'] = 'include';
$this->drupalPostForm('admin/structure/views/nojs/handler/test_user_bulk_form/default/field/user_bulk_form', $edit, t('Apply'));
$this->drupalPostForm(NULL, [], t('Save'));
$this->drupalGet('test-user-bulk-form');
$this->assertOption('edit-action', $action_id);
}
/**
* Tests the user bulk form with a combined field filter on the bulk column.
*/
public function testBulkFormCombineFilter() {
// Add a user.
User::load($this->users[0]->id());
$view = Views::getView('test_user_bulk_form_combine_filter');
$errors = $view->validate();
$this->assertEqual(reset($errors['default']), t('Field %field set in %filter is not usable for this filter type. Combined field filter only works for simple fields.', ['%field' => 'User: Bulk update', '%filter' => 'Global: Combine fields filter']));
}
}

View file

@ -0,0 +1,68 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Tests the permission field handler ui.
*
* @group user
* @see \Drupal\user\Plugin\views\filter\Permissions
*/
class FilterPermissionUiTest extends ViewTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_filter_permission'];
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user', 'user_test_views', 'views_ui'];
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
$this->enableViewsTestModule();
}
/**
* Tests basic filter handler settings in the UI.
*/
public function testHandlerUI() {
$this->drupalLogin($this->drupalCreateUser(['administer views', 'administer users']));
$this->drupalGet('admin/structure/views/view/test_filter_permission/edit/default');
// Verify that the handler summary is correctly displaying the selected
// permission.
$this->assertLink('User: Permission (= View user information)');
$this->drupalPostForm(NULL, [], 'Save');
// Verify that we can save the view.
$this->assertNoText('No valid values found on filter: User: Permission.');
$this->assertText('The view test_filter_permission has been saved.');
// Verify that the handler summary is also correct when multiple values are
// selected in the filter.
$edit = [
'options[value][]' => [
'access user profiles',
'administer views',
],
];
$this->drupalPostForm('admin/structure/views/nojs/handler/test_filter_permission/default/filter/permission', $edit, 'Apply');
$this->assertLink('User: Permission (or View us…)');
$this->drupalPostForm(NULL, [], 'Save');
// Verify that we can save the view.
$this->assertNoText('No valid values found on filter: User: Permission.');
$this->assertText('The view test_filter_permission has been saved.');
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\views\Views;
/**
* Tests the handler of the user: uid Argument.
*
* @group user
*/
class HandlerArgumentUserUidTest extends UserTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_user_uid_argument'];
/**
* Tests the generated title of an user: uid argument.
*/
public function testArgumentTitle() {
$view = Views::getView('test_user_uid_argument');
// Tests an invalid user uid.
$this->executeView($view, [rand(1000, 10000)]);
$this->assertFalse($view->getTitle());
$view->destroy();
// Tests a valid user.
$account = $this->drupalCreateUser();
$this->executeView($view, [$account->id()]);
$this->assertEqual($view->getTitle(), $account->label());
$view->destroy();
// Tests the anonymous user.
$anonymous = $this->config('user.settings')->get('anonymous');
$this->executeView($view, [0]);
$this->assertEqual($view->getTitle(), $anonymous);
$view->destroy();
$view->getDisplay()->getHandler('argument', 'uid')->options['break_phrase'] = TRUE;
$this->executeView($view, [$account->id() . ',0']);
$this->assertEqual($view->getTitle(), $account->label() . ', ' . $anonymous);
$view->destroy();
$view->getDisplay()->getHandler('argument', 'uid')->options['break_phrase'] = TRUE;
$this->executeView($view, ['0,' . $account->id()]);
$this->assertEqual($view->getTitle(), $anonymous . ', ' . $account->label());
$view->destroy();
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\Component\Utility\Html;
use Drupal\user\Entity\User;
/**
* Tests the handler of the user: role field.
*
* @group user
* @see views_handler_field_user_name
*/
class HandlerFieldRoleTest extends UserTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_views_handler_field_role'];
public function testRole() {
// Create a couple of roles for the view.
$rolename_a = 'a' . $this->randomMachineName(8);
$this->drupalCreateRole(['access content'], $rolename_a, '<em>' . $rolename_a . '</em>', 9);
$rolename_b = 'b' . $this->randomMachineName(8);
$this->drupalCreateRole(['access content'], $rolename_b, $rolename_b, 8);
$rolename_not_assigned = $this->randomMachineName(8);
$this->drupalCreateRole(['access content'], $rolename_not_assigned, $rolename_not_assigned);
// Add roles to user 1.
$user = User::load(1);
$user->addRole($rolename_a);
$user->addRole($rolename_b);
$user->save();
$this->drupalLogin($this->createUser(['access user profiles']));
$this->drupalGet('/test-views-handler-field-role');
$this->assertText($rolename_b . Html::escape('<em>' . $rolename_a . '</em>'), 'View test_views_handler_field_role renders role assigned to user in the correct order and markup in role names is escaped.');
$this->assertNoText($rolename_not_assigned, 'View test_views_handler_field_role does not render a role not assigned to a user.');
}
}

View file

@ -0,0 +1,81 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\Core\Render\RenderContext;
use Drupal\views\Views;
/**
* Tests the handler of the user: name field.
*
* @group user
* @see views_handler_field_user_name
*/
class HandlerFieldUserNameTest extends UserTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_views_handler_field_user_name'];
public function testUserName() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$new_user = $this->drupalCreateUser(['access user profiles']);
$this->drupalLogin($new_user);
// Set defaults.
$view = Views::getView('test_views_handler_field_user_name');
$view->initHandlers();
$view->field['name']->options['link_to_user'] = TRUE;
$view->field['name']->options['type'] = 'user_name';
$view->field['name']->init($view, $view->getDisplay('default'));
$view->field['name']->options['id'] = 'name';
$this->executeView($view);
$anon_name = $this->config('user.settings')->get('anonymous');
$view->result[0]->_entity->setUsername('');
$view->result[0]->_entity->uid->value = 0;
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertTrue(strpos($render, $anon_name) !== FALSE, 'For user 0 it should use the default anonymous name by default.');
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view, $new_user) {
return $view->field['name']->advancedRender($view->result[$new_user->id()]);
});
$this->assertTrue(strpos($render, $new_user->getDisplayName()) !== FALSE, 'If link to user is checked the username should be part of the output.');
$this->assertTrue(strpos($render, 'user/' . $new_user->id()) !== FALSE, 'If link to user is checked the link to the user should appear as well.');
$view->field['name']->options['link_to_user'] = FALSE;
$view->field['name']->options['type'] = 'string';
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view, $new_user) {
return $view->field['name']->advancedRender($view->result[$new_user->id()]);
});
$this->assertEqual($render, $new_user->getDisplayName(), 'If the user is not linked the username should be printed out for a normal user.');
}
/**
* Tests that the field handler works when no additional fields are added.
*/
public function testNoAdditionalFields() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$view = Views::getView('test_views_handler_field_user_name');
$this->executeView($view);
$username = $this->randomMachineName();
$view->result[0]->_entity->setUsername($username);
$view->result[0]->_entity->uid->value = 1;
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertTrue(strpos($render, $username) !== FALSE, 'If link to user is checked the username should be part of the output.');
}
}

View file

@ -0,0 +1,183 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\views\Views;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Tests the handler of the user: name filter.
*
* @group user
* @see Views\user\Plugin\views\filter\Name
*/
class HandlerFilterUserNameTest extends ViewTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['views_ui', 'user_test_views'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_user_name'];
/**
* Accounts used by this test.
*
* @var array
*/
protected $accounts = [];
/**
* Usernames of $accounts.
*
* @var array
*/
protected $names = [];
/**
* Stores the column map for this testCase.
*
* @var array
*/
public $columnMap = [
'uid' => 'uid',
];
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
$this->enableViewsTestModule();
$this->accounts = [];
$this->names = [];
for ($i = 0; $i < 3; $i++) {
$this->accounts[] = $account = $this->drupalCreateUser();
$this->names[] = $account->label();
}
}
/**
* Tests just using the filter.
*/
public function testUserNameApi() {
$view = Views::getView('test_user_name');
$view->initHandlers();
$view->filter['uid']->value = [$this->accounts[0]->id()];
$this->executeView($view);
$this->assertIdenticalResultset($view, [['uid' => $this->accounts[0]->id()]], $this->columnMap);
$this->assertEqual($view->filter['uid']->getValueOptions(), NULL);
}
/**
* Tests using the user interface.
*/
public function testAdminUserInterface() {
$admin_user = $this->drupalCreateUser(['administer views', 'administer site configuration']);
$this->drupalLogin($admin_user);
$path = 'admin/structure/views/nojs/handler/test_user_name/default/filter/uid';
$this->drupalGet($path);
// Pass in an invalid username, the validation should catch it.
$users = [$this->randomMachineName()];
$users = array_map('strtolower', $users);
$edit = [
'options[value]' => implode(', ', $users),
];
$this->drupalPostForm($path, $edit, t('Apply'));
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
// Pass in an invalid username and a valid username.
$random_name = $this->randomMachineName();
$users = [$random_name, $this->names[0]];
$users = array_map('strtolower', $users);
$edit = [
'options[value]' => implode(', ', $users),
];
$users = [$users[0]];
$this->drupalPostForm($path, $edit, t('Apply'));
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
// Pass in just valid usernames.
$users = $this->names;
$users = array_map('strtolower', $users);
$edit = [
'options[value]' => implode(', ', $users),
];
$this->drupalPostForm($path, $edit, t('Apply'));
$this->assertNoRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
}
/**
* Tests exposed filters.
*/
public function testExposedFilter() {
$path = 'test_user_name';
$options = [];
// Pass in an invalid username, the validation should catch it.
$users = [$this->randomMachineName()];
$users = array_map('strtolower', $users);
$options['query']['uid'] = implode(', ', $users);
$this->drupalGet($path, $options);
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
// Pass in an invalid target_id in for the entity_autocomplete value format.
// There should be no errors, but all results should be returned as the
// default value for the autocomplete will not match any users so should
// be empty.
$options['query']['uid'] = [['target_id' => 9999]];
$this->drupalGet($path, $options);
// The actual result should contain all of the user ids.
foreach ($this->accounts as $account) {
$this->assertRaw($account->id());
}
// Pass in an invalid username and a valid username.
$users = [$this->randomMachineName(), $this->names[0]];
$users = array_map('strtolower', $users);
$options['query']['uid'] = implode(', ', $users);
$users = [$users[0]];
$this->drupalGet($path, $options);
$this->assertRaw(t('There are no entities matching "%value".', ['%value' => implode(', ', $users)]));
// Pass in just valid usernames.
$users = $this->names;
$options['query']['uid'] = implode(', ', $users);
$this->drupalGet($path, $options);
$this->assertNoRaw('Unable to find user');
// The actual result should contain all of the user ids.
foreach ($this->accounts as $account) {
$this->assertRaw($account->id());
}
// Pass in just valid user IDs in the entity_autocomplete target_id format.
$options['query']['uid'] = array_map(function ($account) {
return ['target_id' => $account->id()];
}, $this->accounts);
$this->drupalGet($path, $options);
$this->assertNoRaw('Unable to find user');
// The actual result should contain all of the user ids.
foreach ($this->accounts as $account) {
$this->assertRaw($account->id());
}
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\views\Views;
/**
* Tests the representative node relationship for users.
*
* @group user
*/
class RelationshipRepresentativeNodeTest extends UserTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_groupwise_user'];
/**
* Tests the relationship.
*/
public function testRelationship() {
$view = Views::getView('test_groupwise_user');
$this->executeView($view);
$map = ['node_field_data_users_field_data_nid' => 'nid', 'uid' => 'uid'];
$expected_result = [
[
'uid' => $this->users[1]->id(),
'nid' => $this->nodes[1]->id(),
],
[
'uid' => $this->users[0]->id(),
'nid' => $this->nodes[0]->id(),
],
];
$this->assertIdenticalResultset($view, $expected_result, $map);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
/**
* Tests the handler of the user: roles argument.
*
* @group user
* @see \Drupal\user\Plugin\views\argument\RolesRid
*/
class RolesRidArgumentTest extends UserTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_user_roles_rid'];
/**
* Tests the generated title of a user: roles argument.
*/
public function testArgumentTitle() {
$role_id = $this->createRole([], 'markup_role_name', '<em>Role name with markup</em>');
$user = $this->createUser();
$user->addRole($role_id);
$user->save();
$this->drupalGet('/user_roles_rid_test/markup_role_name');
$this->assertEscaped('<em>Role name with markup</em>');
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Tests the changed field.
*
* @group user
*/
class UserChangedTest extends ViewTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['views_ui', 'user_test_views'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_user_changed'];
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
$this->enableViewsTestModule();
}
/**
* Tests changed field.
*/
public function testChangedField() {
$path = 'test_user_changed';
$options = [];
$this->drupalGet($path, $options);
$this->assertText(t('Updated date') . ': ' . date('Y-m-d', REQUEST_TIME));
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\views\Views;
/**
* Tests the user data service field handler.
*
* @group user
* @see \Drupal\user\Plugin\views\field\UserData
*/
class UserDataTest extends UserTestBase {
/**
* Provides the user data service object.
*
* @var \Drupal\user\UserDataInterface
*/
protected $userData;
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_user_data'];
/**
* Tests field handler.
*/
public function testDataField() {
// But some random values into the user data service.
$this->userData = $this->container->get('user.data');
$random_value = $this->randomMachineName();
$this->userData->set('views_test_config', $this->users[0]->id(), 'test_value_name', $random_value);
$view = Views::getView('test_user_data');
$this->executeView($view);
$output = $view->field['data']->render($view->result[0]);
$this->assertEqual($output, $random_value, 'A valid user data got rendered.');
$view->field['data']->options['data_name'] = $this->randomMachineName();
$output = $view->field['data']->render($view->result[0]);
$this->assertFalse($output, 'An invalid configuration does not return anything');
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
/**
* Checks if user fields access permissions can be modified by other modules.
*
* @group user
*/
class UserFieldsAccessChangeTest extends UserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user_access_test'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_user_fields_access'];
/**
* Tests if another module can change field access.
*/
public function testUserFieldAccess() {
$path = 'test_user_fields_access';
$this->drupalGet($path);
// User has access to name and created date by default.
$this->assertText(t('Name'));
$this->assertText(t('Created'));
// User does not by default have access to init, mail and status.
$this->assertNoText(t('Init'));
$this->assertNoText(t('Email'));
$this->assertNoText(t('Status'));
// Assign sub-admin role to grant extra access.
$user = $this->drupalCreateUser(['sub-admin']);
$this->drupalLogin($user);
$this->drupalGet($path);
// Access for init, mail and status is added in hook_entity_field_access().
$this->assertText(t('Init'));
$this->assertText(t('Email'));
$this->assertText(t('Status'));
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\Tests\user\Functional\Views;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\user\Entity\User;
/**
* @todo.
*/
abstract class UserTestBase extends ViewTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user_test_views', 'node'];
/**
* Users to use during this test.
*
* @var array
*/
protected $users = [];
/**
* Nodes to use during this test.
*
* @var array
*/
protected $nodes = [];
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['user_test_views']);
$this->users[] = $this->drupalCreateUser();
$this->users[] = User::load(1);
$this->nodes[] = $this->drupalCreateNode(['uid' => $this->users[0]->id()]);
$this->nodes[] = $this->drupalCreateNode(['uid' => 1]);
}
}

View file

@ -0,0 +1,138 @@
<?php
namespace Drupal\Tests\user\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests user registration forms with additional fields.
*
* @group user
*/
class RegistrationWithUserFieldsTest extends WebDriverTestBase {
/**
* WebAssert object.
*
* @var \Drupal\Tests\WebAssert
*/
protected $webAssert;
/**
* DocumentElement object.
*
* @var \Behat\Mink\Element\DocumentElement
*/
protected $page;
/**
* {@inheritdoc}
*/
public static $modules = ['field_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->page = $this->getSession()->getPage();
$this->webAssert = $this->assertSession();
}
/**
* Tests Field API fields on user registration forms.
*/
public function testRegistrationWithUserFields() {
// Create a field on 'user' entity type.
$field_storage = FieldStorageConfig::create([
'field_name' => 'test_user_field',
'entity_type' => 'user',
'type' => 'test_field',
'cardinality' => 1,
]);
$field_storage->save();
$field = FieldConfig::create([
'field_storage' => $field_storage,
'label' => 'Some user field',
'bundle' => 'user',
'required' => TRUE,
]);
$field->save();
entity_get_form_display('user', 'user', 'default')
->setComponent('test_user_field', ['type' => 'test_field_widget'])
->save();
$user_registration_form = entity_get_form_display('user', 'user', 'register');
$user_registration_form->save();
// Check that the field does not appear on the registration form.
$this->drupalGet('user/register');
$this->webAssert->pageTextNotContains($field->label());
// Have the field appear on the registration form.
$user_registration_form->setComponent('test_user_field', ['type' => 'test_field_widget'])->save();
$this->drupalGet('user/register');
$this->webAssert->pageTextContains($field->label());
// In order to check the server side validation the native browser
// validation for required fields needs to be circumvented.
$session = $this->getSession();
$session->executeScript("jQuery('#edit-test-user-field-0-value').prop('required', false);");
// Check that validation errors are correctly reported.
$name = $this->randomMachineName();
$this->page->fillField('edit-name', $name);
$this->page->fillField('edit-mail', $name . '@example.com');
$this->page->pressButton('edit-submit');
$this->webAssert->pageTextContains(t('@name field is required.', ['@name' => $field->label()]));
// Invalid input.
$this->page->fillField('edit-test-user-field-0-value', '-1');
$this->page->pressButton('edit-submit');
$this->webAssert->pageTextContains($field->label() . ' does not accept the value -1.');
// Submit with valid data.
$value = (string) mt_rand(1, 255);
$this->page->fillField('edit-test-user-field-0-value', $value);
$this->page->pressButton('edit-submit');
// Check user fields.
$accounts = $this->container->get('entity_type.manager')->getStorage('user')
->loadByProperties(['name' => $name, 'mail' => $name . '@example.com']);
$new_user = reset($accounts);
$this->assertEquals($value, $new_user->test_user_field->value, 'The field value was correctly saved.');
// Check that the 'add more' button works.
$field_storage->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$field_storage->save();
$name = $this->randomMachineName();
$this->drupalGet('user/register');
$this->page->fillField('edit-name', $name);
$this->page->fillField('edit-mail', $name . '@example.com');
$this->page->fillField('test_user_field[0][value]', $value);
// Add two inputs.
$this->page->pressButton('test_user_field_add_more');
$this->webAssert->waitForElement('css', 'input[name="test_user_field[1][value]"]');
$this->page->fillField('test_user_field[1][value]', $value . '1');
$this->page->pressButton('test_user_field_add_more');
$this->webAssert->waitForElement('css', 'input[name="test_user_field[2][value]"]');
$this->page->fillField('test_user_field[2][value]', $value . '2');
// Submit with three values.
$this->page->pressButton('edit-submit');
// Check user fields.
$accounts = $this->container->get('entity_type.manager')
->getStorage('user')
->loadByProperties(['name' => $name, 'mail' => $name . '@example.com']);
$new_user = reset($accounts);
$this->assertEquals($value, $new_user->test_user_field[0]->value, t('The field value was correctly saved.'));
$this->assertEquals($value . '1', $new_user->test_user_field[1]->value, t('The field value was correctly saved.'));
$this->assertEquals($value . '2', $new_user->test_user_field[2]->value, t('The field value was correctly saved.'));
}
}

View file

@ -0,0 +1,128 @@
<?php
namespace Drupal\Tests\user\FunctionalJavascript;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\Core\Url;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\user\Entity\User;
/**
* Ensure that password reset methods work as expected.
*
* @group user
*/
class UserPasswordResetTest extends WebDriverTestBase {
use AssertMailTrait {
getMails as drupalGetMails;
}
use TestFileCreationTrait {
getTestFiles as drupalGetTestFiles;
}
/**
* The profile to install as a basis for testing.
*
* This test uses the standard profile to test the password reset in
* combination with an ajax request provided by the user picture configuration
* in the standard profile.
*
* @var string
*/
protected $profile = 'standard';
/**
* The user object to test password resetting.
*
* @var \Drupal\user\UserInterface
*/
protected $account;
/**
* {@inheritdoc}
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create a user.
$account = $this->drupalCreateUser();
// Activate user by logging in.
$this->drupalLogin($account);
$this->account = User::load($account->id());
$this->account->pass_raw = $account->pass_raw;
$this->drupalLogout();
// Set the last login time that is used to generate the one-time link so
// that it is definitely over a second ago.
$account->login = REQUEST_TIME - mt_rand(10, 100000);
db_update('users_field_data')
->fields(['login' => $account->getLastLoginTime()])
->condition('uid', $account->id())
->execute();
}
/**
* Tests password reset functionality with an AJAX form.
*
* Make sure the ajax request from uploading a user picture does not
* invalidate the reset token.
*/
public function testUserPasswordResetWithAdditionalAjaxForm() {
$this->drupalGet(Url::fromRoute('user.reset.form', ['uid' => $this->account->id()]));
// Try to reset the password for an invalid account.
$this->drupalGet('user/password');
// Reset the password by username via the password reset page.
$edit['name'] = $this->account->getUsername();
$this->drupalPostForm(NULL, $edit, t('Submit'));
$resetURL = $this->getResetURL();
$this->drupalGet($resetURL);
// Login
$this->drupalPostForm(NULL, NULL, t('Log in'));
// Generate file.
$image_file = current($this->drupalGetTestFiles('image'));
$image_path = \Drupal::service('file_system')->realpath($image_file->uri);
// Upload file.
$this->getSession()->getPage()->attachFileToField('Picture', $image_path);
$this->assertSession()->waitForButton('Remove');
// Change the forgotten password.
$password = user_password();
$edit = ['pass[pass1]' => $password, 'pass[pass2]' => $password];
$this->drupalPostForm(NULL, $edit, t('Save'));
// Verify that the password reset session has been destroyed.
$this->drupalPostForm(NULL, $edit, t('Save'));
// Password needed to make profile changes.
$this->assertSession()->pageTextContains("Your current password is missing or incorrect; it's required to change the Password.");
}
/**
* Retrieves password reset email and extracts the login link.
*/
public function getResetURL() {
// Assume the most recent email.
$_emails = $this->drupalGetMails();
$email = end($_emails);
$urls = [];
preg_match('#.+user/reset/.+#', $email['body'], $urls);
return $urls[0];
}
}

View file

@ -2,7 +2,7 @@
namespace Drupal\Tests\user\Kernel\Condition;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\KernelTests\KernelTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
@ -151,7 +151,7 @@ class UserRoleConditionTest extends KernelTestBase {
$condition->setConfig('roles', [$this->role->id() => $this->role->id()]);
$condition->setConfig('negate', FALSE);
$this->assertTrue($condition->execute(), 'Authenticated user is a member of the custom role.');
$this->assertEqual($condition->summary(), SafeMarkup::format('The user is a member of @roles', ['@roles' => $this->role->label()]));
$this->assertEqual($condition->summary(), new FormattableMarkup('The user is a member of @roles', ['@roles' => $this->role->label()]));
}
}

View file

@ -47,6 +47,10 @@ class MigrateUserProfileEntityDisplayTest extends MigrateDrupal6TestBase {
// Test PROFILE_HIDDEN field is hidden.
$this->assertNull($display->getComponent('profile_sold_to'));
// Test a checkbox field.
$component = $display->getComponent('profile_really_really_love_mig');
$this->assertIdentical('list_default', $component['type']);
}
}

View file

@ -49,7 +49,7 @@ class MigrateUserProfileEntityFormDisplayTest extends MigrateDrupal6TestBase {
$this->assertNull($display->getComponent('profile_sold_to'));
// Test that a checkbox field has the proper display label setting.
$component = $display->getComponent('profile_love_migrations');
$component = $display->getComponent('profile_really_really_love_mig');
$this->assertIdentical('boolean_checkbox', $component['type']);
$this->assertIdentical(TRUE, $component['settings']['display_label']);
}

View file

@ -68,8 +68,8 @@ class MigrateUserProfileFieldInstanceTest extends MigrateDrupal6TestBase {
$this->assertIdentical("Enter your birth date and we'll send you a coupon.", $field->getDescription());
// Another migrated checkbox field, with a different source visibility setting.
$field = FieldConfig::load('user.user.profile_love_migrations');
$this->assertIdentical('I love migrations', $field->label());
$field = FieldConfig::load('user.user.profile_really_really_love_mig');
$this->assertIdentical('I really, really, really love migrations', $field->label());
$this->assertIdentical("If you check this box, you love migrations.", $field->getDescription());
}

View file

@ -59,7 +59,13 @@ EOT;
$this->assertNull($user->profile_blog->title);
$this->assertIdentical([], $user->profile_blog->options);
$this->assertIdentical('http://example.com/blog', $user->profile_blog->uri);
$this->assertNull($user->profile_love_migrations->value);
// Check that the source profile field names that are longer than 32
// characters have been migrated.
$this->assertNotNull($user->getFieldDefinition('profile_really_really_love_mig'));
$this->assertNotNull($user->getFieldDefinition('profile_really_really_love_mig1'));
$this->assertSame('1', $user->profile_really_really_love_mig->value);
$this->assertNull($user->profile_really_really_love_mig1->value);
$user = User::load(8);
$this->assertIdentical('Forward/slash', $user->profile_sold_to->value);

View file

@ -60,7 +60,7 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
'access content',
'migrate test anonymous permission',
// From filter_format tables.
'use text format filtered_html'
'use text format filtered_html',
];
$this->assertRole('anonymous', $permissions, 1, $id_map);
@ -81,7 +81,7 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
'migrate test role 1 test permission',
// From filter format.
'use text format full_html',
'use text format php_code'
'use text format php_code',
];
$this->assertRole('migrate_test_role_1', $permissions, 3, $id_map);
@ -126,7 +126,7 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
'administrator1',
'migrate_test_role_11',
'migrate_test_role_21',
'migrate_test_role_3_that_is_longer_than_thirty_two_characters1'
'migrate_test_role_3_that_is_longer_than_thirty_two_characters1',
];
$this->assertEmpty(Role::loadMultiple($roles));

View file

@ -2,10 +2,9 @@
namespace Drupal\Tests\user\Kernel\Migrate\d7;
use Drupal\comment\Entity\CommentType;
use Drupal\Core\Database\Database;
use Drupal\node\Entity\NodeType;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\migrate\Kernel\NodeCommentCombinationTrait;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
@ -18,16 +17,21 @@ use Drupal\user\UserInterface;
*/
class MigrateUserTest extends MigrateDrupal7TestBase {
use NodeCommentCombinationTrait;
/**
* {@inheritdoc}
*/
public static $modules = [
'comment',
'content_translation',
'datetime',
'file',
'image',
'language',
'link',
// Required for translation migrations.
'migrate_drupal_multilingual',
'node',
'system',
'taxonomy',
@ -43,12 +47,15 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
// Prepare to migrate user pictures as well.
$this->installEntitySchema('file');
$this->createType('page');
$this->createType('article');
$this->createType('blog');
$this->createType('book');
$this->createType('forum');
$this->createType('test_content_type');
$this->installEntitySchema('comment');
$this->installEntitySchema('node');
$this->installEntitySchema('taxonomy_term');
$this->createNodeCommentCombination('page');
$this->createNodeCommentCombination('article');
$this->createNodeCommentCombination('blog');
$this->createNodeCommentCombination('book');
$this->createNodeCommentCombination('forum', 'comment_forum');
$this->createNodeCommentCombination('test_content_type');
Vocabulary::create(['vid' => 'test_vocabulary'])->save();
$this->executeMigrations([
'language',
@ -58,28 +65,11 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
'd7_field',
'd7_field_instance',
'd7_user',
'd7_entity_translation_settings',
'd7_user_entity_translation',
]);
}
/**
* Creates a node type with a corresponding comment type.
*
* @param string $id
* The node type ID.
*/
protected function createType($id) {
NodeType::create([
'type' => $id,
'label' => $this->randomString(),
])->save();
CommentType::create([
'id' => 'comment_node_' . $id,
'label' => $this->randomString(),
'target_entity_type_id' => 'node',
])->save();
}
/**
* Asserts various aspects of a user account.
*
@ -99,8 +89,10 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
* The last login time.
* @param bool $blocked
* Whether or not the account is blocked.
* @param string $langcode
* The user account's language code.
* @param string $entity_langcode
* The user entity language code.
* @param string $prefered_langcode
* The user prefered language code.
* @param string $timezone
* The user account's timezone name.
* @param string $init
@ -114,7 +106,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
* @param bool $has_picture
* (optional) Whether the user is expected to have a picture attached.
*/
protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $langcode, $timezone, $init, $roles, $field_integer, $field_file_target_id = FALSE, $has_picture = FALSE) {
protected function assertEntity($id, $label, $mail, $password, $created, $access, $login, $blocked, $entity_langcode, $prefered_langcode, $timezone, $init, $roles, $field_integer, $field_file_target_id = FALSE, $has_picture = FALSE) {
/** @var \Drupal\user\UserInterface $user */
$user = User::load($id);
$this->assertTrue($user instanceof UserInterface);
@ -133,20 +125,20 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
// test if the value was imported correctly.
$language_manager = $this->container->get('language_manager');
$default_langcode = $language_manager->getDefaultLanguage()->getId();
if ($langcode == '') {
if ($prefered_langcode == '') {
$this->assertSame('en', $user->langcode->value);
$this->assertSame($default_langcode, $user->preferred_langcode->value);
$this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
}
elseif ($language_manager->getLanguage($langcode) === NULL) {
elseif ($language_manager->getLanguage($prefered_langcode) === NULL) {
$this->assertSame($default_langcode, $user->langcode->value);
$this->assertSame($default_langcode, $user->preferred_langcode->value);
$this->assertSame($default_langcode, $user->preferred_admin_langcode->value);
}
else {
$this->assertSame($langcode, $user->langcode->value);
$this->assertSame($langcode, $user->preferred_langcode->value);
$this->assertSame($langcode, $user->preferred_admin_langcode->value);
$this->assertSame($entity_langcode, $user->langcode->value);
$this->assertSame($prefered_langcode, $user->preferred_langcode->value);
$this->assertSame($prefered_langcode, $user->preferred_admin_langcode->value);
}
$this->assertSame($timezone, $user->getTimeZone());
@ -188,10 +180,21 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
$roles[] = reset($role);
}
$entity_translation = Database::getConnection('default', 'migrate')
->select('entity_translation', 'et')
->fields('et', ['language'])
->condition('et.entity_type', 'user')
->condition('et.entity_id', $source->uid)
->condition('et.source', '')
->execute()
->fetchField();
$entity_language = $entity_translation ?: $source->language;
$field_integer = Database::getConnection('default', 'migrate')
->select('field_data_field_integer', 'fi')
->fields('fi', ['field_integer_value'])
->condition('fi.entity_id', $source->uid)
->condition('fi.language', $entity_language)
->execute()
->fetchCol();
$field_integer = !empty($field_integer) ? $field_integer : NULL;
@ -212,6 +215,7 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
$source->access,
$source->login,
$source->status,
$entity_language,
$source->language,
$source->timezone,
$source->init,
@ -236,4 +240,45 @@ class MigrateUserTest extends MigrateDrupal7TestBase {
}
}
/**
* Tests the Drupal 7 user entity translations to Drupal 8 migration.
*/
public function testUserEntityTranslations() {
$manager = $this->container->get('content_translation.manager');
// Get the user and its translations.
$user = User::load(2);
$user_fr = $user->getTranslation('fr');
$user_is = $user->getTranslation('is');
// Test that fields translated with Entity Translation are migrated.
$this->assertSame('99', $user->field_integer->value);
$this->assertSame('9', $user_fr->field_integer->value);
$this->assertSame('1', $user_is->field_integer->value);
// Test that the French translation metadata is correctly migrated.
$metadata_fr = $manager->getTranslationMetadata($user_fr);
$this->assertSame('en', $metadata_fr->getSource());
$this->assertSame('1', $metadata_fr->getAuthor()->uid->value);
$this->assertSame('1531663916', $metadata_fr->getCreatedTime());
$this->assertFalse($metadata_fr->isOutdated());
$this->assertFalse($metadata_fr->isPublished());
// Test that the Icelandic translation metadata is correctly migrated.
$metadata_is = $manager->getTranslationMetadata($user_is);
$this->assertSame('en', $metadata_is->getSource());
$this->assertSame('2', $metadata_is->getAuthor()->uid->value);
$this->assertSame('1531663925', $metadata_is->getCreatedTime());
$this->assertTrue($metadata_is->isOutdated());
$this->assertTrue($metadata_is->isPublished());
// Test that untranslatable properties are the same as the source language.
$this->assertSame($user->label(), $user_fr->label());
$this->assertSame($user->label(), $user_is->label());
$this->assertSame($user->getEmail(), $user_fr->getEmail());
$this->assertSame($user->getEmail(), $user_is->getEmail());
$this->assertSame($user->getPassword(), $user_fr->getPassword());
$this->assertSame($user->getPassword(), $user_is->getPassword());
}
}

View file

@ -99,7 +99,7 @@ class ProfileFieldTest extends MigrateSqlSourceTestBase {
'fid' => 4,
'uid' => 1,
'value' => 'yellow',
]
],
];
// Expected options are:
@ -114,7 +114,7 @@ class ProfileFieldTest extends MigrateSqlSourceTestBase {
'blue' => 'blue',
'green' => 'green',
'yellow' => 'yellow',
]
],
];
$tests[0]['expected_data'] = $profile_fields;

View file

@ -0,0 +1,199 @@
<?php
namespace Drupal\Tests\user\Kernel\Plugin\migrate\source\d7;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D7 user entity translation source plugin.
*
* @covers \Drupal\user\Plugin\migrate\source\d7\UserEntityTranslation
*
* @group user
*/
class UserEntityTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['user', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
// The source data.
$tests[0]['source_data']['entity_translation'] = [
[
'entity_type' => 'user',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'en',
'source' => '',
'uid' => 1,
'status' => 1,
'translate' => 0,
'created' => 1531343498,
'changed' => 1531343498,
],
[
'entity_type' => 'user',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'fr',
'source' => 'en',
'uid' => 2,
'status' => 1,
'translate' => 1,
'created' => 1531343508,
'changed' => 1531343508,
],
[
'entity_type' => 'user',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'es',
'source' => 'en',
'uid' => 1,
'status' => 0,
'translate' => 0,
'created' => 1531343456,
'changed' => 1531343456,
],
];
$tests[0]['source_data']['field_config'] = [
[
'id' => 1,
'field_name' => 'field_test',
'type' => 'text',
'module' => 'text',
'active' => 1,
'storage_type' => 'field_sql_storage',
'storage_module' => 'field_sql_storage',
'storage_active' => 1,
'locked' => 1,
'data' => 'a:0:{}',
'cardinality' => 1,
'translatable' => 1,
'deleted' => 0,
],
];
$tests[0]['source_data']['field_config_instance'] = [
[
'id' => 1,
'field_id' => 1,
'field_name' => 'field_test',
'entity_type' => 'user',
'bundle' => 'user',
'data' => 'a:0:{}',
'deleted' => 0,
],
];
$tests[0]['source_data']['field_data_field_test'] = [
[
'entity_type' => 'user',
'bundle' => 'user',
'deleted' => 0,
'entity_id' => 1,
'revision_id' => 1,
'language' => 'en',
'delta' => 0,
'field_test_value' => 'English field',
'field_test_format' => NULL,
],
[
'entity_type' => 'user',
'bundle' => 'user',
'deleted' => 0,
'entity_id' => 1,
'revision_id' => 1,
'language' => 'fr',
'delta' => 0,
'field_test_value' => 'French field',
'field_test_format' => NULL,
],
[
'entity_type' => 'user',
'bundle' => 'user',
'deleted' => 0,
'entity_id' => 1,
'revision_id' => 1,
'language' => 'es',
'delta' => 0,
'field_test_value' => 'Spanish field',
'field_test_format' => NULL,
],
];
$tests[0]['source_data']['users'] = [
[
'uid' => 1,
'name' => 'admin',
'pass' => 'password123',
'mail' => 'admin@example.com',
'theme' => '',
'signature' => '',
'signature_format' => 'filtered_html',
'created' => 1531343456,
'access' => 1531343456,
'login' => 1531343456,
'status' => 1,
'timezone' => 'America/New_York',
'language' => 'fr',
'picture' => 0,
'init' => 'admin@example.com',
'data' => 'a:0:{}',
],
];
$tests[0]['source_data']['users_roles'] = [
[
'uid' => 1,
'rid' => 3,
],
];
// The expected results.
$tests[0]['expected_data'] = [
[
'entity_type' => 'user',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'fr',
'source' => 'en',
'uid' => 2,
'status' => 1,
'translate' => 1,
'created' => 1531343508,
'changed' => 1531343508,
'field_test' => [
[
'value' => 'French field',
'format' => NULL,
],
],
],
[
'entity_type' => 'user',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'es',
'source' => 'en',
'uid' => 1,
'status' => 0,
'translate' => 0,
'created' => 1531343456,
'changed' => 1531343456,
'field_test' => [
[
'value' => 'Spanish field',
'format' => NULL,
],
],
],
];
return $tests;
}
}

View file

@ -24,6 +24,12 @@ class UserTest extends MigrateSqlSourceTestBase {
$tests = [];
// The source data.
$tests[0]['source_data']['field_config'] = [
[
'id' => '11',
'translatable' => '0',
],
];
$tests[0]['source_data']['field_config_instance'] = [
[
'id' => '33',

View file

@ -12,7 +12,8 @@ use Drupal\Core\Database\Database;
* Tests the temporary object storage system.
*
* @group user
* @see \Drupal\Core\TempStore\TempStore.
* @group legacy
* @see \Drupal\user\SharedTempStore
*/
class TempStoreDatabaseTest extends KernelTestBase {
@ -67,6 +68,9 @@ class TempStoreDatabaseTest extends KernelTestBase {
/**
* Tests the UserTempStore API.
*
* @expectedDeprecation \Drupal\user\SharedTempStoreFactory is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStoreFactory instead. See https://www.drupal.org/node/2935639.
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testUserTempStore() {
// Create a key/value collection.

View file

@ -24,8 +24,8 @@ class UserAccountFormFieldsTest extends KernelTestBase {
* Tests the root user account form section in the "Configure site" form.
*/
public function testInstallConfigureForm() {
require_once \Drupal::root() . '/core/includes/install.core.inc';
require_once \Drupal::root() . '/core/includes/install.inc';
require_once $this->root . '/core/includes/install.core.inc';
require_once $this->root . '/core/includes/install.inc';
$install_state = install_state_defaults();
$form_state = new FormState();
$form_state->addBuildInfo('args', [&$install_state]);

View file

@ -0,0 +1,56 @@
<?php
namespace Drupal\Tests\user\Kernel;
use Drupal\KernelTests\ConfigFormTestBase;
use Drupal\user\AccountSettingsForm;
/**
* Configuration object user.mail and user.settings save test.
*
* @group user
*/
class UserAdminSettingsFormTest extends ConfigFormTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['user', 'system'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->form = AccountSettingsForm::create($this->container);
$this->values = [
'anonymous' => [
'#value' => $this->randomString(10),
'#config_name' => 'user.settings',
'#config_key' => 'anonymous',
],
'user_mail_cancel_confirm_body' => [
'#value' => $this->randomString(),
'#config_name' => 'user.mail',
'#config_key' => 'cancel_confirm.body',
],
'user_mail_cancel_confirm_subject' => [
'#value' => $this->randomString(20),
'#config_name' => 'user.mail',
'#config_key' => 'cancel_confirm.subject',
],
'register_pending_approval_admin_body' => [
'#value' => $this->randomString(),
'#config_name' => 'user.mail',
'#config_key' => 'register_pending_approval_admin.body',
],
'register_pending_approval_admin_subject' => [
'#value' => $this->randomString(20),
'#config_name' => 'user.mail',
'#config_key' => 'register_pending_approval_admin.subject',
],
];
}
}

View file

@ -2,9 +2,9 @@
namespace Drupal\Tests\user\Kernel;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
use Drupal\user\Entity\Role;
/**
@ -77,7 +77,6 @@ class UserEntityReferenceTest extends EntityKernelTestBase {
$user3->addRole($this->role2->id());
$user3->save();
/** @var \Drupal\Core\Entity\EntityAutocompleteMatcher $autocomplete */
$autocomplete = \Drupal::service('entity.autocomplete_matcher');

View file

@ -21,6 +21,14 @@ class UserEntityTest extends KernelTestBase {
*/
public static $modules = ['system', 'user', 'field'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
}
/**
* Tests some of the methods.
*
@ -65,4 +73,22 @@ class UserEntityTest extends KernelTestBase {
$this->assertEqual([RoleInterface::AUTHENTICATED_ID, 'test_role_two'], $user->getRoles());
}
/**
* Tests that all user fields validate properly.
*
* @see \Drupal\Core\Field\FieldItemListInterface::generateSampleItems
* @see \Drupal\Core\Field\FieldItemInterface::generateSampleValue()
* @see \Drupal\Core\Entity\FieldableEntityInterface::validate()
*/
public function testUserValidation() {
$user = User::create([]);
foreach ($user as $field_name => $field) {
if (!in_array($field_name, ['uid'])) {
$user->$field_name->generateSampleItems();
}
}
$violations = $user->validate();
$this->assertFalse((bool) $violations->count());
}
}

View file

@ -28,7 +28,6 @@ class UserInstallTest extends KernelTestBase {
user_install();
}
/**
* Test that the initial users have correct values.
*/

View file

@ -0,0 +1,38 @@
<?php
namespace Drupal\Tests\user\Kernel;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests \Drupal\user\UserServiceProvider.
*
* @group user
* @group legacy
*/
class UserServiceProviderFallbackTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user'];
/**
* Tests that user.tempstore.expire equals tempstore.expire if not customized.
*/
public function testUserServiceProvider() {
$this->assertEquals(1000, $this->container->getParameter('user.tempstore.expire'));
}
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
$container->setParameter('tempstore.expire', 1000);
parent::register($container);
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\user\Kernel;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests \Drupal\user\UserServiceProvider.
*
* @group user
* @group legacy
*/
class UserServiceProviderTest extends KernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['user'];
/**
* Tests that tempstore.expire is set to user.tempstore.expire.
*
* @expectedDeprecation The container parameter "user.tempstore.expire" is deprecated. Use "tempstore.expire" instead. See https://www.drupal.org/node/2935639.
*/
public function testUserServiceProvider() {
$this->assertEquals(1000, $this->container->getParameter('tempstore.expire'));
}
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
$container->setParameter('user.tempstore.expire', 1000);
parent::register($container);
}
}

View file

@ -40,23 +40,29 @@ class UserValidationTest extends KernelTestBase {
* Tests user name validation.
*/
public function testUsernames() {
$test_cases = [ // '<username>' => array('<description>', 'assert<testName>'),
$test_cases = [
// '<username>' => ['<description>', 'assert<testName>'].
'foo' => ['Valid username', 'assertNull'],
'FOO' => ['Valid username', 'assertNull'],
'Foo O\'Bar' => ['Valid username', 'assertNull'],
'foo@bar' => ['Valid username', 'assertNull'],
'foo@example.com' => ['Valid username', 'assertNull'],
'foo@-example.com' => ['Valid username', 'assertNull'], // invalid domains are allowed in usernames
// invalid domains are allowed in usernames.
'foo@-example.com' => ['Valid username', 'assertNull'],
'þòøÇߪř€' => ['Valid username', 'assertNull'],
'foo+bar' => ['Valid username', 'assertNull'], // '+' symbol is allowed
'ᚠᛇᚻ᛫ᛒᛦᚦ' => ['Valid UTF8 username', 'assertNull'], // runes
// '+' symbol is allowed.
'foo+bar' => ['Valid username', 'assertNull'],
// runes.
'ᚠᛇᚻ᛫ᛒᛦᚦ' => ['Valid UTF8 username', 'assertNull'],
' foo' => ['Invalid username that starts with a space', 'assertNotNull'],
'foo ' => ['Invalid username that ends with a space', 'assertNotNull'],
'foo bar' => ['Invalid username that contains 2 spaces \'&nbsp;&nbsp;\'', 'assertNotNull'],
'' => ['Invalid empty username', 'assertNotNull'],
'foo/' => ['Invalid username containing invalid chars', 'assertNotNull'],
'foo' . chr(0) . 'bar' => ['Invalid username containing chr(0)', 'assertNotNull'], // NULL
'foo' . chr(13) . 'bar' => ['Invalid username containing chr(13)', 'assertNotNull'], // CR
// NULL.
'foo' . chr(0) . 'bar' => ['Invalid username containing chr(0)', 'assertNotNull'],
// CR.
'foo' . chr(13) . 'bar' => ['Invalid username containing chr(13)', 'assertNotNull'],
str_repeat('x', USERNAME_MAX_LENGTH + 1) => ['Invalid excessively long username', 'assertNotNull'],
];
foreach ($test_cases as $name => $test_case) {
@ -209,7 +215,7 @@ class UserValidationTest extends KernelTestBase {
protected function assertAllowedValuesViolation(EntityInterface $entity, $field_name) {
$violations = $entity->validate();
$this->assertEqual(count($violations), 1, "Allowed values violation for $field_name found.");
$this->assertEqual($violations[0]->getPropertyPath(), "$field_name.0.value");
$this->assertEqual($violations[0]->getPropertyPath(), $field_name === 'langcode' ? "$field_name.0" : "$field_name.0.value");
$this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.'));
}

View file

@ -0,0 +1,118 @@
<?php
namespace Drupal\Tests\user\Kernel;
use Drupal\block\Entity\Block;
use Drupal\KernelTests\KernelTestBase;
use Drupal\user\Entity\User;
/**
* Tests the Who's Online Block.
*
* @group user
*/
class WhosOnlineBlockTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['system', 'user', 'block', 'views'];
/**
* The block being tested.
*
* @var \Drupal\block\Entity\BlockInterface
*/
protected $block;
/**
* The block storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
*/
protected $controller;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installConfig(['system', 'block', 'views', 'user']);
$this->installSchema('system', ['sequences']);
$this->installEntitySchema('user');
$this->controller = $this->container
->get('entity_type.manager')
->getStorage('block');
// Create a block with only required values.
$this->block = $this->controller->create([
'plugin' => 'views_block:who_s_online-who_s_online_block',
'region' => 'sidebar_first',
'id' => 'views_block__who_s_online_who_s_online_block',
'theme' => \Drupal::configFactory()->get('system.theme')->get('default'),
'label' => "Who's online",
'visibility' => [],
'weight' => 0,
]);
$this->block->save();
$this->container->get('cache.render')->deleteAll();
$this->renderer = $this->container->get('renderer');
}
/**
* Test the Who's Online block.
*/
public function testWhosOnlineBlock() {
$request_time = \Drupal::time()->getRequestTime();
// Generate users.
$user1 = User::create([
'name' => 'user1',
'mail' => 'user1@example.com',
]);
$user1->addRole('administrator');
$user1->activate();
$user1->setLastAccessTime($request_time);
$user1->save();
$user2 = User::create([
'name' => 'user2',
'mail' => 'user2@example.com',
]);
$user2->activate();
$user2->setLastAccessTime($request_time + 1);
$user2->save();
$user3 = User::create([
'name' => 'user3',
'mail' => 'user2@example.com',
]);
$user3->activate();
// Insert an inactive user who should not be seen in the block.
$inactive_time = $request_time - (60 * 60);
$user3->setLastAccessTime($inactive_time);
$user3->save();
// Test block output.
\Drupal::currentUser()->setAccount($user1);
// Test the rendering of a block.
$entity = Block::load('views_block__who_s_online_who_s_online_block');
$output = entity_view($entity, 'block');
$this->setRawContent($this->renderer->renderRoot($output));
$this->assertRaw('2 users', 'Correct number of online users (2 users).');
$this->assertText($user1->getUsername(), 'Active user 1 found in online list.');
$this->assertText($user2->getUsername(), 'Active user 2 found in online list.');
$this->assertNoText($user3->getUsername(), 'Inactive user not found in online list.');
$this->assertTrue(strpos($this->getRawContent(), $user1->getUsername()) > strpos($this->getRawContent(), $user2->getUsername()), 'Online users are ordered correctly.');
}
}

View file

@ -0,0 +1,213 @@
<?php
namespace Drupal\Tests\user\Traits;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
/**
* Provides methods to create additional test users and switch the currently
* logged in one.
*
* This trait is meant to be used only by test classes.
*/
trait UserCreationTrait {
/**
* Switch the current logged in user.
*
* @param \Drupal\Core\Session\AccountInterface $account
* The user account object.
*/
protected function setCurrentUser(AccountInterface $account) {
\Drupal::currentUser()->setAccount($account);
}
/**
* Create a user with a given set of permissions.
*
* @param array $permissions
* Array of permission names to assign to user. Note that the user always
* has the default permissions derived from the "authenticated users" role.
* @param string $name
* The user name.
* @param bool $admin
* (optional) Whether the user should be an administrator
* with all the available permissions.
*
* @return \Drupal\user\Entity\User|false
* A fully loaded user object with pass_raw property, or FALSE if account
* creation fails.
*/
protected function createUser(array $permissions = [], $name = NULL, $admin = FALSE) {
// Create a role with the given permission set, if any.
$rid = FALSE;
if ($permissions) {
$rid = $this->createRole($permissions);
if (!$rid) {
return FALSE;
}
}
// Create a user assigned to that role.
$edit = [];
$edit['name'] = !empty($name) ? $name : $this->randomMachineName();
$edit['mail'] = $edit['name'] . '@example.com';
$edit['pass'] = user_password();
$edit['status'] = 1;
if ($rid) {
$edit['roles'] = [$rid];
}
if ($admin) {
$edit['roles'][] = $this->createAdminRole();
}
$account = User::create($edit);
$account->save();
$this->assertTrue($account->id(), new FormattableMarkup('User created with name %name and pass %pass', ['%name' => $edit['name'], '%pass' => $edit['pass']]), 'User login');
if (!$account->id()) {
return FALSE;
}
// Add the raw password so that we can log in as this user.
$account->pass_raw = $edit['pass'];
// Support BrowserTestBase as well.
$account->passRaw = $account->pass_raw;
return $account;
}
/**
* Creates an administrative role.
*
* @param string $rid
* (optional) The role ID (machine name). Defaults to a random name.
* @param string $name
* (optional) The label for the role. Defaults to a random string.
* @param int $weight
* (optional) The weight for the role. Defaults NULL so that entity_create()
* sets the weight to maximum + 1.
*
* @return string
* Role ID of newly created role, or FALSE if role creation failed.
*/
protected function createAdminRole($rid = NULL, $name = NULL, $weight = NULL) {
$rid = $this->createRole([], $rid, $name, $weight);
if ($rid) {
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load($rid);
$role->setIsAdmin(TRUE);
$role->save();
}
return $rid;
}
/**
* Creates a role with specified permissions.
*
* @param array $permissions
* Array of permission names to assign to role.
* @param string $rid
* (optional) The role ID (machine name). Defaults to a random name.
* @param string $name
* (optional) The label for the role. Defaults to a random string.
* @param int $weight
* (optional) The weight for the role. Defaults NULL so that entity_create()
* sets the weight to maximum + 1.
*
* @return string
* Role ID of newly created role, or FALSE if role creation failed.
*/
protected function createRole(array $permissions, $rid = NULL, $name = NULL, $weight = NULL) {
// Generate a random, lowercase machine name if none was passed.
if (!isset($rid)) {
$rid = strtolower($this->randomMachineName(8));
}
// Generate a random label.
if (!isset($name)) {
// In the role UI role names are trimmed and random string can start or
// end with a space.
$name = trim($this->randomString(8));
}
// Check the all the permissions strings are valid.
if (!$this->checkPermissions($permissions)) {
return FALSE;
}
// Create new role.
$role = Role::create([
'id' => $rid,
'label' => $name,
]);
if (isset($weight)) {
$role->set('weight', $weight);
}
$result = $role->save();
$this->assertIdentical($result, SAVED_NEW, new FormattableMarkup('Created role ID @rid with name @name.', [
'@name' => var_export($role->label(), TRUE),
'@rid' => var_export($role->id(), TRUE),
]), 'Role');
if ($result === SAVED_NEW) {
// Grant the specified permissions to the role, if any.
if (!empty($permissions)) {
$this->grantPermissions($role, $permissions);
$assigned_permissions = Role::load($role->id())->getPermissions();
$missing_permissions = array_diff($permissions, $assigned_permissions);
if (!$missing_permissions) {
$this->pass(new FormattableMarkup('Created permissions: @perms', ['@perms' => implode(', ', $permissions)]), 'Role');
}
else {
$this->fail(new FormattableMarkup('Failed to create permissions: @perms', ['@perms' => implode(', ', $missing_permissions)]), 'Role');
}
}
return $role->id();
}
else {
return FALSE;
}
}
/**
* Checks whether a given list of permission names is valid.
*
* @param array $permissions
* The permission names to check.
*
* @return bool
* TRUE if the permissions are valid, FALSE otherwise.
*/
protected function checkPermissions(array $permissions) {
$available = array_keys(\Drupal::service('user.permissions')->getPermissions());
$valid = TRUE;
foreach ($permissions as $permission) {
if (!in_array($permission, $available)) {
$this->fail(new FormattableMarkup('Invalid permission %permission.', ['%permission' => $permission]), 'Role');
$valid = FALSE;
}
}
return $valid;
}
/**
* Grant permissions to a user role.
*
* @param \Drupal\user\RoleInterface $role
* The ID of a user role to alter.
* @param array $permissions
* (optional) A list of permission names to grant.
*/
protected function grantPermissions(RoleInterface $role, array $permissions) {
foreach ($permissions as $permission) {
$role->grantPermission($permission);
}
$role->trustData()->save();
}
}

View file

@ -69,7 +69,9 @@ class UserLocalTasksTest extends LocalTaskIntegrationTestBase {
$tasks = [
0 => ['entity.user.canonical', 'entity.user.edit_form'],
];
if ($subtask) $tasks[] = $subtask;
if ($subtask) {
$tasks[] = $subtask;
}
$this->assertLocalTasks($route, $tasks);
}

View file

@ -79,7 +79,7 @@ class PermissionAccessCheckTest extends UnitTestCase {
->will($this->returnValueMap([
['allowed', TRUE],
['denied', FALSE],
['other', FALSE]
['other', FALSE],
]
));
$route = new Route('', [], $requirements);

View file

@ -400,7 +400,7 @@ class TestPermissionCallbacks {
public function singleDescription() {
return [
'access_module_a' => 'single_description'
'access_module_a' => 'single_description',
];
}

View file

@ -23,9 +23,9 @@ class AddRoleUserTest extends RoleUserTestBase {
->will($this->returnValue(TRUE));
$config = ['rid' => 'test_role_1'];
$remove_role_plugin = new AddRoleUser($config, 'user_add_role_action', ['type' => 'user'], $this->userRoleEntityType);
$add_role_plugin = new AddRoleUser($config, 'user_add_role_action', ['type' => 'user'], $this->userRoleEntityType);
$remove_role_plugin->execute($this->account);
$add_role_plugin->execute($this->account);
}
/**
@ -41,9 +41,9 @@ class AddRoleUserTest extends RoleUserTestBase {
->will($this->returnValue(FALSE));
$config = ['rid' => 'test_role_1'];
$remove_role_plugin = new AddRoleUser($config, 'user_remove_role_action', ['type' => 'user'], $this->userRoleEntityType);
$add_role_plugin = new AddRoleUser($config, 'user_add_role_action', ['type' => 'user'], $this->userRoleEntityType);
$remove_role_plugin->execute($this->account);
$add_role_plugin->execute($this->account);
}
}

View file

@ -54,6 +54,8 @@ class UserBulkFormTest extends UnitTestCase {
$language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
$messenger = $this->getMock('Drupal\Core\Messenger\MessengerInterface');
$views_data = $this->getMockBuilder('Drupal\views\ViewsData')
->disableOriginalConstructor()
->getMock();
@ -84,7 +86,7 @@ class UserBulkFormTest extends UnitTestCase {
$definition['title'] = '';
$options = [];
$user_bulk_form = new UserBulkForm([], 'user_bulk_form', $definition, $entity_manager, $language_manager);
$user_bulk_form = new UserBulkForm([], 'user_bulk_form', $definition, $entity_manager, $language_manager, $messenger);
$user_bulk_form->init($executable, $display, $options);
$this->assertAttributeEquals(array_slice($actions, 0, -1, TRUE), 'actions', $user_bulk_form);

View file

@ -11,6 +11,9 @@ use Symfony\Component\HttpFoundation\RequestStack;
/**
* @coversDefaultClass \Drupal\user\PrivateTempStore
* @group user
* @group legacy
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
*/
class PrivateTempStoreTest extends UnitTestCase {
@ -93,11 +96,11 @@ class PrivateTempStoreTest extends UnitTestCase {
$this->otherObject->owner = 2;
}
/**
* Tests the get() method.
*
* @covers ::get
* @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testGet() {
$this->keyValue->expects($this->at(0))
@ -122,6 +125,7 @@ class PrivateTempStoreTest extends UnitTestCase {
* Tests the set() method with no lock available.
*
* @covers ::set
* @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testSetWithNoLockAvailable() {
$this->lock->expects($this->at(0))
@ -147,6 +151,7 @@ class PrivateTempStoreTest extends UnitTestCase {
* Tests a successful set() call.
*
* @covers ::set
* @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testSet() {
$this->lock->expects($this->once())
@ -170,6 +175,7 @@ class PrivateTempStoreTest extends UnitTestCase {
* Tests the getMetadata() method.
*
* @covers ::getMetadata
* @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testGetMetadata() {
$this->keyValue->expects($this->at(0))
@ -194,6 +200,7 @@ class PrivateTempStoreTest extends UnitTestCase {
* Tests the locking in the delete() method.
*
* @covers ::delete
* @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testDeleteLocking() {
$this->keyValue->expects($this->once())
@ -221,6 +228,7 @@ class PrivateTempStoreTest extends UnitTestCase {
* Tests the delete() method with no lock available.
*
* @covers ::delete
* @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testDeleteWithNoLockAvailable() {
$this->keyValue->expects($this->once())
@ -250,6 +258,7 @@ class PrivateTempStoreTest extends UnitTestCase {
* Tests the delete() method.
*
* @covers ::delete
* @expectedDeprecation \Drupal\user\PrivateTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\PrivateTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testDelete() {
$this->lock->expects($this->once())

View file

@ -11,6 +11,9 @@ use Symfony\Component\HttpFoundation\RequestStack;
/**
* @coversDefaultClass \Drupal\user\SharedTempStore
* @group user
* @group legacy
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
*/
class SharedTempStoreTest extends UnitTestCase {
@ -90,6 +93,7 @@ class SharedTempStoreTest extends UnitTestCase {
/**
* @covers ::get
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testGet() {
$this->keyValue->expects($this->at(0))
@ -109,6 +113,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the getIfOwner() method.
*
* @covers ::getIfOwner
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testGetIfOwner() {
$this->keyValue->expects($this->at(0))
@ -133,6 +138,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the set() method with no lock available.
*
* @covers ::set
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testSetWithNoLockAvailable() {
$this->lock->expects($this->at(0))
@ -158,6 +164,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests a successful set() call.
*
* @covers ::set
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testSet() {
$this->lock->expects($this->once())
@ -181,6 +188,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the setIfNotExists() methods.
*
* @covers ::setIfNotExists
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testSetIfNotExists() {
$this->keyValue->expects($this->once())
@ -195,6 +203,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the setIfOwner() method when no key exists.
*
* @covers ::setIfOwner
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testSetIfOwnerWhenNotExists() {
$this->keyValue->expects($this->once())
@ -208,6 +217,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the setIfOwner() method when a key already exists but no object.
*
* @covers ::setIfOwner
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testSetIfOwnerNoObject() {
$this->keyValue->expects($this->once())
@ -226,6 +236,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the setIfOwner() method with matching and non matching owners.
*
* @covers ::setIfOwner
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testSetIfOwner() {
$this->lock->expects($this->once())
@ -250,6 +261,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the getMetadata() method.
*
* @covers ::getMetadata
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testGetMetadata() {
$this->keyValue->expects($this->at(0))
@ -274,6 +286,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the delete() method.
*
* @covers ::delete
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testDelete() {
$this->lock->expects($this->once())
@ -297,6 +310,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the delete() method with no lock available.
*
* @covers ::delete
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testDeleteWithNoLockAvailable() {
$this->lock->expects($this->at(0))
@ -322,6 +336,7 @@ class SharedTempStoreTest extends UnitTestCase {
* Tests the deleteIfOwner() method.
*
* @covers ::deleteIfOwner
* @expectedDeprecation \Drupal\user\SharedTempStore is scheduled for removal in Drupal 9.0.0. Use \Drupal\Core\TempStore\SharedTempStore instead. See https://www.drupal.org/node/2935639.
*/
public function testDeleteIfOwner() {
$this->lock->expects($this->once())

View file

@ -3,6 +3,8 @@
namespace Drupal\Tests\user\Unit\Views\Argument;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityManager;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\user\Entity\Role;
use Drupal\user\Plugin\views\argument\RolesRid;
@ -17,11 +19,18 @@ class RolesRidTest extends UnitTestCase {
* Tests the titleQuery method.
*
* @covers ::titleQuery
*
* @group legacy
*
* Note this is only a legacy test because it triggers a call to
* \Drupal\Core\Entity\EntityTypeInterface::getLabelCallback() which is mocked
* and triggers a deprecation error. Remove when ::getLabelCallback() is
* removed.
*/
public function testTitleQuery() {
$role1 = new Role([
'id' => 'test_rid_1',
'label' => 'test rid 1'
'label' => 'test rid 1',
], 'user_role');
$role2 = new Role([
'id' => 'test_rid_2',
@ -44,23 +53,27 @@ class RolesRidTest extends UnitTestCase {
->with('label')
->will($this->returnValue('label'));
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
$entity_manager->expects($this->any())
$entity_manager = new EntityManager();
$entity_type_manager = $this->getMock(EntityTypeManagerInterface::class);
$entity_type_manager->expects($this->any())
->method('getDefinition')
->with($this->equalTo('user_role'))
->will($this->returnValue($entity_type));
$entity_manager
$entity_type_manager
->expects($this->once())
->method('getStorage')
->with($this->equalTo('user_role'))
->will($this->returnValue($role_storage));
// @todo \Drupal\Core\Entity\Entity::entityType() uses a global call to
// entity_get_info(), which in turn wraps \Drupal::entityManager(). Set
// the entity manager until this is fixed.
// Set up a minimal container to satisfy Drupal\Core\Entity\Entity's
// dependency on it.
$container = new ContainerBuilder();
$container->set('entity.manager', $entity_manager);
$container->set('entity_type.manager', $entity_type_manager);
// Inject the container into entity.manager so it can defer to
// entity_type.manager.
$entity_manager->setContainer($container);
\Drupal::setContainer($container);
$roles_rid_argument = new RolesRid([], 'user__roles_rid', [], $entity_manager);