Acceptance Testing XXI - MODX 3 Code Changes

Making the code changes necessary for our test to run in MODX 3


In the previous two articles, we created new _bootstrap and PageObject files for MODX 3. The appropriate bootstrap file will run because we designated it in the modx2.yml and modx3.yml files.

Making Codeception use the appropriate PageObject files is a little trickier. We'll see how to do that in this article. We'll also create a new test that will run correctly in both MODX 2 and MODX 3.


MODX logo

This article assumes that you've installed and configured Composer, Codeception, PhpUnit, MODX, Java, WebDriver, and ChromeDriver as described in earlier articles, and that you've created the acceptance test support files from those articles.


New Test File

Our new test file will be very similar to our T17_ResourceProtectionFinalCest.php file, it will test the same things, but we'll add some small tweaks to make it run correctly in both MODX 2 and MODX 3.

To create the new test file, run this command in the _build directory:

codecept generate:cest acceptance T18_ResourceProtectionMODX3

You should see the test file (T18_ResourceProtectionMODX3.php) in the _build/tests/acceptance/ directory.

Replace everything in the file with this code (also available at GitHub here):

<?php
use Codeception\Util\Fixtures;
use Page\Acceptance\LoginPage;
use Page\Acceptance\LoginPagemodx3;
use Page\Acceptance\ResourceTestPage;
use Page\Acceptance\ResourceTestPagemodx3;

class T18_ResourceProtectionMODX3Cest
{
    /** @var _generated\modX $modx */
    public $modx;

    private const ROLES = array('TestUser');
    private const USER_GROUPS = array('PublicUsers',
        'PrivateUsers');
    private const RESOURCE_GROUPS =
        array('PublicResources', 'PrivateResources');

    public static function _before(\Step\Acceptance\Objects $I) {

        /* Load data files */
        $users = include codecept_data_dir() .
            '/user_data.php';

        $resources = include codecept_data_dir() .
            '/resource_data.php';

        $modx = Fixtures::get('modx');
        assertTrue($modx instanceof modX);
        $I->createRoles($modx, self::ROLES);
        $I->createUserGroups($modx, self::USER_GROUPS);
        $I->createUsers($modx, $users);
        $I->createResourceGroups($modx, self::RESOURCE_GROUPS);
        $I->createResources($modx, $resources);
    }

    public static function _after(\Step\Acceptance\Objects $I) {
     // return;  /* allows examination of objects and ACL */

        $users = include codecept_data_dir() .
            '/user_data.php';
        $resources = include codecept_data_dir() .
            '/resource_data.php';
        $modx = Fixtures::get('modx');
        assertTrue($modx instanceof modX);
        $I->removeRoles($modx, self::ROLES);
        $I->removeUserGroups($modx, self::USER_GROUPS);
        $I->removeUsers($modx, $users);
        $I->removeResourceGroups($modx, self::RESOURCE_GROUPS);
        $I->removeResources($modx, $resources);
    }

    public function ResourceProtectionTest(AcceptanceTester $I, \Codeception\Scenario $scenario)
    {
        $env = $scenario->current('env');

        if (strpos($env,'modx3') !== false) {
            $testPage = new ResourceTestPagemodx3($I);
        } else {
            $testPage = new ResourceTestPage($I);
        }

        $wait = 2;

        /* Login admin user JoeTester2 */
        if (strpos($env, 'modx3') !== false) {
            $loginPage = new LoginPagemodx3($I);
        } else {
            $loginPage = new LoginPage($I);
        }
        $loginPage->login('JoeTester2', 'TesterPassword');
        $I->see('Content');
        $I->see('Manage');
        $I->wait($wait);

        /* *** Create ACL entry *** */

        /* Go to ACL panel */
        $I->wait($wait + 1);
        $I->moveMouseOver($testPage::$systemMenu);
        $I->click($testPage::$systemMenu);
        $I->wait(1);
        $I->moveMouseOver($testPage::$acl_option);

        $I->click($testPage::$acl_option);
        $I->wait($wait);

        /* Update PrivateUser user group */
        $I->clickWithRightButton($testPage::$privateUsersGroup);
        $I->wait($wait+2);

        $I->click($testPage::$updateUserGroupOption);
        $I->wait($wait);

        /* Select Permissions top tab */
        $I->click($testPage::$permissionsTab);
        $I->wait($wait);

        /* Select Resource Group Access left tab */
        $I->click($testPage::$resourceGroupAccessTab);

        $I->wait($wait);

        /* Create actual ACL entry */
        $I->click($testPage::$addResourceGroupButton);

        $I->wait($wait);

        /* Set Resource Group */
        $I->click($testPage::$resourceGroupInput);
        $I->wait($wait);

        $I->click($testPage::$privateResourcesOption);

        /* Set Context */
        $I->click($testPage::$contextInput);
        $I->wait($wait);

        $I->click($testPage::$mgrOption);

        /* Set Role */
        $I->click($testPage::$authorityInput);

        $I->wait($wait+2);

        $I->click($testPage::$testUserOption);

        /* Set Policy */
        $I->click($testPage::$policyInput);
        $I->wait($wait);

        $I->click($testPage::$resourceOption);

        $I->wait($wait);

        /* Save ACL entry */
        $I->click($testPage::$addResourcePanelSaveButton);
        $I->wait($wait);
        $I->reloadPage();

        /* Make sure JoeTester2 can see PublicResource
           and can't see PrivateResource */
        $I->wait($wait+2);
        $I->see("PublicResource");
        $I->dontSee("PrivateResource");

        /* Logout JoeTester2 */
        $I->wait($wait);
        $loginPage->logout();
        $I->wait($wait);
        $I->see('Password');

        /* Login PrivateUser */
        $I->wait($wait);
        $loginPage->login('PrivateUser', 'somepassword');
        $I->see('Content');
        $I->see('Manage');
        $I->wait($wait);

        /* Make sure Private resource is visible */
        $I->wait($wait);
        $I->see("PublicResource");
        $I->see("PrivateResource");

        /* Logout PrivateUser */
        $I->wait($wait);
        $loginPage->logout();
        $I->wait($wait);
        $I->see('Password');

        /* Login PublicUser */
        $I->wait($wait);
        $loginPage->login('PublicUser', 'somepassword');
        $I->see('Content');
        $I->see('Manage');
        $I->wait($wait+2);

        /* Make sure Private resource is not visible */
        $I->see("PublicResource");
        $I->dontSee("PrivateResource");

        /* Logout PublicUser */
        $I->wait($wait);
        $loginPage->logout();
        $I->wait($wait);
        $I->see('Password');
    }
}

Code Changes

The first change is the addition of two new use statements at the top to let Codeception find the PageObject files:

use Page\Acceptance\LoginPagemodx3;
use Page\Acceptance\ResourceTestPagemodx3;

Another important change is to make Codeception use the correct PageObject files for the environment we're running in. Here's the code for that. We'll explain it below.

public function ResourceProtectionTest(AcceptanceTester $I, \Codeception\Scenario $scenario)
    {
        $env = $scenario->current('env');

        if (strpos($env,'modx3') !== false) {
            $testPage = new ResourceTestPagemodx3($I);
        } else {
            $testPage = new ResourceTestPage($I);
        }

        $wait = 2;

        /* Login admin user JoeTester2 */
        if (strpos($env, 'modx3') !== false) {
            $loginPage = new LoginPagemodx3($I);
        } else {
            $loginPage = new LoginPage($I);
        }

In the code above, first, notice the second argument to the test method in the first line: \Codeception\Scenario $scenario. The $scenario object holds information about the test itself. Codeception sends it as a second (optional) argument to all test methods. It is often omitted, but we need it here, because we can query it to get the environment information for the current run with this code:

$env = $scenario->current('env');

The line above returns a string containing the current environment. If you launched the test with --env chrome it will contain chrome. If you launched the test with --env chrome --env firefox, it will contain chrome on the first pass and firefox on the second pass.

Here's the code that loads the correct PageObject file:

if (strpos($env,'modx3') !== false) {
    $testPage = new ResourceTestPagemodx3($I);
} else {
    $testPage = new ResourceTestPage($I);
}

You might wonder why we use strpos() here instead of just $env='modx3'. It's because environments can be combined. For example, you might use --env firefox,modx3. That will run the test in MODX 3 using Firefox, but the environment string will not equal "modx3".

In the code below that, we do the same thing with the LoginPage object. The only other differences from our T17 test are a few longer wait times to make the test more reliable in MODX 3.

Important: You may remember that I started this whole project because of a suspected bug in the resource protection system in MODX 3. The test confirmed the bug. I reported it and am happy to announce that the test will now pass if you have the current version of MODX 3. The test was able to confirm that the fix worked.

If you are running an earlier version of MODX 3 alpha, the test will fail because the PublicUser can see the PrivateResource even though it's supposed to be protected. In that case the user can also edit, save changes to, and delete that resource. If the test fails for you with a message that $I->cantSee('PrivateResource) fails, it's not your fault. The bug is still there in the version you're using.


Running Options

For running any of our tests, you can combine environment options in a number of ways.

/* Run test in chrome and MODX 2 */
codecept run acceptance T18_ResourceProtectionMODX3Cest --env chrome,modx2

/* Run test in chrome and MODX 3 */
codecept run acceptance T18_ResourceProtectionMODX3Cest --env chrome,modx3

/* Run test in both chrome and firefox in MODX 2 */
codecept run acceptance T18_ResourceProtectionMODX3Cest --env chrome,modx2 --env firefox,modx2

/* Run test in both chrome and firefox in MODX 3 */
codecept run acceptance T18_ResourceProtectionMODX3Cest --env chrome,modx3 --env firefox,modx3

For our particular test, the browser always has to come first in these combinations (e.g., --env modx3,chrome won't work. This is because the URL set in the chrome.yml environment file will override the URL for MODX 3. The environment sections separated by commas are processed in the order they're written in.

It would be nice to be able to run the test in all four combinations like this:

codecept run acceptance T18_ResourceProtectionMODX3Cest --env chrome,modx2 --env firefox,modx2 --env chrome,modx3 --env firefox,modx3

Unfortunately, it won't work. The reason is that all tests are run in the same PHP process. This would work, except that the MODX constants like MODX_CORE_PATH can't be changed, so every pass uses the value set on the first pass. That means if the MODX 2 test runs first, the MODX 3 test will be looking for its files in the MODX 2 directories and vice versa, potentially causing some major trouble.

PhpUnit has a method for solving this problem, but it's somewhat unreliable and often doesn't work in Codeception. It also happens too late for our tests, because _bootstrap is run before the test actually starts.

The bottom line is that, 1) the browser must be listed first, and 2) you can't run tests for both MODX 2 and MODX 3 in the same command.

We'll see a workaround for that in the next article.


Coming Up

In the next article, we'll look at running a single test in multiple versions of MODX (MODX 2 and MODX 3) with a single command.



For more information on how to use MODX to create a web site, see my web site Bob's Guides, or better yet, buy my book: MODX: The Official Guide.

Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)



Comments (0)


Please login to comment.

  (Login)