WebAPI: Multiple actions were found that match the request

Standard

If you have two or more web methods and using the “HttpGet” attribute

[HttpGet]
public HttpResponseMessage GetRelatedSearch(HttpRequestMessage request)
{
     return request.CreateResponse(HttpStatusCode.OK); 
}
 
[HttpGet]
public HttpResponseMessage GetWebSearch(HttpRequestMessage request) 
{
     return request.CreateResponse(HttpStatusCode.OK);
}

You will receive error:

2015-11-30_18-44-11

Solution:

Your by-default web api config setting would be like below in WebApiConfig.cs file under App_Start folder:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });

But in order to have multiple actions with the same http method you need to provide webapi with more information in terms of action name via the route like so:

routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });

Notice that the routeTemplate now includes an action.

http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

Advertisements

WebAPI: Get Parameter Binding using [FromURI] with AngularJS $HTTP services

Standard

A HTTP GET request can’t contain data to be posted to the server. What you want is to a a query string to the request. Fortunately angular.http provides an option for it params.

2015-11-27_15-17-04

Angular Source Link for all $http options

If the parameter is a “simple” type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string.

Angular Code:

$http({
         method: 'Get',
         url: 'api/BingApi/Search',
         cache: $templateCache,
         params: { query: "test" }
})

Web API Code:

[HttpGet]
public HttpResponseMessage Search(HttpRequestMessage request, [FromUri] string query)
{
    return string.IsNullOrEmpty(query) ? 
                  request.CreateResponse(HttpStatusCode.BadRequest)
                  request.CreateResponse(HttpStatusCode.OK, query); 
}

Protractor Error for Gulp: Could not find chrome driver

Standard

While invoking protractor test from Gulp.js, I came across below issue:

2015-11-20_11-23-16

My protratcor.config.js file for gulp looks like:

exports.config = {
 
    seleniumAddress: 'http://localhost:4444/wd/hub',
 
    capabilities: {
        'browserName': 'chrome'
    }, 
 
    specs: ['./test1.js', './test2.js'],
    
    jasmineNodeOpts: {
        showColors: true, 
        defaultTimeoutInterval: 30000
    }
};

Solution:

Step 1: First of all make sure you have installed complete protractor package using below statement

npm install --save-dev gulp-protractor protractor

Step 2: Then in CMD after changing directory to your project, execute below command

webdriver-manager update --standalone

Then in explorer check file status under <project>\node_modules\protractor\selenium
2015-11-20_11-30-24

Step 3: Now in your protractor.config.js file for Gulp make sure you need to specify path for chrome driver and selenium jar, Please comment selenium address variable:

exports.config = {
    seleniumServerJar: './node_modules/protractor/selenium/selenium-server-standalone-2.48.2.jar',
 
    chromeDriver: './node_modules/protractor/selenium/chromedriver.exe',
 
    //seleniumAddress: 'http://localhost:4444/wd/hub',
    capabilities: {
        'browserName': 'chrome'
    }, 
    specs: ['./test1.js', './test2.js'],
    
    jasmineNodeOpts: {
        showColors: true, 
        defaultTimeoutInterval: 30000
    }
};

Solution Output:

Now execute your gulp task statement invoking protractor tests, tests would execute successfully.

2015-11-20_11-39-40

Bower Error – fatal: unable to connect to github.com

Standard

If you come across below mentioned issue while installing client side packages like jquery, angular etc using bower.

I type below command:

bower install angular --save

Command Output:

2015-11-13_16-41-00

Solution:

Step 1: Look for .gitconfig in your user profile (I am using windows [don’t kill me !!!])

c: \users\me\.gitconfig

2015-11-13_16-47-48

Step 2: Open it in editor and paste below code

[url "https://"]
    insteadOf = git://
[url "https://github.com"]
    insteadOf = git@github.com

2015-11-13_16-50-46

Now execute command on CMD, this will resolve the issue.

2015-11-13_16-51-52

Alternate Solution:

Try to feed URL into bower command directly from github

bower install https://github.com/angular/angular -save

ECMAScript 6 Tools

Standard

Transpilers

  • Babel – Turn ES6+ code into vanilla ES5 with no runtime
  • Traceur compiler – ES6 features > ES5. Includes classes, generators, promises, destructuring patterns, default parameters & more.
  • es6ify – Traceur compiler wrapped as a Browserify v2 transform
  • babelify – Babel transpiler wrapped as a Browserify transform
  • es6-transpiler – ES6 > ES5. Includes classes, destructuring, default parameters, spread
  • Square’s es6-module-transpiler – ES6 modules to AMD or CJS
  • Facebook’s regenerator – transform ES6 yield/generator functions to ES5
  • Facebook’s jstransform – A simple utility for pluggable JS syntax transforms. Comes with a small set of ES6 -> ES5 transforms
  • defs – ES6 block-scoped const and let variables to ES3 vars
  • es6_module_transpiler-rails – ES6 Modules in the Rails Asset Pipeline
  • Some Sweet.js macros that compile from ES6 to ES5
  • Bitovi’s transpile – Converts ES6 to AMD, CJS, and StealJS.
  • regexpu — Transform Unicode-aware ES6 regular expressions to ES5

Build-time transpilation

Grunt Tasks

Gulp Plugins

Broccoli Plugins

Brunch Plugins

Webpack plugins

Duo plugins

Connect plugins

Gobble plugins

Jade plugins

Jest plugins

Karma plugins

Sprockets plugins

Browser plugins

  • Scratch JS – A Chrome/Opera DevTools extension to run ES6 on a page with either Babel or Traceur

Mocha plugins

  • Mocha Traceur – A simple plugin for Mocha to pass JS files through the Traceur compiler

Module Loaders

Boilerplates

  • es6-boilerplate – Tooling to allow the community to use es6 now via traceur in conjunction with amd and browser global modules, with source maps, concatenation, minification, compression, and unit testing in real browsers.

Code generation

Polyfills

Editors

Parsers

Other

Source

Using Protractor for E2E testing of AngularJS application in Visual Studio

Standard

GitHub Repository:  GitHub Link

Protractor is an end-to-end test framework for AngularJS applications. Protractor runs tests against your application running in a real browser, interacting with it as a user would.Integrating Protractor for AngualrJS application in Visual Studio needs some configuration.

Follow step 1 to step 8 to integrate protractor in your visual studio project from my previous post:

Installation and Setup Protractor in Visual Studio

Step 1: Setup Node.js Console Application

  • Add New Project in main solution file

2015-11-06_17-11-22

  • Search for Nodejs console application template and name it.2015-11-06_17-13-45

Step 2: Add conf.js file for protractor configuration

  • For sample just add below code in file named as ‘conf.js’
// An example configuration file. 
exports.config = {
    // The address of a running selenium server. 
    seleniumAddress: 'http://localhost:4444/wd/hub', 
    
    // Capabilities to be passed to the webdriver instance. 
 
    //capabilities: {
    //    'browserName': 'chrome'
    //}, 
    
    multiCapabilities: [{
            'browserName': 'chrome'
        }, {
            'browserName': 'firefox'
        }],
    
    // Spec patterns are relative to the current working directly when 
    // protractor is called. 
    specs: ['customConfig.js', 'menu.js', 'homePage.js' , 'candidatePage.js'], 
    
    // Options to be passed to Jasmine-node. 
    jasmineNodeOpts: {
        showColors: true, 
        defaultTimeoutInterval: 30000
    }
};
 

2015-11-09_17-36-40

Please Note:

using multiCapabilities will run all the tests in each of the browsers

Step 3: Build Project and get URL of project

Step 4: Syntax Understanding

Protractor API: http://angular.github.io/protractor/#/api

Note: Most commands return promises, so you only resolve their values through using jasmine expect API or using .then(function()) structure.

Protractor Cheatsheet

  • Control browser
browser.get('yoururl// Load address, can also use '#yourpage'
browser.navigate().back();
browser.navigate().forward();
browser.sleep(10000); // if your test is outrunning the browser
browser.pause();
browser.debugger();
browser.waitForAngular(); // if your test is outrunning the browser
browser.getLocationAbsUrl() // get the current address
  • Check Visibility
element(by.id('create')).isPresent();
element(by.id('create')).isEnabled();
element(by.id('create')).isDisplayed();
  • Find an element by id, model, binding
element(by.id('user_name');
element(by.css('#myItem');
element(by.model('person.name');
element(by.binding('person.concatName'));
element(by.textarea('person.extraDetails'));
element(by.input('username'));
element(by.input('username')).clear();
element(by.buttonText('Save'));
element(by.partialButtonText('Save'));
element(by.linkText('Save'));
element(by.partialLinkText('Save'));
element(by.css('[ng-click="cancel()"]'));

var dog = element(by.cssContainingText('.pet', 'Dog'));
var allOptions = element.all(by.options('c c in colors'));
  • Find collection of elements by css, repeater, xpath..
var list = element.all(by.css('.items'));
var list2 = element.all(by.repeater('personhome.results'));
var list3 = element.all(by.xpath('//div');
expect(list.count()).toBe(3);

element(by.id('user_name')).sendKeys('user1');
sendKeys(protractor.Key.ENTER);
sendKeys(protractor.Key.TAB);
element(by.id('user_name')).clear();
element(by.id('item1')).getLocation().then(function(location) {
    var x = location.x;
    var y = location.y;
});
element(by.id('item1')).getSize().then(function(size) {
 var width = size.width;
 var height = size.height;
});

For more detail, check out this cheat sheet

Protractor Concepts

describe("A suite is just a function", function() {
  var a;
  it("and so is a spec", function() {
    a = true;
    expect(a).toBe(true);
  });
});
  • describe Your TestsA test suite begins with a call to the global Protractor function describe with two parameters: a string and a function. The string is a name or title for a spec suite – usually what is being tested. The function is a block of code that implements the suite.
    SpecsSpecs are defined by calling the global Protractor function it, which, like describe takes a string and a function. The string is the title of the spec and the function is the spec, or test. A spec contains one or more expectations that test the state of the code. An expectation in Protractor is an assertion that is either true or false. A spec with all true expectations is a passing spec. A spec with one or more false expectations is a failing spec.
  • It’s Just FunctionsSince describe and it blocks are functions, they can contain any executable code necessary to implement the test. JavaScript scoping rules apply, so variables declared in a describe are available to any it block inside the suite.
  • ExpectationsExpectations are built with the function expect which takes a value, called the actual. It is chained with a Matcher function, which takes the expected value.
  • MatchersEach matcher implements a boolean comparison between the actual value and the expected value. It is responsible for reporting to Protractor if the expectation is true or false. Protractor will then pass or fail the spec.Any matcher can evaluate to a negative assertion by chaining the call to expect with a not before calling the matcher.
  • Setup and TeardownTo help a test suite DRY up any duplicated setup and teardown code, Protractor provides the global beforeEach and afterEach functions. As the name implies, the beforeEach function is called once before each spec in the describe in which it is called, and the afterEach function is called once after each spec. Here is the same set of specs written a little differently. The variable under test is defined at the top-level scope — the describe block — and initialization code is moved into a beforeEach function. The afterEach function resets the variable before continuing.
  • waitForAngular()
    Instruct webdriver to wait until Angular has finished rendering and has no outstanding $http or $timeout calls before continuing. Note that Protractor automatically applies this command before every WebDriver action.
  • sendKeys()
    Schedules a command to type/update a value on the DOM element represented by this instance. I know its bit hard to understand, here is the simple one: “Send one or more keystrokes to the active window as if they were typed at the keyboard”. For example in order to enter value of any text input, we use sendKeys() function.

Step 5a: Add first test spec file for e2e testing

  • For sample just add below code in file named as ‘menu.js’
describe('menu check', function () {
    beforeEach(function () {
        browser.get('http://localhost:3472/#/');
        browser.waitForAngular();
    });
 
    it('Should route to the candidates page from menu', function () {
        element(by.linkText('Users')).click();
        expect(browser.getCurrentUrl()).toMatch('http://localhost:3472/#/candidates');
    });
 
    it('Should route to the home page from menu', function () {
        element(by.linkText('Home')).click();
        expect(browser.getCurrentUrl()).toMatch('http://localhost:3472/#/');
    });
});

2015-11-09_17-39-38

Tests in this file will click on menu item and confirm their updated URL matches with test case expected values i.e. Home and Users

2015-11-09_17-43-09

  • Copy URL from step 3 to browser.get statement in menu.js file
  • Name of file should be similar to what you have defined in conf.js under specs node
// An example configuration file. 
exports.config = {
    // Spec patterns are relative to the current working directly when 
    // protractor is called. 
    specs: ['menu.js'], 
};

Step 5b: Add Second test spec file for e2e testing

  • For sample just add below code in file named as ‘homePage.js’
describe('Testing Homepage', function () {  
    beforeEach(function () {
        browser.get('http://localhost:3472/#/');    
        browser.waitForAngular();
    });
    
    it('should have a title', function () {
        expect(browser.getTitle()).toEqual('TAC Sample Angular Utility');
    });
 
    it('shaould have header of page', function () {
        var header = element(by.binding('type'));
        expect(header.getText()).toMatch('Admin Page');
    });
    
    it('Should update the url', function () {
        expect(browser.getCurrentUrl()).toMatch('http://localhost:3472/#/');
    });
    
    it('Should route to the candidates page', function () {
        element(by.linkText('candidates')).click();
        expect(browser.getCurrentUrl()).toMatch('http://localhost:3472/#/candidates');
    });
 
    it('Should search and return rows', function () {
        var name = element(by.model('search.first_name'));
        name.sendKeys('jack');
        browser.waitForAngular();
        var candidates = element.all(by.repeater('candidate in vm.candidates'));
        expect(candidates.count()).toBe(1);
    });
});

2015-11-09_18-22-49

Tests in this file will check page title, page header, url, click on href link and count of search result as highlight in image below

2015-11-09_18-25-19

  • Name of file should be similar to what you have defined in conf.js under specs node
// An example configuration file. 
exports.config = {
    // Spec patterns are relative to the current working directly when 
    // protractor is called. 
    specs: ['menu.js', 'homePage.js'], 
};

Step 5c: Add Second test spec file for e2e testing

  • For sample just add below code in file named as ‘candidatePage.js’
describe('Testing Candidate Page', function () {
    
    beforeEach(function () {
        browser.get('http://localhost:3472/#/candidates');
        browser.waitForAngular();
    });
    
    it('Should add candidates and return rows', function () {
        element(by.model('vm.newCandidate.first_name')).sendKeys('aaq');
        element(by.model('vm.newCandidate.middle_initial')).sendKeys('aa');
        element(by.model('vm.newCandidate.last_name')).sendKeys('aac');
        element(by.model('vm.newCandidate.email')).sendKeys('haa@aa.com');
        element(by.model('vm.newCandidate.expected_salary')).sendKeys('234');
        
        element(by.css('.btn-primary')).click();
        
        browser.waitForAngular();
        var candidates = element.all(by.repeater('candidate in vm.candidates'));
        expect(candidates.count()).toBe(11);
    });
    
    it('Should invalidate email', function () {
        element(by.model('vm.newCandidate.email')).sendKeys('haa@com');
        element(by.model('vm.newCandidate.expected_salary')).sendKeys('111');
 
        browser.waitForAngular();
 
        element(by.model('vm.newCandidate.email')).getCssValue('background-color').then(function (color) {
            expect(color).toEqual('rgba(255, 255, 0, 1)');
        });
    });
    
    it('Should add edit candidates', function () {
        
        element.all(by.repeater('candidate in vm.candidates')).then(function (candidates) {
            candidates[0].element(by.buttonText('Edit')).click();
            candidates[0].element(by.model('vm.itemToEdit.first_name')).clear().then(function () {
                candidates[0].element(by.model('vm.itemToEdit.first_name')).sendKeys('BillBBP');
            });
            candidates[0].element(by.model('vm.itemToEdit.middle_initial')).clear().then(function () {
                candidates[0].element(by.model('vm.itemToEdit.middle_initial')).sendKeys('BBP');
            });
            candidates[0].element(by.model('vm.itemToEdit.last_name')).clear().then(function () {
                candidates[0].element(by.model('vm.itemToEdit.last_name')).sendKeys('GatesBBP');
            });
            candidates[0].element(by.model('vm.itemToEdit.email')).clear().then(function () {
                candidates[0].element(by.model('vm.itemToEdit.email')).sendKeys('BbP@aa.com');
            });
            candidates[0].element(by.model('vm.itemToEdit.expected_salary')).clear().then(function () {
                candidates[0].element(by.model('vm.itemToEdit.expected_salary')).sendKeys('800');
            });
            candidates[0].element(by.buttonText('Save')).click();
        });
        
        browser.waitForAngular();
        element.all(by.repeater('candidate in vm.candidates')).then(function (candidates) {
            var fName = candidates[0].element(by.binding('candidate.first_name'));
            expect(fName.getText()).toEqual('BillBBP');
        });
    });
 
});

2015-11-09_18-31-45

Tests in this file will add candidate record in grid as highlight in image below

2015-11-09_18-37-33

Tests in this file will check invalidate email value during adding record as highlight in image below

2015-11-09_18-39-57

Tests in this file will edit first candidate record in grid as highlight in image below

2015-11-09_18-41-13

  • Name of file should be similar to what you have defined in conf.js under specs node
// An example configuration file. 
exports.config = {
    // Spec patterns are relative to the current working directly when 
    // protractor is called. 
    specs: ['menu.js', 'homePage.js', 'candidatePage.js'], 
};

Step 6(optional): Reduce Speed of Protractor Tests

You may witness quick execution of these tests, browser will be quickly invoked and these test will be run in flash of seconds. In order to reduce speed follow below steps:

  • For sample just add below code in file named as ‘customConfig.js’
var origFn = browser.driver.controlFlow().execute;
 
browser.driver.controlFlow().execute = function () {
    var args = arguments;
    
    origFn.call(browser.driver.controlFlow(), function () {
        //increase or reduce time value, its in millisecond
        return protractor.promise.delayed(200);
    });
    
    return origFn.apply(browser.driver.controlFlow(), args);
};

2015-11-09_12-37-48

  • Name of file should be similar to what you have defined in conf.js under specs node
// An example configuration file. 
exports.config = {
    // Spec patterns are relative to the current working directly when 
    // protractor is called. 
    specs: ['customConfig.js', 'menu.js', 'homePage.js', 'candidatePage.js'] 
};

Step 7: Run Test Specs

  • Right click on project and choose Open Command prompt here (we can do this in CMD too, but for a moment lets stick to VS environment)
  • run protractor for example_spec file, execute the below command
    protractor conf.js

2015-11-06_17-59-10

Chrome and Firefox Browser behind CMD is opened by selenium web driver and execute our test and output is generated in CMD as below:

2015-11-09_18-51-34

Video of protractor executing these tests is below:

—————————————————————————————————————————————

Happy Coding.

Execute Protractor Test in parallel on different browser

Standard

In conf.js, You already have attribute named as ‘capabilities‘, You can set it to ‘chrome’ or ‘firefox’ or whatever I need and it runs just as expected.

capabilities: {
    'browserName': 'chrome'
},

using multiCapabilities will run all the tests in each of the browsers. So the configuration below will run every test twice, once in Firefox and once in Chrome

multiCapabilities: [{
            'browserName': 'chrome'
        }, {
            'browserName': 'firefox'
        }],

2015-11-09_12-50-15