3

I am working on the development of an AngularJS app, and have recently been asked to look at integrating some automated testing into our development process. I have not used automated testing at all before, so am still getting to grips with it.

The framework that I have decided to use is Protractor, and I'm just looking at starting to write some basic tests, however, I'm having a bit of trouble getting hold of some of the HTML elements from within the spec.js file where my tests will be written. I have the following describe block in spec.js:

describe('My App', function() {
    var loginForm = element(by.id('loginForm'));
    var loginBtn = element(by.className('form-control'));
    var usernameInputField = element(by.id('inputName'));
    var passwordInputField = element(by.id('inputPass'));

    it('should hit the VM hosted site', function() {
        browser.get('http://192.168.x.xyz:8080);
        expect(browser.getTitle()).toEqual('My app');
    });

    //Test to log in:
    it('should enter the username into the username field on log in form', function() {
        usernameInputField.sendKeys('username');
        passwordInputField.sendKeys('password');
    });
});

My conf.js file currently looks like this:

exports.config = {
    framework: 'jasmine',
    seleniumAddress: 'http://localhost:4444/wd/hub',
    specs: ['spec.js'],
    multiCapabilities: {
        browserName: 'firefox'
    }
}

When I run the command protractor conf.js from the command line, I get some output that says:

Failed: No element found using locator: By(css selector, "[id="inputName"])

I have 'inspected' the input field for the username on the form in the browser, and it shows that the HTML element's 'name' attribute is 'inputName'.

I don't understand why the console is giving me the error that says it's trying to find the element using a 'CSS selector'- I'm expecting it to try and find the element by it's HTML tag's 'name' attribute... Clearly I'm doing something wrong in how I'm writing the 'test to log in', because if I comment that out, and run protractor conf.js again, I get 0 failures- so the other test is running successfully...

What am I doing wrong here? How can I get the form input fields and enter correct log in details into them to check that the 'log in' feature works correctly?

As an aside, although I will want to test the 'log in' capability too, is there a way to 'skip' this during testing, so that I can run the tests on all of the features of the app, without Protractor having to log in to the app every time to run the tests?

Edit

I've done what was suggested by @KatePaulk in their answer, and my test now looks like this:

var usernameInputField = element(by.name('inputName'));
var passwordInputField = element(by.name('inputPass'));
...
function loginAsUser(username, password) {
    username = "abc";
    password = "xyz";
    usernameInputField.sendKeys(username);
    passwordInputField.sendKeys(password);

    loginBtn.click();
}

it('should log the user in to app', function() {
    loginAsUser(username, password);
});

and while the script is running, although the username & password fields are correctly populated on the log in form, I get an error message when the script is trying to press the 'Log In' button, to submit the form:

W/element - more than one element found for locator By(css selector, .form-control) = the first result will be used

If I continue the script after this, the browser window is closed, and the script exits.

What am I doing wrong with how I am retrieving & clicking the 'log in' button?

Edit

As requested, the HTML for the form is:

<div class="page-signin">
  <div class="signin-header">
    <section class="logo text-center">
      <h1>Login to {{main.brand}}</h1>
    </section>
  </div>

  <div class="signin-body">
    <div class="container">
      <div class="form-container" data-ng-controller="LoginCtrl">
        <div ng-show="errors.general || errors.username || errors.password"
             class="alert alert-danger">
            {{errors.general || errors.username || errors.password}}
        </div>
        <form class="form-horizontal"
              data-ng-submit="login()"
              name="loginForm">
          <fieldset>
            <div class="form-group">
              <div class="input-group input-group-first">
                <span class="input-group-addon"
                      ng-class="{'login-error-icon': errors.username}">
                  <span class="ti-user"></span>
                </span>
                <input type="text" um-autofocus
                   class="form-control input-lg"
                   data-ng-model="user.username"
                   placeholder="Username"
                   autocapitalize="off"
                   name="inputName">
             </div>
           </div>
            <div class="form-group">
              <div class="input-group">
                <span class="input-group-addon"
                      ng-class="{'login-error-icon': errors.password}">
                  <span class="ti-lock"></span>
                </span>
                <input type="password"
                   class="form-control input-lg"
                   data-ng-model="user.password"
                   placeholder="Password"
                   name="inputPass">
              </div>
           </div>
            <div class="form-group">
                <div class="input-group login-checkbox">
                    <span class="input-group-addon">
                        <label class="ui-checkbox">
                            <input data-ng-model="persistent" type="checkbox"/>
                            <span></span>
                        </label>
                    </span>
                    <label class="form-control input-lg">
                        <input data-ng-model="persistent" type="checkbox"/>
                        <span>Remember me</span>
                    </label>
                </div>
           </div>
           <div class="divider divider-sm"></div>
           <div class="form-control">
              <input type="submit"
                 class="btn btn-brand text-center pull-right"
                 value="Log in"/>
          </div>
          </fieldset>
        </form>

      </div>
    </div>
  </div>
</div>
5

As an aside, although I will want to test the 'log in' capability too, is there a way to 'skip' this during testing, so that I can run the tests on all of the features of the app, without Protractor having to log in to the app every time to run the tests?

You have multiple ways of handling that, but using beforeAll() to provide setup for your it() tests inside a describe() is a common way to approach the problem:

describe("Your test", function () {
    beforeAll(function () {
        LoginAsUser('username', 'password');
        // wait for an expected component after login
    });

    afterAll(function () {
        LogOut();  // log out after all tests are done?
    });

    it('should test something', function() {

    });

    it('should test something else', function() {

    }); 
});

Failed: No element found using locator: By(css selector, .form-control.submit)

Your locator is not correct - currently, you are looking for an element with both form-control and submit classes, but form-control is a class of a div container and submit is not even a class but a type attribute value. Use instead:

.form-control > input[type=submit]
3

HTML id is not the same as HTML name. It's common to have fields where the name and the id are different:

<input id="user_name" name="inputName">

If you change your code to find the fields to element(by.name('inputName')); and element(by.name('inputPass')); it should find them.

The way you handle common routines like logging on is to use something like the Page Object Pattern, where the code that performs the actions is separate from the tests, so your login test would be something like:

it('should enter user credentials and click the login button', function() {
    LoginAsUser('username', 'password');
    // wait for an expected component after login, then assert that the user is logged in
});

Elsewhere, you'd define the function LoginAsUser(username, password) to find the fields, enter the text, and click the login button.

Then, when you're testing something else, you simply start the test with a call to LoginAsUser() followed by whatever else you need to do.

  • Thanks for your answer. I've defined a loginAsUser(username, password) function, as you suggested, and am now calling that function as the first step of my 'Log In test'. As the test executes, I can see that the username & password values are entered into the form correctly, however, I then get an error message that says: Failed: No element found using locator: By(css selector, .form-control.submit) when trying to simulate a click to the form's 'Submit' button. I'll update my OP to show the test script now. – Noble-Surfer Sep 22 '17 at 12:54
  • OP updated to show the issues I'm getting when trying what you've suggested. I think it's heading in the right direction, but probably needs something else too... – Noble-Surfer Sep 22 '17 at 13:15
  • @someone2088 could you include the HTML representation of the form into the question? – alecxe Sep 22 '17 at 13:18
  • Form HTML added to the OP – Noble-Surfer Sep 22 '17 at 13:26
  • @someone2088 addressed your latest issue in my answer (was too long for a comment), check it out. Thanks. – alecxe Sep 22 '17 at 14:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.