diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php index 2e1560123835489e3d8b2e0fbcb7465554334375..9673f0e00cff82125f92214712be98ee9aa1cc0d 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Drupal\Tests\project_browser\FunctionalJavascript; -use Behat\Mink\Element\NodeElement; use Drupal\Core\Recipe\Recipe; use Drupal\Core\State\StateInterface; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; @@ -78,18 +77,8 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { public function testSingleModuleAddAndInstall(): void { TestActivator::handle('drupal/cream_cheese'); - $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $this->assertNotEmpty($download_button); - $this->assertSame('Install Cream cheese on a bagel', $download_button->getText()); - $download_button->click(); - $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000); - $this->assertTrue($assert_session->waitForText('Cream cheese on a bagel is Installed')); - $this->assertSame('Cream cheese on a bagel is Installed', $installed_action->getText()); + $this->installProject('Cream cheese on a bagel'); // The activator in project_browser_test should have logged a message. // @see \Drupal\project_browser_test\TestActivator @@ -107,12 +96,8 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Pinky and the Brain'); - $pinky_brain_selector = '#project-browser .pb-layout__main ul > li:nth-child(2)'; - $action_button = $assert_session->waitForElementVisible('css', "$pinky_brain_selector button.pb__action_button"); - $this->assertNotEmpty($action_button); - $this->assertSame('Install Pinky and the Brain', $action_button->getText()); - $action_button->click(); + $this->waitForProject('Pinky and the Brain') + ->pressButton('Install Pinky and the Brain'); $popup = $assert_session->waitForElementVisible('css', '.project-browser-popup'); $this->assertNotEmpty($popup); // The Pinky and the Brain module doesn't actually exist in the filesystem, @@ -140,28 +125,18 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->inputSearchField('image', TRUE); $assert_session->waitForElementVisible('css', ".search__search-submit")->click(); - $assert_installed = function (NodeElement $card): void { - $installed = $card->waitFor(30, function () use ($card): bool { - return $card->has('css', '.project_status-indicator:contains("Installed")'); - }); - $this->assertTrue($installed); - }; - // Apply a recipe that ships with core. - $card = $assert_session->waitForElementVisible('css', '.pb-project:contains("Image media type")'); - $this->assertNotEmpty($card); - $assert_session->buttonExists('Install', $card)->press(); - $assert_installed($card); + $card = $this->waitForProject('Image media type'); + $card->pressButton('Install'); + $this->waitForProjectToBeInstalled($card); // If we reload, the installation status should be remembered. $this->getSession()->reload(); $this->inputSearchField('image', TRUE); - $submit_button = $assert_session->waitForElementVisible('css', ".search__search-submit"); - $this->assertNotEmpty($submit_button); - $submit_button->click(); - $card = $assert_session->waitForElementVisible('css', '.pb-project:contains("Image media type")'); - $this->assertNotEmpty($card); - $assert_installed($card); + $assert_session->waitForElementVisible('css', ".search__search-submit") + ?->click(); + $card = $this->waitForProject('Image media type'); + $this->waitForProjectToBeInstalled($card); // Apply a recipe that requires user input. // @todo Remove this check in https://www.drupal.org/i/3494848. @@ -170,18 +145,15 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { } $this->inputSearchField('test', TRUE); $assert_session->waitForElementVisible('css', ".search__search-submit")->click(); - $card = $assert_session->waitForElementVisible('css', '.pb-project:contains("Test Recipe")'); - $this->assertNotEmpty($card); - $assert_session->buttonExists('Install', $card)->press(); + $this->waitForProject('Test Recipe')->pressButton('Install'); $field = $assert_session->waitForField('test_recipe[new_name]'); $this->assertNotEmpty($field); $field->setValue('Y halo thar!'); $page->pressButton('Continue'); $this->checkForMetaRefresh(); $this->inputSearchField('test', TRUE); - $assert_session->waitForElementVisible('css', ".search__search-submit")->click(); - $card = $assert_session->waitForElementVisible('css', '.pb-project:contains("Test Recipe")'); - $assert_installed($card); + $assert_session->waitForElementVisible('css', ".search__search-submit")?->click(); + $this->waitForProjectToBeInstalled('Test Recipe'); $this->assertSame('Y halo thar!', $this->config('system.site')->get('name')); } @@ -189,27 +161,16 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { * Tests install UI not available if not enabled. */ public function testAllowUiInstall(): void { - $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Pinky and the Brain'); - - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $this->assertNotEmpty($download_button); - $this->assertSame('Install Cream cheese on a bagel', $download_button->getText()); - $this->drupalGet('/admin/config/development/project_browser'); - $page->find('css', '#edit-allow-ui-install')?->click(); - $assert_session->checkboxNotChecked('edit-allow-ui-install'); - $this->submitForm([], 'Save'); - $this->assertTrue($assert_session->waitForText('The configuration options have been saved.')); - $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); - $action_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $this->assertNotEmpty($action_button); - $this->assertSame('View Commands for Cream cheese on a bagel', $action_button->getText()); + $cream_cheese = $this->waitForProject('Cream cheese on a bagel'); + $this->assertTrue($cream_cheese->hasButton('Install Cream cheese on a bagel')); + $this->config('project_browser.admin_settings') + ->set('allow_ui_install', FALSE) + ->save(); + $this->getSession()->reload(); + $cream_cheese = $this->waitForProject('Cream cheese on a bagel'); + $this->assertTrue($cream_cheese->hasButton('View Commands for Cream cheese on a bagel')); } /** @@ -219,7 +180,6 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { */ public function testCanBreakStageWithMissingProjectBrowserLock(): void { $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); // Start install begin. $this->drupalGet('admin/modules/project_browser/install-begin', [ @@ -227,25 +187,17 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { ]); $this->installState->deleteAll(); $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install while one is in progress, but not yet in // the applying stage. - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button.pb__action_button"); - $cream_cheese_button?->click(); + $this->waitForProject('Cream cheese on a bagel') + ->pressButton('Install Cream cheese on a bagel'); $this->assertTrue($assert_session->waitForText('The process for adding projects is locked, but that lock has expired. Use unlock link to unlock the process and try to add the project again.')); // Click Unlock Install Stage link. $this->clickWithWait('#ui-id-1 > p > a'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install after breaking lock. - $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button.pb__action_button"); - $cream_cheese_button?->click(); - $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000); - $assert_session->waitForText('Cream cheese on a bagel is Installed'); - $this->assertSame('Cream cheese on a bagel is Installed', $installed_action->getText()); - + $this->installProject('Cream cheese on a bagel'); } /** @@ -257,29 +209,21 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { */ public function testCanBreakLock(): void { $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); // Start install begin. $this->drupalGet('admin/modules/project_browser/install-begin', [ 'query' => ['source' => 'project_browser_test_mock'], ]); $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install while one is in progress, but not yet in // the applying stage. - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button.pb__action_button"); - $cream_cheese_button?->click(); + $this->waitForProject('Cream cheese on a bagel') + ->pressButton('Install Cream cheese on a bagel'); $this->assertTrue($assert_session->waitForText('The process for adding projects is locked, but that lock has expired. Use unlock link to unlock the process and try to add the project again.')); // Click Unlock Install Stage link. $this->clickWithWait('#ui-id-1 > p > a'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install after breaking lock. - $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button.pb__action_button"); - $cream_cheese_button?->click(); - $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000); - $assert_session->waitForText('Cream cheese on a bagel is Installed'); - $this->assertSame('Cream cheese on a bagel is Installed', $installed_action->getText()); + $this->installProject('Cream cheese on a bagel'); } /** @@ -292,17 +236,10 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $this->assertNotEmpty($download_button); - $this->assertSame('Install Cream cheese on a bagel', $download_button->getText()); - $download_button->click(); - $this->assertSame('Installing', $download_button->getText()); + $cream_cheese = $this->waitForProject('Cream cheese on a bagel'); + $cream_cheese->pressButton('Install Cream cheese on a bagel'); $this->assertTrue($assert_session->waitForText('Simulate an error message for the project browser.')); - $download_button_text = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button") - ?->getText(); - $this->assertSame('Install Cream cheese on a bagel', $download_button_text); + $this->assertTrue($cream_cheese->hasButton('Install Cream cheese on a bagel')); } /** @@ -313,23 +250,10 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->container->get(StateInterface::class) ->set('project_browser_test.simulated_result_severity', SystemManager::REQUIREMENT_WARNING); - $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $this->assertNotEmpty($download_button); - $this->assertSame('Install Cream cheese on a bagel', $download_button->getText()); - $download_button->click(); - $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000); - $this->assertNotEmpty($installed_action); - $installed_action = $installed_action->waitFor(30, function ($button) { - return $button->getText() === 'Cream cheese on a bagel is Installed'; - }); - $this->assertTrue($installed_action); + $this->installProject('Cream cheese on a bagel'); $this->drupalGet('admin/reports/dblog'); - $assert_session->pageTextContains('Simulate a warning message for the project browser.'); + $this->assertSession()->pageTextContains('Simulate a warning message for the project browser.'); } /** @@ -341,15 +265,10 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); $this->drupalGet('project-browser/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); - $this->svelteInitHelper('text', 'Kangaroo'); - - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $select_button1 = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $this->assertNotEmpty($select_button1); - $this->assertSame('Select Cream cheese on a bagel', $select_button1?->getText()); - $select_button1?->click(); - $was_selected = $select_button1->waitFor(10, fn ($button) => $button->getText() === 'Deselect Cream cheese on a bagel'); + + $cream_cheese = $this->waitForProject('Cream cheese on a bagel'); + $cream_cheese->pressButton('Select Cream cheese on a bagel'); + $was_selected = $cream_cheese->waitFor(10, fn ($card) => $card->hasButton('Deselect Cream cheese on a bagel')); $this->assertTrue($was_selected); $dancing_queen_button = $page->find('css', '#project-browser .pb-layout__main ul > li:nth-child(3) button'); @@ -357,14 +276,9 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->assertNotEmpty($assert_session->waitForButton('Install selected projects')); - $kangaroo_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(4)'; - $select_button2 = $assert_session->waitForElementVisible('css', "$kangaroo_module_selector button.pb__action_button"); - $this->assertNotEmpty($select_button2); - $this->assertSame('Select Kangaroo', $select_button2?->getText()); - $select_button2?->click(); - $was_deselected = $select_button2->waitFor(10, function ($button) { - return $button->getText() === 'Deselect Kangaroo'; - }); + $kangaroo = $this->waitForProject('Kangaroo'); + $kangaroo->pressButton('Select Kangaroo'); + $was_deselected = $kangaroo->waitFor(10, fn ($card) => $card->hasButton('Deselect Kangaroo')); $this->assertTrue($was_deselected); // Select button gets disabled on reaching maximum limit. $assert_session->elementAttributeExists('css', '#project-browser .pb-layout__main ul > li:nth-child(3) button.pb__action_button', 'disabled'); @@ -372,22 +286,15 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->assertNotEmpty($assert_session->waitForButton('Install selected projects')); $page->pressButton('Install selected projects'); - $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000); - $installed_action = $installed_action->waitFor(30, function ($button) { - return $button->getText() === 'Cream cheese on a bagel is Installed'; - }); - $this->assertTrue($installed_action); - - $installed_action = $assert_session->waitForElementVisible('css', "$kangaroo_module_selector .project_status-indicator", 30000); - $installed_action = $installed_action->waitFor(30, function ($button) { - return $button->getText() === 'Kangaroo is Installed'; - }); - $this->assertTrue($installed_action); + $this->waitForProjectToBeInstalled($cream_cheese); + $this->waitForProjectToBeInstalled($kangaroo); // The activator in project_browser_test should have logged a message. // @see \Drupal\project_browser_test\TestActivator - $this->assertContains('Cream cheese on a bagel was activated!', $this->container->get(StateInterface::class)->get('test activator')); - $this->assertContains('Kangaroo was activated!', $this->container->get(StateInterface::class)->get('test activator')); + $activated = $this->container->get(StateInterface::class) + ->get('test activator'); + $this->assertContains('Cream cheese on a bagel was activated!', $activated); + $this->assertContains('Kangaroo was activated!', $activated); } /** @@ -398,15 +305,15 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->container->get('module_installer')->install(['project_browser_devel'], TRUE); $this->drupalGet('project-browser/project_browser_test_mock'); - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $select_button1 = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $select_button1->click(); + $this->waitForProject('Cream cheese on a bagel') + ->pressButton('Select Cream cheese on a bagel'); $this->assertNotEmpty($assert_session->waitForButton('Install selected projects')); - $random_data = '#project-browser .pb-layout__main ul > li:nth-child(2)'; - $select_button2 = $assert_session->waitForElementVisible('css', "$random_data button.pb__action_button"); - $this->assertNotEmpty($select_button2); - $select_button2?->click(); + $projects = $this->getSession() + ->getPage() + ->findAll('css', '.pb-project'); + $this->assertGreaterThanOrEqual(2, count($projects)); + $projects[1]->find('css', '.pb__action_button')?->press(); $this->assertNotEmpty($assert_session->waitForButton('Install selected projects')); } @@ -421,12 +328,8 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { ]); $this->installState->deleteAll(); $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $this->assertNotEmpty($download_button); - $this->assertSame('Install Cream cheese on a bagel', $download_button->getText()); - $download_button->click(); + $this->waitForProject('Cream cheese on a bagel') + ->pressButton('Install Cream cheese on a bagel'); $unlock_url = $assert_session->waitForElementVisible('css', "#unlock-link")->getAttribute('href'); $path_string = parse_url($unlock_url, PHP_URL_PATH); $this->assertIsString($path_string); @@ -445,7 +348,6 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { public function testSelectDeselectToggleInModal(): void { $assert_session = $this->assertSession(); $this->drupalGet('project-browser/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Helvetica'); $assert_session->waitForButton('Helvetica')?->click(); // Click select button in modal. $assert_session->elementExists('css', '.pb-detail-modal__sidebar_element button.pb__action_button')->click(); @@ -456,18 +358,13 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { // Close the modal. $assert_session->waitForButton('Close')?->click(); $assert_session->elementNotExists('xpath', '//span[contains(@class, "ui-dialog-title") and text()="Helvetica"]'); - $select_button = $assert_session->waitForElementVisible('css', "#project-browser .pb-layout__main ul > li:nth-child(7) button.pb__action_button"); - $this->assertNotEmpty($select_button); - // Asserts that the project is selected. - $was_selected = $select_button->waitFor(10, fn ($button) => $button->getText() === 'Deselect Helvetica'); - $this->assertTrue($was_selected); + $this->assertTrue($this->waitForProject('Helvetica')->hasButton('Deselect Helvetica')); } /** * Tests that the install state does not change on error. */ public function testInstallStatusUnchangedOnError(): void { - $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); // Start install begin. @@ -475,19 +372,15 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { 'query' => ['source' => 'project_browser_test_mock'], ]); $this->drupalGet('admin/modules/browse/project_browser_test_mock'); - $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install while one is in progress, but not yet in // the applying stage. - $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; - $cream_cheese_button = $page->find('css', "$cream_cheese_module_selector button.pb__action_button"); - $cream_cheese_button?->click(); + $this->waitForProject('Cream cheese on a bagel') + ->pressButton('Install Cream cheese on a bagel'); // Close the dialog to assert the state of install button. $page->find('css', '.ui-dialog-titlebar-close')?->click(); - $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.pb__action_button"); - $this->assertNotEmpty($download_button); // Assertion that the install state does not change. - $this->assertSame('Install Cream cheese on a bagel', $download_button->getText()); - + $cream_cheese = $this->waitForProject('Cream cheese on a bagel'); + $this->assertTrue($cream_cheese->hasButton('Install Cream cheese on a bagel')); } } diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTestTrait.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTestTrait.php index b17b3420b452b1736449d5584b4df2a884d3afe0..b7f40a12032cdb796a5cb822c99024bc67653d96 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserUiTestTrait.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTestTrait.php @@ -11,6 +11,78 @@ use Behat\Mink\Element\NodeElement; */ trait ProjectBrowserUiTestTrait { + /** + * Installs a specific project. + * + * @param \Behat\Mink\Element\NodeElement|string $card + * The project's card element, or its exact name. + * @param int $timeout + * (optional) How many seconds to wait for the installation to finish. + * Defaults to 30. + * + * @see ::waitForProject() + * @see ::waitForProjectToBeInstalled() + */ + protected function installProject(NodeElement|string $card, int $timeout = 30): void { + if (is_string($card)) { + $card = $this->waitForProject($card); + } + $name = $card->find('css', '.pb-project__title')?->getText(); + $this->assertNotEmpty($name); + $card->pressButton("Install $name"); + $this->waitForProjectToBeInstalled($card, $timeout); + } + + /** + * Waits for a specific project to be installed. + * + * @param \Behat\Mink\Element\NodeElement|string $card + * The project's card element, or its exact name. + * @param int $timeout + * (optional) How many seconds to wait for the installation to finish. + * Defaults to 30. + * + * @see ::waitForProject() + */ + protected function waitForProjectToBeInstalled(NodeElement|string $card, int $timeout = 30): void { + if (is_string($card)) { + $card = $this->waitForProject($card); + } + $name = $card->find('css', '.pb-project__title')?->getText(); + $this->assertNotEmpty($name); + + $indicator = $card->waitFor( + $timeout, + fn (NodeElement $card): ?NodeElement => $card->find('css', '.project_status-indicator'), + ); + $was_installed = $indicator?->waitFor( + $timeout, + fn (NodeElement $indicator) => $indicator->getText() === "$name is Installed", + ); + $this->assertTrue($was_installed, "$name was not installed after waiting $timeout seconds."); + } + + /** + * Waits for a project card to appear, and returns it. + * + * @param string $name + * The full human-readable name of the project as it appears in the UI. + * @param int $timeout + * (optional) How many seconds to wait for the project to appear. Defaults + * to 10. + * + * @return \Behat\Mink\Element\NodeElement + * The project card element. + */ + protected function waitForProject(string $name, int $timeout = 10): NodeElement { + $element = $this->assertSession() + ->waitForElementVisible('css', ".pb-project__title:contains('$name')", $timeout * 1000) + ?->find('xpath', '..') + ?->find('xpath', '..'); + $this->assertNotEmpty($element); + return $element; + } + /** * Asserts that a table row element was dragged to another spot in the table. *