Angular4 + Protractor + Cucumber + TypeScript + weback-dev-server - 找不到角度

时间:2017-06-10 11:18:41

标签: angular webpack protractor e2e-testing cucumberjs

  • OS:macOS Sierra 10.12.4
  • 节点:v7.9.0
  • Npm:5.0.3
  • Cucumber-js:2.3.0
  • 量角器:4.0.14
  • TypeScript:2.2.2
  • webpack-dev-server:2.4.5

我遇到运行e2e测试的问题。当我尝试从页面中获取元素时:

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

使用'browser'我收到此错误:

> 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

这是我的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'
        })
    ],
};

我的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();
    }
};

我的步骤定义文件发生错误:

'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 {};

一切都是由码头工人提供的。搬运工-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"

4 个答案:

答案 0 :(得分:3)

我认为有2"问题"。

首先,您使用的是量角器4.要使用量角器4测试Angular 2,您需要在配置中使用useAllAngular2AppRoots: true,或者,如果可能,请升级到Protractor 5.1.2。这应该可以解决你的第二个角度超时问题。

其次,这是你的第一次超时,你仍然有5秒的CucumberJS默认超时。如果时间超过5秒,那将始终停止步骤。通过将此代码添加到文件中,您可以增加它。



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

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




希望它有所帮助。

<强>更新

只是为了消除不正确的量角器语法:

    量角器5中不需要
  • useAllAngular2AppRoots: true,只有4,我的错,我没有提到
  • 使用&#34;正确&#34;在页面上查找元素语法是element(by.id('app-name')而不是browser.findElement(by.id('app-name')),你的语法是&#34; vanilla&#34; webdriver语法,无需等待Angular

只需1个问题 - 您的初始页面是否真的是Angular页面?

测试项目后更新:

我试图运行你的项目,但我得到了所有不同类型的错误。我无法使用提供的命令

为项目提供服务
ERROR in Cannot read property 'getSymbolByModule' of undefined
ERROR in multi ./src/styles.scss
Module not found:... 

我想只是运行你的测试,看看发生了什么,发生了什么奇怪的事情。步骤Given('I am on the main page')成功,但未加载任何站点。然后我在Then('I should see the form to add an advertisement')上获得了相同的超时。

在查看您的步骤实现后,我认为存在初始错误,您需要在解析callback时调用browser.get(),或者返回一个承诺,请参阅下文。

&#13;
&#13;
// 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');
});
&#13;
&#13;
&#13;

如果您使用上述实现之一,我认为您会看到第一步是问题,而不是Then('I should see the form to add an advertisement')以正确的方式实现callback

工作应用后更新

现在就开始工作了。使用npm run build(手动)。然后将http://alex.local更改为http://localhost:3000

当我运行此测试时

&#13;
&#13;
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();
  });
});
&#13;
&#13;
&#13;

它有效,您将获得此日志

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

你需要启动服务器手册,我不使用Webpack,但通常我会这样做

&#13;
&#13;
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();
      });
    });
})();
&#13;
&#13;
&#13;

我认为您还需要在应用中执行此操作。 (我使用ng-apimock进行模拟)

答案 1 :(得分:1)

请在第一个browser.ignoreSynchronization = true;块内的spec文件中使用describe,以便Protractor不会等待Angular。

答案 2 :(得分:1)

我解决了这个问题。您所要做的就是在测试之前运行包含您的应用程序的开发服务器。这适用于使用webpack。我不知道如何使用SystemJS在量角器中运行dev服务器。如果你知道怎么做,请告诉我;)这是我的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);
        }
    };

答案 3 :(得分:0)

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

  • 运行命令:'npm install'
  • 您可以使用命令'npm run build'来提供app,但是您还必须在主机selenium-standalone-chrome上提供selenium-server:4444或在另一台主机上运行selenium-server并更改量角器中的selenium地址config'seleniumAddress'。
  • 您还可以使用'docker-compose up -d'运行docker。然后你必须在你的操作系统上设置主机,如下所示:
    • 127.0.0.1 alex.local
    • 127.0.0.1 selenium-standalone-chrome

我不知道它在其他系统上是如何工作的,但在Mac上你必须在webpack配置中设置你的实际主机docker ip,如下所示:

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

在'public'中,您必须设置docker host ip。

然后你可以运行'npm run bdd:test'来获取Angular未找到的错误。如果您在运行我的应用程序时遇到任何问题,请写信给我。