Add test for HttpRequest and HttpUrl, bring classes up to spec, change how headers are passed around

This commit is contained in:
Kegan Myers 2014-03-03 12:35:46 -06:00
parent 6fa4249461
commit 67dd1feae8
15 changed files with 392 additions and 118 deletions

4
.travis.yaml Normal file
View file

@ -0,0 +1,4 @@
language: node_js
node_js:
- 0.8
- 0.10

View file

@ -1,7 +1,15 @@
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-typescript');
grunt.initConfig({
karma: {
unit: {
configFile: "karma.conf.js",
singleRun: true,
browsers: ['PhantomJS']
}
},
typescript: {
base: {
src: ["lib/**/*.ts"],
@ -15,4 +23,6 @@ module.exports = function (grunt) {
});
grunt.registerTask('default', ['typescript']);
grunt.registerTask('test', ['typescript', 'karma:unit'])
};

14
build/typertext.d.ts vendored
View file

@ -22,10 +22,10 @@ declare module Typertext {
private headers;
private httpStatus;
private content;
constructor(status: Http.HttpResponseStatus, responseHeaders?: Http.HttpHeaderData, httpResponseCode?: number, responseBody?: T);
constructor(status: Http.HttpResponseStatus, responseHeaderGetter?: (input: string) => string, httpResponseCode?: number, responseBody?: T);
public GetContent(): T;
public GetContentType(): string;
public GetHeaders(): Http.HttpHeaderData;
public GetHeader(name: string): string;
public GetHttpStatus(): number;
public GetStatus(): Http.HttpResponseStatus;
}
@ -39,11 +39,6 @@ declare module Typertext.Http {
class HttpException extends BaseException<HttpResponseStatus> {
}
}
declare module Typertext.Http {
interface HttpHeaderData {
[index: string]: string;
}
}
declare module Typertext.Http {
enum HttpMethod {
GET = 0,
@ -68,7 +63,6 @@ declare module Typertext.Http {
}
declare module Typertext.Http {
class HttpRequest implements GenericRequest<HttpResponseHandler> {
private static parseHeaderString(headerStr);
constructor();
public Get(request: HttpUrl, callback: HttpResponseHandler): void;
public Post(request: HttpUrl, postData: HttpPostData, callback: HttpResponseHandler): void;
@ -77,7 +71,7 @@ declare module Typertext.Http {
}
declare module Typertext.Http {
class HttpResponse extends GenericResponse<string> {
constructor(status: HttpResponseStatus, responseHeaders?: HttpHeaderData, httpResponseCode?: number, responseBody?: string);
constructor(status: HttpResponseStatus, responseHeaderGetter?: (input: string) => string, httpResponseCode?: number, responseBody?: string);
}
}
declare module Typertext.Http {
@ -135,7 +129,7 @@ declare module Typertext.Json {
declare module Typertext.Json {
class JsonResponse extends GenericResponse<JsonObject> {
static fromHttpResponse(httpResponse: Http.HttpResponse): JsonResponse;
constructor(status: Http.HttpResponseStatus, responseHeaders?: Http.HttpHeaderData, httpResponseCode?: number, responseBody?: JsonObject);
constructor(status: Http.HttpResponseStatus, responseHeaderGetter?: (input: string) => string, httpResponseCode?: number, responseBody?: JsonObject);
}
}
declare module Typertext.Json {

View file

@ -28,9 +28,9 @@ var Typertext;
var Typertext;
(function (Typertext) {
var GenericResponse = (function () {
function GenericResponse(status, responseHeaders, httpResponseCode, responseBody) {
function GenericResponse(status, responseHeaderGetter, httpResponseCode, responseBody) {
this.status = status;
this.headers = responseHeaders;
this.headers = responseHeaderGetter;
this.httpStatus = httpResponseCode;
this.content = responseBody;
}
@ -39,11 +39,11 @@ var Typertext;
};
GenericResponse.prototype.GetContentType = function () {
return this.GetHeaders()["Content-Type"];
return this.GetHeader("Content-Type");
};
GenericResponse.prototype.GetHeaders = function () {
return this.headers;
GenericResponse.prototype.GetHeader = function (name) {
return this.headers(name);
};
GenericResponse.prototype.GetHttpStatus = function () {
@ -105,18 +105,6 @@ var Typertext;
var HttpRequest = (function () {
function HttpRequest() {
}
HttpRequest.parseHeaderString = function (headerStr) {
var headers = {}, headerPairs = headerStr.split('\u000d\u000a');
for (var i = 0; i < headerPairs.length; i++) {
var headerPair = headerPairs[i], index = headerPair.indexOf('\u003a\u0020');
if (index > 0) {
var key = headerPair.substring(0, index);
headers[key] = headerPair.substring(index + 2);
}
}
return headers;
};
HttpRequest.prototype.Get = function (request, callback) {
this.RawRequest(0 /* GET */, request, {}, callback);
};
@ -132,9 +120,11 @@ var Typertext;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
var headers = HttpRequest.parseHeaderString(xhr.getAllResponseHeaders());
var getHeader = function (name) {
return xhr.getResponseHeader(name);
};
if (xhr.status == 200) {
callback(new Typertext.Http.HttpResponse(0 /* success */, headers, xhr.status, xhr.responseText));
callback(new Typertext.Http.HttpResponse(0 /* success */, getHeader, xhr.status, xhr.responseText));
} else if (xhr.status >= 400 && xhr.status < 500) {
throw new Typertext.Http.HttpException("Error type is unimplemented", -1, 2 /* clientError */);
} else if (xhr.status >= 500 && xhr.status < 600) {
@ -170,8 +160,8 @@ var Typertext;
(function (Http) {
var HttpResponse = (function (_super) {
__extends(HttpResponse, _super);
function HttpResponse(status, responseHeaders, httpResponseCode, responseBody) {
_super.call(this, status, responseHeaders, httpResponseCode, responseBody);
function HttpResponse(status, responseHeaderGetter, httpResponseCode, responseBody) {
_super.call(this, status, responseHeaderGetter, httpResponseCode, responseBody);
}
return HttpResponse;
})(Typertext.GenericResponse);
@ -218,18 +208,25 @@ var Typertext;
this.port = port;
}
HttpUrl.DefaultPort = function (protocol) {
return ((protocol == 0 /* http */) ? 80 : 443);
switch (protocol) {
case 0 /* http */:
return 80;
case 1 /* https */:
return 443;
default:
return -1;
}
};
HttpUrl.FromUrl = function (location) {
var l = document.createElement("a");
l.href = location;
return new HttpUrl(l.hostname, Typertext.Http.HttpProtocol[l.protocol], l.pathname, HttpUrl.DecodeQueryString(l.search));
return new HttpUrl(l.hostname, Typertext.Http.HttpProtocol[l.protocol.slice(0, -1)], l.pathname, HttpUrl.DecodeQueryString(l.search), parseInt(l.port));
};
HttpUrl.DecodeQueryString = function (queryString) {
if (queryString.length == 0 || queryString == "?") {
return {};
if (queryString.indexOf("?") == 0) {
queryString = queryString.substring(1);
}
return HttpUrl.UrlDecodeString(queryString);
@ -254,20 +251,25 @@ var Typertext;
HttpUrl.UrlDecodeString = function (queryString) {
var returnValue = {}, params = HttpUrl.splitString(queryString, "&");
for (var i = 0; i < params.length; i++) {
var param = HttpUrl.splitString(params[i], "=", 2);
if (param.length == 1) {
returnValue[param[0]] = "";
if (params[i] == "") {
continue;
}
returnValue[param[0]] = param[1];
var param = HttpUrl.splitString(params[i], "=", 2);
var key = decodeURIComponent(param[0]);
if (param.length == 1) {
returnValue[key] = "";
continue;
}
returnValue[key] = decodeURIComponent(param[1]);
}
return returnValue;
};
HttpUrl.splitString = function (input, separator, limit) {
if (typeof limit === "undefined") { limit = 0; }
if (typeof limit === "undefined") { limit = -1; }
limit++;
var chunks = input.split(separator);
if (limit > 0 && chunks.length > limit) {
@ -352,11 +354,11 @@ var Typertext;
(function (Json) {
var JsonResponse = (function (_super) {
__extends(JsonResponse, _super);
function JsonResponse(status, responseHeaders, httpResponseCode, responseBody) {
_super.call(this, status, responseHeaders, httpResponseCode, responseBody);
function JsonResponse(status, responseHeaderGetter, httpResponseCode, responseBody) {
_super.call(this, status, responseHeaderGetter, httpResponseCode, responseBody);
}
JsonResponse.fromHttpResponse = function (httpResponse) {
return new JsonResponse(httpResponse.GetStatus(), httpResponse.GetHeaders(), httpResponse.GetHttpStatus(), window["JSON"].parse(httpResponse.GetContent()));
return new JsonResponse(httpResponse.GetStatus(), httpResponse.GetHeader, httpResponse.GetHttpStatus(), window["JSON"].parse(httpResponse.GetContent()));
};
return JsonResponse;
})(Typertext.GenericResponse);

File diff suppressed because one or more lines are too long

10
karma.conf.js Normal file
View file

@ -0,0 +1,10 @@
module.exports = function (config) {
config.set({
basePath: __dirname,
frameworks: ['jasmine'],
files: [
'test/**/*.test.js',
'build/typertext.js'
]
});
};

View file

@ -4,12 +4,11 @@
* @submodule Json
*/
module Typertext {
import HttpHeaderData = Typertext.Http.HttpHeaderData;
import HttpResponseStatus = Typertext.Http.HttpResponseStatus;
export class GenericResponse<T> {
private status:HttpResponseStatus;
private headers:HttpHeaderData;
private headers:(input:string)=>string;
private httpStatus:number;
private content:T;
@ -21,7 +20,7 @@ module Typertext {
* @uses Typertext.Http.HttpResponseStatus
*
* @param {HttpResponseStatus} status
* @param {HttpHeaderData} responseHeaders
* @param {Function} responseHeaderGetter
* @param {number} httpResponseCode
* @param {T} responseBody
* @constructor
@ -29,9 +28,9 @@ module Typertext {
* @author Kegan Myers <kegan@keganmyers.com>
* @version 0.3.0
*/
constructor(status:HttpResponseStatus, responseHeaders?:HttpHeaderData, httpResponseCode?:number, responseBody?:T) {
constructor(status:HttpResponseStatus, responseHeaderGetter?:(input:string)=>string, httpResponseCode?:number, responseBody?:T) {
this.status = status;
this.headers = responseHeaders;
this.headers = responseHeaderGetter;
this.httpStatus = httpResponseCode;
this.content = responseBody;
}
@ -52,17 +51,17 @@ module Typertext {
* @constructor
*/
public GetContentType():string {
return this.GetHeaders()["Content-Type"];
return this.GetHeader("Content-Type");
}
/**
* Accessor method
*
* @returns {HttpHeaderData}
* @returns {string}
* @constructor
*/
public GetHeaders():HttpHeaderData {
return this.headers;
public GetHeader(name:string):string {
return this.headers(name);
}
/**

View file

@ -1,12 +0,0 @@
/**
* @namespace Typertext
* @module Http
*/
module Typertext.Http {
/**
* @interface HttpHeaderData
*/
export interface HttpHeaderData {
[index:string]:string
}
}

View file

@ -7,26 +7,6 @@
*/
module Typertext.Http {
export class HttpRequest implements Typertext.GenericRequest<HttpResponseHandler> {
/**
* A helper method that takes headers sent by the server and parses it out to an object
*
* @param {string} headerStr
* @returns {HttpHeaderData}
*/
private static parseHeaderString(headerStr:string):HttpHeaderData {
var headers:HttpHeaderData = {},
headerPairs:string[] = headerStr.split('\u000d\u000a');
for (var i:number = 0; i < headerPairs.length; i++) {
var headerPair:string = headerPairs[i],
index:number = headerPair.indexOf('\u003a\u0020');
if (index > 0) {
var key:string = headerPair.substring(0, index);
headers[key] = headerPair.substring(index + 2);
}
}
return headers;
}
/**
* The class that everything that calls an http(s) server should use and build on top of using callbacks
*
@ -76,9 +56,11 @@ module Typertext.Http {
xhr.onreadystatechange = ()=> {
//Once the request completes
if (xhr.readyState == 4) {
var headers:HttpHeaderData = HttpRequest.parseHeaderString(xhr.getAllResponseHeaders());
var getHeader = (name:string):string => {
return xhr.getResponseHeader(name);
};
if (xhr.status == 200) {
callback(new HttpResponse(HttpResponseStatus.success, headers, xhr.status, xhr.responseText));
callback(new HttpResponse(HttpResponseStatus.success, getHeader, xhr.status, xhr.responseText));
} else if (xhr.status >= 400 && xhr.status < 500) {
//TODO generate a client error callback

View file

@ -11,7 +11,7 @@ module Typertext.Http {
* @extends GenericResponse
*
* @param {HttpResponseStatus} status
* @param {HttpHeaderData} responseHeaders
* @param {Function} responseHeaderGetter
* @param {number} httpResponseCode
* @param {string} responseBody
*
@ -19,8 +19,8 @@ module Typertext.Http {
* @version 0.3.0
* @constructor
*/
constructor(status:HttpResponseStatus, responseHeaders?:HttpHeaderData, httpResponseCode?:number, responseBody?:string) {
super(status, responseHeaders, httpResponseCode, responseBody);
constructor(status:HttpResponseStatus, responseHeaderGetter?:(input:string)=>string, httpResponseCode?:number, responseBody?:string) {
super(status, responseHeaderGetter, httpResponseCode, responseBody);
}
}
}

View file

@ -8,9 +8,7 @@ module Typertext.Http {
private path:string;
private port:number;
private protocol:HttpProtocol;
private queryString:{
[index:string]:string
};
private queryString:HttpQueryString;
/**
*
@ -18,7 +16,14 @@ module Typertext.Http {
* @returns {number}
*/
public static DefaultPort(protocol:HttpProtocol) {
return ((protocol == HttpProtocol.http) ? 80 : 443)
switch(protocol) {
case HttpProtocol.http:
return 80;
case HttpProtocol.https:
return 443;
default:
return -1;
}
}
/**
@ -30,7 +35,7 @@ module Typertext.Http {
public static FromUrl(location:string):HttpUrl {
var l = document.createElement("a");
l.href = location;
return new HttpUrl(l.hostname, HttpProtocol[l.protocol], l.pathname, HttpUrl.DecodeQueryString(l.search))
return new HttpUrl(l.hostname, HttpProtocol[l.protocol.slice(0,-1)], l.pathname, HttpUrl.DecodeQueryString(l.search), parseInt(l.port))
}
/**
@ -40,8 +45,8 @@ module Typertext.Http {
* @returns {HttpQueryString}
*/
public static DecodeQueryString(queryString:string):HttpQueryString {
if (queryString.length == 0 || queryString == "?") {
return {};
if (queryString.indexOf("?") == 0) {
queryString = queryString.substring(1);
}
return HttpUrl.UrlDecodeString(queryString);
@ -84,13 +89,18 @@ module Typertext.Http {
public static UrlDecodeString(queryString:string):HttpQueryString {
var returnValue:HttpQueryString = {}, params:string[] = HttpUrl.splitString(queryString, "&");
for (var i:number = 0; i < params.length; i++) {
var param = HttpUrl.splitString(params[i], "=", 2);
if (param.length == 1) {
returnValue[param[0]] = "";
if (params[i] == "") {
continue;
}
returnValue[param[0]] = param[1];
var param = HttpUrl.splitString(params[i], "=", 2);
var key = decodeURIComponent(param[0]);
if (param.length == 1) {
returnValue[key] = "";
continue;
}
returnValue[key] = decodeURIComponent(param[1]);
}
return returnValue;
@ -105,7 +115,7 @@ module Typertext.Http {
* @param {number} limit
* @returns {string[]}
*/
private static splitString(input:string, separator:string, limit:number = 0):string[] {
private static splitString(input:string, separator:string, limit:number = -1):string[] {
limit++;
var chunks:string[] = input.split(separator);
if (limit > 0 && chunks.length > limit) {

View file

@ -5,7 +5,6 @@
module Typertext.Json {
import HttpResponse = Typertext.Http.HttpResponse;
import HttpResponseStatus = Typertext.Http.HttpResponseStatus;
import HttpHeaderData = Typertext.Http.HttpHeaderData;
export class JsonResponse extends Typertext.GenericResponse<JsonObject> {
@ -16,7 +15,7 @@ module Typertext.Json {
* @returns {JsonResponse}
*/
public static fromHttpResponse(httpResponse:HttpResponse):JsonResponse {
return new JsonResponse(httpResponse.GetStatus(), httpResponse.GetHeaders(), httpResponse.GetHttpStatus(), window["JSON"].parse(httpResponse.GetContent()));
return new JsonResponse(httpResponse.GetStatus(), httpResponse.GetHeader, httpResponse.GetHttpStatus(), window["JSON"].parse(httpResponse.GetContent()));
}
/**
@ -31,8 +30,8 @@ module Typertext.Json {
* @author Kegan Myers <kegan@keganmyers.com>
* @version 0.3.0
*/
constructor(status:HttpResponseStatus, responseHeaders?:HttpHeaderData, httpResponseCode?:number, responseBody?:JsonObject) {
super(status, responseHeaders, httpResponseCode, responseBody);
constructor(status:HttpResponseStatus, responseHeaderGetter?:(input:string)=>string, httpResponseCode?:number, responseBody?:JsonObject) {
super(status, responseHeaderGetter, httpResponseCode, responseBody);
}
}
}

View file

@ -1,14 +1,29 @@
{
"name": "Typertext",
"description": "A simple TypeScript HTTP request library",
"repository" : {
"type" : "git",
"url" : "https://github.com/terribleplan/Typertext.git"
},
"version": "0.3.1",
"devDependencies": {
"grunt": "~0.4.2",
"grunt-typescript": "~0.2.7"
},
"license": "MIT"
"name": "Typertext",
"description": "A simple TypeScript HTTP request library",
"repository": {
"type": "git",
"url": "https://github.com/terribleplan/Typertext.git"
},
"version": "0.3.1",
"devDependencies": {
"grunt": "~0.4.2",
"grunt-typescript": "~0.2.7",
"karma-script-launcher": "~0.1.0",
"karma-chrome-launcher": "~0.1.2",
"karma-firefox-launcher": "~0.1.3",
"karma-html2js-preprocessor": "~0.1.0",
"karma-jasmine": "~0.1.5",
"karma-coffee-preprocessor": "~0.1.3",
"requirejs": "~2.1.11",
"karma-requirejs": "~0.2.1",
"karma-phantomjs-launcher": "~0.1.2",
"karma": "~0.10.9",
"grunt-karma": "~0.6.2",
"phantomjs": "~1.9.7-1"
},
"license": "MIT",
"scripts": {
"test": "./node_modules/.bin/karma start karma.conf.js --single-run"
}
}

View file

@ -0,0 +1,5 @@
describe("Typertext.Http.HttpRequest", function() {
it("Exists", function() {
expect(typeof Typertext.Http.HttpRequest).toBe("function");
});
});

View file

@ -0,0 +1,256 @@
describe("Typertext.Http.HttpUrl", function () {
it("exists", function () {
expect(typeof Typertext.Http.HttpUrl).toBe("function");
});
describe("DefaultPort", function () {
it("exists", function () {
expect(typeof Typertext.Http.HttpUrl.DefaultPort).toBe("function");
});
it("returns the correct default port for http", function () {
var input = Typertext.Http.HttpProtocol.http,
expectedOutput = 80,
actualOutput = Typertext.Http.HttpUrl.DefaultPort(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("returns the correct default port for https", function () {
var input = Typertext.Http.HttpProtocol.https,
expectedOutput = 443,
actualOutput = Typertext.Http.HttpUrl.DefaultPort(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("returns -1 (an invalid port) for any unrecognized protocol", function () {
var inputs = [67, "", "nonsense", false, [], function () {
}],
expectedOutput = -1;
for (var i = inputs.length - 1; i >= 0; i--) {
expect(Typertext.Http.HttpUrl.DefaultPort(inputs[i])).toEqual(expectedOutput);
}
});
});
describe("FromUrl", function () {
it("is a function", function () {
expect(typeof Typertext.Http.HttpUrl.FromUrl).toBe("function");
});
it("handles a simple http url", function () {
var input = "http://example.com/",
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.http),
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("handles a simple https url", function () {
var input = "https://example.com/",
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.https),
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("handles an http url with a path", function () {
var input = "http://example.com/hello",
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.http, "/hello"),
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("handles an http url with a path to a file", function () {
var input = "http://example.com/index.html",
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.http, "/index.html"),
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("handles an http url with a query", function () {
var input = "http://example.com/?with=query",
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.http, "/", {with: "query"}),
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("handles an http url with a port", function () {
var input = "http://example.com:81/",
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.http, "/", {}, 81),
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("handles multiple complex urls", function () {
var input = "https://example.com:453/path/to/some.php?with=query",
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.https, "/path/to/some.php", {with: "query"}, 453),
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
input = "http://example.com:22/path/thing/?without";
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.http, "/path/thing/", {without: ""}, 22);
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
input = "https://example.com:80/path/thing/.htaccess?version=125&something=else";
expectedOutput = new Typertext.Http.HttpUrl("example.com", Typertext.Http.HttpProtocol.https, "/path/thing/.htaccess", {version: "125", something: "else"}, 80);
actualOutput = Typertext.Http.HttpUrl.FromUrl(input);
expect(actualOutput).toEqual(expectedOutput);
});
});
describe("DecodeQueryString", function () {
it("follows the same spec as DecodeQueryString, but will remove an optional leading '?'", function () {
var input = "?fizz=buzz",
expectedOutput = {
fizz: "buzz"
},
actualOutput = Typertext.Http.HttpUrl.DecodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
input = "fizz=buzz";
actualOutput = Typertext.Http.HttpUrl.DecodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
input = "?foo=bar&fizz=buzz&your=mom";
expectedOutput = {
fizz: "buzz",
foo: "bar",
your: "mom"
};
actualOutput = Typertext.Http.HttpUrl.DecodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
input = "foo=bar&fizz=buzz&your=mom";
actualOutput = Typertext.Http.HttpUrl.DecodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
input = "?foo=bar&fizz=buzz&enc%26me(=o%40u%23T%24";
expectedOutput = {
"enc&me(": "o@u#T$",
fizz: "buzz",
foo: "bar"
};
actualOutput = Typertext.Http.HttpUrl.DecodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
input = "foo=bar&fizz=buzz&enc%26me(=o%40u%23T%24";
actualOutput = Typertext.Http.HttpUrl.DecodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
});
});
describe("EncodeQueryString", function () {
it("follows the same spec as UrlEncodeObject, but with a prepended '?'", function () {
var input = {},
expectedOutput = "",
actualOutput = Typertext.Http.HttpUrl.EncodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
input = {
foo: "bar"
};
expectedOutput = "?foo=bar";
actualOutput = Typertext.Http.HttpUrl.EncodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
input = {
"enc&me(": "o@u#T$"
};
expectedOutput = "?enc%26me(=o%40u%23T%24";
actualOutput = Typertext.Http.HttpUrl.EncodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
input = {
"foo": "bar",
"fizz": "buzz"
};
expectedOutput = "?foo=bar&fizz=buzz";
actualOutput = Typertext.Http.HttpUrl.EncodeQueryString(input);
expect(actualOutput).toEqual(expectedOutput);
});
});
describe("UrlEncodeObject", function () {
//TODO
it("encodes an empty object as an empty string", function () {
var input = {},
expectedOutput = "",
actualOutput = Typertext.Http.HttpUrl.UrlEncodeObject(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("encodes an object with one k/v string pair", function () {
var input = {
foo: "bar"
},
expectedOutput = "foo=bar",
actualOutput = Typertext.Http.HttpUrl.UrlEncodeObject(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("encodes an object with one k/v string pair with special characters in both the key and value", function () {
var input = {
"enc&me(": "o@u#T$"
},
expectedOutput = "enc%26me(=o%40u%23T%24",
actualOutput = Typertext.Http.HttpUrl.UrlEncodeObject(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("encodes multiple key/value pairs", function () {
var input = {
"foo": "bar",
"fizz": "buzz"
},
expectedOutput = "foo=bar&fizz=buzz",
actualOutput = Typertext.Http.HttpUrl.UrlEncodeObject(input);
expect(actualOutput).toEqual(expectedOutput);
});
});
describe("UrlDecodeObject", function () {
it("decodes an empty string properly", function () {
var input = "",
expectedOutput = {},
actualOutput = Typertext.Http.HttpUrl.UrlDecodeString(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("decodes a single key/value pair", function () {
var input = "fizz=buzz",
expectedOutput = {
fizz: "buzz"
},
actualOutput = Typertext.Http.HttpUrl.UrlDecodeString(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("decodes multiple key/value pairs", function () {
var input = "foo=bar&fizz=buzz&your=mom",
expectedOutput = {
fizz: "buzz",
foo: "bar",
your: "mom"
},
actualOutput = Typertext.Http.HttpUrl.UrlDecodeString(input);
expect(actualOutput).toEqual(expectedOutput);
});
it("decodes key/value pairs with special characters", function () {
var input = "foo=bar&fizz=buzz&enc%26me(=o%40u%23T%24",
expectedOutput = {
"enc&me(": "o@u#T$",
fizz: "buzz",
foo: "bar"
},
actualOutput = Typertext.Http.HttpUrl.UrlDecodeString(input);
expect(actualOutput).toEqual(expectedOutput);
});
});
});