entity_legal-4.0.x-dev/tests/src/Functional/RedirectMethodTest.php
tests/src/Functional/RedirectMethodTest.php
<?php
declare(strict_types=1);
namespace Drupal\Tests\entity_legal\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\Core\Url;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\entity_legal\Traits\EntityLegalTestTrait;
use Drupal\user\Entity\User;
/**
* Tests the 'redirect' plugin.
*
* @group entity_legal
*/
class RedirectMethodTest extends BrowserTestBase {
use AssertMailTrait;
use EntityLegalTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'test_page_test',
'block',
'dynamic_page_cache',
'entity_legal_test',
];
/**
* Redirection method test.
*/
public function testRedirectMethod() {
$this->drupalPlaceBlock('system_menu_block:account');
$document = $this->createDocument(TRUE, TRUE, [
'existing_users' => [
'require_method' => 'redirect',
],
]);
$this->createDocumentVersion($document, TRUE);
$account = $this->createUserWithAcceptancePermissions($document);
$this->drupalLogin($account);
$document_path = $document->toUrl()->setAbsolute()->toString();
/** @var \Drupal\user\UserInterface $user */
$user = \Drupal::entityTypeManager()->getStorage('user')->load($account->id());
$this->assertSession()->addressEquals($document_path);
$this->drupalGet('');
$this->assertSession()->addressEquals($document_path);
// Check that users are able to logout even they don't accept the agreement.
$this->clickLink('Log out');
// Check that the user has been logged out.
$this->assertSession()->linkExists('Log in');
$this->submitForm([
'name' => $account->getAccountName(),
'pass' => $account->pass_raw,
], 'Log in');
$this->assertSession()->pageTextContains('You must accept this agreement before continuing.');
$this->assertSession()->pageTextContains('I agree to the document');
// Agree with the terms.
$this->submitForm(['agree' => TRUE], 'Submit');
// Refresh the page.
$this->drupalGet('');
$this->assertSession()->addressEquals($user->toUrl()->setAbsolute()->toString());
$this->clickLink('Log out');
// Release a new document version.
$this->createDocumentVersion($document, TRUE);
$this->drupalGet(Url::fromRoute('user.pass'));
$this->submitForm(['name' => $account->getAccountName()], 'Submit');
$this->assertSession()->pageTextContains("If {$account->getAccountName()} is a valid account, an email will be sent with instructions to reset your password.");
// Click the one-time login link received by mail and login.
$this->clickMailOneTimeLoginLink();
$this->assertSession()->pageTextContains("This is a one-time login for {$account->getAccountName()} and will expire on");
$this->submitForm([], 'Log in');
// The user has to accept the new document version.
$this->assertSession()->pageTextContains('You must accept this agreement before continuing.');
$this->assertSession()->pageTextContains('I agree to the document');
// Agree with the terms.
$this->submitForm(['agree' => TRUE], 'Submit');
// Check that we're on the user account edit form page.
$this->assertSession()->pageTextContains('You have used a one-time login link. You can set your new password now.');
$this->assertSession()->fieldExists('mail');
$this->assertSession()->fieldExists('pass[pass1]');
$this->assertSession()->fieldExists('pass[pass2]');
// Set a new password.
$new_password = $this->randomString();
$account->pass_raw = $new_password;
$this->submitForm([
'pass[pass1]' => $new_password,
'pass[pass2]' => $new_password,
], 'Save');
$this->assertSession()->pageTextContains('The changes have been saved.');
// Release a new document version.
$this->createDocumentVersion($document, TRUE);
// Try to bypass by faking a user reset URL.
$this->drupalGet("/user/{$account->id()}/edit", [
'query' => [
'pass-reset-token' => 'arbitrary-invalid-token',
],
]);
// Check that the approval gate cannot be bypassed.
$this->assertSession()->pageTextContains('You must accept this agreement before continuing.');
$this->assertSession()->pageTextContains('I agree to the document');
}
/**
* Tests redirect exclusion routes/conditions.
*
* @covers \Drupal\entity_legal\Plugin\EntityLegal\Redirect::isExcludedRoute
* @covers \Drupal\entity_legal\Plugin\EntityLegal\Redirect::execute
*/
public function testRedirectExclusions() {
// Prepare document.
$document = $this->createDocument(TRUE, TRUE, [
'existing_users' => [
'require_method' => 'redirect',
],
]);
$this->createDocumentVersion($document, TRUE);
$account = $this->createUserWithAcceptancePermissions($document);
$this->drupalLogin($account);
$document_path = $document->toUrl()->setAbsolute()->toString();
$this->assertSession()->addressEquals($document_path);
// Prepare CSS/JS files.
\Drupal::service('file_system')->mkdir("$this->publicFilesDirectory/css");
file_put_contents("$this->publicFilesDirectory/css/foo.css", $this->randomMachineName());
\Drupal::service('file_system')->mkdir("$this->publicFilesDirectory/js");
file_put_contents("$this->publicFilesDirectory/js/bar.js", $this->randomMachineName());
// Test excluded routes.
$urls = [
Url::fromRoute('system.csrftoken'),
Url::fromRoute('user.logout.confirm'),
// Aggregated assets are usually created when accessing them for the first
// time. Prefix with index.php to prevent apache from serving the files we
// created directly.
Url::fromRoute('system.css_asset', ['file_name' => 'foo.css'], ['prefix' => 'index.php/']),
Url::fromRoute('system.js_asset', ['file_name' => 'bar.js'], ['prefix' => 'index.php/']),
];
foreach ($urls as $url) {
$this->drupalGet($url);
$this->assertSession()->addressEquals($url);
}
// Non-HTML requests.
\Drupal::service('module_installer')->install(['jsonapi']);
$http_client = \Drupal::httpClient();
$url = Url::fromRoute("jsonapi.entity_legal_document_version--{$document->id()}.collection")
->setAbsolute()
->toString();
$response = $http_client->get($url, [
'headers' => [
'Accept' => 'application/vnd.api+json',
],
]);
$jsonapi = Json::decode($response->getBody()->getContents());
$this->assertTrue(isset($jsonapi['jsonapi']['version']) && isset($jsonapi['data']));
}
/**
* Tests the case when the origin pages has set messages.
*
* An origin page might have set status, warning or error messages to be
* displayed on the destination page. Test that such messages are not
* shown on the entity legal acceptance page.
*/
public function testMessageDisplay(): void {
$document = $this->createDocument(TRUE, TRUE, [
'existing_users' => [
'require_method' => 'redirect',
],
]);
$this->createDocumentVersion($document, TRUE);
$this->drupalLogin($this->createUserWithAcceptancePermissions($document));
// Check that the acceptance form doesn't contain the messages.
$this->assertSession()->pageTextContains('You must accept this agreement before continuing.');
$this->assertSession()->pageTextNotContains('A status message sample');
$this->assertSession()->pageTextNotContains('A warning message sample');
$this->assertSession()->pageTextNotContains('An error message sample');
$this->submitForm(['agree' => TRUE], 'Submit');
// Check that messages are shown on the destination page.
$account = User::load(\Drupal::currentUser()->id());
$this->assertSession()->addressEquals($account->toUrl());
$this->assertSession()->pageTextContains('A status message sample');
$this->assertSession()->pageTextContains('A warning message sample');
$this->assertSession()->pageTextContains('An error message sample');
}
/**
* Checks that the redirection works on cached pages.
*/
public function testRedirectOnCachedPage(): void {
$this->config('system.site')->set('page.front', '/test-page')->save();
$document = $this->createDocument(TRUE, TRUE, [
'existing_users' => [
'require_method' => 'redirect',
],
]);
$this->createDocumentVersion($document, TRUE);
$this->drupalLogin($this->createUserWithAcceptancePermissions($document));
$this->submitForm(['agree' => TRUE], 'Submit');
$this->drupalGet('<front>');
// Visit the front page again and ensure it is cached.
$this->drupalGet('<front>');
$this->assertSame('HIT', $this->getSession()->getResponseHeader('X-Drupal-Dynamic-Cache'));
// Release a new document version.
$this->createDocumentVersion($document, TRUE);
// Reload the front page and check that the user is correctly redirected.
$this->drupalGet('<front>');
$this->assertSession()->pageTextContains('You must accept this agreement before continuing.');
$this->assertSession()->pageTextContains('I agree to the document');
}
/**
* Clicks on the email password reset one-time-login link.
*/
protected function clickMailOneTimeLoginLink(): void {
// Assume the most recent email.
$emails = $this->getMails();
$email = end($emails);
$urls = [];
preg_match('#.+user/reset/.+#', $email['body'], $urls);
$this->drupalGet($urls[0]);
}
/**
* Tests the bypass permission for the redirect method.
*/
public function testBypassAcceptance(): void {
$document = $this->createDocument(TRUE, TRUE, [
'existing_users' => [
'require_method' => 'redirect',
],
]);
$this->createDocumentVersion($document, TRUE);
$account = $this->createUserWithAcceptanceBypassPermissions($document);
$this->drupalLogin($account);
$this->assertSession()->addressEquals($account->toUrl());
}
}
