VSCode cannot find js / tsc / module in a typescript node app

vscode sometimes can’t find files or executables, on windows, because of the path separator. This includes tsc, ionic and cordova

For example,

error TS5058: The specified path does not exist: c:projectssomeprojectstuff.js
cannot find tsc
 \\node_modulesmochabin_mocha 

This could be a bash/cmd thing.

Some possible workarounds…

Add the tsc, ionic, cordova or vscode directory to your path

C:\Users\nick\AppData\Roaming\npm\node_modules\typescript\bin
C:\Users\nick\AppData\Roaming\npm\node_modules\ionic\bin
C:\Users\nick\AppData\Roaming\npm\node_modules\cordova\bin

change to the bash (or cmd) shell in .vscode/settings.json

 {
    "terminal.integrated.shell.windows": "C:\\Program Files\\Git\\bin\\bash.exe",
    "xxterminal.integrated.shell.windows": "C:\\windows\\system32\\cmd.exe"

} 

Make sure there’s a SPACE in your project folder (e.g. c:\projects\My Project). This forces tsc.js to put “” around the path

Install Typescript locally (npm i typescript –save)

Override the typescript in .vscode/tasks.json with your version that includes the full path to tsc…

 {
    "version": "2.0.0",
    "tasks": [
        {
            //"type": "typescript",
            //"tsconfig": "tsconfig.json",
            "label": "tsc",
            "problemMatcher": [
                "$tsc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "type":"shell",
            "windows":{
                "command": "/C/Users/nick/AppData/Roaming/npm/node_modules/typescript/bin/tsc",
                "args": [
                    "-p",
                    "\"${workspaceFolder}/tsconfig.json\""]
            }
        }
    ]
} 

Instead of type: typescript we’ve got type: shell, and a windows {} property. This includes the unix version of the full path to the tsc bash batch file.

This is the build task, but to make sure it’s completed before you launch your node typescript code, in .vscode/launch.json, set it as preLaunchTask like this…

 "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "program": "${workspaceFolder}\\index.ts",
            "outFiles": ["${workspaceFolder}\\index.js"],
            "preLaunchTask": "tsc"
        } 

https://github.com/microsoft/vscode/issues/48149

https://github.com/Microsoft/vscode/issues/35593

Angular Interceptor authorization: headers are now immutable

In Angular 4, headers are immutable, so to add (say) an Authorization header in your interceptor you have to use a cunning overload of the .clone method.

My complete interceptor looks like this…

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http";
import { Observable } from "rxjs/Observable";
//import { FafService } from "./faf-service";
import { Injectable } from "@angular/core";
@Injectable()
export class FafInterceptor implements HttpInterceptor{
public theToken:string = null;
constructor(){
}
intercept(request: HttpRequest, next: HttpHandler): Observable<HttpEvent>
{
let theToken = window.localStorage.getItem('theToken');
//TODO: IsGuid
if(theToken && theToken != 'null'){
var NewRequest = request.clone(
{
setHeaders:{'Authorization': 'Bearer ' + theToken}
}
);
//https://angular.io/api/common/http/HttpRequest
console.log('auth header now ' + NewRequest.headers.get('Authorization'));
return next.handle(NewRequest);
}
return next.handle(request);
}
}

Angular mocking XHRBackend: how to call mockRespond

I’m adding unit tests to my API and had a bit of bother mocking the API response. However after a lot of console.log and much head scratching I found some voodoo that works.

TWO KEY TAKEAWAYS

1 . Josh Morony’s blog post said the call is

connection.mockRespond ( new Response ( newResponseOptions(...

That didn’t work for me, I had to use

connection.mockRespond ( newResponseOptions ( body: myObjects

2. But that’s not all, my live response (from a MS WebApi) has a ._body property, my mocked response has a .body. So I had to do something like this


if(res['body']){ //works with mocks
//...
}else if(res['_body']){//works in live system
//...
}

I started with this complete example:

git clone https://github.com/ionic-team/ionic-unit-testing-example.git

(The version numbers of the npm packages have to be compatible)

And worked through this blog post (and others!):

Test Driven Development in Ionic 2: Http and Mocks

This would have been a lot easier if I could figure out how to attach a debugger to Karma, no luck with that so far. Anyhow my complete dummy-service.spec.ts (including code for the service) is…

(just as soon as i figure out how to paste code into wordpress…)

import {Injectable} from '@angular/core';
import { Http, HttpModule, XHRBackend, ResponseOptions, Response } from '@angular/http';
import { TestBed, inject } from '@angular/core/testing';
import { MockBackend } from '@angular/http/testing';

@Injectable()
export class DummyService {
  access_token:string;
  constructor(public http:Http){}
  get(){
    console.log('DummyService Test1 F');
    this.http.get('http://localhost').subscribe((res:Response)=>{
      console.log('DummyService Test1 G got ' + JSON.stringify(res));
      if(res['body'] && res['body']['access_token']){
        this.access_token = res['body']['access_token']; //works with mocks
        console.log('loginUser got token from body (we are using a mock)');
      }else if(res['_body'] && JSON.parse(res['_body']) 
&& JSON.parse(res['_body']).access_token){
        this.access_token = JSON.parse(res['_body']).access_token; //works with live system
        console.log('loginUser got token from _body (we are live)');
      }
      console.log('access_token is ' + this.access_token);
    });
  }
}

describe ('DummyService http method',()=>{
  beforeEach(()=>{
    TestBed.configureTestingModule(
      {
        imports:[HttpModule],
        providers:[
          {provide: XHRBackend, useClass:MockBackend},
          DummyService
        ]
      }
    );
  }
  );

  it('should call get and get response',inject([DummyService, XHRBackend],(dummyService, mockBackend)=>{

    console.log('DummyService Test1 A');

    let r1 = "hello"; //works
    let r2 = "{access_token:'SOMETOKEN'}"; //works
    let r3 =  //doesn't work, service gets empty object {}
        new Response(
            new ResponseOptions(
            {
                url:'url',
                status:200,
                body: "here is the info"
            }
            )
        );
    let r4 = JSON.stringify(r3);//doesn't work, service gets empty object {}
    let r5 = new ResponseOptions( //aha THIS is the bunny...
        {
            body: {access_token:'SOMETOKEN'}
        }
        );

    mockBackend.connections.subscribe(
      connection=>{
        console.log('DummyService Test1 D v4 r5');
        connection.mockRespond(r5); 
      }
    );

    console.log('DummyService Test1 B');

    dummyService.get();

    console.log('DummyService Test1 C');
    
    expect(dummyService.access_token).toEqual('SOMETOKEN');

  }));

});