Skip to content

Commit

Permalink
Add more unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sgtcoolguy committed Mar 23, 2016
1 parent e28424f commit 1181a67
Show file tree
Hide file tree
Showing 2 changed files with 239 additions and 54 deletions.
162 changes: 108 additions & 54 deletions test/test-oauth.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,5 @@

// TODO Need some way to mock out the webview portion, basically just verify the URL we passed and be able to fake a response URL

let handler = function (httpclient) {
httpclient.responseText = '';
httpclient.onload();
};

/* Generate a mock Ti.Network.HTTPClient for testing */
function HTTPClient(obj) {
this.timeout = obj.timeout;
this.onload = obj.onload;
this.onerror = obj.onerror;
this.headers = {};
}

HTTPClient.prototype.send = function(data) {
this.data = data;
handler(this);
};

HTTPClient.prototype.setRequestHeader = function(name, value) {
this.headers[name] = value;
};

HTTPClient.prototype.open = function(method, url) {
this.method = method;
this.url = url;
};

global.Ti = {
Network: {
createHTTPClient: function(obj) {
return new HTTPClient(obj);
}
},
App: {
Properties: {
_props: {},
setString: function (key, value) {
global.Ti.App.Properties._props[key] = value;
},
getString: function (key, def) {
if (global.Ti.App.Properties._props.hasOwnProperty(key)) {
return global.Ti.App.Properties._props[key];
}
return def;
}
}
}
};
import Titanium from './ti-mock';
const Ti = global.Ti = global.Titanium = Titanium;

import OAuth from '../src/index';

Expand All @@ -69,16 +20,85 @@ const implicit = {
username: 'demouser',
password: 'testpass'
};

// TODO Add tests cases for calling onerror of httpclient for timeouts/etc
describe('OAuth 2.0', () => {
describe('Authorization Code Grant', () => {
});
describe('Implicit Grant', () => {
it('should authorize', (done) => {
Ti.UI.Webview.handler = (webview) => {
// Validate the url set on the webview, etc
expect(webview.url).to.include(`http://brentertainment.com/oauth2/lockdin/authorize?approval_prompt=force&redirect_uri=http%3A%2F%2Flocalhost%2FCallback&response_type=token&client_id=${implicit.clientId}&btmpl=mobile&state=`);
let state = webview.url.slice(webview.url.indexOf('state=') + 6);
// fire error event
let error = webview.eventListeners['error'] || [];
error.forEach( callback => {
callback({ url: `http://localhost/Callback?access_token=997f871ee3194eb39b4f2a19d5871cb8608e9c0a&state=${state}&token_type=Bearer` });
});
};
OAuth.authorizeImplicitly(implicit.url, implicit.clientId, function (err, oauth) {
if (err) {
return done(err);
}

expect(oauth).to.exist;
expect(oauth).to.be.an('object');
expect(oauth.clientId).to.equal(password.clientId);
expect(oauth.accessToken).to.equal('997f871ee3194eb39b4f2a19d5871cb8608e9c0a');
expect(oauth.tokenType).to.equal('Bearer');

done();
});
});

it('should return error for possible CSRF from un-matched state value', (done) => {
Ti.UI.Webview.handler = (webview) => {
// Validate the url set on the webview, etc
expect(webview.url).to.include(`http://brentertainment.com/oauth2/lockdin/authorize?approval_prompt=force&redirect_uri=http%3A%2F%2Flocalhost%2FCallback&response_type=token&client_id=${implicit.clientId}&btmpl=mobile&state=`);
// fire error event
let error = webview.eventListeners['error'] || [];
error.forEach(callback => {
callback({ url: 'http://localhost/Callback?access_token=997f871ee3194eb39b4f2a19d5871cb8608e9c0a&state=madeupstate&token_type=Bearer' });
});
};
OAuth.authorizeImplicitly(implicit.url, implicit.clientId, function (err, oauth) {
expect(err).to.exist;

expect(oauth).not.to.exist;

done();
});
});

it('should not allow refresh of token', (done) => {
Ti.UI.Webview.handler = (webview) => {
// Validate the url set on the webview, etc
expect(webview.url).to.include(`http://brentertainment.com/oauth2/lockdin/authorize?approval_prompt=force&redirect_uri=http%3A%2F%2Flocalhost%2FCallback&response_type=token&client_id=${implicit.clientId}&btmpl=mobile&state=`);
let state = webview.url.slice(webview.url.indexOf('state=') + 6);
// fire error event
let error = webview.eventListeners['error'] || [];
error.forEach( callback => {
callback({ url: `http://localhost/Callback?access_token=997f871ee3194eb39b4f2a19d5871cb8608e9c0a&state=${state}&token_type=Bearer` });
});
};
OAuth.authorizeImplicitly(implicit.url, implicit.clientId, function (err, oauth) {
if (err) {
return done(err);
}

expect(oauth).to.exist;

oauth.refresh('http://example.com/refreshUrl', function (err, refresh_oauth) {
expect(err).to.exist;
expect(refresh_oauth).not.to.exist;
done();
});
});
});
});
describe('Resource Owner Password Credentials Grant', () => {
// TODO Add tests to handle failure cases!
it('should authorize with username and password', (done) => {
handler = function (httpclient) {
Ti.Network.HTTPClient.handler = (httpclient) => {
// Verify headers/data/method/url
expect(httpclient.headers).to.have.property('Content-Type', 'application/x-www-form-urlencoded');
expect(httpclient.method).to.equal('POST');
Expand Down Expand Up @@ -110,6 +130,40 @@ describe('OAuth 2.0', () => {
done();
});
});

it('should allow refresh of token', (done) => {
Ti.Network.HTTPClient.handler = (httpclient) => {
httpclient.responseText = '{"access_token":"997f871ee3194eb39b4f2a19d5871cb8608e9c0a","expires_in":3600,"token_type":"Bearer","scope":null,"refresh_token":"2a6c5b92369fb8c8f56a82e7a1b81dbf6d756a69"}';
httpclient.status = 200;

httpclient.onload();
};
OAuth.authorizeWithPassword(password.url, password.clientId, password.clientSecret, password.username, password.password, function (err, oauth) {
if (err) {
return done(err);
}

expect(oauth).to.exist;
expect(oauth.refreshToken).to.equal('2a6c5b92369fb8c8f56a82e7a1b81dbf6d756a69');

// Set up next response
Ti.Network.HTTPClient.handler = (httpclient) => {
httpclient.responseText = '{"access_token":"2a6c5b92369fb8c8f56a82e7a1b81dbf6d756a60","expires_in":3600,"token_type":"Bearer","scope":null,"refresh_token":"997f871ee3194eb39b4f2a19d5871cb8608e9c0b"}';
httpclient.status = 200;

httpclient.onload();
};

oauth.refresh('http://example.com/refreshUrl', function (err, refresh_oauth) {
expect(err).not.to.exist;
expect(refresh_oauth).to.exist;
expect(oauth.accessToken).to.equal('2a6c5b92369fb8c8f56a82e7a1b81dbf6d756a60');
expect(oauth.refreshToken).to.equal('997f871ee3194eb39b4f2a19d5871cb8608e9c0b');

done();
});
});
});
});
describe('Client Credentials Grant', () => {
});
Expand Down
131 changes: 131 additions & 0 deletions test/ti-mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@

/* Generate a mock Ti.Network.HTTPClient for testing */
function TiNetworkHTTPClient(obj) {
this.timeout = obj.timeout;
this.onload = obj.onload;
this.onerror = obj.onerror;
this.headers = {};
}

TiNetworkHTTPClient.handler = function (httpclient) {
httpclient.responseText = '';
httpclient.status = 200;
httpclient.statusText = 'OK';
httpclient.onload();
};

TiNetworkHTTPClient.prototype.send = function(data) {
this.data = data;
TiNetworkHTTPClient.handler(this);
};

TiNetworkHTTPClient.prototype.setRequestHeader = function(name, value) {
this.headers[name] = value;
};

TiNetworkHTTPClient.prototype.open = function(method, url) {
this.method = method;
this.url = url;
};

function TiUIWindow(obj) {
this.children = [];
}

TiUIWindow.prototype.add = function (view) {
this.children.push(view);
};

TiUIWindow.prototype.open = function () {
this.children.forEach(c => c.show()); // FIXME Should we call show on each child? or some method noting it's been opened?
};

TiUIWindow.prototype.close = function () {
// no-op
};

function TiUIWebview(obj) {
this.url = obj.url;
this.width = obj.width;
this.height = obj.height;
this.eventListeners = {};
}

TiUIWebview.handler = function (webview) {
// fire beforeload
let beforeLoad = webview.eventListeners['beforeload'] || [];
beforeLoad.forEach(callback => callback({ url: webview.url }));
// fire load
let load = webview.eventListeners['load'] || [];
load.forEach(callback => callback({ url: webview.url }));
};

TiUIWebview.prototype.addEventListener = function (name, callback) {
let listeners = this.eventListeners[name] || [];
listeners.push(callback);
this.eventListeners[name] = listeners;
};

TiUIWebview.prototype.show = function () {
// Load the URL
TiUIWebview.handler(this);
};

TiUIWebview.prototype.hide = function () {
// no-op
};

function TiUIActivityIndicator(obj) {

}

TiUIActivityIndicator.prototype.show = function () {
// no-op
};

TiUIActivityIndicator.prototype.hide = function () {
// no-op
};


let Ti = {
Network: {
createHTTPClient: function(obj) {
return new TiNetworkHTTPClient(obj);
},
HTTPClient: TiNetworkHTTPClient
},
App: {
Properties: {
_props: {},
setString: function (key, value) {
global.Ti.App.Properties._props[key] = value;
},
getString: function (key, def) {
if (global.Ti.App.Properties._props.hasOwnProperty(key)) {
return global.Ti.App.Properties._props[key];
}
return def;
}
}
},
UI: {
ActivityIndicator: TiUIActivityIndicator,
Window: TiUIWindow,
Webview: TiUIWebview,
ActivityIndicatorStyle: {
DARK: 1
},
createActivityIndicator: function (obj) {
return new TiUIActivityIndicator(obj);
},
createWindow: function(obj) {
return new TiUIWindow(obj);
},
createWebView: function(obj) {
return new TiUIWebview(obj);
}
}
};

export default Ti;

0 comments on commit 1181a67

Please sign in to comment.