Protractor+Mocha fails suite with TypeError before

2019-03-05 03:58发布

问题:

Context

I'm exploring angular2 + angular-cli + typescript. My objective is to ensure that if I am doing an angular app or a node app in typescript I would be using the same testing technologies as legacy node apps that use mocha. To this end I am trying to reconfigure the angular-cli generated protractor.conf.js to use mocha instead of jasmine.

Question

How do you properly integrate angular-cli + mocha + protractor so that tests will execute with protractor actually providing the mocha specs useful browser/element/by components?

I've already changed the protractor.conf.js to use mocha and chai and the tests complete, however all interactions with protractor components fail i.e. element(by.css('app-root h1')).getText().

converted protractor.conf.js

exports.config = {
  allScriptsTimeout: 11000, // The timeout for a script run on the browser. 
  specs: [
    './e2e/**/*.e2e-spec.ts' // pattern for the test specs
  ],
  baseUrl: 'http://localhost:4200/', // base url of the SUT
  capabilities: {
    'browserName': 'chrome' // browser to use
  },
  directConnect: true, // selenium will not need a server, direct connet to chrome
  framework: 'mocha', // Use mocha instead of jasmine
  mochaOpts: { // Mocha specific options 
    reporter: "spec",
    slow: 3000,
    ui: 'bdd',
    timeout: 30000
  },
  beforeLaunch: function() { // Do all the typescript 
    require('ts-node').register({
      project: 'e2e/tsconfig.e2e.json'
    });
  },
  onPrepare: function() {}
};

Suspicion

It feels like the issue is that the suite is executing (and failing) before the the browser has even loaded the app. This could be the protractor/browser interaction which from what I understand should be agnostic to the spec that's being used. Perhaps this understanding is incorrect?

Example Source

I have a running example on GitHub which I am using to test and compare the conversion

  • Broken branch using mocha
  • Working master using jasmine

Investigation so far

Running protractor (via node) on the command line node node_modules\protractor\bin\protractor protractor.conf.js --stackTrace --troubleshoot shows us that the suite is configured and running with a truthy test, a skipped test, and the actual application tests

Suite results

We see that the truthy test is passing, the skipped test is skipped, and the application tests all fail with the same sort of error

1 passing 
1 pending
5 failing

 1) A descriptive test name that is irrelevant to the error:
     TypeError: obj.indexOf is not a function
      at include (node_modules\chai\lib\chai\core\assertions.js:228:45)
      at doAsserterAsyncAndAddThen (node_modules\chai-as-promised\lib\chai-as-promised.js:293:29)
      at .<anonymous> (node_modules\chai-as-promised\lib\chai-as-promised.js:271:25)
      at chainableBehavior.method (node_modules\chai\lib\chai\utils\overwriteChainableMethod.js:51:34)
      at assert (node_modules\chai\lib\chai\utils\addChainableMethod.js:84:49)
      at Context.it (e2e\search\search.e2e-spec.ts:14:40)
      at runTest (node_modules\selenium-webdriver\testing\index.js:166:22)
      at node_modules\selenium-webdriver\testing\index.js:187:16
      at new ManagedPromise (node_modules\selenium-webdriver\lib\promise.js:1067:7)
      at controlFlowExecute (node_modules\selenium-webdriver\testing\index.js:186:14)
  From: Task: A descriptive test name that is irrelevant to the error
      at Context.ret (node_modules\selenium-webdriver\testing\index.js:185:10)

It appears that the TypeError would be caused because the app itself never seems to have to time to actually load in the browser before the suite is complete

--troubleshoot output

[09:26:33] D/launcher - Running with --troubleshoot
[09:26:33] D/launcher - Protractor version: 5.1.1
[09:26:33] D/launcher - Your base url for tests is http://localhost:4200/
[09:26:33] I/direct - Using ChromeDriver directly...
[09:26:36] D/runner - WebDriver session successfully started with capabilities Capabilities {
  'acceptSslCerts' => true,
  'applicationCacheEnabled' => false,
  'browserConnectionEnabled' => false,
  'browserName' => 'chrome',
  'chrome' => { chromedriverVersion: '2.28.455520 (cc17746adff54984afff480136733114c6b3704b)',
  userDataDir: 'C:\\Users\\abartish\\AppData\\Local\\Temp\\scoped_dir4596_5000' },
  'cssSelectorsEnabled' => true,
  'databaseEnabled' => false,
  'handlesAlerts' => true,
  'hasTouchScreen' => false,
  'javascriptEnabled' => true,
  'locationContextEnabled' => true,
  'mobileEmulationEnabled' => false,
  'nativeEvents' => true,
  'networkConnectionEnabled' => false,
  'pageLoadStrategy' => 'normal',
  'platform' => 'Windows NT',
  'rotatable' => false,
  'takesHeapSnapshot' => true,
  'takesScreenshot' => true,
  'unexpectedAlertBehaviour' => '',
  'version' => '56.0.2924.87',
  'webStorageEnabled' => true }
[09:26:36] D/runner - Running with spec files ./e2e/**/*.e2e-spec.ts

Additional Info

Interestingly enough, if you run protractor with elementExplorer node node_modules\protractor\bin\protractor protractor.conf.js --stackTrace --troubleshoot --elementExplorer it will not run the tests but we do see the SUT resolve in the browser that gets launched. This I can't explain.

回答1:

First off, I am not that familiar with Mocha. However, I can get your tests to pass. This is what you'll need to do:

Set your dependencies

It sometimes is great to roll with the latest and greatest dependencies. The latest chai-as-promised did not work for me. I once tried to update the Protractor dependencies to the latest version of chai and chai-as-promised and ran issues. I had to downgrade your dependencies and ended up working with:

"chai": "~3.5.0",
"chai-as-promised": "~5.3.0",

These are the same versions as the Protractor package.json.

Use the onPrepare plugin

Set chai-as-promised before Protractor runs the test:

onPrepare: function() {
  let chai = require('chai');
  let chaiAsPromised = require("chai-as-promised");
  chai.use(chaiAsPromised);
  global.chai = chai;
}

Edit the test:

Add or modify the following.

app.e2e-spec.ts

import {RootPage} from './root/root.po';
let expect = global["chai"].expect;

  // change the following lines to have "eventually"
  expect(page.getParagraphText()).to.eventually.contain('Car search POC');

  // if you don't want to use "eventually"
  page.getParagraphText().then(paragraph => {
    expect(paragraph).to.contain('Car search POC');
  });

root.e2e-spec.ts:

let expect = global["chai"].expect;

describe('Home page', () => {

  // change the following lines to have "eventually"
  expect(page.getParagraphText()).to.eventually.include('Car search POC');
  expect(browser.getCurrentUrl()).to.eventually.include(homePage.uri());

home.e2e-spec.ts:

import {RootPage} from './root.po';
import {HomePage} from '../home/home.po';
import {WaitCondition} from '../wait.conditions';
let expect = global["chai"].expect;

  // change the following lines to have "eventually"
  expect(page.getParagraphText()).to.eventually.equal('Car search POC');

  // This line will not work. getInnerHtml has been deprecated by both
  // Protractor and selenium-webdriver.
  //
  // If you want to use something similar, do something like:
  // let i = browser.executeScript("return arguments[0].innerHTML;", element(locator));
  // This is noted in the CHANGELOG under the Protractor 5.0.0 release
  expect(page.getListingContent()).to.exist;

search.e2e-spec.ts

import {SearchPage} from './search.po';
let expect = global["chai"].expect;

  // change the following lines to have "eventually"
  expect(page.getParagraphText()).to.eventually.contain('Car search POC');

Console output

Here is my results from running your test. Note that "will have content" fails because getInnerHtml() is not a valid.

angular-cli-seed App
  ✓ will do normal tests
  ✓ will display its title

Home page
  ✓ will display its title
  - will have content

root page
  ✓ will display its title
  ✓ will redirect the URL to the home page

search page
  ✓ will display its title


6 passing (5s)
1 pending

This was a fun StackOverflow question to go through. It was easy to answer since you included a branch of what was not working. Happy testing!