Acceptance Testing IV - Login

Logging in to the MODX in an acceptance test


In the previous article, we created a contact form in the MODX Manager to test with the acceptance test we wrote in the article before that. In this one, we'll look at logging in to the MODX Manager in an acceptance test.


MODX logo

This article assumes that you've installed and configured Composer, Codeception, PhpUnit, MODX, Java, WebDriver, and ChromeDriver as described in earlier articles.


Logging in

If you want to create a test for software that runs in the MODX Manager (such as a Custom Manager Page), or test the MODX Manager itself, you're going to need to log in to the Manager. Eventually, we'll need a standalone method we can call from any test that will log us in. As a first step, we'll just write a quick test of the Login process, making sure that incorrect credentials don't work, and correct ones do.

When automating the login sequence, the first step is to find ways of identifying the fields and buttons you'll be interacting with. The specification for an individual element in Codeception and WebDriver is called a "locator." Locators take a number of forms. We'll look first at finding the locators, then we'll look at specifying them in your acceptance code.


Finding the Locators

There are two methods for finding locators, and you probably know both of them already. One is to view the source code of the page (Ctrl-u in most browsers - or "View Source" on the menu). This works fine for simple sections like the Manager Login form. Here's an abbreviated version that leaves out the parts we don't want to interact with or test:

<form id="modx-login-form" action="" method="post">

    <div class="x-form-item login-form-item login-form-item-first">
        <label for="modx-login-username">Username</label>
        <div class="x-form-element login-form-element">
            <input type="text" id="modx-login-username"
                   name="username" autocomplete="on" autofocus value="" class="x-form-text
                    x-form-field" aria-required="true" required/>
        </div>
    </div>

    <div class="x-form-item login-form-item">
        <label for="modx-login-password">Password</label>
        <div class="x-form-element login-form-element">
            <input type="password" id="modx-login-password"
                   name="password" autocomplete="on"
                   class="x-form-text x-form-field"
                   aria-required="true" required/>
        </div>
    </div>

    <div class="login-cb-row">
        <div class="login-cb-col two">
            <button class="x-btn x-btn-small x-btn-icon-small-left
                primary-button x-btn-noicon login-form-btn"
                name="login" type="submit" value="1"
                id="modx-login-btn">Login
            </button>
        </div>
    </div>
</form>

(If we were doing a complete test of the login form, we'd include the "forgot password" and "remember me" sections, but we're not. We're just writing a simple test with correct and incorrect credentials.)

Looking at the code, we see three relevant CSS ids: modx-login-username, modx-login-password, and modx-login-btn. These are our three locators.

The second method for finding locators is to use the dev. tools built into various browsers. I use Chrome (and sometimes Firefox). For those, you can launch the dev. tool with Ctrl-shift-i, or F12 (those also work in Edge and some other browsers).

If you load the MODX Manager Login page (e.g., localhost/test/manager) and then launch dev. tools, at the top left of the dev. tools window, you'll see a rectangle pierced by a small arrow. If you click on that, then click on the element you want to find a locator for, dev. tools will highlight the code for that element in the window. You can find the id there. When pages get more complex (as in the Manager itself), using dev. tools can be a lot easier than wading through all the code.

There are also browser tools that will show you the locators, which can be very helpful when you need XPath locators, as you often do in the Manager. We'll see more on this in later articles.


Specifying a Locator

When you want to fill a field ($I->fillField()), see if something is visible ($I->see(), or click on something ($I-click(), you need to put the locator in the parentheses.

The best option, if it's available, is a CSS ID locator. For code of your own that will undergo acceptance testing, you'll want to create a CSS id for every element you might want to interact with. MODX has done this with the Manager Login page (though not for the Manager, because ExtJS creates dynamic ids that can change).

Using the id is not the only method, though it's the fastest and most reliable. Here are ways we could specify the username input:

/* CSS id attribute */
$I->fillField('#modx-login-username', 'JoeTester');

/* Label (assumes 'for' is used in the label tag) */
$I->fillField('Username', 'JoeTester');

/* CSS name attribute */
$I->fillField("input[name='username']", 'JoeTester');

/* XPath */
"//input[@type='text'][contains(@name, 'username')]"

You can also find things by class, but unless the class is unique, it can be tricky to make it work. We'll look at methods of specifying locators in more detail in a future article. Since we have an id attribute here, our Login test code is fairly simple. Create the test file with this command in the _build directory:

codecept generate:cest T13_Login

Edit the T13_LoginCest.php file and replace everything with this code (also available at GitHub, here).

<?php
use Codeception\Util\Fixtures;

class T13_LoginCest
{
    /** @var modX $modx */
    protected $modx;
    public function _before(AcceptanceTester $I)
    {
        // lexicon->load() crashes with no $_SESSION set
        $_SESSION['dummy'] = 'x';
        $this->modx = Fixtures::get('modx');
        $this->modx->lexicon->load('en:login');
    }

    // tests
    /** @throws Exception */
    public function testLogin(AcceptanceTester $I)
    {
        $errorMsg = $this->modx->lexicon('login_cannot_locate_account');
        $delay = 1;
        /* Incorrect username */
        $I->amOnPage('manager');
        $I->fillField('#modx-login-username', 'XJoeTester');
        $I->fillField('#modx-login-password', 'TesterPassword');
        $I->wait($delay);
        $I->click('#modx-login-btn');
        $I->see($errorMsg);
        $I->wait($delay);

        /* Incorrect password */
        $I->amOnPage('manager');
        $I->fillField('#modx-login-username', 'JoeTester');
        $I->fillField('#modx-login-password', 'XTesterPassword');
        $I->wait($delay);
        $I->click('#modx-login-btn');
        $I->see($errorMsg);
        $I->wait($delay);

        /* Correct credentials */
        $I->fillField('#modx-login-username', 'JoeTester');
        $I->fillField('#modx-login-password', 'TesterPassword');
        $I->wait($delay);
        $I->click('#modx-login-btn');
        $I->see('Content');
        $I->see('Manage');
        $I->dontSee('#modx-login-username');
        $I->dontSee('#modx-login-password');
        $I->dontSee($errorMsg);
        $I->wait($delay);
    }
}

In one of our integration tests, we added code to use the MODX object as a fixture in the integration _bootstrap.php file. We suggested moving that code to the master _bootstrap.php file in the _build/tests/ directory, but instead of doing that, just copy the _bootstrap.php file from the integration directory to the acceptance directory.

If you have the same code in the tests/_bootstrap.php file, remove or comment it out so the $modx object won't be created twice. Be sure that the tests/_bootstrap.php file still contains these lines:

require_once 'c:/xampp/htdocs/addons/vendor/autoload.php';
require_once 'c:/xampp/htdocs/addons/vendor/phpunit/phpunit/src/Framework/Assert/Functions.php';

In the _before() method of the code above, we get the $modx object from the fixtures array. The only reason we need MODX here is to run $modx->lexicon->load(), and later get the lexicon string used for the bad credentials error message in the current language. If we knew the language and it never changed, we could skip getting the $modx object and just hard-code the error message.

The rest of the code is fairly self-explanatory. We fill the two form fields and click on the submit button. The first two times, the credentials are wrong and we expect to see the error message. The third time, we enter the correct credentials, submit the form, then and check to make sure we've made it into the MODX Manager.

You've probably noticed a questionable design choice in our test code — we've hard-coded the usernames into our test. It works, but it isn't going to help use create any other tests that require a login. We'll see a better method in the next article.


Coming Up

In the next article, we'll take a look at Page Objects and make the login code into a page method that can be used with multiple test files.



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)