[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] [taler-backoffice] 01/02: better separation logic -vs- prse
From: |
gnunet |
Subject: |
[GNUnet-SVN] [taler-backoffice] 01/02: better separation logic -vs- prsentation |
Date: |
Fri, 12 Jan 2018 14:09:06 +0100 |
This is an automated email from the git hooks/post-receive script.
marcello pushed a commit to branch master
in repository backoffice.
commit 5af0b8e20fb2fb19281f01538749b1ece2ce79d5
Author: Marcello Stanisci <address@hidden>
AuthorDate: Fri Jan 12 12:40:21 2018 +0100
better separation logic -vs- prsentation
---
js/backoffice.js | 334 +++++++++++++++++++++++++++++++++++++------------------
js/test/main.js | 77 ++-----------
2 files changed, 232 insertions(+), 179 deletions(-)
diff --git a/js/backoffice.js b/js/backoffice.js
index c5524e1..91f6a81 100644
--- a/js/backoffice.js
+++ b/js/backoffice.js
@@ -28,7 +28,6 @@
"use strict";
var FRACTION = 100000000;
-var TIMESTAMP = 0;
var START = 0;
var DELTA = 5
var LAST = 0;
@@ -40,10 +39,10 @@ var LAST = 0;
* it wrong.
*/
function amount_to_string(amount){
- var number = Number(amount.value) + (Number(amount.fraction)/FRACTION);
+ var number = Number(amount.value) +
+ (Number(amount.fraction)/FRACTION);
return `${number.toFixed(2)} ${amount.currency}`;
}
-module.exports.amount_to_string = amount_to_string;
function close_popup(){
@@ -57,7 +56,6 @@ function close_popup(){
toggle_overlay(true);
}
-module.exports.close_popup = close_popup;
function amount_sum(a1, a2){
if(a1.currency != a2.currency)
@@ -72,6 +70,9 @@ function amount_sum(a1, a2){
return ret;
}
+/**
+ * Parse Taler /Date(x)/ into human-friendly format.
+ */
function parse_date(date){
var split = date.match(/Date\(([0-9]+)\)/);
@@ -92,6 +93,10 @@ function parse_date(date){
return `${d.getDate()} ${months[d.getMonth()]} ${d.getFullYear()},
${hours}:${minutes}`;
}
+/**
+ * Make screen-centerd box (which show a order's tracks)
+ * appear/disappear.
+ */
function toggle_overlay(force_close){
var overlay = document.getElementsByClassName("overlay")[0];
if(overlay.style.visibility == "visible" || force_close){
@@ -106,6 +111,10 @@ function toggle_overlay(force_close){
}
+/**
+ * Draws a line at the bottom of a orders list to indicate
+ * they have all being paid out by the same WTID.
+ */
function make_marker(wtid){
var tr = document.createElement("tr");
var td = document.createElement("td");
@@ -118,10 +127,20 @@ function make_marker(wtid){
return tr;
}
-function track_transfer(exchange, wtid){
- var loader = document.getElementsByClassName("loader")[0];
- loader.style.visibility = "visible";
- var qs =
`/track/transfer?exchange=${exchange}&wtid=${wtid}&instance=${get_instance()}`;
+
+/**
+ * Use the /track/transfer API from the frontend. Once data
+ * arrives, it calls a UI function which lists all the entries
+ * in the page.
+ *
+ * 'cb' is a UI transforming function. Typically, it is set to
+ * 'fill_table()'.
+ */
+module.exports.track_transfer = function(exchange, wtid, cb){
+ var qs = `/track/transfer?` +
+ `exchange=${exchange}&` +
+ `wtid=${wtid}&` +
+ `instance=${get_instance()}`;
var req = new XMLHttpRequest();
req.open("GET", qs, true);
req.onload = function(){
@@ -129,12 +148,10 @@ function track_transfer(exchange, wtid){
switch(req.status){
case 200:
var tracks = JSON.parse(req.responseText);
- clean_results();
/**
* close the popup which is now on the focus showing
* the tracks from one order. */
- close_popup();
- fill_table(tracks.deposits_sums, tracks.execution_time, wtid);
+ cb(true, tracks.deposits_sums, tracks.execution_time, wtid);
break;
case 400:
console.log("Bad request, check submitted data!");
@@ -146,98 +163,160 @@ function track_transfer(exchange, wtid){
}
req.send();
}
-module.exports.track_transfer = track_transfer;
-function track_order(order_id){
+
+/**
+ * Call /track/order API offered by the frontend. Once data
+ * arrives it calls a UI routine which fills the "entries table"
+ * in the page.
+ */
+module.exports.track_order = function(order_id, cb){
var req = new XMLHttpRequest();
- req.open("GET",
`/track/order?order_id=${order_id}&instance=${get_instance()}`, true);
+ var url = `/track/order?` +
+ `order_id=${order_id}&` +
+ `instance=${get_instance()}`
+ req.open("GET", url, true);
req.onload = function(){
if (4 == req.readyState){
- if(200 == req.status){
- var tracks = JSON.parse(req.responseText);
- if(!tracks)
- console.log("Got invalid JSON");
- if(0 == tracks.length || !tracks.length){
- console.log("Got no tracks and status == 200. Should not be here.");
- return;
- }
- for(var i=0; i<tracks.length; i++){
- var entry = tracks[i];
- /**
- * 'track_content' contains the tracks (WTID/Amount/Date) about
- * *one* order. It is contained in the 'overlay', which renders
- * it in a "box" in the middle of the screen. This "box" is then
- * toggleable as visible/hidden.
- */
- var overlay = document.getElementsByClassName("overlay")[0];
- var track_content =
document.getElementsByClassName("track-content")[0];
- var table = document.evaluate("table/tbody",
- track_content,
- null,
-
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
- null).snapshotItem(0);
- var row = document.createElement("tr");
- var subject = `${entry.wtid} ${entry.exchange_url}`;
- console.log("Subject", subject);
-
- row.innerHTML = `<td class="wtid">
- <a
onclick="track_transfer(\'${entry.exchange_url}\', \'${entry.wtid}\')"
- href="#${i}">${subject.substring(0, 20)}...</a>
- </td>
- <td
class="amount">${amount_to_string(entry.amount)}</td>
- <td
class="date">${parse_date(entry.execution_time)}</td>`;
-
- table.appendChild(row);
- toggle_overlay();
- }
- }
- if (202 == req.status){
- console.log("Pending order.");
- var overlay = document.getElementsByClassName("overlay")[0];
- var track_content =
document.getElementsByClassName("track-content")[0];
- track_content.innerHTML = "This order is still waiting to be paid
back.";
- toggle_overlay();
- }
- if (404 == req.status)
- alert("Order ID unknown");
+ cb(tracks, req.status);
+ return;
}
}
req.send();
}
-module.exports.track_order = track_order;
+/**
+ * Fill the screen-centered box with data from /track/order.
+ */
+function fill_box(tracks, http_code)
+{
+
+ if (http_code == 404){
+ alert("No tracks for that order.");
+ return;
+ }
+
+ if (http_code == 202){
+ console.log("Pending order.");
+ var overlay = document.getElementsByClassName("overlay")[0];
+ var track_content = document
+ .getElementsByClassName("track-content")[0];
+ track_content.innerHTML = "This order is still" +
+ "waiting to be paid back.";
+ return;
+ }
+
+ if(!tracks)
+ console.log("Got invalid JSON");
+ if(0 == tracks.length || !tracks.length){
+ console.log(`Got no tracks and status == ${http_code}. ` +
+ `Should not be here.`);
+ return;
+ }
+
+
+ for(var i=0; i<tracks.length; i++){
+ var entry = tracks[i];
+ /**
+ * 'track_content' contains the tracks (WTID/Amount/Date)
+ * about *one* order. It is contained in the 'overlay',
+ * which renders it in a "box" in the middle of the
+ * screen. This "box" is then toggleable as visible/hidden.
+ */
+ var overlay = document.getElementsByClassName("overlay")[0];
+ var track_content = document.getElementsByClassName
+ ("track-content")[0];
+ var table = document.evaluate
+ ("table/tbody",
+ track_content,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null).snapshotItem(0);
+ var row = document.createElement("tr");
+ var subject = `${entry.wtid} ${entry.exchange_url}`;
+ console.log("Subject", subject);
+ row.innerHTML = `` +
+ `<td class="wtid">` +
+ `<a onclick="track_transfer(\'${entry.exchange_url}\', ` +
+ `\'${entry.wtid}\')"` +
+ `href="#${i}">${subject.substring(0, 20)}...` +
+ `</a>` +
+ `</td>`
+ `<td class="amount">` +
+ `${amount_to_string(entry.amount)}` +
+ `</td>` +
+ `<td class="date">` +
+ `${parse_date(entry.execution_time)}` +
+ `</td>`;
+ table.appendChild(row);
+ toggle_overlay();
+ }
+}
+
+/**
+ * Helper function which abstracts the hard-to-remember
+ * API offered by document.evaluate().
+ */
function xpath_get(xpath, ctx){
- var ret = document.evaluate(xpath,
- ctx,
- null,
- XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
- null);
+ var ret = document.evaluate
+ (xpath,
+ ctx,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
return ret;
}
/**
- * Append results to the table showing results.
+ * Modify the table showing orders. Those orders could be
+ * either from /history or /track/transfer. If data is _not_
+ * to be appended to previous results in the table, this
+ * function will erase previous results and either show new
+ * data or make the "no results" message available. Otherwise,
+ * it will just append data to what already exists in the table.
+ *
+ * Data is non-appending if (1) the page has been (re)loaded,
+ * (2) the user queried /track/transfer, (3) the user changed
+ * the instance being tracked. Those 1-3 cases will all have
+ * the 'refresh' parameter set to true.
+ *
+ * 'wtid_marker' - set to <WTID> - serves to show a line at the
+ * bottom of the lines being added in the table which indicates
+ * that all those entries - orders - have been paid out by the
+ * same WTID.
+ *
+ * FIXME: 'execution_time' is a problematic field: when showing
+ * /history results, it shows the order timestamp. When instead
+ * /track/tranfer results are being shown, it indicates the time
+ * the exchange wire transferred this order's amount.
*/
-function fill_table(history, execution_time, wtid_marker){
+function fill_table(refresh, data, execution_time, wtid_marker){
+ toggle_loader();
var table = document.getElementById("history");
var tbody = xpath_get("tbody", table).snapshotItem(0);
- var nr = xpath_get("address@hidden'no-records']", tbody).snapshotItem(0)
- if(0 == history.length){
- nr.style.display = "block";
- xpath_get("address@hidden'headers']",
tbody).snapshotItem(0).style.visibility = "hidden";
- window.setTimeout(remove_loader, 900);
- console.log("Nothing gotten!");
- return;
+ var nr = xpath_get("address@hidden'no-records']", tbody)
+ .snapshotItem(0)
+ if (refresh){
+ clean_results();
+ if(0 == data.length){
+ nr.style.display = "block";
+ xpath_get("address@hidden'headers']", tbody)
+ .snapshotItem(0).style.visibility = "hidden";
+ window.setTimeout(remove_loader, 900);
+ console.log("Nothing gotten!");
+ return;
+ }
}
/* hide "no records" message */
nr.style.display = "none";
/* Make table's header visible */
- xpath_get("address@hidden'headers']",
tbody).snapshotItem(0).style.visibility = "";
+ xpath_get("address@hidden'headers']", tbody)
+ .snapshotItem(0).style.visibility = "";
- for (var i=0; i<history.length; i++){
- var entry = history[i];
+ for (var i=0; i<data.length; i++){
+ var entry = data[i];
var row = document.createElement("tr");
row.className = "even";
var row_summary = document.createElement("tr");
@@ -251,11 +330,15 @@ function fill_table(history, execution_time, wtid_marker){
td_amount.className = "amount";
var td_date = document.createElement("td");
td_date.className = "date";
- td_order_id.innerHTML = `<a href="#${i}"
onclick='track_order("${entry.order_id}");'>${entry.order_id}</a>`;
+ td_order_id.innerHTML = `<a href="#${i}"` +
+ onclick='track_order("${entry.order_id}");'>` +
+ ${entry.order_id}</a>`;
td_summary.className = "summary";
td_summary.innerHTML = entry.summary;
- td_amount.innerHTML = amount_to_string(entry.amount ||
entry.deposit_value);
- td_date.innerHTML = parse_date(entry.timestamp || execution_time);
+ td_amount.innerHTML = amount_to_string
+ (entry.amount || entry.deposit_value);
+ td_date.innerHTML = parse_date
+ (entry.timestamp || execution_time);
row.appendChild(td_order_id);
row.appendChild(td_summary);
row.appendChild(td_amount);
@@ -265,21 +348,35 @@ function fill_table(history, execution_time, wtid_marker){
tbody.appendChild(row_summary);
}
if (wtid_marker){
+ // close popup showing wire transfer information
+ close_popup();
// draw a line @ the bottom, mentioning the WTID.
var marker = make_marker(wtid_marker);
tbody.appendChild(marker);
}
- function remove_loader(){
- var loader = document.getElementsByClassName("loader")[0];
+
+ window.setTimeout(function(){
+ toggle_loader();
+ table.style.visibility = "";
+ }, 900);
+}
+
+/**
+ * Make the spinning wheel appear if it is not shown,
+ * and viceversa.
+ */
+function toggle_loader(){
+ var loader = document.getElementsByClassName("loader")[0];
+ if (loader.style.visibility == "hidden")
+ loader.style.visibility = ""
+ else
loader.style.visibility = "hidden";
- table.style.visibility = "";
}
- window.setTimeout(remove_loader, 900);
-}
-module.exports.fill_table = fill_table;
/**
- * Issue a direrct /track via Web form.
+ * Issue a /track/order (/track/transfer) depending on
+ * whether the user selected "order" ("transfer") on the
+ * given form.
*/
function track_cherry_pick(form){
var types = xpath_get("address@hidden'radio']", form);
@@ -288,25 +385,35 @@ function track_cherry_pick(form){
continue;
var type = types.snapshotItem(i).value;
if ("order" == type){
- var order_id = xpath_get("address@hidden'order']", form).snapshotItem(0);
+ var order_id = xpath_get("address@hidden'order']", form)
+ .snapshotItem(0);
track_order(order_id.value);
}
else{
var data = xpath_get("address@hidden'transfer']", form);
var wtid = data.snapshotItem(0);
var exchange = data.snapshotItem(1);
- track_transfer(exchange.value, wtid.value);
+ track_transfer(exchange.value, wtid.value, fill_table);
}
}
}
/**
- * Restore the cherry-pick form to the initial state.
- * In other words, it formats the form so that /track/order
- * can be called.
+ * The "cherry pick" form allows the user to track a particular
+ * order or wire transfer by entering its "id" in the input field.
+ *
+ * The UI is such that a radio button can switch the form "type"
+ * to query for a order or for a wire transfer; the following two
+ * functions help to switch the form appearance according to this
+ * order/wire-transfer switch.
+ */
+
+/**
+ * Make the "cherry pick" form be suitable to query /track/order.
*/
function cherry_pick_form_order(form){
- var input_order = xpath_get("address@hidden'order']", form).snapshotItem(0);
+ var input_order = xpath_get("address@hidden'order']", form)
+ .snapshotItem(0);
input_order.style.visibility = "";
var input_transfer = xpath_get("address@hidden'transfer']", form);
for(var i in [0, 1])
@@ -314,11 +421,11 @@ function cherry_pick_form_order(form){
}
/**
- * Change the cherry-pick form to the format useful for
- * tracking wire transfers.
+ * Make the "cherry pick" form be suitable to query /track/transfer.
*/
function cherry_pick_form_transfer(form){
- var input_order = xpath_get("address@hidden'order']", form).snapshotItem(0);
+ var input_order = xpath_get("address@hidden'order']", form)
+ .snapshotItem(0);
input_order.style.visibility = "hidden";
var input_transfer = xpath_get("address@hidden'transfer']", form);
for(var i in [0, 1])
@@ -349,19 +456,21 @@ function clean_results(){
* selected one.
*/
function change_instance(){
- clean_results();
- get_history();
+ get_history(false, cb);
}
/**
- * scroll if true, the logic tries to retrieve the
+ * If 'scroll' is true, the logic tries to retrieve the
* "next page" of all the proposals known to the merchant.
+ * Otherwise, it gets the merchant's history from the beginning
+ * of their business.
+ *
+ * 'cb' is a UI transforming function. Typically, it is set
+ * to 'fill_table()'.
*/
-function get_history(scroll){
- var loader = document.getElementsByClassName("loader")[0];
- loader.style.visibility = "visible";
+function get_history(refresh, cb){
var qs = `/history?instance=${get_instance()}&delta=${DELTA}`;
- if(scroll){
+ if(!refresh){
START = LAST;
qs += `&start=${START}`;
}
@@ -379,7 +488,7 @@ function get_history(scroll){
console.log(history);
LAST = history[history.length - 1].row_id;
}
- fill_table(history);
+ cb(refresh, history);
}
else{
console.log("error: status != 200");
@@ -390,11 +499,18 @@ function get_history(scroll){
req.send();
}
-document.addEventListener("DOMContentLoaded", () => get_history(false));
+document.addEventListener
+ ("DOMContentLoaded",
+ function(){
+ toggle_loader();
+ get_history(true, fill_table);});
document.addEventListener("scroll", function(){
/* If page bottom is hit */
- if(window.innerHeight + window.scrollY >= document.body.offsetHeight)
- window.setTimeout(()=>get_history(true), 400);
+ if(window.innerHeight + window.scrollY
+ >= document.body.offsetHeight)
+ window.setTimeout(function(){
+ toggle_loader();
+ get_history(false, fill_table), 400);};
});
/* Close dialog on ESC press */
diff --git a/js/test/main.js b/js/test/main.js
index 66c715b..58a3b48 100644
--- a/js/test/main.js
+++ b/js/test/main.js
@@ -28,9 +28,6 @@
const ava = require("ava");
const sinon = require("sinon");
-const swig = require("swig");
-const jsdom = require("jsdom");
-const xpath = require("xpath");
const pd = require("pretty-data");
ava.test.beforeEach(t => {
@@ -42,63 +39,34 @@ ava.test.beforeEach(t => {
t.context.requests.push(xhr);};
global.XMLHttpRequest = t.context.xhr;};
- function document_mock() {
- var template = swig.compileFile(
- __dirname + "/" + "../../" +
- "talerbackoffice/backoffice/" +
- "templates/backoffice.html");
- var html = template({instances: ["mock-instance"]});
- var dom = new jsdom.JSDOM(html);
- dom.window.document.addEventListener = function(){};
- global.document = dom.window.document;};
-
function minor_mocks() {
global.alert = console.log;
- global.XPathResult = xpath.XPathResult;
// disable logging for tests
global.console.log = function(){};
};
-
xhr_mock();
- document_mock();
minor_mocks();
-
t.context.bo = require("../backoffice");
});
ava.test.afterEach(t => {
delete global.XMLHttpRequest;
- delete global.document;
delete global.alert;
delete t.context.xhr;
delete t.context.requests;
- var bo_handle = require.resolve("../backoffice");
- delete require.cache[bo_handle];
});
ava.test("Tracking a wire transfer", (t) => {
- /**
- * This test case checks whether the popup where
- * the user clicked to track a wire transfer got
- * closed; then it checks that the 'table' of the
- * orders associated with this wire transfer got
- * correctly filled.
- */
-
var mock_tracks = {
deposits_sums: ["mock-tracks"],
execution_time: "/Date(0)/"};
- sinon.stub(t.context.bo, "close_popup");
- sinon.stub(t.context.bo, "fill_table");
- t.context.bo.track_transfer("http://exchange.mock/", "mock-wtid");
+ var cb = sinon.spy();
+ t.context.bo.track_transfer("http://exchange.mock/", "mock-wtid", cb);
t.context.requests[0].respond(200, "application/json",
JSON.stringify(mock_tracks));
- sinon.assert.calledOnce(t.context.bo.close_popup);
- sinon.assert.calledWith(t.context.bo.fill_table,
- ["mock-tracks"],
- "/Date(0)/",
- "mock-wtid");
+ sinon.assert.calledWith(cb, true, mock_tracks.deposit_sums,
+ mock_tracks.execution_time, "mock-wtid");
});
ava.test("Tracking order id", t => {
@@ -108,7 +76,8 @@ ava.test("Tracking order id", t => {
* page contains the expected tracks and it is made visible.
*/
- t.context.bo.track_order(22);
+ var cb = sinon.spy();
+ t.context.bo.track_order(22, cb);
// mocking a "/track/transaction" response from the backend
var mock_tracks = [
@@ -121,37 +90,5 @@ ava.test("Tracking order id", t => {
(200, "application/json",
JSON.stringify(mock_tracks));
- var overlay = document.getElementById("popup1");
- t.true(overlay.style.visibility == "visible");
-
- var rendered_tracks = document.evaluate
- ("div/div/table/tbody/tr[2]",
- overlay,
- null,
- XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
- null).snapshotItem(0);
- var expected_html =
- "<tr>" +
- "<td class=\"wtid\">" +
- "<a onclick=\"track_transfer('http://exchange.mock/', " +
- "'mock wtid')\" " +
- "href=\"#0\">mock wtid http://exc...</a>" +
- "</td>" +
- "<td class=\"amount\">2.00 MOCK</td>" +
- "<td class=\"date\">1 Jan 1970, 01:00</td>" +
- "</tr>";
-
- t.true(pd.pd.xmlmin(rendered_tracks.outerHTML) == expected_html);
-});
-
-ava.test("Order id not found", (t) => {
- /**
- * FIXME: improve the reaction to a order id not found.
- * Alerting a message is not acceptable.
- */
- var alert = sinon.spy(global, "alert");
- t.context.bo.track_order(22);
- t.context.requests[0].respond(404);
- sinon.assert.calledWith(alert, "Order ID unknown");
- t.pass();
+ sinon.assert.calledWith(cb, mock_tracks, 200);
});
--
To stop receiving notification emails like this one, please contact
address@hidden