BrowserTestBaseTest.php 36.8 KB
Newer Older
1 2
<?php

alexpott's avatar
alexpott committed
3
namespace Drupal\FunctionalTests;
4

5
use Behat\Mink\Exception\ElementNotFoundException;
6
use Behat\Mink\Exception\ExpectationException;
7
use Drupal\Component\Serialization\Json;
8
use Drupal\Component\Utility\Html;
9
use Drupal\Core\Url;
10
use Drupal\Tests\BrowserTestBase;
11
use Drupal\Tests\Traits\Core\CronRunTrait;
12
use PHPUnit\Framework\ExpectationFailedException;
13 14 15 16

/**
 * Tests BrowserTestBase functionality.
 *
alexpott's avatar
alexpott committed
17
 * @group browsertestbase
18 19 20
 */
class BrowserTestBaseTest extends BrowserTestBase {

21 22
  use CronRunTrait;

23 24 25 26 27
  /**
   * Modules to enable.
   *
   * @var array
   */
28 29 30 31 32 33
  protected static $modules = [
    'test_page_test',
    'form_test',
    'system_test',
    'node',
  ];
34

35 36 37 38 39
  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'classy';

40 41 42 43 44 45 46 47
  /**
   * Tests basic page test.
   */
  public function testGoTo() {
    $account = $this->drupalCreateUser();
    $this->drupalLogin($account);

    // Visit a Drupal page that requires login.
48
    $this->drupalGet('test-page');
49 50 51 52
    $this->assertSession()->statusCodeEquals(200);

    // Test page contains some text.
    $this->assertSession()->pageTextContains('Test page text.');
53

54 55
    // Check that returned plain text is correct.
    $text = $this->getTextContent();
56 57
    $this->assertStringContainsString('Test page text.', $text);
    $this->assertStringNotContainsString('</html>', $text);
58

59
    // Response includes cache tags that we can assert.
60
    $this->assertSession()->responseHeaderExists('X-Drupal-Cache-Tags');
61
    $this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Tags', 'http_response rendered');
62

63 64 65 66
    // Test that we can read the JS settings.
    $js_settings = $this->getDrupalSettings();
    $this->assertSame('azAZ09();.,\\\/-_{}', $js_settings['test-setting']);

67 68 69 70 71 72 73
    // Test drupalGet with a url object.
    $url = Url::fromRoute('test_page_test.render_title');
    $this->drupalGet($url);
    $this->assertSession()->statusCodeEquals(200);

    // Test page contains some text.
    $this->assertSession()->pageTextContains('Hello Drupal');
74 75

    // Test that setting headers with drupalGet() works.
76
    $this->drupalGet('system-test/header', [], [
77
      'Test-Header' => 'header value',
78
    ]);
79
    $this->assertSession()->responseHeaderExists('Test-Header');
80
    $this->assertSession()->responseHeaderEquals('Test-Header', 'header value');
81 82 83 84

    // Ensure that \Drupal\Tests\UiHelperTrait::isTestUsingGuzzleClient() works
    // as expected.
    $this->assertTrue($this->isTestUsingGuzzleClient());
85 86
  }

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
  /**
   * Tests drupalGet().
   */
  public function testDrupalGet() {
    $this->drupalGet('test-page');
    $this->assertSession()->statusCodeEquals(200);
    $this->assertSession()->addressEquals('test-page');
    $this->drupalGet('/test-page');
    $this->assertSession()->statusCodeEquals(200);
    $this->assertSession()->addressEquals('test-page');
    $this->drupalGet('/test-page/');
    $this->assertSession()->statusCodeEquals(200);
    $this->assertSession()->addressEquals('/test-page/');
  }

102 103 104 105 106
  /**
   * Tests basic form functionality.
   */
  public function testForm() {
    // Ensure the proper response code for a _form route.
107
    $this->drupalGet('form-test/object-builder');
108 109 110 111 112 113
    $this->assertSession()->statusCodeEquals(200);

    // Ensure the form and text field exist.
    $this->assertSession()->elementExists('css', 'form#form-test-form-test-object');
    $this->assertSession()->fieldExists('bananas');

114 115 116 117 118 119 120 121 122 123 124
    // Check that the hidden field exists and has a specific value.
    $this->assertSession()->hiddenFieldExists('strawberry');
    $this->assertSession()->hiddenFieldExists('red');
    $this->assertSession()->hiddenFieldExists('redstrawberryhiddenfield');
    $this->assertSession()->hiddenFieldValueNotEquals('strawberry', 'brown');
    $this->assertSession()->hiddenFieldValueEquals('strawberry', 'red');

    // Check that a hidden field does not exist.
    $this->assertSession()->hiddenFieldNotExists('bananas');
    $this->assertSession()->hiddenFieldNotExists('pineapple');

125 126 127 128 129 130
    $edit = ['bananas' => 'green'];
    $this->submitForm($edit, 'Save', 'form-test-form-test-object');

    $config_factory = $this->container->get('config.factory');
    $value = $config_factory->get('form_test.object')->get('bananas');
    $this->assertSame('green', $value);
131 132 133

    // Test drupalPostForm().
    $edit = ['bananas' => 'red'];
134
    // Submit the form using the button label.
135 136
    $result = $this->drupalPostForm('form-test/object-builder', $edit, 'Save');
    $this->assertSame($this->getSession()->getPage()->getContent(), $result);
137 138 139
    $value = $config_factory->get('form_test.object')->get('bananas');
    $this->assertSame('red', $value);

140
    $this->drupalPostForm('form-test/object-builder', [], 'Save');
141 142
    $value = $config_factory->get('form_test.object')->get('bananas');
    $this->assertSame('', $value);
143

144 145 146 147 148 149 150 151 152 153 154 155 156 157
    // Submit the form using the button id.
    $edit = ['bananas' => 'blue'];
    $result = $this->drupalPostForm('form-test/object-builder', $edit, 'edit-submit');
    $this->assertSame($this->getSession()->getPage()->getContent(), $result);
    $value = $config_factory->get('form_test.object')->get('bananas');
    $this->assertSame('blue', $value);

    // Submit the form using the button name.
    $edit = ['bananas' => 'purple'];
    $result = $this->drupalPostForm('form-test/object-builder', $edit, 'op');
    $this->assertSame($this->getSession()->getPage()->getContent(), $result);
    $value = $config_factory->get('form_test.object')->get('bananas');
    $this->assertSame('purple', $value);

158
    // Test drupalPostForm() with no-html response.
159
    $values = Json::decode($this->drupalPostForm('form_test/form-state-values-clean', [], 'Submit'));
160
    $this->assertSame(1000, $values['beer']);
161 162 163 164 165 166 167 168 169

    // Test drupalPostForm() with form by HTML id.
    $this->drupalCreateContentType(['type' => 'page']);
    $this->drupalLogin($this->drupalCreateUser(['create page content']));
    $this->drupalGet('form-test/two-instances-of-same-form');
    $this->getSession()->getPage()->fillField('edit-title-0-value', 'form1');
    $this->getSession()->getPage()->fillField('edit-title-0-value--2', 'form2');
    $this->drupalPostForm(NULL, [], 'Save', [], 'node-page-form--2');
    $this->assertSession()->pageTextContains('Page form2 has been created.');
170 171
  }

172 173 174 175 176 177
  /**
   * Tests clickLink() functionality.
   */
  public function testClickLink() {
    $this->drupalGet('test-page');
    $this->clickLink('Visually identical test links');
178
    $this->assertStringContainsString('user/login', $this->getSession()->getCurrentUrl());
179 180
    $this->drupalGet('test-page');
    $this->clickLink('Visually identical test links', 0);
181
    $this->assertStringContainsString('user/login', $this->getSession()->getCurrentUrl());
182 183
    $this->drupalGet('test-page');
    $this->clickLink('Visually identical test links', 1);
184
    $this->assertStringContainsString('user/register', $this->getSession()->getCurrentUrl());
185 186
  }

187
  public function testError() {
188 189
    $this->expectException('\Exception');
    $this->expectExceptionMessage('User notice: foo');
190 191 192
    $this->drupalGet('test-error');
  }

193 194 195 196 197 198 199 200 201 202
  /**
   * Tests linkExists() with pipe character (|) in locator.
   *
   * @see \Drupal\Tests\WebAssert::linkExists()
   */
  public function testPipeCharInLocator() {
    $this->drupalGet('test-pipe-char');
    $this->assertSession()->linkExists('foo|bar|baz');
  }

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
  /**
   * Tests linkExistsExact() functionality.
   *
   * @see \Drupal\Tests\WebAssert::linkExistsExact()
   */
  public function testLinkExistsExact() {
    $this->drupalGet('test-pipe-char');
    $this->assertSession()->linkExistsExact('foo|bar|baz');
  }

  /**
   * Tests linkExistsExact() functionality fail.
   *
   * @see \Drupal\Tests\WebAssert::linkExistsExact()
   */
  public function testInvalidLinkExistsExact() {
    $this->drupalGet('test-pipe-char');
220 221
    $this->expectException(ExpectationException::class);
    $this->expectExceptionMessage('Link with label foo|bar found');
222 223 224 225 226 227 228 229 230 231 232 233 234
    $this->assertSession()->linkExistsExact('foo|bar');
  }

  /**
   * Tests linkNotExistsExact() functionality.
   *
   * @see \Drupal\Tests\WebAssert::linkNotExistsExact()
   */
  public function testLinkNotExistsExact() {
    $this->drupalGet('test-pipe-char');
    $this->assertSession()->linkNotExistsExact('foo|bar');
  }

235 236 237 238 239 240 241 242 243 244
  /**
   * Tests responseHeaderDoesNotExist() functionality.
   *
   * @see \Drupal\Tests\WebAssert::responseHeaderDoesNotExist()
   */
  public function testResponseHeaderDoesNotExist() {
    $this->drupalGet('test-pipe-char');
    $this->assertSession()->responseHeaderDoesNotExist('Foo-Bar');
  }

245 246 247 248 249 250 251
  /**
   * Tests linkNotExistsExact() functionality fail.
   *
   * @see \Drupal\Tests\WebAssert::linkNotExistsExact()
   */
  public function testInvalidLinkNotExistsExact() {
    $this->drupalGet('test-pipe-char');
252 253
    $this->expectException(ExpectationException::class);
    $this->expectExceptionMessage('Link with label foo|bar|baz not found');
254 255 256
    $this->assertSession()->linkNotExistsExact('foo|bar|baz');
  }

257 258 259 260 261 262
  /**
   * Tests legacy assertResponse().
   *
   * @group legacy
   */
  public function testAssertResponse() {
263
    $this->expectDeprecation('AssertLegacyTrait::assertResponse() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->statusCodeEquals() instead. See https://www.drupal.org/node/3129738');
264 265 266 267
    $this->drupalGet('test-encoded');
    $this->assertResponse(200);
  }

268 269 270 271 272 273
  /**
   * Tests legacy assertTitle().
   *
   * @group legacy
   */
  public function testAssertTitle() {
274
    $this->expectDeprecation('AssertLegacyTrait::assertTitle() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->titleEquals() instead. See https://www.drupal.org/node/3129738');
275 276 277 278
    $this->drupalGet('test-encoded');
    $this->assertTitle("Page with encoded HTML | Drupal");
  }

279 280 281 282 283 284
  /**
   * Tests legacy assertHeader().
   *
   * @group legacy
   */
  public function testAssertHeader() {
285
    $this->expectDeprecation('AssertLegacyTrait::assertHeader() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseHeaderEquals() instead. See https://www.drupal.org/node/3129738');
286 287 288 289 290 291
    $account = $this->drupalCreateUser();
    $this->drupalLogin($account);
    $this->drupalGet('test-page');
    $this->assertHeader('X-Drupal-Cache-Tags', 'http_response rendered');
  }

292
  /**
293
   * Tests legacy text asserts.
294
   */
295
  public function testTextAsserts() {
296 297 298 299 300
    $this->drupalGet('test-encoded');
    $dangerous = 'Bad html <script>alert(123);</script>';
    $sanitized = Html::escape($dangerous);
    $this->assertNoText($dangerous);
    $this->assertText($sanitized);
301
  }
302

303 304 305 306 307 308
  /**
   * Tests legacy assertPattern().
   *
   * @group legacy
   */
  public function testAssertPattern() {
309
    $this->expectDeprecation('AssertLegacyTrait::assertPattern() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->responseMatches() instead. See https://www.drupal.org/node/3129738');
310 311 312 313
    $this->drupalGet('test-escaped-characters');
    $this->assertPattern('/div class.*escaped/');
  }

314 315 316 317 318 319
  /**
   * Tests legacy getRawContent().
   *
   * @group legacy
   */
  public function testGetRawContent() {
320
    $this->expectDeprecation('AssertLegacyTrait::getRawContent() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->getSession()->getPage()->getContent() instead. See https://www.drupal.org/node/3129738');
321 322
    $this->drupalGet('test-encoded');
    $this->assertSame($this->getSession()->getPage()->getContent(), $this->getRawContent());
323 324
  }

325 326 327 328 329 330
  /**
   * Tests legacy buildXPathQuery().
   *
   * @group legacy
   */
  public function testBuildXPathQuery() {
331
    $this->expectDeprecation('AssertLegacyTrait::buildXPathQuery() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->buildXPathQuery() instead. See https://www.drupal.org/node/3129738');
332 333 334
    $this->buildXPathQuery('\\html');
  }

335
  /**
336
   * Tests legacy field asserts which use xpath directly.
337
   */
338
  public function testXpathAsserts() {
339 340 341
    $this->drupalGet('test-field-xpath');
    $this->assertFieldsByValue($this->xpath("//h1[@class = 'page-title']"), NULL);
    $this->assertFieldsByValue($this->xpath('//table/tbody/tr[2]/td[1]'), 'one');
342
    $this->assertSession()->elementTextContains('xpath', '//table/tbody/tr[2]/td[1]', 'one');
343 344

    $this->assertFieldsByValue($this->xpath("//input[@id = 'edit-name']"), 'Test name');
345
    $this->assertSession()->fieldValueEquals('edit-name', 'Test name');
346
    $this->assertFieldsByValue($this->xpath("//select[@id = 'edit-options']"), '2');
347
    $this->assertSession()->fieldValueEquals('edit-options', '2');
348

349 350
    $this->assertSession()->elementNotExists('xpath', '//notexisting');
    $this->assertSession()->fieldValueNotEquals('edit-name', 'wrong value');
351

352 353
    // Test that the assertion fails correctly.
    try {
354
      $this->assertSession()->fieldExists('notexisting');
355 356
      $this->fail('The "notexisting" field was found.');
    }
357
    catch (ExpectationException $e) {
358
      // Expected exception; just continue testing.
359 360 361
    }

    try {
362
      $this->assertSession()->fieldNotExists('edit-name');
363 364
      $this->fail('The "edit-name" field was not found.');
    }
365
    catch (ExpectationException $e) {
366
      // Expected exception; just continue testing.
367 368 369 370 371 372
    }

    try {
      $this->assertFieldsByValue($this->xpath("//input[@id = 'edit-name']"), 'not the value');
      $this->fail('The "edit-name" field is found with the value "not the value".');
    }
373
    catch (ExpectationFailedException $e) {
374
      // Expected exception; just continue testing.
375 376 377 378 379
    }
  }

  /**
   * Tests legacy field asserts using textfields.
380 381 382 383
   *
   * @group legacy
   */
  public function testAssertField() {
384 385
    $this->expectDeprecation('AssertLegacyTrait::assertField() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldExists() or $this->assertSession()->buttonExists() instead. See https://www.drupal.org/node/3129738');
    $this->expectDeprecation('AssertLegacyTrait::assertNoField() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldNotExists() or $this->assertSession()->buttonNotExists() instead. See https://www.drupal.org/node/3129738');
386 387 388 389 390
    $this->drupalGet('test-field-xpath');
    $this->assertField('name');
    $this->assertNoField('invalid_name_and_id');
  }

391
  /**
392
   * Tests legacy field asserts by id and by Xpath.
393 394 395 396
   *
   * @group legacy
   */
  public function testAssertFieldById() {
397 398
    $this->expectDeprecation('AssertLegacyTrait::assertFieldById() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldExists() or $this->assertSession()->buttonExists() or $this->assertSession()->fieldValueEquals() instead. See https://www.drupal.org/node/3129738');
    $this->expectDeprecation('AssertLegacyTrait::assertNoFieldById() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldNotExists() or $this->assertSession()->buttonNotExists() or $this->assertSession()->fieldValueNotEquals() instead. See https://www.drupal.org/node/3129738');
399 400
    $this->expectDeprecation('AssertLegacyTrait::assertFieldByXPath() is deprecated in drupal:8.3.0 and is removed from drupal:10.0.0. Use $this->xpath() instead and check the values directly in the test. See https://www.drupal.org/node/3129738');
    $this->expectDeprecation('AssertLegacyTrait::assertNoFieldByXPath() is deprecated in drupal:8.3.0 and is removed from drupal:10.0.0. Use $this->xpath() instead and assert that the result is empty. See https://www.drupal.org/node/3129738');
401 402 403
    $this->drupalGet('test-field-xpath');
    $this->assertFieldById('edit-save', NULL);
    $this->assertNoFieldById('invalid', NULL);
404 405
    $this->assertFieldByXPath("//input[@id = 'edit-name']", 'Test name');
    $this->assertNoFieldByXPath("//input[@id = 'edit-name']", 'wrong value');
406 407
  }

408 409
  /**
   * Tests field asserts using textfields.
410
   */
411
  public function testFieldAssertsForTextfields() {
412 413
    $this->drupalGet('test-field-xpath');

414 415
    // *** 1. fieldNotExists().
    $this->assertSession()->fieldNotExists('invalid_name_and_id');
416 417 418

    // Test that the assertion fails correctly when searching by name.
    try {
419
      $this->assertSession()->fieldNotExists('name');
420 421 422
      $this->fail('The "name" field was not found based on name.');
    }
    catch (ExpectationException $e) {
423
      // Expected exception; just continue testing.
424 425 426 427
    }

    // Test that the assertion fails correctly when searching by id.
    try {
428
      $this->assertSession()->fieldNotExists('edit-name');
429 430 431
      $this->fail('The "name" field was not found based on id.');
    }
    catch (ExpectationException $e) {
432
      // Expected exception; just continue testing.
433 434
    }

435 436 437
    // *** 2. fieldExists().
    $this->assertSession()->fieldExists('name');
    $this->assertSession()->fieldExists('edit-name');
438 439 440

    // Test that the assertion fails correctly if the field does not exist.
    try {
441
      $this->assertSession()->fieldExists('invalid_name_and_id');
442 443
      $this->fail('The "invalid_name_and_id" field was found.');
    }
444
    catch (ElementNotFoundException $e) {
445
      // Expected exception; just continue testing.
446 447
    }
    // *** 3. assertNoFieldById().
448 449
    $this->assertSession()->fieldValueNotEquals('name', 'not the value');
    $this->assertSession()->fieldNotExists('notexisting');
450 451
    // Test that the assertion fails correctly if no value is passed in.
    try {
452
      $this->assertSession()->fieldNotExists('edit-description');
453 454 455
      $this->fail('The "description" field, with no value was not found.');
    }
    catch (ExpectationException $e) {
456
      // Expected exception; just continue testing.
457 458 459 460
    }

    // Test that the assertion fails correctly if a NULL value is passed in.
    try {
461
      $this->assertSession()->fieldNotExists('name', NULL);
462 463 464
      $this->fail('The "name" field was not found.');
    }
    catch (ExpectationException $e) {
465
      // Expected exception; just continue testing.
466
    }
467

468
    // *** 4. assertFieldById().
469 470 471 472
    $this->assertSession()->fieldExists('edit-name');
    $this->assertSession()->fieldValueEquals('edit-name', 'Test name');
    $this->assertSession()->fieldExists('edit-description');
    $this->assertSession()->fieldValueEquals('edit-description', '');
473 474 475

    // Test that the assertion fails correctly if no value is passed in.
    try {
476
      $this->assertSession()->fieldValueNotEquals('edit-name', '');
477
    }
478
    catch (ExpectationFailedException $e) {
479
      // Expected exception; just continue testing.
480 481
    }

482
    // Test that the assertion fails correctly if the wrong value is passed in.
483
    try {
484
      $this->assertSession()->fieldValueNotEquals('edit-name', 'not the value');
485
    }
486
    catch (ExpectationFailedException $e) {
487
      // Expected exception; just continue testing.
488 489
    }

490 491
    // *** 5. fieldValueNotEquals().
    $this->assertSession()->fieldValueNotEquals('name', 'not the value');
492

493
    // Test that the assertion fails correctly if given the right value.
494
    try {
495 496
      $this->assertSession()->fieldValueNotEquals('name', 'Test name');
      $this->fail('fieldValueNotEquals failed to throw an exception.');
497 498
    }
    catch (ExpectationException $e) {
499
      // Expected exception; just continue testing.
500 501
    }

502 503 504
    // *** 6. fieldValueEquals().
    $this->assertSession()->fieldValueEquals('name', 'Test name');
    $this->assertSession()->fieldValueEquals('description', '');
505 506 507

    // Test that the assertion fails correctly if given the wrong value.
    try {
508 509
      $this->assertSession()->fieldValueEquals('name', 'not the value');
      $this->fail('fieldValueEquals failed to throw an exception.');
510
    }
511
    catch (ExpectationException $e) {
512
      // Expected exception; just continue testing.
513
    }
514 515 516

    // Test that text areas can contain new lines.
    $this->assertFieldsByValue($this->xpath("//textarea[@id = 'edit-test-textarea-with-newline']"), "Test text with\nnewline");
517 518 519
  }

  /**
520
   * Tests legacy field asserts for options field type.
521 522
   *
   * @group legacy
523
   */
524
  public function testFieldAssertsForOptions() {
525 526 527
    $this->expectDeprecation('AssertLegacyTrait::assertOptionByText() is deprecated in drupal:8.4.0 and is removed from drupal:10.0.0. Use $this->assertSession()->optionExists() instead. See https://www.drupal.org/node/3129738');
    $this->expectDeprecation('AssertLegacyTrait::assertOption() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->optionExists() instead. See https://www.drupal.org/node/3129738');
    $this->expectDeprecation('AssertLegacyTrait::assertNoOption() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->optionNotExists() instead. See https://www.drupal.org/node/3129738');
528 529 530
    $this->drupalGet('test-field-xpath');

    // Option field type.
531 532 533 534 535 536
    $this->assertOptionByText('options', 'one');
    try {
      $this->assertOptionByText('options', 'four');
      $this->fail('The select option "four" was found.');
    }
    catch (ExpectationException $e) {
537
      // Expected exception; just continue testing.
538
    }
539

540 541 542 543 544 545
    $this->assertOption('options', 1);
    try {
      $this->assertOption('options', 4);
      $this->fail('The select option "4" was found.');
    }
    catch (ExpectationException $e) {
546
      // Expected exception; just continue testing.
547 548 549 550 551 552 553 554
    }

    $this->assertNoOption('options', 'non-existing');
    try {
      $this->assertNoOption('options', 'one');
      $this->fail('The select option "one" was not found.');
    }
    catch (ExpectationException $e) {
555
      // Expected exception; just continue testing.
556 557
    }

558
    $this->assertTrue($this->assertSession()->optionExists('options', 2)->isSelected());
559
    try {
560
      $this->assertTrue($this->assertSession()->optionExists('options', 4)->isSelected());
561 562 563
      $this->fail('The select option "4" was selected.');
    }
    catch (ExpectationException $e) {
564
      // Expected exception; just continue testing.
565 566 567
    }

    try {
568
      $this->assertTrue($this->assertSession()->optionExists('options', 1)->isSelected());
569 570
      $this->fail('The select option "1" was selected.');
    }
571
    catch (ExpectationFailedException $e) {
572
      // Expected exception; just continue testing.
573
    }
574

575 576 577 578 579
  }

  /**
   * Tests legacy field asserts for button field type.
   */
580
  public function testFieldAssertsForButton() {
581
    $this->drupalGet('test-field-xpath');
582

583 584 585 586 587 588
    // Verify if the test passes with button ID.
    $this->assertSession()->buttonExists('edit-save');
    // Verify if the test passes with button Value.
    $this->assertSession()->buttonExists('Save');
    // Verify if the test passes with button Name.
    $this->assertSession()->buttonExists('op');
589

590 591 592 593 594 595
    // Verify if the test passes with button ID.
    $this->assertSession()->buttonNotExists('i-do-not-exist');
    // Verify if the test passes with button Value.
    $this->assertSession()->buttonNotExists('I do not exist');
    // Verify if the test passes with button Name.
    $this->assertSession()->buttonNotExists('no');
596

597
    // Test that multiple fields with the same name are validated correctly.
598
    $this->assertSession()->buttonExists('duplicate_button');
599 600 601
    $this->assertSession()->buttonExists('Duplicate button 1');
    $this->assertSession()->buttonExists('Duplicate button 2');
    $this->assertSession()->buttonNotExists('Rabbit');
602 603

    try {
604
      $this->assertSession()->buttonNotExists('Duplicate button 2');
605 606 607
      $this->fail('The "duplicate_button" field with the value Duplicate button 2 was not found.');
    }
    catch (ExpectationException $e) {
608
      // Expected exception; just continue testing.
609 610 611
    }
  }

612 613 614 615 616 617
  /**
   * Tests legacy assertFieldChecked() and assertNoFieldChecked().
   *
   * @group legacy
   */
  public function testLegacyFieldAssertsForCheckbox() {
618 619
    $this->expectDeprecation('AssertLegacyTrait::assertFieldChecked() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->checkboxChecked() instead. See https://www.drupal.org/node/3129738');
    $this->expectDeprecation('AssertLegacyTrait::assertNoFieldChecked() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->checkboxNotChecked() instead. See https://www.drupal.org/node/3129738');
620 621 622 623 624
    $this->drupalGet('test-field-xpath');
    $this->assertFieldChecked('edit-checkbox-enabled');
    $this->assertNoFieldChecked('edit-checkbox-disabled');
  }

625 626 627
  /**
   * Tests legacy field asserts for checkbox field type.
   */
628
  public function testFieldAssertsForCheckbox() {
629 630 631
    $this->drupalGet('test-field-xpath');

    // Part 1 - Test by name.
632 633
    // Test that checkboxes are found/not found correctly by name, when using
    // TRUE or FALSE to match their 'checked' state.
634 635 636 637 638 639
    $this->assertSession()->fieldExists('checkbox_enabled');
    $this->assertSession()->fieldExists('checkbox_disabled');
    $this->assertSession()->fieldValueEquals('checkbox_enabled', TRUE);
    $this->assertSession()->fieldValueEquals('checkbox_disabled', FALSE);
    $this->assertSession()->fieldValueNotEquals('checkbox_enabled', FALSE);
    $this->assertSession()->fieldValueNotEquals('checkbox_disabled', TRUE);
640 641

    // Test that we have legacy support.
642 643
    $this->assertSession()->fieldValueEquals('checkbox_enabled', '1');
    $this->assertSession()->fieldValueEquals('checkbox_disabled', '');
644

645
    // Test that the assertion fails correctly if given the right value.
646
    try {
647 648
      $this->assertSession()->fieldValueNotEquals('checkbox_enabled', TRUE);
      $this->fail('fieldValueNotEquals failed to throw an exception.');
649 650
    }
    catch (ExpectationException $e) {
651
      // Expected exception; just continue testing.
652 653 654
    }

    // Part 2 - Test by ID.
655 656
    // Test that checkboxes are found/not found correctly by ID, when using
    // TRUE or FALSE to match their 'checked' state.
657 658 659 660
    $this->assertSession()->fieldValueEquals('edit-checkbox-enabled', TRUE);
    $this->assertSession()->fieldValueEquals('edit-checkbox-disabled', FALSE);
    $this->assertSession()->fieldValueNotEquals('edit-checkbox-enabled', FALSE);
    $this->assertSession()->fieldValueNotEquals('edit-checkbox-disabled', TRUE);
661

662
    // Test that checkboxes are found by ID, when using NULL to ignore the
663
    // 'checked' state.
664 665
    $this->assertSession()->fieldExists('edit-checkbox-enabled');
    $this->assertSession()->fieldExists('edit-checkbox-disabled');
666

667
    // Test that checkboxes are found by ID when passing no second parameter.
668 669
    $this->assertSession()->fieldExists('edit-checkbox-enabled');
    $this->assertSession()->fieldExists('edit-checkbox-disabled');
670 671

    // Test that we have legacy support.
672 673
    $this->assertSession()->fieldValueEquals('edit-checkbox-enabled', '1');
    $this->assertSession()->fieldValueEquals('edit-checkbox-disabled', '');
674 675 676

    // Test that the assertion fails correctly when using NULL to ignore state.
    try {
677
      $this->assertSession()->fieldNotExists('edit-checkbox-disabled', NULL);
678 679 680
      $this->fail('The "edit-checkbox-disabled" field was not found by ID, using NULL value.');
    }
    catch (ExpectationException $e) {
681
      // Expected exception; just continue testing.
682 683
    }

684
    // Part 3 - Test the specific 'checked' assertions.
685 686
    $this->assertSession()->checkboxChecked('edit-checkbox-enabled');
    $this->assertSession()->checkboxNotChecked('edit-checkbox-disabled');
687

688
    // Test that the assertion fails correctly with non-existent field id.
689
    try {
690
      $this->assertSession()->checkboxNotChecked('incorrect_checkbox_id');
691 692 693
      $this->fail('The "incorrect_checkbox_id" field was found');
    }
    catch (ExpectationException $e) {
694
      // Expected exception; just continue testing.
695 696 697 698
    }

    // Test that the assertion fails correctly for a checkbox that is checked.
    try {
699
      $this->assertSession()->checkboxNotChecked('edit-checkbox-enabled');
700 701 702
      $this->fail('The "edit-checkbox-enabled" field was not found in a checked state.');
    }
    catch (ExpectationException $e) {
703
      // Expected exception; just continue testing.
704 705 706 707 708
    }

    // Test that the assertion fails correctly for a checkbox that is not
    // checked.
    try {
709
      $this->assertSession()->checkboxChecked('edit-checkbox-disabled');
710 711 712
      $this->fail('The "edit-checkbox-disabled" field was found and checked.');
    }
    catch (ExpectationException $e) {
713
      // Expected exception; just continue testing.
714
    }
715 716
  }

717 718 719 720 721 722 723 724 725 726 727 728
  /**
   * Tests the ::cronRun() method.
   */
  public function testCronRun() {
    $last_cron_time = \Drupal::state()->get('system.cron_last');
    $this->cronRun();
    $this->assertSession()->statusCodeEquals(204);
    $next_cron_time = \Drupal::state()->get('system.cron_last');

    $this->assertGreaterThan($last_cron_time, $next_cron_time);
  }

729 730 731 732 733
  /**
   * Tests the Drupal install done in \Drupal\Tests\BrowserTestBase::setUp().
   */
  public function testInstall() {
    $htaccess_filename = $this->tempFilesDirectory . '/.htaccess';
734
    $this->assertFileExists($htaccess_filename);
735 736
  }

737 738 739 740
  /**
   * Tests the assumption that local time is in 'Australia/Sydney'.
   */
  public function testLocalTimeZone() {
741
    $expected = 'Australia/Sydney';
742
    // The 'Australia/Sydney' time zone is set in core/tests/bootstrap.php
743
    $this->assertEquals($expected, date_default_timezone_get());
744 745 746 747 748

    // The 'Australia/Sydney' time zone is also set in
    // FunctionalTestSetupTrait::initConfig().
    $config_factory = $this->container->get('config.factory');
    $value = $config_factory->get('system.date')->get('timezone.default');
749 750 751 752 753 754
    $this->assertEquals($expected, $value);

    // Test that users have the correct time zone set.
    $this->assertEquals($expected, $this->rootUser->getTimeZone());
    $admin_user = $this->drupalCreateUser(['administer site configuration']);
    $this->assertEquals($expected, $admin_user->getTimeZone());
755 756
  }

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
  /**
   * Tests the ::checkForMetaRefresh() method.
   */
  public function testCheckForMetaRefresh() {
    // Disable following redirects in the client.
    $this->getSession()->getDriver()->getClient()->followRedirects(FALSE);
    // Set the maximumMetaRefreshCount to zero to make sure the redirect doesn't
    // happen when doing a drupalGet.
    $this->maximumMetaRefreshCount = 0;
    $this->drupalGet('test-meta-refresh');
    $this->assertNotEmpty($this->cssSelect('meta[http-equiv="refresh"]'));
    // Allow one redirect to happen.
    $this->maximumMetaRefreshCount = 1;
    $this->checkForMetaRefresh();
    // Check that we are now on the test page.
    $this->assertSession()->pageTextContains('Test page text.');
  }

775 776 777 778 779 780
  public function testGetDefaultDriveInstance() {
    putenv('MINK_DRIVER_ARGS=' . json_encode([NULL, ['key1' => ['key2' => ['key3' => 3, 'key3.1' => 3.1]]]]));
    $this->getDefaultDriverInstance();
    $this->assertEquals([NULL, ['key1' => ['key2' => ['key3' => 3, 'key3.1' => 3.1]]]], $this->minkDefaultDriverArgs);
  }

781 782 783 784
  /**
   * Ensures we can't access modules we shouldn't be able to after install.
   */
  public function testProfileModules() {
785 786
    $this->expectException(\InvalidArgumentException::class);
    $this->expectExceptionMessage('The module demo_umami_content does not exist.');
787 788 789 790
    $this->assertFileExists('core/profiles/demo_umami/modules/demo_umami_content/demo_umami_content.info.yml');
    \Drupal::service('extension.list.module')->getPathname('demo_umami_content');
  }

791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
  /**
   * Test the protections provided by .htkey.
   */
  public function testHtkey() {
    // Remove the Simpletest private key file so we can test the protection
    // against requests that forge a valid testing user agent to gain access
    // to the installer.
    // @see drupal_valid_test_ua()
    // Not using File API; a potential error must trigger a PHP warning.
    $install_url = Url::fromUri('base:core/install.php', ['external' => TRUE, 'absolute' => TRUE])->toString();
    $this->drupalGet($install_url);
    $this->assertSession()->statusCodeEquals(200);
    unlink($this->siteDirectory . '/.htkey');
    $this->drupalGet($install_url);
    $this->assertSession()->statusCodeEquals(403);
  }

808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
  /**
   * Tests pageContainsNoDuplicateId() functionality.
   *
   * @see \Drupal\Tests\WebAssert::pageContainsNoDuplicateId()
   */
  public function testPageContainsNoDuplicateId() {
    $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->expectException(ExpectationException::class);
    $this->expectExceptionMessage('The page contains a duplicate HTML ID "page-element".');
    $assert_session->pageContainsNoDuplicateId();
  }

824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
  /**
   * Tests assertEscaped() and assertUnescaped().
   *
   * @see \Drupal\Tests\WebAssert::assertNoEscaped()
   * @see \Drupal\Tests\WebAssert::assertEscaped()
   */
  public function testEscapingAssertions() {
    $assert = $this->assertSession();

    $this->drupalGet('test-escaped-characters');
    $assert->assertNoEscaped('<div class="escaped">');
    $assert->responseContains('<div class="escaped">');
    $assert->assertEscaped('Escaped: <"\'&>');

    $this->drupalGet('test-escaped-script');
    $assert->assertNoEscaped('<div class="escaped">');
    $assert->responseContains('<div class="escaped">');
    $assert->assertEscaped("<script>alert('XSS');alert(\"XSS\");</script>");

    $this->drupalGet('test-unescaped-script');
    $assert->assertNoEscaped('<div class="unescaped">');
    $assert->responseContains('<div class="unescaped">');
    $assert->responseContains("<script>alert('Marked safe');alert(\"Marked safe\");</script>");
    $assert->assertNoEscaped("<script>alert('Marked safe');alert(\"Marked safe\");</script>");
  }

850 851 852 853 854 855
  /**
   * Tests deprecation of legacy assertEscaped() and assertNoEscaped().
   *
   * @group legacy
   */
  public function testLegacyEscapingAssertions(): void {
856 857
    $this->expectDeprecation('AssertLegacyTrait::assertNoEscaped() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->assertNoEscaped() instead. See https://www.drupal.org/node/3129738');
    $this->expectDeprecation('AssertLegacyTrait::assertEscaped() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->assertEscaped() instead. See https://www.drupal.org/node/3129738');
858 859 860 861 862
    $this->drupalGet('test-escaped-characters');
    $this->assertNoEscaped('<div class="escaped">');
    $this->assertEscaped('Escaped: <"\'&>');
  }

863 864 865 866 867 868
  /**
   * Tests deprecation of drupalPostForm().
   *
   * @group legacy
   */
  public function testLegacyDrupalPostForm(): void {
869
    $this->expectDeprecation('Calling Drupal\Tests\UiHelperTrait::drupalPostForm() with $submit as an object is deprecated in drupal:9.2.0 and the method is removed in drupal:10.0.0. Use $this->submitForm() instead. See https://www.drupal.org/node/3168858');
870
    $this->expectDeprecation('Calling Drupal\Tests\UiHelperTrait::drupalPostForm() with $edit set to NULL is deprecated in drupal:9.1.0 and the method is removed in drupal:10.0.0. Use $this->submitForm() instead. See https://www.drupal.org/node/3168858');
871
    $this->drupalPostForm('form-test/object-builder', NULL, t('Save'));
872 873
  }

874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
  /**
   * Tests that deprecation headers do not get duplicated.
   *
   * @group legacy
   *
   * @see \Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware::__invoke()
   */
  public function testDeprecationHeaders() {
    $this->drupalGet('/test-deprecations');

    $deprecation_messages = [];
    foreach ($this->getSession()->getResponseHeaders() as $name => $values) {
      if (preg_match('/^X-Drupal-Assertion-[0-9]+$/', $name, $matches)) {
        foreach ($values as $value) {
          // Call \Drupal\simpletest\WebTestBase::error() with the parameters from
          // the header.
          $parameters = unserialize(urldecode($value));
          if (count($parameters) === 3) {
            if ($parameters[1] === 'User deprecated function') {
              $deprecation_messages[] = (string) $parameters[0];
            }
          }
        }
      }
    }

    $this->assertContains('Test deprecation message', $deprecation_messages);
    $test_deprecation_messages = array_filter($deprecation_messages, function ($message) {
      return $message === 'Test deprecation message';
    });
    $this->assertCount(1, $test_deprecation_messages);
  }

907 908 909 910 911 912
  /**
   * Tests legacy assertFieldByName() and assertNoFieldByName().
   *
   * @group legacy
   */
  public function testLegacyFieldAssertsByName() {
913 914
    $this->expectDeprecation('AssertLegacyTrait::assertFieldByName() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldExists() or $this->assertSession()->buttonExists() or $this->assertSession()->fieldValueEquals() instead. See https://www.drupal.org/node/3129738');
    $this->expectDeprecation('AssertLegacyTrait::assertNoFieldByName() is deprecated in drupal:8.2.0 and is removed from drupal:10.0.0. Use $this->assertSession()->fieldNotExists() or $this->assertSession()->buttonNotExists() or $this->assertSession()->fieldValueNotEquals() instead. See https://www.drupal.org/node/3129738');
915 916 917 918 919
    $this->drupalGet('test-field-xpath');
    $this->assertFieldByName('checkbox_enabled', TRUE);
    $this->assertNoFieldByName('checkbox_enabled', FALSE);
  }

920
}