diff --git a/core/tests/Drupal/FunctionalTests/WebAssertTest.php b/core/tests/Drupal/Tests/Core/Test/WebAssertTest.php similarity index 68% rename from core/tests/Drupal/FunctionalTests/WebAssertTest.php rename to core/tests/Drupal/Tests/Core/Test/WebAssertTest.php index ae3c0141155..2ca1e40abb0 100644 --- a/core/tests/Drupal/FunctionalTests/WebAssertTest.php +++ b/core/tests/Drupal/Tests/Core/Test/WebAssertTest.php @@ -2,35 +2,70 @@ declare(strict_types=1); -namespace Drupal\FunctionalTests; +namespace Drupal\Tests\Core\Test; +use Behat\Mink\Driver\BrowserKitDriver; use Behat\Mink\Exception\ExpectationException; +use Behat\Mink\Exception\ResponseTextException; +use Behat\Mink\Session; use Drupal\Component\Utility\Html; use Drupal\Core\Url; -use Drupal\Tests\BrowserTestBase; -use Behat\Mink\Exception\ResponseTextException; +use Drupal\Tests\UnitTestCase; +use Drupal\Tests\WebAssert; use PHPUnit\Framework\AssertionFailedError; +use Symfony\Component\BrowserKit\AbstractBrowser; +use Symfony\Component\BrowserKit\Response; /** * Tests WebAssert functionality. * * @group browsertestbase - * @group #slow * @coversDefaultClass \Drupal\Tests\WebAssert */ -class WebAssertTest extends BrowserTestBase { +class WebAssertTest extends UnitTestCase { + + /** + * Session mock. + */ + protected Session $session; + + /** + * Client mock. + */ + protected AbstractBrowser $client; /** * {@inheritdoc} */ - protected static $modules = [ - 'test_page_test', - ]; + public function setUp(): void { + parent::setUp(); + $this->client = new MockClient(); + $driver = new BrowserKitDriver($this->client); + $this->session = new Session($driver); + } /** - * {@inheritdoc} + * Get the mocked session. */ - protected $defaultTheme = 'stark'; + protected function assertSession(): WebAssert { + return new WebAssert($this->session); + } + + /** + * Simulate a page visit and expect a response. + * + * @param string $uri + * The URI to visit. This is only required if assertions are made about the + * URL, otherwise it can be left empty. + * @param string $content + * The expected response content. + * @param array $responseHeaders + * The expected response headers. + */ + protected function visit(string $uri = '', string $content = '', array $responseHeaders = []): void { + $this->client->setExpectedResponse(new Response($content, 200, $responseHeaders)); + $this->session->visit($uri); + } /** * Tests WebAssert::responseHeaderExists(). @@ -38,9 +73,8 @@ class WebAssertTest extends BrowserTestBase { * @covers ::responseHeaderExists */ public function testResponseHeaderExists(): void { - $this->drupalGet('test-null-header'); + $this->visit('', '', ['Null-Header' => '']); $this->assertSession()->responseHeaderExists('Null-Header'); - $this->expectException(AssertionFailedError::class); $this->expectExceptionMessage("Failed asserting that the response has a 'does-not-exist' header."); $this->assertSession()->responseHeaderExists('does-not-exist'); @@ -52,7 +86,7 @@ class WebAssertTest extends BrowserTestBase { * @covers ::responseHeaderDoesNotExist */ public function testResponseHeaderDoesNotExist(): void { - $this->drupalGet('test-null-header'); + $this->visit('', '', ['Null-Header' => '']); $this->assertSession()->responseHeaderDoesNotExist('does-not-exist'); $this->expectException(AssertionFailedError::class); @@ -64,10 +98,7 @@ class WebAssertTest extends BrowserTestBase { * @covers ::pageTextMatchesCount */ public function testPageTextMatchesCount(): void { - $this->drupalLogin($this->drupalCreateUser()); - - // Visit a Drupal page that requires login. - $this->drupalGet('test-page'); + $this->visit('', 'Test page text. Foo'); $this->assertSession()->pageTextMatchesCount(1, '/Test page text\./'); $this->expectException(AssertionFailedError::class); @@ -79,10 +110,7 @@ class WebAssertTest extends BrowserTestBase { * @covers ::pageTextContainsOnce */ public function testPageTextContainsOnce(): void { - $this->drupalLogin($this->drupalCreateUser()); - - // Visit a Drupal page that requires login. - $this->drupalGet('test-page'); + $this->visit('', 'Test page text. Foo'); $this->assertSession()->pageTextContainsOnce('Test page text.'); $this->expectException(ResponseTextException::class); @@ -94,7 +122,7 @@ class WebAssertTest extends BrowserTestBase { * @covers ::elementTextEquals */ public function testElementTextEquals(): void { - $this->drupalGet('test-page'); + $this->visit('', '

Test page

'); $this->assertSession()->elementTextEquals('xpath', '//h1', 'Test page'); $this->expectException(AssertionFailedError::class); @@ -106,16 +134,23 @@ class WebAssertTest extends BrowserTestBase { * @covers ::addressEquals */ public function testAddressEquals(): void { - $this->drupalGet('test-page'); + $this->visit('http://localhost/test-page'); $this->assertSession()->addressEquals('test-page'); $this->assertSession()->addressEquals('test-page?'); $this->assertSession()->addressNotEquals('test-page?a=b'); $this->assertSession()->addressNotEquals('other-page'); - $this->drupalGet('test-page', ['query' => ['a' => 'b', 'c' => 'd']]); + $this->visit('http://localhost/test-page?a=b&c=d'); $this->assertSession()->addressEquals('test-page'); $this->assertSession()->addressEquals('test-page?a=b&c=d'); - $this->assertSession()->addressEquals(Url::fromRoute('test_page_test.test_page', [], ['query' => ['a' => 'b', 'c' => 'd']])); + $url = $this->createMock(Url::class); + $url->expects($this->any()) + ->method('setAbsolute') + ->willReturn($url); + $url->expects($this->any()) + ->method('toString') + ->willReturn('test-page?a=b&c=d'); + $this->assertSession()->addressEquals($url); $this->assertSession()->addressNotEquals('test-page?c=d&a=b'); $this->assertSession()->addressNotEquals('test-page?a=b'); $this->assertSession()->addressNotEquals('test-page?a=b&c=d&e=f'); @@ -131,7 +166,8 @@ class WebAssertTest extends BrowserTestBase { * @covers ::addressNotEquals */ public function testAddressNotEqualsException(): void { - $this->drupalGet('test-page', ['query' => ['a' => 'b', 'c' => 'd']]); + $this->visit('http://localhost/test-page?a=b&c=d'); + $this->expectException(ExpectationException::class); $this->expectExceptionMessage('Current page is "/test-page?a=b&c=d", but should not be.'); $this->assertSession()->addressNotEquals('test-page?a=b&c=d'); @@ -143,8 +179,9 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkExists */ public function testPipeCharInLocator(): void { - $this->drupalGet('test-pipe-char'); + $this->visit('', 'foo|bar|baz'); $this->assertSession()->linkExists('foo|bar|baz'); + $this->addToAssertionCount(1); } /** @@ -153,8 +190,9 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkExistsExact */ public function testLinkExistsExact(): void { - $this->drupalGet('test-pipe-char'); + $this->visit('', 'foo|bar|baz'); $this->assertSession()->linkExistsExact('foo|bar|baz'); + $this->addToAssertionCount(1); } /** @@ -163,7 +201,7 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkExistsExact */ public function testInvalidLinkExistsExact(): void { - $this->drupalGet('test-pipe-char'); + $this->visit('', 'foo|bar|baz'); $this->expectException(ExpectationException::class); $this->expectExceptionMessage('Link with label foo|bar not found'); $this->assertSession()->linkExistsExact('foo|bar'); @@ -175,8 +213,9 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkNotExistsExact */ public function testLinkNotExistsExact(): void { - $this->drupalGet('test-pipe-char'); + $this->visit('', 'foo|bar|baz'); $this->assertSession()->linkNotExistsExact('foo|bar'); + $this->addToAssertionCount(1); } /** @@ -185,10 +224,11 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkNotExistsExact */ public function testInvalidLinkNotExistsExact(): void { - $this->drupalGet('test-pipe-char'); + $this->visit('', 'foo|bar|baz'); $this->expectException(ExpectationException::class); $this->expectExceptionMessage('Link with label foo|bar|baz found'); $this->assertSession()->linkNotExistsExact('foo|bar|baz'); + $this->addToAssertionCount(1); } /** @@ -197,11 +237,12 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefExists */ public function testLinkByHrefExists(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); // Partial matching. $this->assertSession()->linkByHrefExists('/user'); // Full matching. $this->assertSession()->linkByHrefExists('/user/login'); + $this->addToAssertionCount(1); } /** @@ -210,9 +251,10 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefExists */ public function testInvalidLinkByHrefExists(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); $this->expectException(ExpectationException::class); $this->assertSession()->linkByHrefExists('/foo'); + $this->addToAssertionCount(1); } /** @@ -221,8 +263,9 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefNotExists */ public function testLinkByHrefNotExists(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); $this->assertSession()->linkByHrefNotExists('/foo'); + $this->addToAssertionCount(1); } /** @@ -231,9 +274,10 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefNotExists */ public function testInvalidLinkByHrefNotExistsPartial(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); $this->expectException(ExpectationException::class); $this->assertSession()->linkByHrefNotExists('/user'); + $this->addToAssertionCount(1); } /** @@ -242,7 +286,7 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefNotExists */ public function testInvalidLinkByHrefNotExistsFull(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); $this->expectException(ExpectationException::class); $this->assertSession()->linkByHrefNotExists('/user/login'); } @@ -253,8 +297,9 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefExistsExact */ public function testLinkByHrefExistsExact(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); $this->assertSession()->linkByHrefExistsExact('/user/login'); + $this->addToAssertionCount(1); } /** @@ -263,7 +308,7 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefExistsExact */ public function testInvalidLinkByHrefExistsExact(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); $this->expectException(ExpectationException::class); $this->assertSession()->linkByHrefExistsExact('/foo'); } @@ -274,8 +319,9 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefNotExistsExact */ public function testLinkByHrefNotExistsExact(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); $this->assertSession()->linkByHrefNotExistsExact('/foo'); + $this->addToAssertionCount(1); } /** @@ -284,7 +330,7 @@ class WebAssertTest extends BrowserTestBase { * @covers ::linkByHrefNotExistsExact */ public function testInvalidLinkByHrefNotExistsExact(): void { - $this->drupalGet('test-page'); + $this->visit('', 'Log inRegister'); $this->expectException(ExpectationException::class); $this->assertSession()->linkByHrefNotExistsExact('/user/login'); } @@ -296,11 +342,12 @@ class WebAssertTest extends BrowserTestBase { * @covers ::responseNotContains */ public function testTextAsserts(): void { - $this->drupalGet('test-encoded'); + $this->visit('', 'Bad html <script>alert(123);</script>'); $dangerous = 'Bad html '; $sanitized = Html::escape($dangerous); $this->assertSession()->responseNotContains($dangerous); $this->assertSession()->responseContains($sanitized); + $this->addToAssertionCount(2); } /** @@ -310,7 +357,11 @@ class WebAssertTest extends BrowserTestBase { * @covers ::buttonNotExists */ public function testFieldAssertsForButton(): void { - $this->drupalGet('test-field-xpath'); + $this->visit('', << + + +HTML); // Verify if the test passes with button ID. $this->assertSession()->buttonExists('edit-save'); @@ -339,6 +390,7 @@ class WebAssertTest extends BrowserTestBase { catch (ExpectationException) { // Expected exception; just continue testing. } + $this->addToAssertionCount(11); } /** @@ -347,11 +399,17 @@ class WebAssertTest extends BrowserTestBase { * @covers ::pageContainsNoDuplicateId */ public function testPageContainsNoDuplicateId(): void { + $this->visit('', <<Hello +

World

+HTML); $assert_session = $this->assertSession(); - $this->drupalGet(Url::fromRoute('test_page_test.page_without_duplicate_ids')); $assert_session->pageContainsNoDuplicateId(); - $this->drupalGet(Url::fromRoute('test_page_test.page_with_duplicate_ids')); + $this->visit('', <<Hello +

World

+HTML); $this->expectException(ExpectationException::class); $this->expectExceptionMessage('The page contains a duplicate HTML ID "page-element".'); $assert_session->pageContainsNoDuplicateId(); @@ -366,21 +424,40 @@ class WebAssertTest extends BrowserTestBase { public function testEscapingAssertions(): void { $assert = $this->assertSession(); - $this->drupalGet('test-escaped-characters'); + $this->visit('', '
Escaped: <"'&>
'); $assert->assertNoEscaped('
'); $assert->responseContains('
'); $assert->assertEscaped('Escaped: <"\'&>'); - $this->drupalGet('test-escaped-script'); + $this->visit('', '
<script>alert('XSS');alert("XSS");</script>
'); $assert->assertNoEscaped('
'); $assert->responseContains('
'); $assert->assertEscaped(""); - $this->drupalGet('test-unescaped-script'); + $this->visit('', <<
+HTML); + $this->session->visit(''); $assert->assertNoEscaped('
'); $assert->responseContains('
'); $assert->responseContains(""); $assert->assertNoEscaped(""); + $this->addToAssertionCount(10); + } + +} + +/** + * A mock client. + */ +class MockClient extends AbstractBrowser { + + public function setExpectedResponse(Response $response) { + $this->response = $response; + } + + protected function doRequest(object $request) { + return $this->response ?? new Response(); } }