Permalink
Browse files

Big refactoring + unit tests; allow setting the number of loans to show

* Add UI for changing the number of loans to fetch
* Create a <style> element to put the map CSS
* Use that single, static element instead of creating a lot of anonymous
  <style> elements all the time
* Uppercase KivaMap and KivaData
* Make KivaMap receive two new parameters: a CSS selector for the map
  HTML and a CSS selector for the map CSS
* Big refactoring of KivaData: add 'dataSource' attribute, pointing to
  the source to get the loan data from; add 'numberLoans' attribute,
  with the number of loans to fetch; make the loan data loading much
  more flexible *and* testable; add 'eachLoan' iterator to avoid
  accessing 'loanInfo' directly from the outside
* Add two Javascript fixture files
* Add a lot of new unit tests
  • Loading branch information...
emanchado committed Mar 29, 2009
1 parent c6c2468 commit 425a4a854bc074c825b6f15546235958977da1d5

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
View
@@ -4,6 +4,8 @@
<head>
<script src="../widget/javascript/jquery.js"></script>
<script src="../widget/javascript/kivadata.js"></script>
<script src="fixtures/loans-simple.js"></script>
<script src="fixtures/loans-two-pages.js"></script>
<script src="../widget/javascript/map.js"></script>
<link rel="stylesheet" href="testsuite.css" type="text/css" media="screen" />
@@ -12,13 +14,171 @@
module("Latitude/longitude conversion");
test("a basic test example", function() {
test("Basic latLongToPixels tests", function() {
var x, y;
[x,y] = mapMaker.latLongToPixels(12.5833, -16.2719);
equals(x, 118, "The 'x' is correct");
equals(y, 250, "The 'y' is correct");
});
module("eachLoan iterator");
test("eachLoan simple test", function() {
var kivaData = new KivaData();
kivaData.loanInfo = loansSimple;
var loanList = [];
kivaData.eachLoan(function () {
loanList.push(this);
});
equals(loanList.length, 3,
"Should have iterated over 3 loans");
equals(loanList[0].name, 'Fatoumata Coly',
"Name of first loan should be correct");
equals(loanList[1].name, 'Marie Gomis',
"Name of second loan should be correct");
equals(loanList[2].name, 'Lydia Afriyie',
"Name of third loan should be correct");
});
module("Data fetching");
test("fetchLoanData function test", function() {
var kivaData = new KivaData();
kivaData.dataSource = function (n) { return "function value" };
var result = undefined;
kivaData.fetchLoanData(1, function (data) { result = data; });
equals(result, 'function value',
"fetchLoanData should call dataSource if it's a function");
});
test("fetchLoanData object test", function() {
var kivaData = new KivaData();
kivaData.dataSource = ['some', 'list'];
var result = undefined;
kivaData.fetchLoanData(1, function (data) { result = data; });
equals(result.length, 2, "The result list should have two elements");
equals(result[0], 'some', "The result's first element should be 'some'");
equals(result[1], 'list', "The result's second element should be 'list'");
});
test("refresh method", function() {
var kivaData = new KivaData();
kivaData.dataSource = ['some', 'list'];
var result = undefined;
kivaData.fetchLoanData(1, function (data) { result = data; });
equals(result.length, 2, "The result list should have two elements");
equals(result[0], 'some', "The result's first element should be 'some'");
equals(result[1], 'list', "The result's second element should be 'list'");
});
test("addLoans method", function() {
var kivaData = new KivaData();
equals(kivaData.loanInfo.loans.length, 0,
"There should be no loans initially");
kivaData.addLoans({'loans': [{'foo': 'bar'}, {'qux': 'flux'}]});
equals(kivaData.loanInfo.loans.length, 2,
"There should be 2 loans");
// Load at most one more loan
kivaData.addLoans({'loans': [{'third': 'and last'},
{'extra': 'should not load'}]},
1);
equals(kivaData.loanInfo.loans.length, 3,
"atMost parameter should be honoured");
// Less loans than atMost
kivaData.addLoans({'loans': [{'one': 'first'}, {'two': 'last'}]},
3);
equals(kivaData.loanInfo.loans.length, 5,
"Nothing strange should happen if there are less loans than atMost");
});
test("loadLoans infinite loop protection", function() {
expect(1);
var kivaData = new KivaData();
kivaData.numberLoans = 3;
// Dummy data source, never returns loans
kivaData.dataSource = {'loans': []}
kivaData.loadLoans(1, function () {
equals(kivaData.loanInfo.loans.length, 0,
"If there are no loans, it should not get into an infinite loop");
});
});
test("loadLoans pagination tests", function() {
expect(18);
var kivaData = new KivaData();
kivaData.numberLoans = 3;
// This data source will iterate over the pages in the loanPages variable
kivaData.dataSource = function (counter) {
return loanPages[(counter-1) % loanPages.length]
};
equals(kivaData.loanInfo.loans.length, 0,
"There should be no loans initially");
kivaData.loadLoans(1, function () {
equals(kivaData.loanInfo.loans.length, 3,
"There should be 3 loans");
equals(kivaData.loanInfo.loans[0].name, 'Fatoumata Coly',
"The name for the first loan should be correct");
equals(kivaData.loanInfo.loans[1].name, 'Marie Gomis',
"The name for the second loan should be correct");
equals(kivaData.loanInfo.loans[2].name, 'Lydia Afriyie',
"The name for the third loan should be correct");
});
// Again, now needing one loan from the second page
var kivaDataSecondPage = new KivaData();
kivaDataSecondPage.numberLoans = 4;
// This data source will iterate over the pages in the loanPages variable
kivaDataSecondPage.dataSource = function (counter) {
return loanPages[(counter-1) % loanPages.length]
};
equals(kivaDataSecondPage.loanInfo.loans.length, 0,
"There should be no loans initially");
kivaDataSecondPage.loadLoans(1, function () {
equals(kivaDataSecondPage.loanInfo.loans.length, 4,
"There should be 4 loans");
equals(kivaDataSecondPage.loanInfo.loans[0].name, 'Fatoumata Coly',
"The name for the first loan should be correct");
equals(kivaDataSecondPage.loanInfo.loans[1].name, 'Marie Gomis',
"The name for the second loan should be correct");
equals(kivaDataSecondPage.loanInfo.loans[2].name, 'Lydia Afriyie',
"The name for the third loan should be correct");
equals(kivaDataSecondPage.loanInfo.loans[3].name, 'Esther Adu',
"The name for the fourth loan should be correct");
});
// Again, now needing one loan from the second page
var kivaDataThreePages = new KivaData();
kivaDataThreePages.numberLoans = 8;
// This data source will iterate over the pages in the loanPages variable
kivaDataThreePages.dataSource = function (counter) {
return loanPages[(counter-1) % loanPages.length]
};
equals(kivaDataThreePages.loanInfo.loans.length, 0,
"There should be no loans initially");
kivaDataThreePages.loadLoans(1, function () {
equals(kivaDataThreePages.loanInfo.loans.length, 8,
"There should be 8 loans");
equals(kivaDataThreePages.loanInfo.loans[0].name, 'Fatoumata Coly',
"The name for the first loan should be correct");
equals(kivaDataThreePages.loanInfo.loans[1].name, 'Marie Gomis',
"The name for the second loan should be correct");
equals(kivaDataThreePages.loanInfo.loans[2].name, 'Lydia Afriyie',
"The name for the third loan should be correct");
equals(kivaDataThreePages.loanInfo.loans[3].name, 'Esther Adu',
"The name for the fourth loan should be correct");
equals(kivaDataThreePages.loanInfo.loans[7].name, 'Fatoumata Coly',
"The name for the eighth loan should be correct");
});
});
});
</script>
View
@@ -9,8 +9,8 @@
<script type="text/javascript" src="javascript/kivamap.js"></script>
<script type="text/javascript">
var data = new kivaData();
var map = new kivaMap(data);
var data = new KivaData();
var map = new KivaMap('#main-map', '#map-css', data);
$(document).ready(function(ev) {
$('#spinner').show('fast');
@@ -27,7 +27,10 @@
window.close();
});
$('#numberloans').val(data.numberLoans);
$('#updatebutton').click(function () {
data.numberLoans = $('#numberloans').val();
$('#spinner').show('fast');
data.refresh(function () {
map.refresh();
@@ -50,6 +53,7 @@
}
}
</script>
<style type="text/css" id="map-css"></style>
</head>
<body>
<div id="container">
@@ -76,10 +80,11 @@ <h1>World Loanmeter</h1>
<div id="config">
<h2>Configuration</h2>
<p class="instructions">Click "Update" to update the data from
Kiva.org.</p>
<div style="padding-bottom: 10px">
<div>
Number of loans to show:
<input type="text" id="numberloans" name="numberloans" size="3" maxlength="3" value="" />
</div>
<button id="updatebutton" type="button">Update</button>
</div>
</div>
@@ -1,4 +1,5 @@
function kivaData () {
function KivaData () {
this.dataSource = 'http://api.kivaws.org/v1/loans/newest.json';
// See http://build.kiva.org/docs/data/loans
this.linkTemplate = 'http://kiva.org/app.php?page=businesses&action=about&id=LOANID';
// See http://build.kiva.org/docs/data/media
@@ -7,20 +8,79 @@ function kivaData () {
"pattern": "http:\/\/www.kiva.org\/img\/<size>\/<id>.jpg"}
];
// Cached copy of the loan information
this.loanInfo = undefined;
this.loanInfo = {'loans': []};
// Number of loans to fetch
this.numberLoans = 20;
// Fetches the loan data from the data source. If dataSource is a string
// (the default unless overriden), it's interpreted as a URL and an Ajax
// call is made to fetch the data. If it's a function, the function is
// called without parameters. Otherwise, its value is taken directly as the
// value to fetch.
// Parameters:
// n - Number of times the function has been called, including current.
// f - Callback function. It will be called with the received data.
this.fetchLoanData = function(n, f) {
if (typeof(this.dataSource) == 'string') {
finalUrl = this.dataSource + "?page=" + n
opera.postError('Requesting from ' + finalUrl);
jQuery.getJSON(finalUrl, {}, f);
} else if (typeof(this.dataSource) == 'function') {
f(this.dataSource(n));
} else {
f(this.dataSource);
}
};
// Adds the given loans to the current list of loans. If the second
// parameter, atMost, is given, no more than that number of loans are
// loaded
this.addLoans = function(data, atMost) {
var self = this;
if (!atMost) { atMost = 9999; }
jQuery.each(data.loans, function () {
if (atMost-- > 0) {
self.loanInfo.loans.push(this);
}
});
};
// Loads loans from the data source until there is no more data, or there
// are enough (numberLoans property)
this.loadLoans = function(n, f) {
var self = this;
var initialLoans = self.loanInfo.loans.length;
if (initialLoans < self.numberLoans) {
self.fetchLoanData(n, function (data) {
self.addLoans(data,
self.numberLoans -
initialLoans);
// If we didn't get any data stop now
if (self.loanInfo.loans.length >
initialLoans) {
self.loadLoans(n+1, f);
}
else if (f) {
f();
}
});
} else {
if (f) {
f();
}
}
};
// Refresh the copy of the loan information. Takes an optional function,
// that will be executed if given
this.refresh = function (f) {
var self = this;
jQuery.getJSON('http://api.kivaws.org/v1/loans/newest.json',
{},
function (data) {
self.loanInfo = data;
if (f) {
f();
}
});
this.loanInfo = {'loans': []};
this.loadLoans(1, f);
};
this.eachLoan = function(f) {
jQuery.each(this.loanInfo.loans, f);
};
this._findImageTemplate = function (templateId) {
@@ -1,10 +1,12 @@
function kivaMap (data) {
this.data = data;
this.loansInPlace = {};
function KivaMap (mapSelector, mapCssSelector, data) {
this.mapSelector = mapSelector;
this.mapCssSelector = mapCssSelector;
this.data = data;
this.loansInPlace = {};
this.placeLoans = function () {
var self = this;
jQuery.each(this.data.loanInfo.loans, function () {
this.data.eachLoan(function () {
var loan = this;
// Collect all the loans in each place, and put only one dot in the
@@ -14,14 +16,15 @@ function kivaMap (data) {
if (self.loansInPlace[placeId] == undefined) { // First loan
[lat, lon] = loan.location.geo.pairs.split(' ');
mapMaker.placeIdByLatitudeAndLongitude('#'+placeId,
mapMaker.placeIdByLatitudeAndLongitude(self.mapCssSelector,
'#'+placeId,
parseFloat(lat),
parseFloat(lon));
htmlString = '<a class="location" href="#" ' +
'id="' + placeId +
'">' + placeId + '</a>';
$('#main-map').prepend(htmlString);
$(self.mapSelector).prepend(htmlString);
var f = function () { map.showInfoInPanel(placeId); return false };
$('#'+placeId).mouseover(f).focus(f);
@@ -53,7 +56,8 @@ function kivaMap (data) {
this.refresh = function () {
this.loansInPlace = {};
$('#main-map').html('');
$(this.mapSelector).html('');
$(this.mapCssSelector).html('');
this.placeLoans();
};
Oops, something went wrong.

0 comments on commit 425a4a8

Please sign in to comment.