End to End testing with PhantomJS and Angular

Testing is one of the most important parts of development and there is no argument to skip it. Building an angular application is not an exception and testing should be part of your development flow. Most of the logic should be covered by unit tests, but having these will not prevent us from deploying an application to production with some flaws. To avoid it as much as possible we should cover main business paths by e2e tests (sometimes called functional tests) as well.

It is very important that our tests are part of our continuous integration, running them manually poses too much risk. You can forget about them, you can have differently configured platforms on which are your tests running etc.

To run our tests on the CI, we had to make it running in PhantomJS.
Note: Chrome headless is available since version 59 but has still some issues so we in Erento cannot use it … yet!

I assume you are using the latest version of Angular and Angular CLI. To run e2e tests we will use Protractor and run it with Angular CLI.

Let’s start.

First we need to install PhantomJS and protractor as a dependency:
npm i --save-dev phantomjs-prebuilt protractor jasmine-spec-reporter

then we need to add a config file to the root directory. Let’s call it protractor.phantomjs.conf.js:

const {SpecReporter} = require('jasmine-spec-reporter');
process.env.LANG = 'en'; // set your browser language
exports.config = {
allScriptsTimeout: 110000,
specs: [
capabilities: {
'browserName': 'phantomjs',
'phantomjs.binary.path': require('phantomjs-prebuilt').path,
'phantomjs.cli.args': ['--remote-debugger-port=8081'],
'phantomjs.ghostdriver.cli.args': ['--loglevel=DEBUG'],
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 50000,
print: function () {}
useAllAngular2AppRoots: true,
beforeLaunch: function () {
project: 'e2e/tsconfig.e2e.json',
onPrepare: function () {
jasmine.getEnv().addReporter(new SpecReporter({spec: {displayStacktrace: true}}));

This config file requires to have an e2e folder in the root and to name our tests with the .e2e-spec.ts suffix. This is the same setup as in the newly generated code from Angular CLI.

Now we need to add the NPM script to run it. It is recommended to separate the execution script from the necessary setup so we are using the combination of the NPM script with the pre hook:

"e2e:ci": "npm run ng e2e -- --serve=true --config protractor.phantomjs.conf.js --webdriver-update=false",
"pree2e:ci": "npm run webdriver-manager update -- --gecko=false --chrome=false",

If you need a specific version of the standalone server, you can specify it with another flag in the pre-hook --versions.standalone=2.53.1 .

The next requirement to successfully run PhantomJS is to enable at least these polyfills (be aware that all the es6 polyfills are recommended when you want to run your app in IE):

import 'core-js/es6/object';
import 'core-js/es6/string';
import 'core-js/es6/array';

When you try to run the tests now in PhantomJS it will work until you use browser.sleep , browser.wait or browser.isElementPresent etc. With that you can, in some cases, find problems like:
✗ should show message has to be filled on non modified form
Error: Timeout — Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

To solve it create a file setup.e2e-spec.ts:

import {browser} from 'protractor';
beforeEach(() => {

This will execute before each test and will make the trick. Maybe someone knows why it helps, but it cost me a lot of time to find this solution. Now all your tests should work. If you encounter any issue, share it in the comments and hopefully we can make it work together.

As I said, there is already the possibility to use Chrome --headless. Unfortunately it doesn’t allow to switch a language or maximize a window, but it is just matter of time and I hope when they fix it we all will be happy again :)

We will never do enough of testing.

If you liked this, please click the ♥ below so it will reach other people here on Medium.