Showing posts with label Jasmine. Show all posts
Showing posts with label Jasmine. Show all posts

Tuesday 31 December 2019

Implementing OfType in Typescript

I am working on my Linq library for Typescript and wanted to implement OfType. Turns out, this is hard because the generic type arguments in Typescript usually requires a value, i.e. an object instance of type T to get any shape information at run-time. So I ended up passing in a vanilla object setting default property values instead. Here is how my implementation ended up:

function isOfSimilarShape<T>(input: any, compareObject: T): boolean {
  if (input === undefined || input === null || compareObject === undefined || compareObject === null)
    return false;

  let propsOfInput = Object.keys(input);
  let propsOfCompareObject = Object.keys(compareObject);
  //debugger
  let sameShapeOfInputAndCompareObject = propsOfInput.EqualTo(propsOfCompareObject);
  return sameShapeOfInputAndCompareObject;
}

if (!Array.prototype.OfType) {
  Array.prototype.OfType = function <T>(compareObject: T): T[] {
    let result: T[] = [];
    this.forEach(el => {
      //debugger
      let t: T = null;
      if (isOfSimilarShape(el, compareObject))
        result.push(el);
    });
    return result;
  }
}

The following Jasmine test shows its usage:

describe('Array Extensions tests', () => {

  it('can find desired items using OfType of type T', () => {
    let someMixedArray: any[] = [];
    someMixedArray.push(<SomeClass>{ Name: "Foo", Num: 1 });
    someMixedArray.push(<SomeOtherClass>{ SomeName: "BarBazBaze", SomeOtherNum: 813 });
    someMixedArray.push(<SomeClass>{ Name: "FooBaz", Num: 4 });
    someMixedArray.push(<SomeOtherClass>{ SomeName: "BarBaze", SomeOtherNum: 13 });
    someMixedArray.push(<SomeClass>{ Name: "AllyoBaze", Num: 7 });

    let compareObject = <SomeClass>{ Name: "", Num: 0 };
    let filteredArrayBySpecifiedType = someMixedArray.OfType(compareObject);
    console.log(filteredArrayBySpecifiedType);

    expect(filteredArrayBySpecifiedType.All(item => <SomeClass>item !== undefined)).toBe(true, "Expected only items of type SomeOtherClass in the filtered array after running OfType of SomeOtherClass on it.");
  });

It would be nice if we did not have to pass in a vanilla object and populate its properties, but I could not find any tips online or in the Typescript documentation for how to implement extracting type information from generic arguments of Typescript. This is very easy in C#, but while Typescript gives compilation type information, getting runtime information from generic arguments in the Javascript code that Typescript compiles into turned much harder.

Tuesday 24 September 2019

Getting started with tests on controllers in AngularJs

Some notes - I had to work with AngularJs tests today and needed to look into Jasmine and mocking enough and import enough to have running tests. I use the Chutzpah test runner to run the Jasmine Tests. The unit test below should get you started writing tests for controllers in AngularJs. The key concepts is to import jQuery, Bootstrap, Angular Animate, Angular-Mocks and your module and controller through using the reference path syntax at the top and then define a beforeEach that capture the _$controller_ and $_rootScope_ variables and using $rootScope.$new() to create a scope. But in my case I also had to specify a provided factory 'bootstrappedData' since my controller reads the 'model' property inside that. By specifying the value this provided factory returns at the top of each tests, I got the amount of DRY I needed to get started testing. I had to this since my controller got the data in an indirect manner, using he factory. I then create a new instance of the controller after updating the 'bootstrappedData' factory.

/// 
/// 
/// 
/// 
/// 
/// 
/// 

describe('someController works', function () {
    beforeEach(module('app'));
    var $scope;
    var $rootScope;
    var $controller;
    var $bootstrappedData;
    var $repository;
    var ctrl;
    var provide;

    beforeEach(module(function ($provide) {
        provide = $provide;
      
    }));

    beforeEach(module(function ($provide) {
        $provide.factory('repository', function () {
            return {
                model: {
                }
            };
        });
    }));

    beforeEach(inject(function (_$controller_, _$rootScope_) {
        $controller = _$controller_;
        $rootScope = _$rootScope_;
        scope = $rootScope.$new();

    }));

    it('Creates the AngularJs controller someController', function () {
        provide.factory('bootstrappedData', function () {
            return {
                model: {
                }
            };
        });

        ctrl = $controller('someController', { $scope: scope });
        expect(ctrl).not.toBe(null);

    });

    it('Method someproperty returns expected', function () {
        provide.factory('bootstrappedData', function () {
            return {
                model: {
                    SomeProperty: '3'
                }
            };
        });
        ctrl = $controller('KontrollskjemaController', { $scope: scope });
        var someprop = scope.isSomeConditionalPropertyReturningTrueIfSomePropertyIsThree;
        expect(someprop).toBe(true);
    });

});


A tip is to add a file called Chutzpah.json and ignoring well known Javascript libraries to only run code coverage on your own code:

{
  "CodeCoverageExcludes": [ "*\\jquery.js", "*\\angular.js", "*\\bootstrap.min.js", "*\\jquery*", "*\\angular-animate.min.js" ]
  //"CodeCoverageIncludes": [ "*\\*Spec.js" ]
}