ElixirVault/tests/DataSetup/Traits/App/Helpers/tableLayoutTrait.php

566 lines
20 KiB
PHP

<?php
namespace LoretaAudit\Tests\DataSetup\Traits\App\Helpers;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\WebDriverKeys;
trait tableLayoutTrait
{
private $baseTableData = '#base_table > div.table-data';
private $colHeaderSelector = '#cms-table-drop-marker ul.thead';
private $tableBodyContainer = '#cms-table-drop-marker .table-rows .tbody';
private $tablePagerContainer = "//ul[@class='dx pager-cnt']";
public function testTableHeaderTitles(array $idToTitleText = null): void
{
if (is_null($idToTitleText)) {
return;
}
foreach ($idToTitleText as $id => $textToValidate) {
$this->assertTrue(
$this->elementExists("$this->colHeaderSelector #$id", self::CSS_SELECTOR),
"Failed locating element by CSS selector: $this->colHeaderSelector #$id"
);
switch ($id) {
case 'h_checkbox':
break;
case 'h_buttons':
$this->assertStringContainsString(
$textToValidate,
$this->byCssSelector("$this->colHeaderSelector #$id > a")->getText()
);
break;
default:
$this->assertEquals(
$textToValidate,
$this->byCssSelector("$this->colHeaderSelector #$id div")->getText()
);
break;
}
}
}
public function testTableRowBaseCheckboxes(): void
{}
public function testTableBaseFunctionality()
{
// Test header columns
// Test column sort
// Test column reorder
// Test rows per page
// Test column visibility
// Test row search
}
/** Table settings */
public function openTableSettings(string $id = 'iTableSettings', string $class = 'base-table-settings'): void
{
$headerSelector = "#dialogSlider.$class .slider-wrapper .header > h3";
if (
$this->elementExists($headerSelector, self::CSS_SELECTOR, true) &&
$this->byCssSelector($headerSelector)->isDisplayed()
) {
return;
}
$this->selectTableActionById($id);
$this->waitTableSlider($headerSelector);
}
public function closeTableSettings(): void
{
$headerSelector = '#dialogSlider .slider-wrapper .header > h3';
if (
$this->elementExists($headerSelector, self::CSS_SELECTOR) &&
$this->byCssSelector($headerSelector)->isDisplayed()
) {
$this->scrollElementIntoView($headerSelector, self::CSS_SELECTOR);
$this->byCssSelector("$headerSelector a.close")->click();
$this->elementWaitRemoved("$headerSelector a.close", self::CSS_SELECTOR);
}
}
public function updateTableSettingsViewMaxRows(int $rows = 40): void
{
$this->openTableSettings();
$this->setTableSettingsMaxRowsPerPage($rows);
$this->cmsConfirmDialogSliderActionYes();
$this->waitUntilDomReadyState();
$rowsListed = $this->getRemoteWebElements("$this->tableBodyContainer > ul", self::CSS_SELECTOR);
$this->assertTrue(count($rowsListed) <= $rows);
}
public function setTableSettingsMaxRowsPerPage(int $rowsPerPage = 10): void
{
$this->openTableSettings();
$inputSelector = '#dialogSlider .slider-wrapper #pBase #a_rec_per_page';
$this->scrollElementIntoView($inputSelector, self::CSS_SELECTOR);
$this->byCssSelector($inputSelector)->clear()->sendKeys($rowsPerPage);
$this->webDriver->executeScript('$("' . $inputSelector . '").focus().blur();');
}
public function selectTableSettingsCheckbox(string $id, bool $visibility): void
{
$chkSelector = "#dialogSlider .slider-wrapper .document-settings .inner-cnt > ul #$id";
$this->scrollElementIntoView($chkSelector, self::CSS_SELECTOR);
$element = $this->byCssSelector($chkSelector);
$isChecked = str_contains($element->getAttribute('class'), "chked");
if ($isChecked !== $visibility) {
$element->click();
}
}
public function selectTableRow(int $row): void
{
$chkSelector = $this->tableBodyContainer . " > ul:nth-child($row) > li.first > div > div.chk";
$this->toggleCheckbox($chkSelector);
}
public function resetTableSettingsColumnConfig(array $defaultConfig): void
{
$this->openTableSettings();
$this->scrollIntoViewHoverClick("#dialogSlider .slider-wrapper #reset_cfg", self::CSS_SELECTOR);
$this->waitUntilDomReadyState();
$this->elementWaitRemoved("#dialogSlider .slider-wrapper a.close", self::CSS_SELECTOR);
$this->testTableHeaderTitles($defaultConfig);
}
public function updateTableSettingsColumnsVisibility(array $config): void
{
$this->openTableSettings();
foreach ($config as $id => $data) {
$this->selectTableSettingsCheckbox($id, $data);
}
$this->cmsConfirmDialogSliderActionYes();
$this->waitUntilDomReadyState();
foreach ($config as $id => $visibility) {
$elementExists = $this->elementExists("$this->colHeaderSelector > #$id", self::CSS_SELECTOR, true);
$this->assertEquals($visibility, $elementExists, "Element id: $id");
}
}
public function reorderTableSettingsColumnConfig(array $orderedIds): void
{
$this->openTableSettings();
$allItemsSel = '#dialogSlider .slider-wrapper .document-settings .inner-cnt li';
$allItems = $this->getRemoteWebElements($allItemsSel, self::CSS_SELECTOR);
foreach ($orderedIds as $targetPosition => $itemId) {
$elementXpath = "//div[@id='dialogSlider']/div[2]//ul/li[.//*[@id='$itemId']]";
$targetPosition += 1; // First is reserved
$this->dragTableSettingListItemToPosition($elementXpath, $allItems, $targetPosition);
}
$this->cmsConfirmDialogSliderActionYes();
foreach ($orderedIds as $itemId) {
$exists = $this->elementExists("$this->colHeaderSelector > #$itemId", self::CSS_SELECTOR, true);
$this->assertTrue($exists, 'Failed on: ' . $itemId);
}
}
public function reorderTableColumn(array $orderedIds): void
{
foreach ($orderedIds as $targetPosition => $itemId) {
$elementXpath = "//*[@id='cms-table-drop-marker']//li[@id='$itemId']";
$allItemsSel = $this->colHeaderSelector . ' > li';
$allItems = $this->getRemoteWebElements($allItemsSel, self::CSS_SELECTOR);
$targetPosition += 2;
$this->dragTableSettingListItemToPosition($elementXpath, $allItems, $targetPosition);
}
foreach ($orderedIds as $itemId) {
$exists = $this->elementExists("$this->colHeaderSelector > #$itemId", self::CSS_SELECTOR, true);
$this->assertTrue($exists, 'Failed on: ' . $itemId);
}
}
public function dragTableSettingListItemToPosition(string $xpath, array $allItems, int $targetPosition): void
{
$sourceElement = $this->scrollElementIntoView($xpath, self::XPATH);
$currentPosition = $this->getElementPosition($sourceElement, $allItems);
if ($currentPosition === $targetPosition) {
return;
}
$targetElement = $allItems[$targetPosition];
$movingLeft = $currentPosition > $targetPosition;
// Target near edge of the target element — avoids misfiring on wide columns
// Offset is from the center: negative = left side, positive = right side
$targetWidth = $targetElement->getSize()->getWidth();
$xOffset = $movingLeft
? -intdiv($targetWidth, 2) + 5 // near left edge
: intdiv($targetWidth, 2) - 5; // near right edge
try {
$this->webDriver->action()
->clickAndHold($sourceElement)
->moveToElement($targetElement, $xOffset, 0)
->release()
->perform();
$this->waitUntilDomReadyState();
} catch (WebDriverException $e) {
$this->assertFalse(
sprintf(
"dragListItemToPosition failed.\nSource xpath: %s\nError: %s",
$xpath,
$e->getMessage()
)
);
}
}
public function waitTableSlider($headerSelector): void
{
$this->elementWait($headerSelector, self::CSS_SELECTOR);
$this->scrollElementIntoView($headerSelector, self::CSS_SELECTOR);
$exists = $this->elementExists($headerSelector, self::CSS_SELECTOR, true);
$this->assertTrue($exists, "Failed locating element by CSS selector: $headerSelector");
}
/** Table actions */
public function selectTableActionById($id): void
{
$this->scrollIntoViewHoverClick("$this->colHeaderSelector #h_buttons a", self::CSS_SELECTOR);
$this->scrollIntoViewHoverClick("#table-actions li#$id a", self::CSS_SELECTOR);
$this->waitUntilDomReadyState();
}
public function deleteTableRow($row, bool $scrollIntoView = false): void
{
$selector = $this->tableBodyContainer . " > ul:nth-child($row) > li.last.actions > div > a#ldelete";
$this->openConfirmDialog($selector, $scrollIntoView);
$this->cmsConfirmDialogActionYes();
}
public function editTableRow($row, bool $scrollIntoView = false): void
{
$selector = $this->tableBodyContainer . " > ul:nth-child($row) > li.last.actions > div > a#ledit";
if ($scrollIntoView) {
$this->scrollElementIntoView($selector, self::CSS_SELECTOR);
}
$this->byCssSelector($selector)->click();
$this->waitUntilDomReadyState();
}
public function editTableRowById(int $id, bool $scrollIntoView = false): void
{
$selector = $this->tableBodyContainer . " ul[id='$id'] > li.last.actions > div > a#ledit";
if ($scrollIntoView) {
$this->scrollElementIntoView($selector, self::CSS_SELECTOR);
}
$this->byCssSelector($selector)->click();
$this->waitUntilDomReadyState();
}
public function clickRowExcelExport($row, bool $scrollIntoView = false): void
{
$selector = $this->tableBodyContainer . " > ul:nth-child($row) > li > div > a.download_pdf";
if ($scrollIntoView) {
$this->scrollElementIntoView($selector, self::CSS_SELECTOR);
}
$this->byCssSelector($selector)->click();
$this->waitUntilDomReadyState();
}
public function clickRowExcelExportById(int $id, bool $scrollIntoView = false): void
{
$selector = $this->tableBodyContainer . " > ul[id='$id'] > li > div > a.download_pdf";
if ($scrollIntoView) {
$this->scrollElementIntoView($selector, self::CSS_SELECTOR);
}
$this->byCssSelector($selector)->click();
$this->waitUntilDomReadyState();
}
public function addNewRecord(): void
{
$this->scrollIntoViewHoverClick('.table-bactions ' . $this->getNewButtonId(), self::CSS_SELECTOR);
$this->waitUntilDomReadyState();
}
public function getNewButtonId(): string
{
return '#bButtonNew';
}
public function cloneTableRow(int $row): void
{
$this->selectTableRow($row);
$this->byId('clone')->click();
$this->waitUntilDomReadyState();
}
/** Table search */
public function openCmsTableSearch(): void
{
$this->byId("btnSearch")->click();
}
public function resetCmsTableSearch(): void
{
$this->byCssSelector(".sticky-header .search-hidden #tSearch > ul #tclear")->click();
$this->waitUntilDomReadyState();
}
public function searchCmsTableByColumn(string $columnID, string $value, bool $submit = true): void
{
if (!$this->getRemoteWebElement('tSearch')->isDisplayed()) {
$this->openCmsTableSearch();
}
$search = ".sticky-header .search-hidden #tSearch > ul";
$selector = "$search #$columnID";
$parentLi = $this->byCssSelector($selector)->findElement(WebDriverBy::xpath("./ancestor::li[1]"));
if (strpos($parentLi->getAttribute('class'), 'dropBox') !== false) {
$this->scrollIntoViewHoverClick("//input[@id='$columnID']/preceding-sibling::span[1]", self::XPATH);
$selectedValue = "//input[@id='$columnID']/ancestor::li//ul/li[@id='$value']";
$this->scrollIntoViewHoverClick($selectedValue, self::XPATH);
$this->waitUntilDomReadyState();
// We don't need to submit the search as DropBox auto submits the form.
} else {
$element = $this->byCssSelector($selector);
$element->click()->clear()->sendKeys($value);
if ($submit) {
$element->click()->sendKeys(WebDriverKeys::ENTER);
$this->waitUntilDomReadyState();
}
}
}
public function sortTableByColumn(string $columnID, string $orderWay = 'ASC'): void
{
$colSelector = "$this->colHeaderSelector #$columnID";
$this->assertTrue(
$this->elementExists($colSelector, self::CSS_SELECTOR),
"Failed locating element by CSS selector: $this->colHeaderSelector #$columnID"
);
if (
$orderWay == 'ASC' &&
(
$this->elementHasClass('sortDesc', $colSelector, self::CSS_SELECTOR) ||
!$this->elementHasClass('sort', $colSelector, self::CSS_SELECTOR)
)
) {
$this->byCssSelector($colSelector)->click();
$this->elementWait("$this->colHeaderSelector #$columnID.sort", self::CSS_SELECTOR);
}
if (
$orderWay == 'DESC' &&
!$this->elementHasClass('sortDesc', $colSelector, self::CSS_SELECTOR)
) {
if (!$this->elementHasClass('sort', $colSelector, self::CSS_SELECTOR)) {
$this->byCssSelector($colSelector)->click();
$this->elementWait("$this->colHeaderSelector #$columnID.sort", self::CSS_SELECTOR);
}
$this->byCssSelector($colSelector)->click();
$this->elementWait("$this->colHeaderSelector #$columnID.sortDesc", self::CSS_SELECTOR);
}
}
/** Footer helper functions **/
public function navigateThroughPager($expectedPages, $pageCallback = null, int $fix = 7)
{
$this->waitUntilDomReadyState();
$this->assertCurrentPage(1);
$this->assertPreviousButtonExists(false);
// If only 1 page, then run the callback and go back.
if ($expectedPages === 1) {
$this->assertNextButtonExists(false);
if ($pageCallback) {
call_user_func($pageCallback, 1);
}
return;
}
$this->assertNextButtonExists($this->shouldShowNextButton(1, $expectedPages));
// Navigate through each page - test the next button.
for ($page = 2; $page <= $expectedPages; $page++) {
if (!$this->shouldShowNextButton($page, $expectedPages)) {
$this->clickPageNumber($page);
} else {
$this->clickNext();
}
$this->waitUntilDomReadyState();
$this->assertCurrentPage($page);
$this->assertPreviousButtonExists($this->shouldShowPreviousButton($page, $expectedPages, $fix));
$this->assertNextButtonExists($this->shouldShowNextButton($page, $expectedPages, $fix));
if ($pageCallback) {
call_user_func($pageCallback, $page);
}
}
// Go back to page 1 - test the previous button
for ($page = $expectedPages - 1; $page >= 1; $page--) {
if ($this->shouldShowPreviousButton($page, $expectedPages,$fix)) {
$this->clickPrevious();
} else {
$this->clickPageNumber($page);
}
$this->waitUntilDomReadyState();
$this->assertCurrentPage($page);
$this->assertPreviousButtonExists($this->shouldShowPreviousButton($page, $expectedPages,$fix));
$this->assertNextButtonExists($this->shouldShowNextButton($page, $expectedPages,$fix));
}
}
public function shouldShowPreviousButton($currentPage, $totalPages, int $fix = 7): bool
{
/** Previous is displayed only if total pages over 7, and we are over the fifth page. */
return $totalPages > $fix && $currentPage > 4;
}
public function shouldShowNextButton($currentPage, $totalPages, int $fix = 7): bool
{
/** The Next button is displayed if there are more than 7 pages, and we're not on the last 4 pages. */
return $totalPages > $fix && $currentPage < $totalPages - 3;
}
public function clickNext(): void
{
$this->scrollIntoViewHoverClick(
"$this->tablePagerContainer/li[contains(@class, 'last')]//a[@class='nextp']",
self::XPATH,
);
}
public function clickPrevious(): void
{
$this->scrollIntoViewHoverClick(
"$this->tablePagerContainer/li[contains(@class, 'first')]//a[@class='prevp']",
self::XPATH,
);
}
public function clickPageNumber($pageNumber): void
{
$this->scrollIntoViewHoverClick("$this->tablePagerContainer/li[@id='$pageNumber']//a", self::XPATH);
}
public function assertPreviousButtonExists(bool $exists = true): void
{
$selector = "$this->tablePagerContainer/li[contains(@class, 'first')]//a[@class='prevp']";
$this->assertSame($exists, $this->elementExists($selector, self::XPATH, true));
}
public function assertNextButtonExists(bool $exists = true): void
{
$selector = "$this->tablePagerContainer/li[contains(@class, 'last')]//a[@class='nextp']";
$this->assertSame($exists, $this->elementExists($selector, self::XPATH, true));
}
public function assertCurrentPage($pageNumber): void
{
$selectedPage = $this->byXPath("$this->tablePagerContainer/li[contains(@class, 'sel')]");
$actualPage = (int)$selectedPage->getAttribute('id');
$this->assertEquals($pageNumber, $actualPage, "Wrong page. Expected: $pageNumber, actual: $actualPage");
// Check <a> has 'selected'
$link = $selectedPage->findElement(WebDriverBy::tagName('a'));
$msg = "Page $pageNumber link has not been marked as selected";
$this->assertStringContainsString('selected', $link->getAttribute('class'), $msg);
}
public function assertTablePagerCount(string $pager): void
{
$selector = "#cms-table-drop-marker .pager .rcount";
$this->assertStringContainsString($pager, $this->byCssSelector($selector)->getText());
}
public function toggleTableRowLastCheckbox(int $id, bool $tick = true): void
{
$chkSelector = "//ul[@id='$id']//li[not(contains(@class, 'first'))][last()]//div[@class='ncheckbox']";
$this->scrollElementIntoView($chkSelector, self::XPATH);
$element = $this->byXPath($chkSelector);
$isChecked = str_contains($element->getAttribute('class'), "chked");
if ($isChecked !== $tick) {
$element->click();
$this->waitUntilDomReadyState();
}
}
public function searchCmsTableByColumnId(int $id, bool $submit = true): void
{
$this->searchCmsTableByColumn('id', (string) $id, $submit);
}
public function sortCmsTableByColumnId(string $order = 'ASC'): void
{
$this->sortTableByColumn('h_id', $order);
}
public function validateTableOrderBy($page, array $expectedData): void
{
$col = 3;
foreach ($page->tableColumns as $tableColumn) {
if (in_array($tableColumn, $page->tableColumnsToSkip ?? [])) {
continue;
}
if (!isset($expectedData[$tableColumn])) {
$col++;
continue;
}
$this->sortTableByColumn($tableColumn);
$this->assertCmsTableRowData(1, $col, $expectedData[$tableColumn]['ASC']);
$this->sortTableByColumn($tableColumn, 'DESC');
$this->assertCmsTableRowData(1, $col, $expectedData[$tableColumn]['DESC']);
$col++;
}
}
}