Angular 7 karma test with importing external JS modules - javascript

I am testing a Angular 7 component which has importing JS modules like:
component.ts
import * as classA from '../../classA'; // Imported JS modules
export class component implements OnInit {
public a = new classA(10); // Instantiate
...
}
classA.js
class classA {
constructor (a) {
this.max = a;
...
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = classA;
}
component.spec.ts
import * as classA from '../../classA';
I am importing classA as what I did in component.ts.
Component.ts is working perfectly but when I am running ng test, it gives an error: TypeError: classA is not a constructor
I tried to include it in karma.conf.js like:
module.exports = function (config) {
config.set({
...
files: [
"../app/classA.js"
]
});
};
But still get same error. Anyone has any idea how to import JS modules in unit testing?

You use es6 modules import but define commonjs module.
Better use es6 modules as well.
classA.js
class classA {
constructor (a) {
this.max = a;
...
}
export default classA
Or use require:
let classA = require('../../classA');

I found the way to fix this testing error. In Angular 7, the right way to import JS commonjs module in component.ts is
import classA from '../../classA';
with
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
in tsconfig.json

Related

webpack es6 module not found

I am new to webpack, want to bundle my js classes
I am using es6 classes and linqjs.min, jquery-3.4.1.js and gsap.min
for starter I want only to bundle index.js and my class but I get an error
Module not found: cant resolve (myclass) in src directory
in my index.js for importing class:
import myClass from 'js/myClass'
and for exporting my class I used
export class myClass{ } and
module.export= myClass
my webpack.config.js is:
const HTMLWebPackPlugin = require ('html-webpack-plugin');
module.exports = {
module:{
rules:[
{
test: /\.js$/,
exclude:/node_modules/,
use:{
loader:"babel-loader"
}
},
{
test:/\.html$/,
use:[{
loader:"html-loader",
options:{minimize:true}
}]
}
]
},
plugins:[
new HTMLWebPackPlugin({
template:"./src/index.html",
filename: "./index.html"
})
]}
I don't understand my mistake how can I fix it. Thanks.
You are trying to do a default import, whereas you are doing a named export.
Either go with a default export, adding the default keyword:
export default class MyClass {}
along with
import MyClass from './js/MyClass.js';
Or, go with a named export / import:
export class MyClass {}
with
import { MyClass } from './js/MyClass.js';
Please note that by convention, class names start with a capital letter.
Medium has a good article on the difference: https://medium.com/#etherealm/named-export-vs-default-export-in-es6-affb483a0910

How to declare a JS mixin for vue?

I'm writting a vue project with typescript and I want to use a mixin from thrid-part library which write by javascript, how to write a .d.ts to make ts can find function define in the mixin?
I tried this way and it not working:
// common.d.ts
declare module 'thrid-part-lib' {
import { VueClass } from 'vue-class-component/lib/declarations';
export interface SomeMixin<T> extends VueClass<T> {
refresh(): Promise<void>;
}
}
// index.ts
import { Component, Mixins } from 'vue-property-decorator';
import { SomeMixin } from 'thrid-part-lib';
#Component
export default class Index extends Mixins(SomeMixin) {
public foo() {
this.refresh(); // 'refresh' is not define.
}
}
You can augment a third party mixin with creating a file like vuelidate-error-extractor.d.ts:
declare module 'vuelidate-error-extractor' {
import { ValidationRule } from 'vuelidate/lib/validators';
// eslint-disable-next-line #typescript-eslint/class-name-casing
export class singleErrorExtractorMixin extends Vue {
readonly events: any;
readonly name: string;
readonly isValid: boolean;
readonly hasErrors: boolean;
readonly preferredValidator: ValidationRule;
}
}
This augments this JS file, but in an incomplete manner.
This is documented in "Augmenting Types for Use with Plugins".
Put this in a .d.ts file in your project to add a refresh() mixin method to components:
// 1. Make sure to import 'vue' before declaring augmented types
import Vue from 'vue'
// 2. Specify a file with the types you want to augment
// Vue has the constructor type in types/vue.d.ts
declare module 'vue/types/vue' {
// 3. Declare augmentation for Vue
interface Vue {
refresh(): void;
}
}

Issue related to import/export with typings

I have JS file and want to write typings for it.
import { ApiService } from './src/ApiService'
Then I write typings and export it
export declare class ApiService {
constructor(adapter: any, options: any);
on:(evt, cb) => any;
extend: (opts) => any;
}
error TS2440: Import declaration conflicts with local declaration of ApiService
How can I fix It?
Put your declaration in a file ApiService.d.ts (but the keyword declare is unnecessary):
// src/ApiService.d.ts
export class ApiService {
constructor(adapter: any, options: any);
on:(evt, cb) => any;
extend: (opts) => any;
}
Notice that the TS definition file name must be the same as the JavaScript file name: ApiService.d.ts describes a JavaScript file ApiService.js.
Then, import it:
// test.ts
import { ApiService } from './src/ApiService'
It should works.

How to import Javascript file into Typescript

I was wondering how I start the Twitter-Bootstrap from Typescript.
$('.carousel').carousel()
I had to implement jquery.d.ts to fix the $-sign call, but then I'm still getting the error that .carousel() could not be found in jquery.d.ts.
I tried to do this with bundling the javascript to a module and call it like that. But it does not seem to work.
This is my code:
carousel.d.ts
declare module 'carousel/carousel' {
var start: any;
export = start;
}
carousel.js
System.register('carousel/carousel', [], true, function () {
var carousel = function () {
function carousel() {
}
carousel.prototype.start = function () {
$('.carousel').carousel();
}
}
exports.carousel = carousel;
});
app.ts
import {Component} from "angular2/core";
import {bootstrap} from 'angular2/platform/browser';
import {Carousel} from "carousel/carousel";
#Component({
selector: "carousel",
bindings: [CarouselComponent],
templateUrl: 'carousel.html'
})
export class CarouselComponent {
start() {
carousel.start();
}
}
}
bootstrap(CarouselComponent)
Thanks for helping out.
The problem is that you don't have the typing definition for carousel(). Like you mentioned - it's a function in Twitter-Bootstrap, but you only included the typing definitions (*.d.ts) for jQuery. You need to include them for Bootstrap the same way.
You can get the full Bootstrap tying definitions from the DefinitelyTyped project, either from their GitHub or as a NuGet package. Here are the essential parts:
interface CarouselOptions {
interval?: number;
pause?: string;
wrap?: boolean;
keybord?: boolean;
}
interface JQuery {
carousel(options?: CarouselOptions): JQuery;
carousel(command: string): JQuery;
}
I would refactor your carousel.js file like this:
System.register("carousel/carousel", [], true, function(require, exports, module) {
var carousel = function () {
function carousel() {
}
carousel.prototype.start = function () {
$('.carousel').carousel();
}
}
exports.carousel = carousel;
});
Create a file "jquery-caroussel.d.ts" (and add-it to your reference.ts)
inside it:
interface JQuery {
carousel();
}
It will say to the ts compilator than there is a methode carousel() which will be implemented later. (in the browser, by the carousel.js file.)
If you have a similar issue with another lib than carousel, there is plenty of sample of interface here:https://github.com/DefinitelyTyped/DefinitelyTyped
You can import JS files by declaring an ambient module for each library you need. You can have an ambient.d.ts file where you store all ambient module declarations (ie for JavaScript libraries you'd like to import, but for which you do not have the type definitions)
ambient.d.ts:
// You would have a separate module declaration for each JavaScript library
// you want to import for which you do not have any type definitions
declare module 'my-module-i-want-to-import' {
const a : any;
export default a;
}
any other *.ts file that needs my-module-i-want-to-import:
// Include reference to your ambient module declarations
///<reference path="./ambient.d.ts" />
import myModule from 'my-module-i-want-to-import';
// Do something with myModule
console.log(myModule);
In the end, I've changed my code to use the "InjectionToken".
As described here: Use jQuery in Angular/Typescript without a type definition
You can simply inject the jQuery global instance and use it. For this you won't be needing any type definitions or typings.
import { InjectionToken } from '#angular/core';
export const JQ_TOKEN = new InjectionToken('jQuery');
export function jQueryFactory() {
return window['jQuery'];
}
export const JQUERY_PROVIDER = [
{ provide: JQ_TOKEN, useFactory: jQueryFactory },
];
When set correctly in your app.module.ts:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { AppComponent } from './app.component';
import { JQ_TOKEN } from './jQuery.service';
declare let jQuery: Object;
#NgModule({
imports: [
BrowserModule
],
declarations: [
AppComponent
],
providers: [
{ provide: JQ_TOKEN, useValue: jQuery }
],
bootstrap: [AppComponent]
})
export class AppModule { }
You can start using it in your components:
import { Component, Inject } from '#angular/core';
import { JQ_TOKEN } from './jQuery.service';
#Component({
selector: "selector",
templateUrl: 'somefile.html'
})
export class SomeComponent {
constructor( #Inject(JQ_TOKEN) private $: any) { }
somefunction() {
this.$('...').doSomething();
}
}
After Angular's Final Release, (for jquery.js and bootstrap.js)
1) add following npm packages
npm install --save-dev #types/jquery
npm install --save-dev #types/bootstrap
2) in tsconfig.json add following entries in types array,
"types": [
"jquery",
"bootstrap",
]
And now you are good to go now.
Jquery carousel method error in Angular2/typescript

Typescript - internal module cannot be imported - unexpected behavior

I have 2 modules spread across 2 files as such:
--App.Tools.Utils.ts
export interface ILogger {
log: (msg: string) => void;
}
module App.Tools.Utils {
export class Logger implements ILogger { ... }
export function someFunction(){ ... }
}
then in the second file I import the module:
--App.Routers.ts
/// <reference path="App.Tools.Utils.ts" />
module App.Routers {
import Utils = App.Tools.Utils; //ERROR: A module cannot be aliased to a non-module type
}
I eventually figured out the solution was to move the ILogger interface INSIDE the App.Tools.Utils module to resolve the error. At first it made some sense, I figured the compiler wouldn't let me import the module because the Logger class implemented an interface that was not included in the module. To test, I moved the ILogger interface inside the module (error resolved) but then added an arbitrary interface right outside the module (one that is not used inside the module or anywhere) and the error returns..
export interface INeverUsed { } //generates same error
module App.Tools.Utils {
export interface ILogger {
log: (msg: string) => void;
}
export class Logger implements ILogger { ... }
export function someFunction(){ ... }
}
Looking at the generated JS, adding the interface outside the module generates a define(["require", "exports"] wrapper and that results in the error when trying to import the App.Tools.Utils module in the other file. Removing the interface removes the define wrapper and the resolves the error.
Is it behavior expected? It makes no sense to me why a module is suddenly "closed" when I define an interface in the same file but outside the module, especially if that interface is not even used inside the module.
Because you are using the export keyword on the interface, the compiler is treating the file as a module.
If you remove the export keyword on the interface, it should work:
interface ILogger {
log: (msg: string) => void;
}
module App.Tools.Utils {
export class Logger implements ILogger {
log(msg: string) { }
}
export function someFunction(){ }
}
And
///<reference path="game.ts" />
module App.Routers {
import Utils = App.Tools.Utils; // works
}

Categories

Resources