2
  • OS: macOS Sierra 10.12.4
  • Node: v7.9.0
  • Npm: 5.0.3
  • Cucumber-js: 2.3.0
  • Protractor: 4.0.14
  • TypeScript: 2.2.2
  • webpack-dev-server: 2.4.5

i have a problem with running e2e tests. When I try to get an element from the page:

const el = browser.findElement(by.id('app-name'));
el.getText().then(function (text) {
    console.log(text);
    callback();
});

using 'browser' I get this error:

> test-app@0.0.1 bdd:test /Users/p24/PhpstormProjects/alex-angular2
> node ./node_modules/.bin/protractor

(node:28198) DeprecationWarning: os.tmpDir() is deprecated. Use os.tmpdir() instead.
[13:03:31] I/hosted - Using the selenium server at http://selenium-standalone-chrome:4444/wd/hub
[13:03:31] I/launcher - Running 1 instances of WebDriver
Feature: : adding an advertisement

  @web-test
  Scenario: : adding new advertisement
  ✔ Given I am on the main page
  ✔ When I click the add advertisement button
  ✖ Then I should see the form to add an advertisement

Failures:

1) Scenario: : adding new advertisement - features/adding_advertisement.feature:5
   Step: Then I should see the form to add an advertisement - features/adding_advertisement.feature:8
   Step Definition: features/step_definitions/advertisement.ts:32
   Message:
     Error: function timed out after 5000 milliseconds
         at Timeout._onTimeout (/Users/p24/PhpstormProjects/alex-angular2/node_modules/cucumber/lib/user_code_runner.js:91:22)
         at ontimeout (timers.js:386:14)
         at tryOnTimeout (timers.js:250:5)
         at Timer.listOnTimeout (timers.js:214:5)

1 scenario (1 failed)
3 steps (1 failed, 2 passed)
0m05.026s
Cucumber HTML report report/html/cucumber_report_hierarchy.html generated successfully.
Modified files: 
[13:03:47] E/protractor - Could not find Angular on page http://alex.local/ : retries looking for angular exceeded
[13:03:47] E/launcher - Angular could not be found on the page http://alex.local/. If this is not an Angular application, you may need to turn off waiting for Angular. Please see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load
[13:03:47] E/launcher - Error: Angular could not be found on the page http://alex.local/. If this is not an Angular application, you may need to turn off waiting for Angular. Please see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load
    at /Users/p24/PhpstormProjects/alex-angular2/node_modules/protractor/built/browser.js:506:23
    at ManagedPromise.invokeCallback_ (/Users/p24/PhpstormProjects/alex-angular2/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:1379:14)
    at TaskQueue.execute_ (/Users/p24/PhpstormProjects/alex-angular2/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2913:14)
    at TaskQueue.executeNext_ (/Users/p24/PhpstormProjects/alex-angular2/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2896:21)
    at asyncRun (/Users/p24/PhpstormProjects/alex-angular2/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:2775:27)
    at /Users/p24/PhpstormProjects/alex-angular2/node_modules/protractor/node_modules/selenium-webdriver/lib/promise.js:639:7
    at process._tickCallback (internal/process/next_tick.js:109:7)
[13:03:47] E/launcher - Process exited with error code 199
npm ERR! code ELIFECYCLE
npm ERR! errno 199
npm ERR! test-app@0.0.1 bdd:test: `node ./node_modules/.bin/protractor`
npm ERR! Exit status 199
npm ERR! 
npm ERR! Failed at the test-app@0.0.1 bdd:test script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/p24/.npm/_logs/2017-06-10T11_03_47_694Z-debug.log

Here is my webpack.config:

'use strict';

const HtmlWebpack = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractPlugin = new ExtractTextPlugin({
   filename: 'main.css'
});
const CleanWebpack = require('clean-webpack-plugin');

const rootDir = path.resolve(__dirname);

module.exports = {
    devServer: {
        contentBase: path.resolve(rootDir, 'dist'),
        port: 3000,
        host: '0.0.0.0',
        disableHostCheck: true,
        public: '172.23.0.1'
    },
    watchOptions: {
        poll: true
    },
    devtool: 'source-map',
    entry: {
        "bundle": "./src/webpack.main.js",
        "plugins": "./src/plugins.js"
    },
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "[name].js",
        // publicPath: "/dist"
    },
    resolve: {
        extensions: [ '.js', '.ts' ]
    },
    module: {
        exprContextCritical: false,
        rules: [
            {
                test: /\.html$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: '[name].[ext]'
                        },
                    },
                    { loader: 'extract-loader' },
                    { loader: 'html-loader' }
                ],
                exclude: path.resolve(__dirname, 'src/index.html')
            },
            {
                test: /\index.html$/,
                loader: ['html-loader']
            },
            {
                test: /\.ts$/,
                loader: 'ts-loader',
                exclude: /node_modules/
            },
            {
                test: /\.(scss|css)$/,
                use: extractPlugin.extract({
                    use: ['css-loader', 'sass-loader']
                })
            },
            {
                test: /\.(jpg|png|gif|eot|ttf|svg|woff|woff2)$/,
                use: [{
                    loader: 'file-loader',
                    options: {
                        name: '[name].[ext]',
                        outputPath: 'img/',
                        publicPath: 'img/'
                    }
                }]
            },
        ],
    },
    plugins: [
        new CleanWebpack(['dist']),
        extractPlugin,
        new HtmlWebpack({
            template: 'src/index.html'
        }),
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery",
            "Tether": 'tether'
        })
    ],
};

My protractor.config:

// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

exports.config = {
    seleniumAddress: 'http://selenium-standalone-chrome:4444/wd/hub',

    baseUrl: 'http://alex.local',

    capabilities: {
        browserName:'chrome'
    },

    framework: 'custom',

    frameworkPath: require.resolve('protractor-cucumber-framework'),

    specs: [
        './features/*.feature'
    ],

    cucumberOpts: {
        require: ['./features/step_definitions/*.ts'],
        tags: [],
        strict: false,
        format: ["pretty", "json:report/json/cucumber_report.json"],
        dryRun: false,
        compiler: ["ts:ts-node/register"]
    },

    onPrepare: function () {
        browser.manage().window().maximize();
    }
};

And my step definition file where an error occurs:

'use strict';

import {browser, element, by, By, $, $$, ExpectedConditions} from 'protractor';

const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');

chai.use(chaiAsPromised);
const expect = chai.expect;

const {defineSupportCode} = require('cucumber');

    defineSupportCode(function(context: any) {

        const Given = context.Given;
        const When = context.When;
        const Then = context.Then;

        Given('I am on the main page', function (callback: any) {
            browser.get('http://alex.local');
            callback();
        });

        When('I click the add advertisement button', function (callback: any) {
            callback();
        });

        Then('I should see the form to add an advertisement', function (callback: any) {
            const el = browser.findElement(by.id('app-name'));
            el.getText().then(function (text) {
                console.log(text);
                callback();
            });
        });

    });

    export {};

Everything is serve by the docker. docker-compose.yml:

version: '2'
services:
  proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy-alex
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
  app:
     build: .
     command: npm run build
     environment:
       - NODE_ENV=development
       - VIRTUAL_HOST=alex.local
     volumes:
       - .:/usr/src/app
       - /usr/src/app/node_modules
     ports:
      - "8080:3000"
  selenium-chrome:
    image: selenium/standalone-chrome
    environment:
      - VIRTUAL_HOST=selenium-standalone-chrome
    ports:
      - "4444:4444"
Jakub Saleniuk
  • 405
  • 5
  • 15
  • Did you ever solve this? I'm running into the same problem. Can't use `browser.ignoreSynchronization = true;` for the reasons you listed below either. – Nxt3 Nov 27 '17 at 20:21
  • 1
    @Nxt3, yeah i solved this problem and my tests work, but I need to look back to the project to refresh my memory. I will try to explain the solution asap. – Jakub Saleniuk Dec 08 '17 at 07:32

4 Answers4

3

I think there are 2 "problems".

First of all, you are using protractor 4. To test Angular 2 with Protractor 4, you will need to use useAllAngular2AppRoots: true in your config, or, if it is possible, upgrade to Protractor 5.1.2. That should solve your second Angular Timeout issue.

Second, and that is your first timeout, you still have the CucumberJS default timeout on 5 seconds. That will always stop steps if they take longer than 5 seconds. By adding this code into a file, you can increase it.

// cucumber.config.js file
import { defineSupportCode } from 'cucumber';

defineSupportCode(({setDefaultTimeout}) => {
    setDefaultTimeout(11000);
});

Hope it helps.

Update:

Just to eliminate incorrect protractor syntax:

  • useAllAngular2AppRoots: true is not needed in protractor 5, only in 4, my fault that I didn't mention that
  • finding elements on a page with the "correct" syntax is with element(by.id('app-name') instead browser.findElement(by.id('app-name')), your syntax is "vanilla" webdriver syntax without waiting for Angular

Just 1 check question- Is your initial page really an Angular page?

Update after testing your project:

I tried to run your project but I get all different kind off errors. I can't serve the project with the provided commands

ERROR in Cannot read property 'getSymbolByModule' of undefined
ERROR in multi ./src/styles.scss
Module not found:... 

I thought to just run your tests to see what happens and something strange happend. The step Given('I am on the main page') succeeded, but no site was loaded. Then I got the same timeout on your Then('I should see the form to add an advertisement').

After looking at your step implementation, I think there it the initial error, you need to call the callback when the browser.get() is resolved, or return a promise, see below.

// Or resolve the callback
Given('I am on the main page', function(callback: any) {
  browser.get('http://alex.local').then(callback);
});

// Or return a promise
Given('I am on the main page', function() {
  return browser.get('http://alex.local');
});

If you use one of the above implementations, I think you will see that the first step is the problem, and not the Then('I should see the form to add an advertisement') in which you implemented the callback in a correct way.

Update after working app

Got it working now. With the npm run build (manual). Then changed the http://alex.local to http://localhost:3000.

When I run this test

Given('I am on the main page', function(callback: any) {
  // browser.ignoreSynchronization = true;
  browser.get('http://localhost:3000').then(callback);
});

When('I click the add advertisement button', function(callback: any) {
  callback();
});

Then('I should see the form to add an advertisement', function(callback: any) {
  const el = element(by.css('.btn.btn-default'));
  el.getText().then(function(text) {
    console.log(text);
    callback();
  });
});

It works, you will get this log

Feature: : adding an advertisement

  @web-test
  Scenario: : adding new advertisement
  ✔ Given I am on the main page
  ✔ When I click the add advertisement button
ADD AD
  ✔ Then I should see the form to add an advertisement

1 scenario (1 passed)
3 steps (3 passed)
0m00.897s

You need to start the server manual, I don't use Webpack, but normally I do this

const express = require('express');
const path = require('path');
const app = express();
const child_process = require('child_process');

const e2e = path.resolve(process.cwd(), './e2e-tests/config/');
const port = 5555;
const root = path.resolve(process.cwd(), './dist/prod/');

/**
 * Start a server
 */
class Protractor {
  server(port, dir) {
    app.set('port', port);
    app.use(express.static(dir));

    return new Promise((resolve) => {
      let server = app.listen(port, () => {
        resolve(server);
      });
    });
  }
}

/**
 * Start server and then run protractor
 */
(() => {
  process.env.LANG = 'en_US.UTF-8';
  const child = child_process.exec('npm run e2e');
  new Protractor()
    .server(port, root)
    .then((server) => {
      child.stdout.pipe(process.stdout);
      child.stderr.pipe(process.stderr);
      // Stop the server if Protractor crashes or we're done testing
      child.on('exit', () => {
        server.close();
      });
    });
})();

I think you also need to do this with your app. (I use ng-apimock for mocking)

Pang
  • 9,564
  • 146
  • 81
  • 122
wswebcreation
  • 2,365
  • 2
  • 10
  • 18
  • Ty very much for a reply. I updated Protractor to version 5, added useAllAngular2AppRoots and set it to true also I tried to set default timeout to 11 sec but I still get an error: [17:47:36] E/launcher - Error: Angular could not be found on the page http://alex.local/.If this is not an Angular application, you may need to turn off waiting for Angular. Please see https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load – Jakub Saleniuk Jun 10 '17 at 15:51
  • Yes it is, my components are loaded there and it works. I tried to use syntax element(by.id('app-name') but same error occurs. – Jakub Saleniuk Jun 10 '17 at 16:14
  • do you have an example project? – wswebcreation Jun 10 '17 at 16:14
  • Unfortunately I cannot upload this project on public. My check question: does Protractor support Angular4? – Jakub Saleniuk Jun 10 '17 at 16:20
  • Yep, I'm using it with Angular 4 for the Dutch Railways, they started using SemVer, so it's still "Angular 2" – wswebcreation Jun 10 '17 at 16:22
  • If I won't be able to resolve this problem I will upload somewhere a basic version of this project just with configuration and simple component. – Jakub Saleniuk Jun 10 '17 at 16:26
  • Well you are right. I am new in writing tests in Protractor. I changed the implementation by your suggestion. Now I get same error but on first step (Given). – Jakub Saleniuk Jun 11 '17 at 08:31
  • Can you see if the URL/site is also loaded? – wswebcreation Jun 11 '17 at 08:33
  • Did you try to run the project with command 'npm run build' or 'npm run start'? I use Webpack instead of SystemJS to build the bundles. So try with 'npm run build' command. – Jakub Saleniuk Jun 11 '17 at 08:36
  • Nothing happened. It just waits few seconds and then I get the error that Angular could not be found. – Jakub Saleniuk Jun 11 '17 at 08:38
  • That means the app isn't loaded when starting the URL. You first need to fix that, I couldn't get your app running on my local machine (Mac), see my logging above – wswebcreation Jun 11 '17 at 09:21
  • Please try to use webpack instead of systemJS as above - 'npm run build' - if I use 'ng serve' I get same error because I haven't configured systemJS to run app. – Jakub Saleniuk Jun 11 '17 at 09:53
  • Well I think I know what should I do to resolve this problem but still I do not know how. I use webpack and when I run protractor I do not see webpack to compile. I think I have to in some way run webpack to compile my app in onPrepare function in protractor. – Jakub Saleniuk Jun 11 '17 at 10:21
  • https://workingdevblog.wordpress.com/tag/protractor/ I found a great article. I did everything in this article. I run webpack-dev-server manually in protractor but still it does not work. – Jakub Saleniuk Jun 11 '17 at 10:50
  • I got it working, so you should also be able to do that, see my update above – wswebcreation Jun 11 '17 at 10:52
  • Well I solved this. Kind of. When I used browser directly instead of selenium server a site shows message: 'Invalid host header'. I changed webpack-dev-server to version 2.4.2 from version 2.4.5 and everything works fine. They added some kind of checking the host in version 2.4.3 to prevent web attacks. I do not know how to config webpack to protractor works. I will create another task to solve this issue. Anyway ty very much for help;) – Jakub Saleniuk Jun 11 '17 at 18:58
1

Please use browser.ignoreSynchronization = true;in your spec file inside the first describe block so that Protractor doesn't wait for Angular.

demouser123
  • 4,108
  • 9
  • 50
  • 82
  • 1
    I updated my protractor to version 5. Now it shows that browser.ignoreSynchronization is deprecated. I tried to use this anyway. Now I instantly get an error: [17:56:27] E/launcher - No element found using locator: By(css selector, *[id="app-name"]) – Jakub Saleniuk Jun 10 '17 at 15:57
  • I am using Protractor version 5.1.2, the latest as per the Protractor page and I don't see this deprecation warning anywhere. Also the change log doesn't mention anything. I am using Protractor 5.1.2 with Angular 4 without facing any issues. – demouser123 Jun 10 '17 at 16:41
1

I solved this problem. All you have to do is run a dev server containing your app before tests. This works using webpack. I don't know how to run dev server in protractor using SystemJS. If you know how, please let me know;) Here is my protractor.conf.js:

// Protractor configuration file, see link for more information
    // https://github.com/angular/protractor/blob/master/lib/config.ts
    var webpack = require('webpack');
    var WebpackDevServer = require('webpack-dev-server');
    var webpackConfig = require('./webpack.config');
    var deasync = require('deasync');
    var ip = require('ip');
    var server;
    var port = 5000;
    var baseUrl = 'http://' + ip.address() + ':' + port;
    function writeScreenShot(data, filename) {
        const stream = fs.createWriteStream(filename);
        stream.write(new Buffer(data, 'base64'));
        stream.end();
    }
    exports.config = {
        seleniumAddress: 'http://selenium-standalone-chrome:4444/wd/hub',
        baseUrl: baseUrl,
        useAllAngular2AppRoots: true,
        // directConnect: true,
        // chromeOnly:true,
        restartBrowserBetweenTests: true,
        ignoreUncaughtExceptions: true,
        beforeLaunch: () => {
            var isServerReady = false;
            var isBundleReady = false;
            var compiler = webpack(webpackConfig, () => {
                isBundleReady = true;
            });
            server = new WebpackDevServer(compiler, {});
            server.listen(port, () => {
                isServerReady = true;
            });
            deasync.loopWhile(() => !isServerReady || !isBundleReady);
        },
        afterLaunch: () => {
            server.close();
        },
        capabilities: {
            browserName: 'chrome',
        },
        framework: 'custom',
        frameworkPath: require.resolve('protractor-cucumber-framework'),
        specs: [
            './features/*.feature'
        ],
        cucumberOpts: {
            require: ['./features/step_definitions/*.ts'],
            tags: [],
            strict: false,
            format: ["pretty", "json:report/json/cucumber_report.json"],
            dryRun: false,
            compiler: ["ts:ts-node/register"],
        },
        onPrepare: function () {
            browser.ignoreSynchronization = true;
            browser.manage().window().setSize(1366, 768);
        }
    };
Jakub Saleniuk
  • 405
  • 5
  • 15
0

https://gitlab.com/Saleniuk/simple-app

  • Run command: 'npm install'
  • You can serve app with command 'npm run build' but then you must also serve the selenium-server on host selenium-standalone-chrome:4444 or run selenium-server on the other host and change the selenium address in the protractor config 'seleniumAddress'.
  • You can also run docker with 'docker-compose up -d'. Then you must set the hosts on you OS like this:
    • 127.0.0.1 alex.local
    • 127.0.0.1 selenium-standalone-chrome

I do not know how it works on the other systems but on the Mac you must set your actual host docker ip in the webpack config like this:

devServer: {
        contentBase: path.resolve(rootDir, 'dist'),
        port: 3000,
        host: '0.0.0.0',
        disableHostCheck: true,
        public: '172.24.0.1'
    },

In the 'public' you must set up your docker host ip.

Then you can run 'npm run bdd:test' to get the error with Angular not found. Write to me if you have any problem with running my app.

Jakub Saleniuk
  • 405
  • 5
  • 15