/** * Mink support function: assert a warning message in the lightbox. * * @param Element $page Page element * @param string $message Expected message * * @return void */ protected function assertLightboxWarning(Element $page, $message) { $warning = $page->find('css', '.modal-body .alert-danger .message'); if (!$warning || strlen(trim($warning->getText())) == 0) { $warning = $this->findCss($page, '.modal-body .alert-danger'); } $this->assertEquals($message, $warning->getText()); }
/** * Have to be called after get the MultipleElement to find all its siblings * @param \Behat\Mink\Element\Element $parent * @return $this */ public function setParent(\Behat\Mink\Element\Element $parent) { $selectorType = key($this->selector); $locator = $this->selector[$selectorType]; $this->siblings = $parent->findAll($selectorType, $locator); if ($this->valid()) { $this->setInstance(); } return $this; }
/** * Returns exception message with additional context info. * * @return string */ public function __toString() { try { $pageText = $this->trimString($this->element->getHtml()); $string = sprintf("%s\n\n%s%s", $this->getMessage(), $this->getResponseInfo(), $this->pipeString($pageText . "\n")); } catch (\Exception $e) { return $this->getMessage(); } return $string; }
/** * {@inheritdoc} * * @throws TimeoutException */ public function fillField($locator, $value, Element $modal = null) { $selectContainer = $this->spin(function () use($modal) { return $modal->find('css', '.select2-container'); }); $placeholder = $selectContainer->find('css', sprintf('.select2-chosen:contains("%s")', $locator)); if ($placeholder) { $this->fillSelect2Field($selectContainer, $value); } else { parent::fillField($locator, $value, $modal); } }
protected function findLinkByContent(Element $page, $link) { $literal = $this->session->getSelectorsHandler()->xpathLiteral($link); return $page->find('xpath', './/a[normalize-space(.)=normalize-space(' . $literal . ')]'); }
/** * Asserts a particular row from the cache purge rules overview. * * @param Element $row * The table row. * @param array $expected_rule * The expected values for the cache purge rule. */ protected function assertOverviewCachePurgeRule(Element $row, array $expected_rule) { /** @var Element[] $cells */ $cells = $row->findAll('css', 'td'); assert($cells[0]->getText(), equals($expected_rule['Content Type'])); assert($cells[1]->getText(), equals($expected_rule['Paths to Purge'])); }
protected function getContext() { return $this->element->getOuterHtml(); }
/** * Retrieve a table row containing specified text from a given element. * * @param \Behat\Mink\Element\Element * @param string * The text to search for in the table row. * * @return \Behat\Mink\Element\NodeElement * * @throws \Exception */ public function getTableRow(Element $element, $search) { $rows = $element->findAll('css', 'tr'); if (empty($rows)) { throw new \Exception(sprintf('No rows found on the page %s', $this->getSession()->getCurrentUrl())); } foreach ($rows as $row) { if (strpos($row->getText(), $search) !== FALSE) { return $row; } } throw new \Exception(sprintf('Failed to find a row containing "%s" on the page %s', $search, $this->getSession()->getCurrentUrl())); }
/** * This method allows to fill a compound field by passing the label in reversed order separated * with whitespaces. * * Example: * We have a field "$" embedded inside a "Price" field * We can call fillField('$ Price', 26) to set the "$" value of parent field "Price" * * @param string $labelContent * @param string $value * @param Element $element * * @return null */ public function fillField($labelContent, $value, Element $element = null) { $subLabelContent = null; if (false !== strpbrk($labelContent, '€$')) { if (false !== strpos($labelContent, ' ')) { list($subLabelContent, $labelContent) = explode(' ', $labelContent); } } if ($element) { $label = $element->find('css', sprintf('label:contains("%s")', $labelContent)); } else { $label = $this->find('css', sprintf('label:contains("%s")', $labelContent)); } if (null === $label) { return parent::fillField($labelContent, $value); } if ($label->hasAttribute('for')) { if (false === strpos($value, ',')) { $for = $label->getAttribute('for'); if (0 === strpos($for, 's2id_')) { // We are playing with a select2 widget if (null !== ($field = $label->getParent()->find('css', 'select'))) { return $field->selectOption($value); } // Maybe it's an ajax select2? if (null !== ($link = $label->getParent()->find('css', 'a.select2-choice'))) { $link->click(); // Wait for the ajax request to finish $this->getSession()->wait(5000, '!$.active'); // Select the value in the displayed dropdown if (null !== ($item = $this->find('css', sprintf('#select2-drop li:contains("%s")', $value)))) { return $item->click(); } } throw new \InvalidArgumentException(sprintf('Could not find select2 widget inside %s', $label->getParent()->getHtml())); } elseif (preg_match('/_date$/', $for)) { $this->getSession()->executeScript(sprintf("\$('#%s').val('%s').trigger('change');", $for, $value)); } else { $field = $this->find('css', sprintf('#%s', $for)); if ($field->getTagName() === 'select') { $field->selectOption($value); } else { if (strpos($field->getAttribute('class'), 'wysiwyg') !== false) { $this->getSession()->executeScript(sprintf("\$('#%s').val('%s');", $for, $value)); } else { $field->setValue($value); } } } } else { foreach (explode(',', $value) as $value) { $label->getParent()->find('css', 'input[type="text"]')->click(); $this->getSession()->wait(100000, "\$('div:contains(\"Searching\")').length == 0"); $option = $this->find('css', sprintf('li:contains("%s")', trim($value))); if (!$option) { throw new \InvalidArgumentException(sprintf('Could not find option "%s" for "%s"', trim($value), $label->getText())); } $option->click(); } } } else { if (!$subLabelContent) { throw new \InvalidArgumentException(sprintf('The "%s" field is compound but the sub label was not provided', $labelContent)); } // it is a compound field, so let's expand the values $this->expand($label); $field = $this->findPriceField($labelContent, $subLabelContent); $field->setValue($value); } }
/** * Extracts and return the label NodeElement, identified by $field content and $element * * @param string $field * @param Element $element * * @return \Behat\Mink\Element\NodeElement */ protected function extractLabelElement($field, $element) { $subLabelContent = null; $labelContent = $field; if (false !== strpbrk($field, '€$')) { if (false !== strpos($field, ' ')) { list($subLabelContent, $labelContent) = explode(' ', $field); } } if ($element) { $label = $element->find('css', sprintf('label:contains("%s")', $labelContent)); } else { $label = $this->find('css', sprintf('label:contains("%s")', $labelContent)); } if (!$label) { $label = new \stdClass(); } $label->labelContent = $labelContent; $label->subLabelContent = $subLabelContent; return $label; }
/** * Check whether an element containing the specified text exists. * * @param Element $page Page element * @param string $selector CSS selector * @param string $text Expected text * * @return bool */ protected function hasElementsMatchingText(Element $page, $selector, $text) { foreach ($page->findAll('css', $selector) as $current) { if ($text === $current->getText()) { return true; } } return false; }
/** * Sets callnumber_handler to true * * @param string $type dewey or lcc * @param \Behat\Mink\Element\Element $page page element * @param boolean $expectLinks links on multiple? * * @return void */ protected function activateAndTestLinks($type, $page, $expectLinks) { // Single callnumbers (Sample) $this->changeConfigs(['config' => ['Catalog' => ['driver' => 'Sample'], 'Item_Status' => ['callnumber_handler' => $type]]]); $this->getMinkSession()->reload(); $this->snooze(); $link = $page->find('css', '.callnumber a,.groupCallnumber a,.fullCallnumber a'); $this->checkLink($link, $type); // Multiple callnumbers $this->setupMultipleCallnumbers(); $this->getMinkSession()->reload(); $this->snooze(); $link = $page->find('css', '.callnumber a,.groupCallnumber a,.fullCallnumber a'); if ($expectLinks) { $this->checkLink($link, $type); // TODO // if 'all' // - refresh until multiple // - test multiple // else } else { $this->assertTrue(is_null($link)); } }
/** * Returns exception message with additional context info. * * @return string */ public function __toString() { return $this->getMessage() . "\n\n" . $this->getResponseInfo() . $this->pipeString($this->trimString($this->element->getHtml()) . "\n"); }
/** * Assert that the "login required" message is visible in the lightbox. * * @param Element $page Page element * * @return void */ protected function checkForLoginMessage(Element $page) { $warning = $page->find('css', '.modal-body .alert-danger'); $this->assertTrue(is_object($warning)); $this->assertEquals('You must be logged in first', $warning->getText()); }
/** * Initialises exception. * * @param Element $element optional message * @param \Exception $exception exception */ public function __construct(Element $element, \Exception $exception) { $this->element = $element; parent::__construct(sprintf("Exception thrown by %s\n%s", $element->getXpath(), $exception->getMessage())); }
/** * Wait for an element to exist, then retrieve it. * * @param Element $page Page element * @param string $selector CSS selector * @param int $timeout Wait timeout (in ms) * * @return mixed */ protected function findCss(Element $page, $selector, $timeout = 1000) { $session = $this->getMinkSession(); $session->wait($timeout, "\$('{$selector}').length > 0"); $result = $page->find('css', $selector); $this->assertTrue(is_object($result)); return $result; }
/** * @param string $locator * @param string $selector * @param Element $container * * @return NodeElement * * @throws ElementNotFoundException */ protected function findElement($locator, $selector = 'xpath', Element $container = null) { if (null !== $container) { $field = $container->find($selector, $locator); } else { $field = $this->getSession()->getPage()->find($selector, $locator); } if (null === $field) { throw new ElementNotFoundException($this->getSession(), 'element', 'xpath', $this->getSession()->getSelectorsHandler()->xpathLiteral($locator)); } return $field; }
/** * Extracts and returns the label NodeElement, identified by $field content and $element * * @param string $field * @param Element $element * * @return NodeElement */ protected function extractLabelElement($field, $element) { $subLabelContent = null; $labelContent = $field; if (strstr($field, 'USD') || strstr($field, 'EUR')) { if (false !== strpos($field, ' ')) { list($subLabelContent, $labelContent) = explode(' ', $field); } } if ($element) { $label = $this->spin(function () use($element, $labelContent) { return $element->find('css', sprintf('label:contains("%s")', $labelContent)); }, 10, sprintf('unable to find label %s in element : %s', $labelContent, $element->getHtml())); } else { $label = $this->spin(function () use($labelContent) { return $this->find('css', sprintf('label:contains("%s")', $labelContent)); }, 10, sprintf('unable to find label %s', $labelContent)); } if (!$label) { $label = new \StdClass(); } $label->labelContent = $labelContent; $label->subLabelContent = $subLabelContent; return $label; }
/** * Extracts and return the label NodeElement, identified by $field content and $element * * @param string $field * @param Element $element * * @return \Behat\Mink\Element\NodeElement */ protected function extractLabelElement($field, $element) { $subLabelContent = null; $channel = null; $labelContent = $field; if (false !== strpbrk($field, '€$')) { if (false !== strpos($field, ' ')) { list($subLabelContent, $labelContent) = explode(' ', $field); } } if ($element) { $label = $this->spin(function () use($element, $labelContent) { return $element->find('css', sprintf('label:contains("%s")', $labelContent)); }); } else { $labeParts = explode(' ', $labelContent); $channel = in_array(reset($labeParts), ['mobile', 'ecommerce', 'print', 'tablet']) ? reset($labeParts) : null; if (null !== $channel) { $labelContent = substr($labelContent, strlen($channel . ' ')); } $label = $this->spin(function () use($labelContent) { return $this->find('css', sprintf('label:contains("%s")', $labelContent)); }); } if (!$label) { $label = new \stdClass(); } $label->channel = $channel; $label->labelContent = $labelContent; $label->subLabelContent = $subLabelContent; return $label; }
/** * Try to find value in element and retry for a given time. * * @param Element $element * @param string $selectorType xpath|css * @param string $value * @param int $timeout */ protected function findOrRetry(Element $element, $selectorType, $value, $timeout = 10000) { if ($timeout <= 0) { return; } $item = $element->find($selectorType, $value); if ($item) { return $item; } else { $this->getSession()->wait(100); return $this->findOrRetry($element, $selectorType, $value, $timeout - 100); } }
/** * Helper function for facets lists * * @param \Behat\Mink\Element\Element $page Mink page object * @param int $limit Configured lightbox length * @param bool $exclusionActive Is facet exclusion on? * * @return void */ protected function facetListProcedure($page, $limit, $exclusionActive = false) { $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-count .js-facet-item'); $this->assertEquals($limit, count($items)); $excludes = $page->findAll('css', '#modal #facet-list-count .badge .fa-times'); $this->assertEquals($exclusionActive ? $limit : 0, count($excludes)); // more $this->findCss($page, '#modal .js-facet-next-page')->click(); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-count .js-facet-item'); $this->assertEquals($limit * 2, count($items)); $this->assertEquals('Weird IDs 9 ' . 'Fiction 7 ' . 'The Study Of P|pes 1 ' . 'The Study and Scor_ng of Dots.and-Dashes:Colons 1 ' . 'The Study of "Important" Things 1 ' . 'The Study of %\'s? 1 ' . 'The Study of +\'s? 1 ' . 'The Study of @Twitter #test 1 ' . 'more ...', $this->findCss($page, '#modal #facet-list-count')->getText()); $excludes = $page->findAll('css', '#modal #facet-list-count .badge .fa-times'); $this->assertEquals($exclusionActive ? $limit * 2 : 0, count($excludes)); // sort by title $this->findCss($page, '[data-sort="index"]')->click(); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-index .js-facet-item'); $this->assertEquals($limit, count($items)); // reset number of items $this->assertEquals('Fiction 7 ' . 'The Study Of P|pes 1 ' . 'The Study and Scor_ng of Dots.and-Dashes:Colons 1 ' . 'The Study of "Important" Things 1 ' . 'more ...', $this->findCss($page, '#modal #facet-list-index')->getText()); $excludes = $page->findAll('css', '#modal #facet-list-index .badge .fa-times'); $this->assertEquals($exclusionActive ? $limit : 0, count($excludes)); // sort by index again $this->findCss($page, '[data-sort="count"]')->click(); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-count .js-facet-item'); $this->assertEquals($limit * 2, count($items)); // maintain number of items // When exclusion is active, the result count is outside of the link tag: $expectedLinkText = $exclusionActive ? 'Weird IDs' : 'Weird IDs 9'; $weirdIDs = $this->findAndAssertLink($page->findById('modal'), $expectedLinkText); $this->assertEquals($expectedLinkText, $weirdIDs->getText()); // apply US facet $weirdIDs->click(); $this->snooze(); }
/** * Select all of the items currently in the cart lightbox. * * @param Element $page Page element * * @return void */ protected function selectAllItemsInCart(Element $page) { $cartSelectAll = $page->find('css', '.modal-dialog .checkbox-select-all'); $cartSelectAll->check(); }
/** * Submit the login form (assuming it's open). * * @param Element $page Page element. * * @return void */ protected function submitLoginForm(Element $page) { $button = $page->find('css', '.modal-body .btn.btn-primary'); $this->assertNotNull($button); $button->click(); }