Protractor
Probably every frontend developer have heard about Protractor. An end-to-end test framework for Angular. There are many other similar frameworks. However Protractor has one great feature when testing Angular application. It automatically waits for your website to be ready. It does not test your website in the middle of loading. Protractor knows when to wait and when to test.
Protractor can be used with any website. No matter if it is written in Angular, React, jQuery or a static html. To be able to do that you have to disable a synchronization in Protractor config file
onPrepare: function() {
browser.ignoreSynchronization = true;
}
This disables waiting forcing Protractor to test as quickly as possible, even before your page fully loads and β¦ most likely it will start failing.
The workaround is to do the waiting manually.
await browser.get("/login")
await $("#username").sendKeys("user");
await $("#password").sendKeys("password");
await $("#loginBUtton").click();
expect(await $("#message).getText()).toEqual("Welcome 'user'");
Code that is very clear and contains only user actions and expectations must be extended with waits, sleeps and timeouts
await browser.get("/login")
// Wait for page load by checking presence of login button
await browser.wait(EC.presenceOf($(#loginButton)));
await $("#username").sendKeys("user");
await $("#password").sendKeys("password");
await $("#loginButton").click();
// Wait for login call & new page render
await browser.sleep(2000);
expect(await $("#message).getText()).toEqual("Welcome 'user'");
This works but is very fragile. browser.sleep
waits 2 seconds. In that time the user will be most likely logged in (or maybe not). The usual "fix" is to use very long sleeps, wait for some specific page elements, or markers that your app injects into a page when it is ready or similar workarounds.
You might be wondering how is it possible that it is so easy with Angular and so complicated with other frameworks. Well Protractor has actually two parts. One is the Protractor itself and the other piece is in Angular framework. These two parts communicate together during a running E2E test and ensure that everything works.
Synctractor
There comes synctractor. A library that allows you to use Protractor with non-Angular apps (react, vue) and rely on build-in synchronization and automatic waiting. It wraps asynchronous calls (fetch
, setTimeout
) and provides needed information for Protractor during a test run by emulating the Angular part.
It is easy to use
- install it
npm i -save synctractor
- Add this to the very first line of your app entry point
import * as synctractor from 'synctractor';
synctractor.init();
synctractor.monitorFetch();
synctractor.monitorTimeout((_, t) => t !== 11000);
(see github for explanation of the magic number 11000)
That is it. You can remove browser.ignoreSynchronization = true;
from your Protractor config file and all sleeps from your spec files. Protractor will communicate with your app and wait when it is needed.
Check React and Vue examples in a synctractor repo
PS: Currently only fetch is supported. AJAX calls are not monitored and Protractor won't wait for it.
Morcatko / synctractor
Angular-Protractor synchronization for non-angular apps (React, Vue, ...)
Synctractor
Angular-Protractor synchronization for non-angular apps (React, Vue, ...)
Using this library you can get rid of almost all browser.sleeps
and browser.waits
in your protractor tests and relly on the same synchronization mechanism that is used by protract and angular.
Quick Setup
- Install synctractor
npm i --save synctractor
- Remove
ignoreSynchronization
from protractor config as it is not needed anymore - Add this as the very first lines of your app entry point
(see
import * as synctractor from 'synctractor'; synctractor.init(); synctractor.monitorFetch(); synctractor.monitorTimeout((_, t) => t !== 11000);
setTimeout
details bellow for explanation of this magic number)
Manual Mode
There is automatic mode (synctractor.monitorXXX()
) where you setup synctractor on your app entry point and that is all and there is also a manual mode, where you only initialize synctractor but you have to update calls all over your code. In automatic mode. you can get to unmonitored calls by synctractor.nativeXXX()
- β¦
Top comments (0)