2

I'm trying out TestCafe for an AngularJS (v1.6) application.

I have a button then when clicked, opens a modal (from UI bootstrap). This works fine when I try myself in Chrome.

<button class="btn" ng-click="open()">Open</button>

Our application requires user authentication, and the login page is not Angular-based. That phase of my test works fine.

However, when the actual test runs, it "clicks" the button but nothing happens.

I suspect, but can't prove, that it's clicked before AngularJS has properly initialized on the page.

With some research, I found the testcafe-angular-selectors project and a waitForAngular method but that appears to apply only to Angular2+.

import { Role, Selector } from 'testcafe';

const regularAccUser = Role('http://127.0.0.1:8080', async t => {
    await t
        .typeText('[name=username]', 'abc')
        .typeText('[name=password]', '123')
        .click('.btn-primary');
});

fixture`Characters Modal`;

test('modal title', async t => {
    await t
        .useRole(regularAccUser)
        .navigateTo('http://127.0.0.1:8080/fake/page')
        .click('.btn')
        .expect(Selector('.modal-title').innerText).eql('Insert Symbol');
});

Adding .wait(1000) before the click solves the issue. It's not waiting for Angular to load. I'd rather not have waits in every test - is there some other technique I can use?

Alex Skorkin
  • 4,264
  • 3
  • 25
  • 47
helion3
  • 34,737
  • 15
  • 57
  • 100

2 Answers2

3

You can use TestCafe assertions as a mechanism to wait until an element is ready before acting on it.

A typical waiting mechanism would be:

const button = Selector('button.btn')
    .with({visibilityCheck: true});

await t
    .expect(button.exists) // wait until component is mounted in DOM
    .ok({timeout: 10000})  // wait enough time 
    .hover(button)         // move TestCafe cursor over the component
    .expect(button.hasAttribute('disabled'))
    .notOk({timeout: 10000}) // wait until the button is enabled
    .click(button); // now we are sure the button is there and is clickable

This article may also help you in managing all those waiting mechanisms.

hdorgeval
  • 3,000
  • 8
  • 17
2

As you correctly mentioned, the waitForAngular method is intended for Angular only, not for AngularJS. I recommend you create your own waitForAngularJS function and call it on the beforeEach hook and after the role was initialized.

In the simplest case, it can be implemented as follows:

function waitForAngularJS (t) {
    await t.wait(1000);
}

fixture `App tests`
    .page('page with angularjs')
    .beforeEach(async t => {
        await waitForAngularJS(t);
    });

However, the use of the wait method is not a solid solution. I recommend you find a way to detect if AngularJS is loaded on a page on the client side. If it is possible, you can implement the waitForAngularJS method using the TestCafe ClientFunctions mechanism.

This post can be useful as well: How to check if angular is loaded correctly

Alex Kamaev
  • 6,198
  • 1
  • 14
  • 29
  • I've tried this route, but I think what's happening is that because it's in beforeEach, it triggers before the `useRole` authentication, and then never fires on the actual page I'm trying to test. Our app has a non-angular login form. On login, we redirect the user to another page in the app. Unless I use `navigateTo` in my test, tc never goes to the right page. I need this code to execute after that navigation, not before the test. – helion3 Feb 25 '19 at 20:34
  • I think you can specify 'about:blank' as a page, set the 'preserveUrl' flag to 'true' and use 't.navigateTo` and `waitForAngular` inside the role. – Andrey Belym Apr 10 '19 at 12:39