Protractor syncing to an Angular page after redirection to Auth0 non-Angular page

前端 未结 2 1330
面向向阳花
面向向阳花 2020-12-12 01:31

I have an Angular web app that uses Protractor for e2e tests. I recently added OAuth0 authentication. I disable Angular synchronisation before redirection to the non-Angul

相关标签:
2条回答
  • 2020-12-12 02:04

    Answer - yes you can reenable angular in protractor. However, protractor doesn't always work with angular apps. So just make sure protractors synchronization will be working as it should with your app AND the page is ready for this.

    First, manually open the app, and get authorized. Then in console run getAllAngularTestabilities(). If this command in not available, protractor can't work with angular. If the command is successful, take a look at returned object, specifically at hasPendingMacrotasks and hasPendingMicrotasks properties of obj[0]._ngZone. If any of them is true, protractor can't work with this page. If both of them are false then you can proceed to the next step

    Now, when you now the page can talk to protractor with browser.waitForAngularEnabled(true) you need to implement the following method for your tests

    let login = async function (username, password) {
      await browser.waitForAngularEnabled(false);
      await browser.get(url);
      await $usernameInput.sendKeys(username);
      await $passwordInput.sendKeys(password);
      await $loginButton.click();
      // MOST IMPORTANTLY, YOU HAVE TO WAIT UNTIL YOUR APP FULLY LOADED
      await browser.wait(
        // whatever you need to wait for,
        timeout,
        'failure message'
      );
      // AND FINALLY
      await browser.waitForAngularEnabled(true);
    }
    

    I mean you don't have to have this method, I just showed you the order of actions you have to follow to achieve your results.

    Basically, the point is, when you login, make sure the non-angular login page is gone, and your angular page is fully loaded before reenabling waiting for angular.

    Two approaches you could use:

    • wait for all key elements to be present
    • or write a function which will be returning return !obj[0]._ngZone.hasPendingMacrotasks && !obj[0]._ngZone.hasPendingMicrotasks and pass it to the browser.wait
    0 讨论(0)
  • 2020-12-12 02:19

    My answer as fyi for all:

    It appears that when the createAuth0Client method of the auth0-spa-js SDK (which is used to implement Auth0 authentication on an Angular SPA app) is called following redirect from the Auth0 site (after successful authentication) that hasPendingMacroTasks is set to true and is never reset. That prevents Protractor syncing as explained above.

    I refactored my implementation of the auth0-spa-js to match the latest guide on the Auth0 website (using Observables as opposed to async-await) and the exact same problem exists.

    I looked into what createAuth0Client is doing: It has gotten a JWT (token) from the Auth0 server before it redirects to the app and when it is called it caches that token and sets a timer to delete that cache entry. The setTimeout call in createAuth0Client is wrapped by Angular Zone and that holds hasPendingMacroTasks true which prevents Protractor from getting notification that Angular has completed loading. The timer is set to the lifetime of the token which is a setting one sets in the Auth0 server so I tested this by setting the token timeout to 3s and hasPendingMacroTasks was reset to false and Protractor successfully sync'd after I waited 5s in my e2e tests. However Protractor would have to log in on the Auth0 server again once the timer expires before it can make API calls so shortening the timer is not a feasible workaround.

    I looked into running createAuth0Client outside the Angular NgZone but that proved extremely difficult if not impossible:

    1. You need access to the created Auth0 client instance and once you make it available in NgZone you see the hasPendingMacroTasks set to true again.
    2. You can't just make the SDK methods (eg login, logout) available back in NgZone as they need the client instance context to run ( I don't think you can hold state (the Auth0 client instance) outside the Angular zone and access it when you need it).
    3. You can't create a client instance and run an SDK function outside the Angular zone every time you need to run an SDK function as if you create more than one client instance you can get errors.
    0 讨论(0)
提交回复
热议问题