Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EP-5420: More thorough affiliate widgets tests #190

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions src/tests/SeleniumBrowser.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace Organic;

use Exception;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\WebDriverExpectedCondition;
use Facebook\WebDriver\WebDriverWait;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\Chrome\ChromeOptions;
Expand All @@ -16,6 +18,7 @@

const WP_LOGIN_URL = WP_HOME . '/wp-login.php';
const WP_NEW_POST_URL = WP_HOME . '/wp-admin/post-new.php';
const WP_POSTS_HOME = WP_HOME . '/wp-admin/edit.php';

class SeleniumBrowser {

Expand Down Expand Up @@ -316,4 +319,62 @@ function fillParagraphBlock( int $index = 0, string $text = '' ) {
$this->fillTextInput( $this->getElementIfItExists( 'p', true )[$index], $text );
}

/**
* @return void
*/
function refreshPage(): void {
$this->driver->navigate()->refresh();
}

/**
* @return void
* @throws Exception
*/
function savePostDraft() {
$this->click( 'button[aria-label="Save draft"]' );
// Once saved, we are redirected to a URL with a post ID. We wait for the save to complete.
$condition = WebDriverExpectedCondition::urlContains( 'post=' );
$this->waitForCondition( $condition );
}

/**
* @return string
*/
function getCurrentPostID(): string
{
$url = $this->driver->getCurrentUrl();
preg_match('/post=(\d+)(&|$)/', $url, $matches );
return $matches[1];
}

/**
* Delete posts corresponding to post IDs. This assumes the posts are all on the front
* page of the WP Admin Post Editor list.
* @return void
* @throws Exception
*/
function deletePosts( array $postIDs ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see any way to permanently delete posts without first moving them to the trash. Note that we don't have access to functions like wp_delete_post() because we aren't in the WordPress instance.

$this->openPage( WP_POSTS_HOME );
# Select all the posts to move to the trash.
foreach ( $postIDs as $postID ) {
$this->click( "#cb-select-{$postID}" );
}
# Specify we want to move them to the trash.
$this->click('#bulk-action-selector-top' );
$this->click('option[value="trash"]' );
# Move them to the trash.
$this->click('#doaction' );
# Now we need to go to the Trash tab.
$this->click('li.trash' );
# Select all the posts to delete permanently.
foreach ( $postIDs as $postID ) {
$this->click( "#cb-select-{$postID}" );
}
# Specify we want to delete them.
$this->click('#bulk-action-selector-top' );
$this->click('option[value="delete"]' );
# Delete them.
$this->click('#doaction' );
}

}
135 changes: 120 additions & 15 deletions src/tests/test_affiliate/WidgetsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,134 @@ class WidgetsTest extends TestCase {
* Note that these tests will fail for non-Gutenberg WordPress (< version 5).
*/

// Keep track of posts created during the tests so that we can delete them on tear down.
static $postIDsToDelete = [];

static function tearDownAfterClass(): void
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See https://docs.phpunit.de/en/10.0/fixtures.html for documentation on PHPUnit's tearDownAfterClass.

{
$browser = SeleniumBrowser::getTestBrowser();
try {
$browser->deletePosts( WidgetsTest::$postIDsToDelete );
} catch ( Exception $e ) {
error_log( 'Failed to delete test posts: ' . $e->getMessage() );
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't really matter for CI, but should be flagged for local dev.

} finally {
$browser->quit();
parent::tearDownAfterClass();
}
}

function wordPressVersionTooLow() : bool {
if ( !empty( WP_VERSION ) && intval( substr( WP_VERSION, 0, 1 ) ) < 5 ) {
return true;
}
return false;
}

function checkWidgetIsAvailable( $blockType ) {
/**
* When the user selects an Organic Affiliate widgets block, an IFrame with login fields will appear.
* This function logs the test user in so that we can customize and insert the widget.
* @param SeleniumBrowser $browser
* @return void
* @throws Exception
*/
private function logIntoWidgetsSelectionIFrame( SeleniumBrowser $browser ) {
$iframe = $browser->getOrganicIframe();
$browser->switchToIframe( $iframe );
# Log in with test account.
$browser->fillTextInput( '#email', ORGANIC_TEST_USER_EMAIL );
$browser->fillTextInput( '#password', ORGANIC_TEST_USER_PASSWORD );
$browser->click( '#signin-button' );
}

/**
* Search for and select the Test Product in our Organic Affiliate widgets IFrame.
* @param SeleniumBrowser $browser
* @return void
* @throws Exception
*/
private function selectTestProduct( SeleniumBrowser $browser ) {
$test_product_guid = TEST_PRODUCT_GUID;
$browser->fillTextInput( '#affiliate-product-search-entry', 'Selenium Test Product' );
try {
$browser->click("[data-test-element=\"affiliate-product-select-{$test_product_guid}\"]" );
} catch ( Exception $e ) {
$browser->quit();
$this->fail( 'Was Organic Demo\'s Selenium Test Product deleted? ' . $e->getMessage() );
}
}

/**
* Search for and select the Test Product offer link in our Organic Affiliate widgets IFrame.
* @param SeleniumBrowser $browser
* @return void
* @throws Exception
*/
private function selectTestProductOfferLink( SeleniumBrowser $browser ) {
$browser->fillTextInput( '#affiliate-product-search-entry', 'Selenium Test Product' );
$browser->click( '[data-test-element="affiliate-offer-button"]' );
}

/**
* Certain selectors are based on, e.g., product-card rather than organic-affiliate-product-card.
* This returns the shorter version.
* @param string $blockType
* @return false|string
*/
private function truncateBlockType( string $blockType ) {
return substr( $blockType, strlen('organic-affiliate-' ) );
}

/**
* Once the widget is customized as wanted, confirm and insert the widget into the editor.
* @param SeleniumBrowser $browser
* @param string $blockType
* @return void
* @throws Exception
*/
private function confirmWidgetSelection( SeleniumBrowser $browser, string $blockType ) {
$blockTypeTruncated = $this->truncateBlockType( $blockType );
$browser->click( "[data-test-element=\"affiliate-create-{$blockTypeTruncated}-confirm\"]" );
$browser->switchToDefaultContext();
}

/**
* Find and return the Organic Affiliate widgets iframe.
Copy link
Contributor Author

@Mwindo Mwindo Jul 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make this comment slightly more explicit since there are two iframes, the widget selection iframe (the editor logs in and customizes the widget) and the widget display iframe ("the widget"--what will be rendered on the actual site).

* @return mixed
* @throws Exception
*/
function getRenderedWidgetIFrame( SeleniumBrowser $browser, string $blockType ) {
$blockTypeTruncated = $this->truncateBlockType( $blockType );
return $browser->waitFor(
"div[data-organic-affiliate-integration={$blockTypeTruncated}] > iframe", null
);
}

/**
* @param $blockType
* @return void
*/
function checkWidgetInsertion( $blockType ) {
if ( $this->wordPressVersionTooLow() ) {
$this->fail( 'WordPress version ' . WP_VERSION . ' does not support custom blocks.' );
}
$browser = SeleniumBrowser::getTestBrowser();
try {
$browser->goToNewPost();
$browser->addBlock( $blockType );
// Check the Organic iframe appears.
$browser->getOrganicIframe();
$this->logIntoWidgetsSelectionIFrame( $browser );
$this->selectTestProduct( $browser );
$this->confirmWidgetSelection( $browser, $blockType );
# First, check that the widget is rendered (as an IFrame) upon insertion.
$this->getRenderedWidgetIFrame( $browser, $blockType );
// Next, we'll check that the widget is still rendered after refreshing the page.
// To refresh the page, we need to save.
$browser->savePostDraft();
// We mark the post for eventual deletion when we tear down the tests.
WidgetsTest::$postIDsToDelete[] = $browser->getCurrentPostID();
$browser->refreshPage();
// Check to see that the widget is rendered after refreshing the page.
$this->getRenderedWidgetIFrame( $browser, $blockType );

$browser->quit();
$this->assertTrue( true );
} catch ( Exception $e ) {
Expand All @@ -49,15 +160,15 @@ function checkWidgetIsAvailable( $blockType ) {
* @group selenium_test
*/
public function testProductCardAvailable() {
$this->checkWidgetIsAvailable( 'organic-affiliate-product-card' );
$this->checkWidgetInsertion( 'organic-affiliate-product-card' );
}

/**
* Test that the product carousel block is available.
* @group selenium_test
*/
public function testProductCarouselAvailable() {
$this->checkWidgetIsAvailable( 'organic-affiliate-product-carousel' );
$this->checkWidgetInsertion( 'organic-affiliate-product-carousel' );
}

/**
Expand All @@ -83,16 +194,10 @@ public function testInsertMagicLink() {
$browser->click( '[aria-label="Organic Tools"]' );
// Then we click the submenu item.
$browser->click( 'button[role="menuitem"]' );
// The Organic iframe should appear.
$iframe = $browser->getOrganicIframe();
$browser->switchToIframe( $iframe );
# Log in with test account.
$browser->fillTextInput( '#email', ORGANIC_TEST_USER_EMAIL );
$browser->fillTextInput( '#password', ORGANIC_TEST_USER_PASSWORD );
$browser->click( '#signin-button' );
# Search for the test product.
$browser->fillTextInput( '#affiliate-product-search-entry', 'Selenium Test Product' );
$browser->click( '[data-test-element="affiliate-offer-button"]' );
# The Organic IFrame should appear. We log in.
$this->logIntoWidgetsSelectionIFrame( $browser );
# Then we search for and select the test product.
$this->selectTestProductOfferLink( $browser );
# We move out of the iframe and check that the link has been added for the test product.
$browser->switchToDefaultContext();
$browser->wait();
Expand Down