gnunet-svn
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]