Synctractor - Testing React/Vue apps with Protractor 
Tags: javascript, testing, react, vue
Published: 2019-05-14 12:49:57

# 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
1. install it `npm i -save synctractor`
2. 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](https://github.com/Morcatko/synctractor#settimeout) 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](https://github.com/Morcatko/synctractor/tree/master/examples)

PS: Currently only fetch is supported. AJAX calls are not monitored and Protractor won't wait for it.


{% github Morcatko/synctractor %}