From 2c7f6b5dd892a4d52ef513cde735e7a12936ad4b Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Mon, 22 May 2017 13:41:57 +0200
Subject: [PATCH] searc by coordinates returns empty list if click is too far

---
 frontend-js/src/main/js/Functions.js          | 35 ++++++++++
 .../src/main/js/map/AbstractCustomMap.js      |  2 +-
 frontend-js/src/main/js/map/CustomMap.js      | 53 +++++++++++++++
 .../main/js/map/overlay/AbstractDbOverlay.js  |  6 +-
 .../main/js/map/overlay/SearchDbOverlay.js    | 42 +++++++++---
 .../test/js/gui/leftPanel/SearchPanel-test.js |  3 +-
 frontend-js/src/test/js/map/CustomMap-test.js | 63 ++++++++++--------
 .../js/map/overlay/SearchDbOverlay-test.js    | 66 +++++++++++++++++++
 ...5781&projectId=sample&token=MOCK_TOKEN_ID& |  1 +
 ...5781&projectId=sample&token=MOCK_TOKEN_ID& |  1 +
 ...5781&projectId=sample&token=MOCK_TOKEN_ID& |  1 +
 ...5781&projectId=sample&token=MOCK_TOKEN_ID& |  1 +
 ...9172&projectId=sample&token=MOCK_TOKEN_ID& |  1 +
 ...9171&projectId=sample&token=MOCK_TOKEN_ID& |  1 +
 ...9171&projectId=sample&token=MOCK_TOKEN_ID& |  1 +
 ...3521&projectId=sample&token=MOCK_TOKEN_ID& |  1 +
 16 files changed, 237 insertions(+), 41 deletions(-)
 create mode 100644 frontend-js/src/test/js/map/overlay/SearchDbOverlay-test.js
 create mode 100644 frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=184.79,365.76&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
 create mode 100644 frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=207.73,479.18&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
 create mode 100644 frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=457.51,356.84&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
 create mode 100644 frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=553.10,479.18&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
 create mode 100644 frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329158,329165,329172&projectId=sample&token=MOCK_TOKEN_ID&
 create mode 100644 frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329171&projectId=sample&token=MOCK_TOKEN_ID&
 create mode 100644 frontend-js/testFiles/apiCalls/project/getElements/columns=id,bounds,modelId&id=329171&projectId=sample&token=MOCK_TOKEN_ID&
 create mode 100644 frontend-js/testFiles/apiCalls/project/getReactions/columns=&id=153521&projectId=sample&token=MOCK_TOKEN_ID&

diff --git a/frontend-js/src/main/js/Functions.js b/frontend-js/src/main/js/Functions.js
index 1d32b43798..af62768bb7 100644
--- a/frontend-js/src/main/js/Functions.js
+++ b/frontend-js/src/main/js/Functions.js
@@ -1,5 +1,9 @@
 "use strict";
 
+/* exported logger */
+
+var logger = require('./logger');
+
 var Functions = {};
 
 /**
@@ -254,4 +258,35 @@ Functions.createElement = function(params) {
   return result;
 };
 
+function sqr(x) {
+  return x * x
+}
+
+function dist2(v, w) {
+  return sqr(v.x - w.x) + sqr(v.y - w.y)
+}
+
+function distToSegmentSquared(p, v, w) {
+  var l2 = dist2(v, w);
+
+  if (l2 == 0)
+    return dist2(p, v);
+
+  var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
+
+  if (t < 0)
+    return dist2(p, v);
+  if (t > 1)
+    return dist2(p, w);
+
+  return dist2(p, new google.maps.Point(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)));
+}
+Functions.distance = function(p1, el2) {
+  if (el2 instanceof google.maps.Point) {
+    var p2 = el2;
+    return Math.sqrt((Math.pow(p1.x - p2.x, 2)) + (Math.pow(p1.y - p2.y, 2)))
+  } else {
+    return Math.sqrt(distToSegmentSquared(p1, el2.start, el2.end));
+  }
+};
 module.exports = Functions;
diff --git a/frontend-js/src/main/js/map/AbstractCustomMap.js b/frontend-js/src/main/js/map/AbstractCustomMap.js
index 52a2150c14..f5d3fad9f2 100644
--- a/frontend-js/src/main/js/map/AbstractCustomMap.js
+++ b/frontend-js/src/main/js/map/AbstractCustomMap.js
@@ -317,7 +317,7 @@ AbstractCustomMap.prototype.registerMapClickEvents = function() {
   google.maps.event.addListener(this.getGoogleMap(), 'click', function(mouseEvent) {
     var point = self.fromLatLngToPoint(mouseEvent.latLng);
     var searchDb = customMap.getOverlayByName('search');
-    return searchDb.searchByCoordinates({modelId: self.getModel().getId(), coordinates:point}).catch(GuiConnector.alert);
+    return searchDb.searchByCoordinates({modelId: self.getModel().getId(), coordinates:point, zoom: self.getGoogleMap().getZoom()}).catch(GuiConnector.alert);
   });
 
   // select last clicked map
diff --git a/frontend-js/src/main/js/map/CustomMap.js b/frontend-js/src/main/js/map/CustomMap.js
index 71c01b9e11..bb5e7d4370 100644
--- a/frontend-js/src/main/js/map/CustomMap.js
+++ b/frontend-js/src/main/js/map/CustomMap.js
@@ -8,11 +8,13 @@ var Functions = require('../Functions');
 var AbstractCustomMap = require('./AbstractCustomMap');
 var AbstractDbOverlay = require('./overlay/AbstractDbOverlay');
 var AliasMarker = require('./marker/AliasMarker');
+var Alias = require('./data/Alias');
 var CommentDialog = require('../gui/CommentDialog');
 var ControlType = require('./ControlType');
 var CustomMapOptions = require('./CustomMapOptions');
 var IdentifiedElement = require('./data/IdentifiedElement');
 var PointMarker = require('./marker/PointMarker');
+var Reaction = require('./data/Reaction');
 var ReactionMarker = require('./marker/ReactionMarker');
 var ReactionOverlay = require('./overlay/ReactionOverlay');
 var ReferenceGenome = require('./data/ReferenceGenome');
@@ -1243,6 +1245,57 @@ CustomMap.prototype.getSelectedPolygon = function() {
   return this._selectedPolygon;
 };
 
+CustomMap.prototype.getDistance = function(params) {
+  var self = this;
+  var ie = params.element;
+  var model = self.getSubmodelById(ie.getModelId()).getModel();
+  if (ie.getModelId() !== params.modelId) {
+    throw new Error("Element and coordinates are on different maps: " + ie.getModelId() + ", " + params.modelId);
+  }
+  var x = params.coordinates.x;
+  var y = params.coordinates.y;
+  var p1 = new google.maps.Point(x, y);
+  return model.getByIdentifiedElement(ie).then(function(element) {
+    if (element instanceof Alias) {
+      if (element.getX() <= x && element.getX() + element.getWidth() >= x) {
+        if (element.getY() <= y && element.getY() + element.getHeight() >= y) {
+          return 0;
+        } else {
+          return Math.min( //
+          Math.abs(element.getY() - y), //
+          Math.abs(element.getY() + element.getHeight() - y) // 
+          );
+        }
+      } else if (element.getY() <= y && element.getY() + element.getHeight() >= y) {
+        return Math.min( //
+        Math.abs(element.getX() - x), //
+        Math.abs(element.getX() + element.getWidth() - x) // 
+        );
+      } else {
+        var elementX = element.getX();
+        var elementY = element.getY();
+        var elementWidth = element.getWidth();
+        var elementHeight = element.getHeight();
+        return Math.min( //
+        Functions.distance(p1, new google.maps.Point(elementX, y)), //
+        Functions.distance(p1, new google.maps.Point(elementX + elementWidth, elementY)), //
+        Functions.distance(p1, new google.maps.Point(elementX, elementY + elementHeight)), //
+        Functions.distance(p1, new google.maps.Point(elementX + elementWidth, elementY + elementHeight)) //
+        );
+      }
+    } else if (element instanceof Reaction) {
+      var distance = Number.POSITIVE_INFINITY;
+      var lines = element.getLines();
+      for (var i = 0; i < lines.length; i++) {
+        distance = Math.min(distance, Functions.distance(p1, lines[i]));
+      }
+      return distance;
+    } else {
+      throw new Error("Unknown element type: " + (typeof element));
+    }
+  });
+};
+
 CustomMap.prototype.getSubmaps = function() {
   var submaps = this.submaps;
   if (submaps === undefined) {
diff --git a/frontend-js/src/main/js/map/overlay/AbstractDbOverlay.js b/frontend-js/src/main/js/map/overlay/AbstractDbOverlay.js
index 8f5b65bf1f..8e53a66fb5 100644
--- a/frontend-js/src/main/js/map/overlay/AbstractDbOverlay.js
+++ b/frontend-js/src/main/js/map/overlay/AbstractDbOverlay.js
@@ -45,14 +45,16 @@ AbstractDbOverlay.QueryType = {
   SEARCH_BY_QUERY : "SEARCH_BY_QUERY",
 };
 
-AbstractDbOverlay.prototype.encodeQuery = function(type, arg0, arg1) {
+AbstractDbOverlay.prototype.encodeQuery = function(type, arg0, arg1, arg2) {
   if (type === AbstractDbOverlay.QueryType.SEARCH_BY_COORDINATES) {
     var modelId = arg0;
     var coordinates = arg1;
+    var zoom = arg2;
     return JSON.stringify({
       type : type,
       modelId : modelId,
-      coordinates : coordinates
+      coordinates : coordinates,
+      zoom : arg2,
     });
   } else if (type === AbstractDbOverlay.QueryType.SEARCH_BY_TARGET) {
     var target = arg0;
diff --git a/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js b/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js
index 15a8e3eb7c..5882419385 100644
--- a/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js
+++ b/frontend-js/src/main/js/map/overlay/SearchDbOverlay.js
@@ -66,9 +66,10 @@ SearchDbOverlay.prototype.getElementsByQuery = function(query) {
 SearchDbOverlay.prototype.searchByCoordinates = function(params) {
   var modelId = params.modelId;
   var coordinates = params.coordinates;
-  
+  var zoom = params.zoom;
+
   var self = this;
-  var query = self.encodeQuery(AbstractDbOverlay.QueryType.SEARCH_BY_COORDINATES, modelId, coordinates);
+  var query = self.encodeQuery(AbstractDbOverlay.QueryType.SEARCH_BY_COORDINATES, modelId, coordinates, zoom);
 
   ServerConnector.getSessionData().setSearchQuery(query);
 
@@ -79,25 +80,46 @@ SearchDbOverlay.prototype.searchByCoordinates = function(params) {
       Promise.resolve(self._elementsByQuery[query]);
     });
   } else {
-    return ServerConnector.getClosestElementsByCoordinates({
-      modelId : modelId,
-      coordinates : coordinates,
-      count : 1
+    var searchResult = null;
+    var maxDistance;
+
+    return ServerConnector.getMaxSearchDistance().then(function(distance) {
+      var maxZoom = self.getMap().getSubmodelById(modelId).getModel().getMaxZoom();
+      var zoomDiff = maxZoom - zoom;
+      for (var i = 0; i < zoomDiff; i++) {
+        distance = distance * 1.5;
+      }
+      maxDistance = distance;
+      return ServerConnector.getClosestElementsByCoordinates({
+        modelId : modelId,
+        coordinates : coordinates,
+        count : 1
+      });
     }).then(function(elements) {
-      self._elementsByQuery[query] = elements;
+      searchResult = elements;
       if (elements[0].getType() === "REACTION") {
         var model = self.getMap().getSubmodelById(elements[0].getModelId()).getModel();
         return model.getReactionById(elements[0].getId(), true).then(function(reaction) {
           var i = 0;
           var reactionElements = reaction.getElements();
           for (i = 0; i < reactionElements.length; i++) {
-            self._elementsByQuery[query].push(new IdentifiedElement(reactionElements[i]));
+            elements.push(new IdentifiedElement(reactionElements[i]));
           }
         });
-      } else {
-        return null;
       }
     }).then(function() {
+      return self.getMap().getDistance({
+        modelId : modelId,
+        coordinates : coordinates,
+        element : searchResult[0],
+      });
+    }).then(function(distance) {
+      logger.debug(distance + ", " + maxDistance);
+      if (distance <= maxDistance) {
+        self._elementsByQuery[query] = searchResult;
+      } else {
+        self._elementsByQuery[query] = [];
+      }
       return self.callListeners('onSearch');
     }).then(function() {
       return self._elementsByQuery[query];
diff --git a/frontend-js/src/test/js/gui/leftPanel/SearchPanel-test.js b/frontend-js/src/test/js/gui/leftPanel/SearchPanel-test.js
index 483b7f4297..f16eeea600 100644
--- a/frontend-js/src/test/js/gui/leftPanel/SearchPanel-test.js
+++ b/frontend-js/src/test/js/gui/leftPanel/SearchPanel-test.js
@@ -42,7 +42,8 @@ describe('SearchPanel', function() {
 
     var searchParams = {
       modelId : map.getModel().getId(),
-      coordinates : new google.maps.Point(26547.33, 39419.29)
+      coordinates : new google.maps.Point(457.51, 356.84),
+      zoom : 4,
     };
 
     return searchDbOverlay.searchByCoordinates(searchParams).then(function() {
diff --git a/frontend-js/src/test/js/map/CustomMap-test.js b/frontend-js/src/test/js/map/CustomMap-test.js
index 67e9d2cb3f..55b7a478bb 100644
--- a/frontend-js/src/test/js/map/CustomMap-test.js
+++ b/frontend-js/src/test/js/map/CustomMap-test.js
@@ -415,40 +415,49 @@ describe('CustomMap', function() {
   });
 
   it("left click on map", function() {
-    var map = helper.createCustomMap();
-    map.getModel().setId(15781);
-
-    var searchOverlay = helper.createSearchDbOverlay(map);
+    var map;
+    var searchOverlay;
+    return ServerConnector.getProject().then(function(project) {
+      map = helper.createCustomMap(project);
+      searchOverlay = helper.createSearchDbOverlay(map);
+
+      var mev = {
+        stop : null,
+        latLng : new google.maps.LatLng(82.32061703407554, -167.25586206896548),
+      };
+      map.getGoogleMap().setZoom(4);
+      // latLng : new google.maps.LatLng(82.40238643645326, -148.44758620689652)
+
+      assert.notOk(map.getActiveSubmapId());
+      return google.maps.event.trigger(map.getGoogleMap(), 'click', mev);
 
-    var mev = {
-      stop : null,
-      latLng : new google.maps.LatLng(40.0, -90.0)
-    };
-
-    assert.notOk(map.getActiveSubmapId());
-    return google.maps.event.trigger(map.getGoogleMap(), 'click', mev).then(function() {
+    }).then(function() {
       assert.equal(map.getId(), map.getActiveSubmapId());
-      assert.ok(searchOverlay.aliasMarkers[329173]);
+      assert.ok(searchOverlay.aliasMarkers[329171]);
     });
+
   });
 
   it("left click on reaction", function() {
-    var map = helper.createCustomMap();
-    map.getModel().setId(15781);
-
-    var searchOverlay = helper.createSearchDbOverlay(map);
-
-    var mev = {
-      stop : null,
-      latLng : new google.maps.LatLng(42.0, -90.0)
-    };
-
-    assert.notOk(map.getActiveSubmapId());
-    return google.maps.event.trigger(map.getGoogleMap(), 'click', mev).then(function() {
+    var map;
+    var searchOverlay;
+    return ServerConnector.getProject().then(function(project) {
+      map = helper.createCustomMap(project);
+      searchOverlay = helper.createSearchDbOverlay(map);
+
+      var mev = {
+        stop : null,
+        latLng : new google.maps.LatLng(82.40238643645326, -148.44758620689652)
+      };
+      map.getGoogleMap().setZoom(4);
+
+      assert.notOk(map.getActiveSubmapId());
+      return google.maps.event.trigger(map.getGoogleMap(), 'click', mev);
+    }).then(function() {
       assert.equal(map.getId(), map.getActiveSubmapId());
-      assert.ok(searchOverlay.reactionMarkers[153515]);
-      assert.ok(searchOverlay.reactionMarkers[153515].isShown());
-      assert.ok(searchOverlay.aliasMarkers[329162]);
+      assert.ok(searchOverlay.reactionMarkers[153521]);
+      assert.ok(searchOverlay.reactionMarkers[153521].isShown());
+      assert.ok(searchOverlay.aliasMarkers[329165]);
     });
   });
 
diff --git a/frontend-js/src/test/js/map/overlay/SearchDbOverlay-test.js b/frontend-js/src/test/js/map/overlay/SearchDbOverlay-test.js
new file mode 100644
index 0000000000..c208703820
--- /dev/null
+++ b/frontend-js/src/test/js/map/overlay/SearchDbOverlay-test.js
@@ -0,0 +1,66 @@
+"use strict";
+
+var Helper = require('../../Helper');
+
+var logger = require('../../logger');
+
+var IdentifiedElement = require('../../../../main/js/map/data/IdentifiedElement');
+var SearchDbOverlay = require('../../../../main/js/map/overlay/SearchDbOverlay');
+
+var assert = require('assert');
+
+describe('SearchDbOverlay', function() {
+  var helper;
+  before(function() {
+    helper = new Helper();
+  });
+
+  it("constructor 1", function() {
+    var map = helper.createCustomMap();
+    var oc = new SearchDbOverlay({
+      map : map,
+      name : 'search'
+    });
+    assert.ok(oc);
+    assert.equal(oc.getName(), 'search');
+
+    assert.equal(logger.getWarnings.length, 0);
+  });
+
+  it("searchByCoordinates with too far alias as result", function() {
+    return ServerConnector.getProject().then(function(project) {
+      var map = helper.createCustomMap(project);
+      map.getModel().setId(15781);
+      var searchDb = helper.createSearchDbOverlay(map);
+
+      var searchParams = {
+        modelId : map.getModel().getId(),
+        coordinates : new google.maps.Point(207.73, 479.18),
+        zoom : 4,
+      };
+      return searchDb.searchByCoordinates(searchParams);
+    }).then(function(result) {
+      assert.equal(result.length, 0);
+    });
+
+  });
+
+  it("searchByCoordinates with too far reaction as result", function() {
+    return ServerConnector.getProject().then(function(project) {
+      var map = helper.createCustomMap(project);
+      map.getModel().setId(15781);
+      var searchDb = helper.createSearchDbOverlay(map);
+
+      var searchParams = {
+        modelId : map.getModel().getId(),
+        coordinates : new google.maps.Point(553.10,479.18),
+        zoom : 4,
+      };
+      return searchDb.searchByCoordinates(searchParams);
+    }).then(function(result) {
+      assert.equal(result.length, 0);
+    });
+
+  });
+
+});
diff --git a/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=184.79,365.76&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=184.79,365.76&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
new file mode 100644
index 0000000000..3ab3e25631
--- /dev/null
+++ b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=184.79,365.76&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
@@ -0,0 +1 @@
+[{"modelId":15781,"id":329171,"type":"ALIAS"}]
\ No newline at end of file
diff --git a/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=207.73,479.18&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=207.73,479.18&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
new file mode 100644
index 0000000000..3ab3e25631
--- /dev/null
+++ b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=207.73,479.18&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
@@ -0,0 +1 @@
+[{"modelId":15781,"id":329171,"type":"ALIAS"}]
\ No newline at end of file
diff --git a/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=457.51,356.84&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=457.51,356.84&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
new file mode 100644
index 0000000000..8916c4e02e
--- /dev/null
+++ b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=457.51,356.84&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
@@ -0,0 +1 @@
+[{"modelId":15781,"id":153521,"type":"REACTION"}]
\ No newline at end of file
diff --git a/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=553.10,479.18&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=553.10,479.18&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
new file mode 100644
index 0000000000..8916c4e02e
--- /dev/null
+++ b/frontend-js/testFiles/apiCalls/project/getClosestElementsByCoordinates/coordinates=553.10,479.18&count=1&modelId=15781&projectId=sample&token=MOCK_TOKEN_ID&
@@ -0,0 +1 @@
+[{"modelId":15781,"id":153521,"type":"REACTION"}]
\ No newline at end of file
diff --git a/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329158,329165,329172&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329158,329165,329172&projectId=sample&token=MOCK_TOKEN_ID&
new file mode 100644
index 0000000000..f488c10208
--- /dev/null
+++ b/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329158,329165,329172&projectId=sample&token=MOCK_TOKEN_ID&
@@ -0,0 +1 @@
+[{"formerSymbols":[],"references":[],"modelId":15781,"synonyms":[],"description":"","type":"Phenotype","name":"s7","bounds":{"x":213.0,"y":128.0,"width":80.0,"height":30.0},"id":329172},{"formerSymbols":[],"references":[],"modelId":15781,"synonyms":[],"description":"","type":"Ion","name":"s8","bounds":{"x":358.5,"y":125.5,"width":25.0,"height":25.0},"id":329165},{"formerSymbols":[],"references":[],"modelId":15781,"synonyms":[],"description":"","type":"Complex","name":"s12","bounds":{"x":271.0,"y":207.0,"width":101.0,"height":164.0},"id":329158}]
\ No newline at end of file
diff --git a/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329171&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329171&projectId=sample&token=MOCK_TOKEN_ID&
new file mode 100644
index 0000000000..956ac7a6af
--- /dev/null
+++ b/frontend-js/testFiles/apiCalls/project/getElements/columns=&id=329171&projectId=sample&token=MOCK_TOKEN_ID&
@@ -0,0 +1 @@
+[{"modelId":15781,"bounds":{"x":160.0,"y":332.0,"width":119.0,"height":63.0},"id":329171}]
\ No newline at end of file
diff --git a/frontend-js/testFiles/apiCalls/project/getElements/columns=id,bounds,modelId&id=329171&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getElements/columns=id,bounds,modelId&id=329171&projectId=sample&token=MOCK_TOKEN_ID&
new file mode 100644
index 0000000000..956ac7a6af
--- /dev/null
+++ b/frontend-js/testFiles/apiCalls/project/getElements/columns=id,bounds,modelId&id=329171&projectId=sample&token=MOCK_TOKEN_ID&
@@ -0,0 +1 @@
+[{"modelId":15781,"bounds":{"x":160.0,"y":332.0,"width":119.0,"height":63.0},"id":329171}]
\ No newline at end of file
diff --git a/frontend-js/testFiles/apiCalls/project/getReactions/columns=&id=153521&projectId=sample&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/project/getReactions/columns=&id=153521&projectId=sample&token=MOCK_TOKEN_ID&
new file mode 100644
index 0000000000..715ec32565
--- /dev/null
+++ b/frontend-js/testFiles/apiCalls/project/getReactions/columns=&id=153521&projectId=sample&token=MOCK_TOKEN_ID&
@@ -0,0 +1 @@
+[{"modelId":15781,"reactants":"329158","reactionId":"re23","id":153521,"type":"Dissociation","lines":[{"start":{"x":371.99999999999994,"y":315.10161915826575},"end":{"x":411.64153465957406,"y":335.590891248036},"type":"START"},{"start":{"x":508.05348418531685,"y":279.95891320505365},"end":{"x":379.6820939222325,"y":146.9928441065957},"type":"END"},{"start":{"x":543.7499999999999,"y":362.874999999999},"end":{"x":508.05348418531685,"y":279.95891320505365},"type":"MIDDLE"},{"start":{"x":449.8434737629195,"y":307.88463901503604},"end":{"x":270.9073813308621,"y":158.0},"type":"END"},{"start":{"x":543.7499999999999,"y":362.874999999999},"end":{"x":546.6153123135401,"y":307.94923690478544},"type":"MIDDLE"},{"start":{"x":546.6153123135401,"y":307.94923690478544},"end":{"x":449.8434737629195,"y":307.88463901503604},"type":"MIDDLE"},{"start":{"x":543.7499999999999,"y":362.874999999999},"end":{"x":500.90979517443395,"y":356.00742559541567},"type":"MIDDLE"},{"start":{"x":500.90979517443395,"y":356.00742559541567},"end":{"x":458.3899053124841,"y":359.75342920945576},"type":"MIDDLE"},{"start":{"x":458.3899053124841,"y":359.75342920945576},"end":{"x":418.7483706529099,"y":339.2641571196856},"type":"MIDDLE"}],"modifiers":"","centerPoint":{"x":415.194952656242,"y":337.4275241838608},"products":"329165,329172"}]
\ No newline at end of file
-- 
GitLab