7
votes

I want to do e2e testing on a hello world app in Angular2 with specs written in typescript. However, protractor crashes with the following message:

Using the selenium server at http://localhost:4444/wd/hub
[launcher] Running 1 instances of WebDriver
[launcher] Error: ReferenceError: System is not defined
    at Object.<anonymous> (C:\Dev\d2ipm\test\e2e\overview.js:1:1)
    at Module._compile (module.js:413:34)
    at Object.Module._extensions..js (module.js:422:10)
    at Module.load (module.js:357:32)
    at Function.Module._load (module.js:314:12)
    at Module.require (module.js:367:17)
    at require (internal/module.js:16:19)
    at C:\Users\%username%\AppData\Roaming\npm\node_modules\protractor\node_modules\jasmine\lib\jasmine.js:63:5
    at Array.forEach (native)
    at Jasmine.loadSpecs (C:\Users\%username%\AppData\Roaming\npm\node_modules\protractor\node_modules\jasmine\lib\jasmine.js:62:18)
[launcher] Process exited with error code 100

Since I use SystemJS as Angular2 suggests by default, System is contained in every compiled .js file, including the test specs. My index.html configures System as suggested in the tutorials, and the app works:

    <script src="node_modules/es6-shim/es6-shim.min.js"></script>
    <script src="node_modules/systemjs/dist/system-polyfills.js"></script>

    <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <script src="node_modules/rxjs/bundles/Rx.js"></script>
    <script src="node_modules/angular2/bundles/angular2.dev.js"></script>

    <!-- 2. Configure SystemJS -->
    <script>
      System.config({
        packages: {        
          app: {
            format: 'register',
            defaultExtension: 'js'
          }
        },
        globalEvaluationScope: false
      });
      System.import('app/boot')
            .then(null, console.error.bind(console));
    </script>

Running tests written in pure JS works fine. How can I make my test environment see System?

1
In the testing tutorials they use CommonJS not System. And they use Jasmine, for what I've seen Protractor is not done for Angular 2 is it?Langley
@Langley The testing tutorials deal with unit testing, not e2e. And protractor uses jasmine, too. Angular2 has protractor configs in the repository, so it does not seem like they will not use it in version 2.0.Anton Poznyakovskiy
I know it uses Jasmine, but its built for Angular 1, I haven't found it for Angular 2.Langley
@Langley What would you advise using with Angular2?Anton Poznyakovskiy
Well, I believe protractor is built on top of Selenium so maybe that can do the trick. me, I'll just wait until they make a new Protractor.Langley

1 Answers

2
votes

When you run an e2e test through Protractor, there are actually 2 environments:

  • The first is the environment being tested
    • it runs in a browser,
    • said browser is controlled through the "Selenium Webdriver" automation system,
    • it loads and execute your whole application as an end-used browser would, including loading the index.html file.
  • The second is the environment running the tests themselves
    • In runs in as a local node.js application,
    • It uses protractor, which runs Jasmine, which runs the e2e test files
    • It controls the browser through Selenium

Even if you load libraries in your index.html file, the your e2e-spec file won't have access to them. That includes systemjs. This is an issue when the Typescript compiler uses "module": "system"

How to fix it?

One solution is to configure your build process so that the e2e-spec files are compiled with the "module": "commonjs" option. If you want to be able to import files from your web application into the test spec, you would have to compile the whole application twice (though I am not sure there are much use cases where it makes sense to do so).

For example, using gulp

const gulpTypings = require('gulp-typings')
const sourcemaps = require('gulp-sourcemaps')
const ts = require('gulp-typescript')

const tsProject = ts.createProject('tsconfig.json')
const tsProjectE2E = ts.createProject('tsconfig-e2e.json')

const tsFiles = [
  'typings/index.d.ts',
  'app/**/*.ts',
  '!**/*.e2e-spec.ts',
  '!node_modules/**/*'
]

const tsFilesE2E = [
  'typings/index.d.ts',
  'app/**/*.e2e-spec.ts',
  'app/**/*.ts', // You should not need that unless you use your app files in your tests
  '!node_modules/**/*'
]

gulp.task('typings', () => gulp
  .src('./typings.json')
  .pipe(gulpTypings())
)

gulp.task('ts', ['typings'], () => gulp
  .src(tsFiles)
  .pipe(sourcemaps.init())
  .pipe(ts(tsProject))
  .js
  .pipe(sourcemaps.write('.'))
  .pipe(gulp.dest('app'))
)

gulp.task('ts-e2e', ['typings'], () => gulp
  .src(tsFilesE2E)
  .pipe(sourcemaps.init())
  .pipe(ts(tsProjectE2E))
  .js
  .pipe(sourcemaps.write('.'))
  .pipe(gulp.dest('e2e')) // Note that your compiled tests files are put in the e2e/ folder now
)