Nu pot importa clasa în Karma.js cu Typescript, Webpack


Am încercat să fac Karma.js să funcționeze cu suita mea de teste foarte simplă. Site-ul funcționează într-un browser, deci codul în sine este în regulă. Am avut câteva probleme, dar aceasta nu reușesc să o rezolv nici după ore de efort. Acesta este fișierul meu de test - dacă decomentez chestia legată de store (comentată cu //TODO), atunci se blochează, dar trece fără probleme dacă o las comentată, chiar și când import modulul Page. Se pare că doar store cauzează blocarea.

//TODO UNCOMMENT THIS LINE import { Store } from "../../js/react/utils/store"
import { Page } from "../../js/react/utils/consts"
import { assert } from "chai"

describe('Array', function() {
    it('should start empty', function() {
        var arr = []
        assert.equal(arr.length, 0)
    })
    it('Tests importing Page', () => {
        //TODO UNCOMMENT THIS LINE const default_store = new Store()
        //TODO UNCOMMENT THIS LINE assert(default_store.page == Page.landing)
        assert("__LANDING_PAGE__" == Page.landing)
    })
})

Acesta este mesajul de eroare

... all the webpack output saying it compiled ok ...
16 08 2018 21:31:59.648:INFO [karma]: Karma v3.0.0 server started at http://0.0.0.0:9876/
16 08 2018 21:31:59.649:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
16 08 2018 21:31:59.688:INFO [launcher]: Starting browser PhantomJS
16 08 2018 21:32:00.636:INFO [PhantomJS 2.1.1 (Mac OS X 0.0.0)]: Connected on socket KYSAZvAPo7_oibqeAAAA with id 80055480
PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
  {
    "message": "ReferenceError: Can't find variable: Symbol\nat test/unit/store.test.ts:12084:22",
    "str": "ReferenceError: Can't find variable: Symbol\nat test/unit/store.test.ts:12084:22"
  }

Finished in 0.086 secs / 0 secs @ 21:32:00 GMT+1000 (AEST)

SUMMARY:
✔ 0 tests completed
npm ERR! code ELIFECYCLE
npm ERR! errno 1
...etc. etc. more npm errors...

Acesta este modulul Store, folosește mobx și decoratorii lor. Așa cum am menționat înainte, pagina în care se află această clasă funcționează bine și este transpusă perfect folosind actualul webpack.config.js, doar karma/phantomJS nu funcționează

// import * as React from "react";
import { observable, action } from "mobx"
// import { observer } from "mobx-react";
import { AVATARS, Page } from "./consts"
import { Bot } from "../models/bot"
import { Message } from "../models/message"
import { WebsocketService } from "./websocket_service"


/*
**  A global store, accessible by any component decorated with
**  @observable
**
*/


//TODO create hover_type here
// Props object for LandingPage
class LandingStore {
    @observable current_hover: "__MENU__" | Bot | null = null
    constructor() {
        this.current_hover = null
    }

    @action set_hover(new_hover: "__MENU__" | Bot | null) {
        this.current_hover = new_hover
    }
}

class ChatWindowStore {
    @observable messages: Array<Message>
    constructor() {
        this.messages = []
    }

    @action msg_in(msg: Message) {
        this.messages.push(msg)
    } // When receiving msgs

//   @computed get size() { return this.messages.length }
//   indexOf(ii: number) { return this.messages[ii] }

}

// Global store
export class Store {

    // websocket connection
    @observable channel = WebsocketService.initialize_channel(this) //this._initializeChannel(this.socket)
    // @observables
    @observable page: Page
    @observable some_global_value: number
    @observable current_bot: Bot
    // Individual stores
    @observable landing_page: LandingStore
    @observable chat_window: ChatWindowStore
    constructor() {
        this.page = Page.landing
        this.some_global_value = 1337
        this.current_bot = new Bot("Captain Moonbeam", AVATARS.CAPTAIN_MOONBEAM)
        this.landing_page = new LandingStore()
        this.chat_window = new ChatWindowStore()
    }

    public static bs() {
        return 1
    }

    // @actions
    @action change_page(newPage: Page) { this.page = newPage }
    @action reset() { this.constructor() } //TODO also delete session & cookies
    @action increment() {
        console.log("CLICKED!")
        this.some_global_value++
    }
    @action change_bot(bot: Bot) {
        this.current_bot = bot
    }
}

Iată karma.conf.ts-ul meu, vă rog să scuzați comentariile inutile, am încercat multe lucruri, inclusiv să nu folosesc webpack pentru transpiling, diferite motoare de browser, diferite căi de fișiere, multe lucruri tipice de depanare

module.exports = function(config: any) {
    config.set({
        frameworks: ['mocha'],
        // basePath: "./",
        files: [
            "test/**/*.ts",
            // "js/app.js"
        ],
        preprocessors: {
            // "js/app.js": ["webpack"],
            // "**/*.tsx": ["webpack"],
            "**/*.ts": ["webpack"]
        },
        plugins : [
            'karma-mocha',
            'karma-mocha-reporter',
            'karma-phantomjs-launcher',
            // 'karma-chrome-launcher',
            'karma-webpack',
            'karma-typescript-preprocessor'
            // 'karma-chai',
            // 'karma-coverage',
        ],
        // typescriptPreprocessor: {
        //  // options passed to the typescript compiler
        //  options: {
        //      sourceMap: false, // (optional) Generates corresponding .map file.
        //      target: 'ES3', // (optional) Specify ECMAScript target version: 'ES3' (default), or 'ES5'
        //      module: 'commonjs', // (optional) Specify module code generation: 'commonjs' or 'amd'
        //      noImplicitAny: true, // (optional) Warn on expressions and declarations with an implied 'any' type.
        //      noResolve: true, // (optional) Skip resolution and preprocessing.
        //      removeComments: true, // (optional) Do not emit comments to output.
        //      concatenateOutput: false // (optional) Concatenate and emit output to single file. By default true if module option is omited, otherwise false.
        //  },
        //  // transforming the filenames
        //  // transformPath: function(path) {
        //  //   return path.replace(/\.ts$/, '.js');
        //  // }
        // },
        webpack: require('./webpack.config.js'),
        reporters: ["mocha"],
        browsers: ["PhantomJS"],
        // browsers: ["Chrome"],
        // colors: true,
        // logLevel: config.LOG_INFO,
    });
};

webpack.config.js

const path = require("path")
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin")

const config = {
    entry: [
        path.resolve(__dirname, "css/app.css"), 
        path.resolve(__dirname, "js/app.js")],
    output: {
        path: path.resolve(__dirname, "../priv/static"),
        filename: "js/app.js"},
    mode: 'development',
    devtool: "inline-source-map",
    // devtool: 'source-map',
    resolve: {
        extensions: [".ts", ".tsx", ".js", ".jsx"],
        modules: ["deps", "node_modules"]
      },
    module: {
        rules:
        [{
            test: /\.jsx?$/,
            use: "babel-loader"
        },
        {
            test: /\.tsx?$/,
            use: ["babel-loader", "ts-loader"] //NOTE loaders are applied last-first
        },
        {
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
                fallback: "style-loader",
                use: "css-loader"
            })
        },
        {
            test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
            loader: 'file-loader?name=fonts/[name].[ext]'
        }]
    },
    plugins: [
        new ExtractTextPlugin("css/app.css"),
        new CopyWebpackPlugin([{ from: "static" }])
    ]
};

module.exports = config;

Și iată tsconfig.js-ul meu, doar pentru noroc

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "jsx": "react",
    "sourceMap": true,
    "moduleResolution": "node",
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": true,
    "experimentalDecorators": true // https://mobx.js.org/best/decorators.html
  },
  "exclude": [
    "node_modules",
    "priv",
    "ts-build",
    "webpack",
    "jest"
  ]
}