Merge pull request #1 from terribleplan/add-testing

Add testing
This commit is contained in:
Kegan Myers 2014-03-03 15:50:34 -06:00
commit 3bcc30a9ed
22 changed files with 580 additions and 115 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,19 @@
module.exports = function (grunt) { module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-typescript'); grunt.loadNpmTasks('grunt-typescript');
grunt.initConfig({ grunt.initConfig({
karma: {
unit: {
configFile: "karma.conf.js",
singleRun: true,
browsers: ["PhantomJS", "Chrome"]
},
watch: {
configFile: "karma.conf.js",
browsers: ["PhantomJS", "Chrome"]
}
},
typescript: { typescript: {
base: { base: {
src: ["lib/**/*.ts"], src: ["lib/**/*.ts"],
@ -15,4 +27,6 @@ module.exports = function (grunt) {
}); });
grunt.registerTask('default', ['typescript']); grunt.registerTask('default', ['typescript']);
grunt.registerTask('test', ['typescript', 'karma:unit'])
}; };

View File

@ -2,6 +2,8 @@ Typertext
========= =========
Typertext is a TypeScript native library for simple, sane, and extensible HTTP requests. Typertext is a TypeScript native library for simple, sane, and extensible HTTP requests.
[![Build Status](https://secure.travis-ci.org/terribleplan/Typertext.png?branch=master)](https://travis-ci.org/terribleplan/Typertext)
Usage Usage
----- -----
```` ````
@ -11,9 +13,8 @@ Usage
Todo Todo
---- ----
- More robust error handling - More robust error handling
- Testing
- IE 8-9 CORS support (XDomain) - IE 8-9 CORS support (XDomain)
- CI - Integrate with Sauce Labs
Notes Notes
----- -----

14
build/typertext.d.ts vendored
View File

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

View File

@ -28,9 +28,9 @@ var Typertext;
var Typertext; var Typertext;
(function (Typertext) { (function (Typertext) {
var GenericResponse = (function () { var GenericResponse = (function () {
function GenericResponse(status, responseHeaders, httpResponseCode, responseBody) { function GenericResponse(status, responseHeaderGetter, httpResponseCode, responseBody) {
this.status = status; this.status = status;
this.headers = responseHeaders; this.headers = responseHeaderGetter;
this.httpStatus = httpResponseCode; this.httpStatus = httpResponseCode;
this.content = responseBody; this.content = responseBody;
} }
@ -39,11 +39,11 @@ var Typertext;
}; };
GenericResponse.prototype.GetContentType = function () { GenericResponse.prototype.GetContentType = function () {
return this.GetHeaders()["Content-Type"]; return this.GetHeader("Content-Type");
}; };
GenericResponse.prototype.GetHeaders = function () { GenericResponse.prototype.GetHeader = function (name) {
return this.headers; return this.headers(name);
}; };
GenericResponse.prototype.GetHttpStatus = function () { GenericResponse.prototype.GetHttpStatus = function () {
@ -105,18 +105,6 @@ var Typertext;
var HttpRequest = (function () { var HttpRequest = (function () {
function HttpRequest() { 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) { HttpRequest.prototype.Get = function (request, callback) {
this.RawRequest(0 /* GET */, request, {}, callback); this.RawRequest(0 /* GET */, request, {}, callback);
}; };
@ -132,9 +120,11 @@ var Typertext;
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
var headers = HttpRequest.parseHeaderString(xhr.getAllResponseHeaders()); var getHeader = function (name) {
return xhr.getResponseHeader(name);
};
if (xhr.status == 200) { 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) { } else if (xhr.status >= 400 && xhr.status < 500) {
throw new Typertext.Http.HttpException("Error type is unimplemented", -1, 2 /* clientError */); throw new Typertext.Http.HttpException("Error type is unimplemented", -1, 2 /* clientError */);
} else if (xhr.status >= 500 && xhr.status < 600) { } else if (xhr.status >= 500 && xhr.status < 600) {
@ -170,8 +160,8 @@ var Typertext;
(function (Http) { (function (Http) {
var HttpResponse = (function (_super) { var HttpResponse = (function (_super) {
__extends(HttpResponse, _super); __extends(HttpResponse, _super);
function HttpResponse(status, responseHeaders, httpResponseCode, responseBody) { function HttpResponse(status, responseHeaderGetter, httpResponseCode, responseBody) {
_super.call(this, status, responseHeaders, httpResponseCode, responseBody); _super.call(this, status, responseHeaderGetter, httpResponseCode, responseBody);
} }
return HttpResponse; return HttpResponse;
})(Typertext.GenericResponse); })(Typertext.GenericResponse);
@ -203,7 +193,7 @@ var Typertext;
if (typeof path === "undefined") { path = "/"; } if (typeof path === "undefined") { path = "/"; }
if (typeof queryString === "undefined") { queryString = {}; } if (typeof queryString === "undefined") { queryString = {}; }
if (typeof port === "undefined") { port = 0; } if (typeof port === "undefined") { port = 0; }
if (port < 1 || port > 65535) { if (port < 1 || port > 65535 || isNaN(port)) {
port = HttpUrl.DefaultPort(protocol); port = HttpUrl.DefaultPort(protocol);
} }
@ -218,18 +208,25 @@ var Typertext;
this.port = port; this.port = port;
} }
HttpUrl.DefaultPort = function (protocol) { 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) { HttpUrl.FromUrl = function (location) {
var l = document.createElement("a"); var l = document.createElement("a");
l.href = location; 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) { HttpUrl.DecodeQueryString = function (queryString) {
if (queryString.length == 0 || queryString == "?") { if (queryString.indexOf("?") == 0) {
return {}; queryString = queryString.substring(1);
} }
return HttpUrl.UrlDecodeString(queryString); return HttpUrl.UrlDecodeString(queryString);
@ -254,20 +251,25 @@ var Typertext;
HttpUrl.UrlDecodeString = function (queryString) { HttpUrl.UrlDecodeString = function (queryString) {
var returnValue = {}, params = HttpUrl.splitString(queryString, "&"); var returnValue = {}, params = HttpUrl.splitString(queryString, "&");
for (var i = 0; i < params.length; i++) { for (var i = 0; i < params.length; i++) {
var param = HttpUrl.splitString(params[i], "=", 2); if (params[i] == "") {
if (param.length == 1) {
returnValue[param[0]] = "";
continue; 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; return returnValue;
}; };
HttpUrl.splitString = function (input, separator, limit) { HttpUrl.splitString = function (input, separator, limit) {
if (typeof limit === "undefined") { limit = 0; } if (typeof limit === "undefined") { limit = -1; }
limit++; limit++;
var chunks = input.split(separator); var chunks = input.split(separator);
if (limit > 0 && chunks.length > limit) { if (limit > 0 && chunks.length > limit) {
@ -352,11 +354,11 @@ var Typertext;
(function (Json) { (function (Json) {
var JsonResponse = (function (_super) { var JsonResponse = (function (_super) {
__extends(JsonResponse, _super); __extends(JsonResponse, _super);
function JsonResponse(status, responseHeaders, httpResponseCode, responseBody) { function JsonResponse(status, responseHeaderGetter, httpResponseCode, responseBody) {
_super.call(this, status, responseHeaders, httpResponseCode, responseBody); _super.call(this, status, responseHeaderGetter, httpResponseCode, responseBody);
} }
JsonResponse.fromHttpResponse = function (httpResponse) { 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; return JsonResponse;
})(Typertext.GenericResponse); })(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 * @submodule Json
*/ */
module Typertext { module Typertext {
import HttpHeaderData = Typertext.Http.HttpHeaderData;
import HttpResponseStatus = Typertext.Http.HttpResponseStatus; import HttpResponseStatus = Typertext.Http.HttpResponseStatus;
export class GenericResponse<T> { export class GenericResponse<T> {
private status:HttpResponseStatus; private status:HttpResponseStatus;
private headers:HttpHeaderData; private headers:(input:string)=>string;
private httpStatus:number; private httpStatus:number;
private content:T; private content:T;
@ -21,7 +20,7 @@ module Typertext {
* @uses Typertext.Http.HttpResponseStatus * @uses Typertext.Http.HttpResponseStatus
* *
* @param {HttpResponseStatus} status * @param {HttpResponseStatus} status
* @param {HttpHeaderData} responseHeaders * @param {Function} responseHeaderGetter
* @param {number} httpResponseCode * @param {number} httpResponseCode
* @param {T} responseBody * @param {T} responseBody
* @constructor * @constructor
@ -29,9 +28,9 @@ module Typertext {
* @author Kegan Myers <kegan@keganmyers.com> * @author Kegan Myers <kegan@keganmyers.com>
* @version 0.3.0 * @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.status = status;
this.headers = responseHeaders; this.headers = responseHeaderGetter;
this.httpStatus = httpResponseCode; this.httpStatus = httpResponseCode;
this.content = responseBody; this.content = responseBody;
} }
@ -52,17 +51,17 @@ module Typertext {
* @constructor * @constructor
*/ */
public GetContentType():string { public GetContentType():string {
return this.GetHeaders()["Content-Type"]; return this.GetHeader("Content-Type");
} }
/** /**
* Accessor method * Accessor method
* *
* @returns {HttpHeaderData} * @returns {string}
* @constructor * @constructor
*/ */
public GetHeaders():HttpHeaderData { public GetHeader(name:string):string {
return this.headers; 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 { module Typertext.Http {
export class HttpRequest implements Typertext.GenericRequest<HttpResponseHandler> { 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 * 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 = ()=> { xhr.onreadystatechange = ()=> {
//Once the request completes //Once the request completes
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
var headers:HttpHeaderData = HttpRequest.parseHeaderString(xhr.getAllResponseHeaders()); var getHeader = (name:string):string => {
return xhr.getResponseHeader(name);
};
if (xhr.status == 200) { 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) { } else if (xhr.status >= 400 && xhr.status < 500) {
//TODO generate a client error callback //TODO generate a client error callback

View File

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

View File

@ -8,9 +8,7 @@ module Typertext.Http {
private path:string; private path:string;
private port:number; private port:number;
private protocol:HttpProtocol; private protocol:HttpProtocol;
private queryString:{ private queryString:HttpQueryString;
[index:string]:string
};
/** /**
* *
@ -18,7 +16,14 @@ module Typertext.Http {
* @returns {number} * @returns {number}
*/ */
public static DefaultPort(protocol:HttpProtocol) { 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 { public static FromUrl(location:string):HttpUrl {
var l = document.createElement("a"); var l = document.createElement("a");
l.href = location; 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} * @returns {HttpQueryString}
*/ */
public static DecodeQueryString(queryString:string):HttpQueryString { public static DecodeQueryString(queryString:string):HttpQueryString {
if (queryString.length == 0 || queryString == "?") { if (queryString.indexOf("?") == 0) {
return {}; queryString = queryString.substring(1);
} }
return HttpUrl.UrlDecodeString(queryString); return HttpUrl.UrlDecodeString(queryString);
@ -84,13 +89,18 @@ module Typertext.Http {
public static UrlDecodeString(queryString:string):HttpQueryString { public static UrlDecodeString(queryString:string):HttpQueryString {
var returnValue:HttpQueryString = {}, params:string[] = HttpUrl.splitString(queryString, "&"); var returnValue:HttpQueryString = {}, params:string[] = HttpUrl.splitString(queryString, "&");
for (var i:number = 0; i < params.length; i++) { for (var i:number = 0; i < params.length; i++) {
var param = HttpUrl.splitString(params[i], "=", 2); if (params[i] == "") {
if (param.length == 1) {
returnValue[param[0]] = "";
continue; 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; return returnValue;
@ -105,7 +115,7 @@ module Typertext.Http {
* @param {number} limit * @param {number} limit
* @returns {string[]} * @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++; limit++;
var chunks:string[] = input.split(separator); var chunks:string[] = input.split(separator);
if (limit > 0 && chunks.length > limit) { if (limit > 0 && chunks.length > limit) {
@ -131,7 +141,7 @@ module Typertext.Http {
* @constructor * @constructor
*/ */
constructor(domain:string, protocol:HttpProtocol = HttpProtocol.http, path:string = "/", queryString:HttpQueryString = {}, port:number = 0) { constructor(domain:string, protocol:HttpProtocol = HttpProtocol.http, path:string = "/", queryString:HttpQueryString = {}, port:number = 0) {
if (port < 1 || port > 65535) { if (port < 1 || port > 65535 || isNaN(port)) {
port = HttpUrl.DefaultPort(protocol); port = HttpUrl.DefaultPort(protocol);
} }

View File

@ -5,7 +5,6 @@
module Typertext.Json { module Typertext.Json {
import HttpResponse = Typertext.Http.HttpResponse; import HttpResponse = Typertext.Http.HttpResponse;
import HttpResponseStatus = Typertext.Http.HttpResponseStatus; import HttpResponseStatus = Typertext.Http.HttpResponseStatus;
import HttpHeaderData = Typertext.Http.HttpHeaderData;
export class JsonResponse extends Typertext.GenericResponse<JsonObject> { export class JsonResponse extends Typertext.GenericResponse<JsonObject> {
@ -16,7 +15,7 @@ module Typertext.Json {
* @returns {JsonResponse} * @returns {JsonResponse}
*/ */
public static fromHttpResponse(httpResponse:HttpResponse):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> * @author Kegan Myers <kegan@keganmyers.com>
* @version 0.3.0 * @version 0.3.0
*/ */
constructor(status:HttpResponseStatus, responseHeaders?:HttpHeaderData, httpResponseCode?:number, responseBody?:JsonObject) { constructor(status:HttpResponseStatus, responseHeaderGetter?:(input:string)=>string, httpResponseCode?:number, responseBody?:JsonObject) {
super(status, responseHeaders, httpResponseCode, responseBody); super(status, responseHeaderGetter, httpResponseCode, responseBody);
} }
} }
} }

View File

@ -1,14 +1,30 @@
{ {
"name": "Typertext", "name": "Typertext",
"description": "A simple TypeScript HTTP request library", "description": "A simple TypeScript HTTP request library",
"repository" : { "repository": {
"type" : "git", "type": "git",
"url" : "https://github.com/terribleplan/Typertext.git" "url": "https://github.com/terribleplan/Typertext.git"
}, },
"version": "0.3.1", "version": "0.3.1",
"devDependencies": { "devDependencies": {
"grunt": "~0.4.2", "grunt": "~0.4.2",
"grunt-typescript": "~0.2.7" "grunt-cli": "~0.1.13",
"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" "license": "MIT",
"scripts": {
"test": "./node_modules/.bin/grunt test"
}
} }

View File

@ -0,0 +1,16 @@
describe("Typertext.BaseException", function () {
it("exists", function () {
expect(typeof Typertext.BaseException).toEqual("function");
});
it("works", function () {
var inputString = "Test message",
inputCode = 239,
inputCustom = -1,
testClass = new Typertext.Http.HttpException(inputString, inputCode, inputCustom);
expect(testClass.GetCode()).toEqual(inputCode);
expect(testClass.GetCustom()).toEqual(inputCustom);
expect(testClass.GetMessage()).toEqual(inputString);
});
});

View File

@ -0,0 +1,20 @@
describe("Typertext.GenericResponse", function () {
it("exists", function () {
expect(typeof Typertext.GenericResponse).toEqual("function");
});
it("works", function () {
function hf(input) {
return "TestString" + input;
}
var inputString = "Test message",
inputCode = 239,
inputHttp = Typertext.Http.HttpResponseStatus.clientError,
testClass = new Typertext.Http.HttpResponse(inputHttp, hf, inputCode, inputString);
expect(testClass.GetContent()).toEqual(inputString);
expect(testClass.GetHeader("foo")).toEqual("TestStringfoo");
expect(testClass.GetHttpStatus()).toEqual(inputCode);
expect(testClass.GetStatus()).toEqual(inputHttp);
});
});

View File

@ -0,0 +1,16 @@
describe("Typertext.Http.HttpException", function () {
it("exists", function () {
expect(typeof Typertext.Http.HttpException).toEqual("function");
});
it("works according to the parent class", function () {
var inputString = "Test message",
inputCode = 239,
inputHttp = Typertext.Http.HttpResponseStatus.clientError,
testClass = new Typertext.Http.HttpException(inputString, inputCode, inputHttp);
expect(testClass.GetCode()).toEqual(inputCode);
expect(testClass.GetCustom()).toEqual(inputHttp);
expect(testClass.GetMessage()).toEqual(inputString);
});
});

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,20 @@
describe("Typertext.Http.HttpResponse", function () {
it("exists", function () {
expect(typeof Typertext.Http.HttpResponse).toEqual("function");
});
it("works according to the parent class", function () {
function hf(input) {
return "TestString" + input;
}
var inputString = "Test message",
inputCode = 239,
inputHttp = Typertext.Http.HttpResponseStatus.clientError,
testClass = new Typertext.Http.HttpResponse(inputHttp, hf, inputCode, inputString);
expect(testClass.GetContent()).toEqual(inputString);
expect(testClass.GetHeader("foo")).toEqual("TestStringfoo");
expect(testClass.GetHttpStatus()).toEqual(inputCode);
expect(testClass.GetStatus()).toEqual(inputHttp);
});
});

View File

@ -0,0 +1,295 @@
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);
});
});
describe("ToString", function () {
it("handles simple urls", function () {
var input = "http://example.com/",
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
input = "https://example.com/";
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
input = "http://example.com/hello";
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
input = "http://example.com/index.html";
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
input = "http://example.com/?with=query";
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
input = "http://example.com:81/";
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
});
it("handles multiple complex urls", function () {
var input = "https://example.com:453/path/to/some.php?with=query",
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
input = "http://example.com:22/path/thing/?without=";
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
input = "https://example.com:80/path/thing/.htaccess?version=125&something=else";
actualOutput = Typertext.Http.HttpUrl.FromUrl(input).ToString();
expect(actualOutput).toEqual(input);
});
})
});

View File

@ -0,0 +1,15 @@
describe("Typertext.Json.JsonException", function () {
it("exists", function () {
expect(typeof Typertext.Json.JsonException).toEqual("function");
});
it("works according to the parent class", function () {
var inputString = "Test message",
inputCode = 239,
testClass = new Typertext.Json.JsonException(inputString, inputCode, null);
expect(testClass.GetCode()).toEqual(inputCode);
expect(testClass.GetCustom()).toEqual(null);
expect(testClass.GetMessage()).toEqual(inputString);
});
});

View File

@ -0,0 +1,59 @@
describe("Typertext.Json.JsonResponse", function () {
it("exists", function () {
expect(typeof Typertext.Json.JsonResponse).toBe("function");
});
describe("fromHttpResponse", function () {
it("exists", function () {
expect(typeof Typertext.Json.JsonResponse.fromHttpResponse).toBe("function");
});
it("handles an empty json object response", function () {
function hf() {
return "";
}
var inputBody = "{}",
input = new Typertext.Http.HttpResponse(Typertext.Http.HttpResponseStatus.success, hf, 200, inputBody),
expectedBody = {},
expectedOutput = new Typertext.Json.JsonResponse(Typertext.Http.HttpResponseStatus.success, hf, 200, expectedBody),
actualOutput = Typertext.Json.JsonResponse.fromHttpResponse(input);
expect(window["JSON"].parse(inputBody)).toEqual(expectedBody);
expect(window["JSON"].stringify(actualOutput)).toEqual(window["JSON"].stringify(expectedOutput));
});
it("handles an empty string", function () {
function hf() {
return "";
}
var inputBody = "",
input = new Typertext.Http.HttpResponse(Typertext.Http.HttpResponseStatus.success, hf, 200, inputBody);
expect(function () {
Typertext.Json.JsonResponse.fromHttpResponse(input);
}).toThrow();
});
it("handles an example server response", function () {
function hf() {
return "";
}
var inputBody = "{\"access_token\":\"0d95289cb2f54831dc435ce9274b1d1bdf8f5949\",\"expires_in\":86400," +
"\"token_type\":\"Bearer\",\"scope\":null," +
"\"refresh_token\":\"8a4431470af2edc3fdf747eca5f71451a3ad2d98\"}",
input = new Typertext.Http.HttpResponse(Typertext.Http.HttpResponseStatus.success, hf, 200, inputBody),
expectedBody = {
"access_token": "0d95289cb2f54831dc435ce9274b1d1bdf8f5949",
"expires_in": 86400,
"token_type": "Bearer",
"scope": null,
"refresh_token": "8a4431470af2edc3fdf747eca5f71451a3ad2d98"
},
expectedOutput = new Typertext.Json.JsonResponse(Typertext.Http.HttpResponseStatus.success, hf, 200, expectedBody),
actualOutput = Typertext.Json.JsonResponse.fromHttpResponse(input);
expect(window["JSON"].parse(inputBody)).toEqual(expectedBody);
expect(window["JSON"].stringify(actualOutput)).toEqual(window["JSON"].stringify(expectedOutput));
});
});
});