angular

Configuring and running Angular Protractor Tests

Recently I’ve been working on a digital party invites project. It’s written in Angular and both Unit testing and E2E testing. It uses firebase as the backend.

The unit testing part is easy, I use Karma with Jasmine, the setup was quite straight forward, you can read more about it here. However, I found the e2e testing with Protractor was quite a bit more complicated, here is how I did it.

Firstly you need to install protractor both globally and in the dev environment. Installing it globally makes $ protractor available.

Protractor runs on Selenium server, which can now be installed globally by running:

$ webdriver-manager update

Selenium requires Java runtime environment, if you already have Java installed, you should be able to run webdriver-manager start and a web server will start. If you get a popup error message saying you need to install Java runtime environment, rather than following the link to the oracle website and install the latest Java SDK, you should go to this link to install the Java for IOS. At the time of writing, Java6 for IOS is the latest version.

Now you should be able to run

$ webdriver-manager start.

The next step is to configure protractor using a config file. The name of the file can be anything, since when you are running the test, you are calling

$ protractor config-file-name.

A basic config file needs to know the framework you are using (in this case, jasmine), the address of the Selenium server, the browser in which you want the test to run, and your test spec file. The following is an example conf.js:

exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: [
    '*.js'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  baseUrl: 'http://localhost:8080',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true
  }
};

http://localhost:4444/wd/hub is the default selenium address.

Now we need to create a spec file that contains our test, let’s start with a simple one:

describe('Birthday invite', function() {
  it('should have a title', function() {
    browser.get('http://localhost:8080');
    expect(browser.getTitle()).toEqual('You are invited');
  });
});

Now if you run $ protractor conf.js, the test should pass.

Make Protractor wait

The issue I had was that my app queries the database on firebase and it took about 2 seconds to get the data ready. When I write a test testing the updated data, it returns empty because the data is not yet available.

For example, the following test:

describe("index", function () {
    var btnDecline = element(by.id('btn-decline')),
        intro = element(by.binding('intro'));
    it("should display correct message when decline button is clicked", function btnDeclineTest(){
    browser.get('http://localhost:8080/#/josh');
    expect(intro.getText()).toBe("Hi Josh, You are invited to an awesome Party!");
    btnDecline.click();
    expect(intro.getText()).toBe("Hi Josh, You won't be coming the awesome Party!");
    });

The test will not pass, and the error will be something like

expecting '' to be 'Hi Josh, You are invited to an awesome Party!'.

Another potential cause of error is when you use hide/show the button using css transition, since it takes time to finish the transition, the test will report “ElementNotVisibleError: element not visible” if it runs before the transition is finished.

There are many ways to make protractor wait, For example, we can create a promise on protractor and use flow.execute. WebDriverJS has the concept of control flows.You can register functions that return promises on the control flow, and the driver will take care of chaining them together:

  var flow = protractor.promise.controlFlow();
  function waitOne() {
    return protractor.promise.delayed(5000);
  }

  function sleep() {
    flow.execute(waitOne);
  }

Now we rewrite our test:

describe("index", function () {
    var btnDecline = element(by.id('btn-decline')),
        intro = element(by.binding('intro')),
        btnClose = element(by.id('close'));
    it("should display correct message when decline button is clicked", function btnDeclineTest(){
    browser.get('http://localhost:8080/#/josh');
    sleep();
    expect(intro.getText()).toBe("Hi Josh, You are invited to an awesome Party!");
    btnDecline.click();
    sleep();
    btnClsoe.click();
    expect(intro.getText()).toBe("Hi Josh, You won't be coming the awesome Party!");
    });

Now the test will wait for 5s after page load, so the data can be ready, and wait for another 5s after a button is clicked, so the close button will appear.

protractor project protractor project

when we run protractor conf.js, all test should pass!

Another way is to use browser.wait() to check if certain element present:

browser.wait(element(by.id('some-element')).isPresent);

You can read more about browser.wait() here.

You can checkout the project here, or the source code on github.

 

Leave a Reply

Your email address will not be published. Required fields are marked *