Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"ReferenceError: System is not defined" when using protractor with Angular2 and .ts specs

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?

like image 382
Anton Poznyakovskiy Avatar asked Jan 27 '16 14:01

Anton Poznyakovskiy


1 Answers

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
)
like image 96
Philippe Plantier Avatar answered Nov 12 '22 10:11

Philippe Plantier