You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by sr...@apache.org on 2016/03/25 19:53:36 UTC

tez git commit: TEZ-3171. Tez UI 2: Swimlane - Tooltip, zoom & redirection (sree)

Repository: tez
Updated Branches:
  refs/heads/master 8a233ecc1 -> 182c9970a


TEZ-3171. Tez UI 2: Swimlane - Tooltip, zoom & redirection (sree)


Project: http://git-wip-us.apache.org/repos/asf/tez/repo
Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/182c9970
Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/182c9970
Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/182c9970

Branch: refs/heads/master
Commit: 182c9970a792cb47b958daff5e64a89a3b6174b6
Parents: 8a233ec
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Sat Mar 26 00:20:14 2016 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Sat Mar 26 00:20:14 2016 +0530

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../components/em-swimlane-blocking-event.js    |  16 ++
 .../app/components/em-swimlane-event-bar.js     |  23 ++-
 .../webapp/app/components/em-swimlane-event.js  |  21 ++-
 .../app/components/em-swimlane-process-line.js  |  54 +++++++
 .../app/components/em-swimlane-process-name.js  |  19 ++-
 .../components/em-swimlane-process-visual.js    |  42 ++++-
 .../main/webapp/app/components/em-swimlane.js   |  23 ++-
 .../main/webapp/app/components/em-tooltip.js    | 158 +++++++++++++++++++
 .../main/webapp/app/controllers/dag/swimlane.js |  98 ++++++++++--
 tez-ui2/src/main/webapp/app/styles/app.less     |   1 +
 .../src/main/webapp/app/styles/em-swimlane.less |  77 ++++-----
 .../src/main/webapp/app/styles/em-tooltip.less  | 117 ++++++++++++++
 .../main/webapp/app/styles/swimlane-page.less   |  17 ++
 .../templates/components/em-swimlane-event.hbs  |   2 +-
 .../components/em-swimlane-process-line.hbs     |  19 +++
 .../components/em-swimlane-process-visual.hbs   |  32 +++-
 .../app/templates/components/em-swimlane.hbs    |  30 ++--
 .../app/templates/components/em-tooltip.hbs     |  54 +++++++
 .../main/webapp/app/templates/dag/swimlane.hbs  |   7 +
 tez-ui2/src/main/webapp/app/utils/process.js    |   7 +
 .../src/main/webapp/app/utils/vertex-process.js |  51 +++++-
 .../components/em-swimlane-process-line-test.js |  65 ++++++++
 .../em-swimlane-process-visual-test.js          |   3 +-
 .../integration/components/em-swimlane-test.js  |   7 +
 .../integration/components/em-tooltip-test.js   |  80 ++++++++++
 .../webapp/tests/unit/utils/process-test.js     |   1 +
 .../tests/unit/utils/vertex-process-test.js     |  80 ++++++++++
 28 files changed, 1022 insertions(+), 83 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 7c4939c..4b1e91c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1891,6 +1891,7 @@ ALL CHANGES
   TEZ-3160. Tez UI 2: Swimlane - Create swimlane page & component (sree)
   TEZ-3170. Tez UI 2: Swimlane - Display computed events, event bars & dependencies (sree)
   TEZ-3152. Tez UI 2: Build fails when run by multiple users or when node_modules is old (sree)
+  TEZ-3171. Tez UI 2: Swimlane - Tooltip, zoom & redirection (sree)
 
 Release 0.2.0-incubating: 2013-11-30
 

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.js
index a58ded8..c40bec9 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.js
@@ -47,4 +47,20 @@ export default Ember.Component.extend({
     });
   }),
 
+  sendMouseAction: function (name, mouseEvent) {
+    this.sendAction(name, "blocking-event", this.get("process"), {
+      mouseEvent: mouseEvent,
+      blocking: this.get("blocking"),
+      blockingEvent: this.get("blockingEvent")
+    });
+  },
+
+  mouseEnter: function (mouseEvent) {
+    this.sendMouseAction("showTooltip", mouseEvent);
+  },
+
+  mouseLeave: function (mouseEvent) {
+    this.sendMouseAction("hideTooltip", mouseEvent);
+  },
+
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js
index e0e835e..7afb312 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js
@@ -62,6 +62,27 @@ export default Ember.Component.extend({
     else {
       this.$().hide();
     }
-  })
+  }),
+
+  sendMouseAction: function (name, mouseEvent) {
+    this.sendAction(name, "event-bar", this.get("process"), {
+      mouseEvent: mouseEvent,
+      bar: this.get("bar"),
+      fromEvent: this.get("fromEvent"),
+      toEvent: this.get("toEvent")
+    });
+  },
+
+  mouseEnter: function (mouseEvent) {
+    this.sendMouseAction("showTooltip", mouseEvent);
+  },
+
+  mouseLeave: function (mouseEvent) {
+    this.sendMouseAction("hideTooltip", mouseEvent);
+  },
+
+  mouseUp: function (mouseEvent) {
+    this.sendMouseAction("click", mouseEvent);
+  }
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js
index 5eb6f0a..c4d31ca 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js
@@ -35,10 +35,23 @@ export default Ember.Component.extend({
     this.$(".event-bubble").css("border-color", color);
   }),
 
-  actions: {
-    showTooltip: function () {
-      console.log(this.get("event.name"));
-    }
+  sendMouseAction: function (name, mouseEvent) {
+    this.sendAction(name, "event", this.get("process"), {
+      mouseEvent: mouseEvent,
+      events: [this.get("event")]
+    });
+  },
+
+  mouseEnter: function (mouseEvent) {
+    this.sendMouseAction("showTooltip", mouseEvent);
+  },
+
+  mouseLeave: function (mouseEvent) {
+    this.sendMouseAction("hideTooltip", mouseEvent);
+  },
+
+  mouseUp: function (mouseEvent) {
+    this.sendMouseAction("click", mouseEvent);
   }
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/components/em-swimlane-process-line.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-process-line.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-process-line.js
new file mode 100644
index 0000000..ab4972b
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-process-line.js
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+
+  process: null,
+  startEvent: null,
+  endEvent: null,
+
+  didInsertElement: Ember.observer("startEvent.pos", "endEvent.pos", function () {
+    this.$(".process-line").css({
+      left: this.get("startEvent.pos") + "%",
+      right: (100 - this.get("endEvent.pos")) + "%",
+      "background-color": this.get("process").getColor()
+    });
+  }),
+
+  sendMouseAction: function (name, mouseEvent) {
+    this.sendAction(name, "process-line", this.get("process"), {
+      mouseEvent: mouseEvent,
+    });
+  },
+
+  mouseEnter: function (mouseEvent) {
+    this.sendMouseAction("showTooltip", mouseEvent);
+  },
+
+  mouseLeave: function (mouseEvent) {
+    this.sendMouseAction("hideTooltip", mouseEvent);
+  },
+
+  mouseUp: function (mouseEvent) {
+    this.sendMouseAction("click", mouseEvent);
+  }
+
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/components/em-swimlane-process-name.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-process-name.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-process-name.js
index f2b7ca9..eea897d 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane-process-name.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-process-name.js
@@ -21,8 +21,25 @@ import Ember from 'ember';
 export default Ember.Component.extend({
 
   process: null,
-  definition: null,
 
   classNames: ["em-swimlane-process-name"],
 
+  sendMouseAction: function (name, mouseEvent) {
+    this.sendAction(name, "process-name", this.get("process"), {
+      mouseEvent: mouseEvent,
+    });
+  },
+
+  mouseEnter: function (mouseEvent) {
+    this.sendMouseAction("showTooltip", mouseEvent);
+  },
+
+  mouseLeave: function (mouseEvent) {
+    this.sendMouseAction("hideTooltip", mouseEvent);
+  },
+
+  mouseUp: function (mouseEvent) {
+    this.sendMouseAction("click", mouseEvent);
+  }
+
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js
index 5942cf1..caa7603 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js
@@ -18,6 +18,8 @@
 
 import Ember from 'ember';
 
+const BUBBLE_RADIUS = 8; // Same as that in css
+
 export default Ember.Component.extend({
   process: null,
   definition: null,
@@ -71,12 +73,38 @@ export default Ember.Component.extend({
     });
   }),
 
-  drawEventWindow: Ember.observer("startEvent.pos", "endEvent.pos", function () {
-    this.$(".event-window-line").css({
-      left: this.get("startEvent.pos") + "%",
-      right: (100 - this.get("endEvent.pos")) + "%",
-      "background-color": this.get("process").getColor()
-    });
-  })
+  actions: {
+    showTooltip: function(type, process, options) {
+
+      if(type === "event") {
+        let mouseEvent = options.mouseEvent,
+            normalizedEvents = this.get("normalizedEvents"),
+            events = [];
+
+        this.$(".em-swimlane-event").each(function (index) {
+          var offset = Ember.$(this).offset();
+
+          if(mouseEvent.clientX >= offset.left - BUBBLE_RADIUS &&
+              mouseEvent.clientX <= offset.left + BUBBLE_RADIUS &&
+              mouseEvent.clientY >= offset.top - BUBBLE_RADIUS &&
+              mouseEvent.clientY <= offset.top + BUBBLE_RADIUS) {
+            events.push(normalizedEvents[index]);
+          }
+        });
+
+        if(events.length) {
+          options.events = events;
+        }
+      }
+
+      this.sendAction("showTooltip", type, process, options);
+    },
+    hideTooltip: function(type, process, options) {
+      this.sendAction("hideTooltip", type, process, options);
+    },
+    click: function (type, process, options) {
+      this.sendAction("click", type, process, options);
+    }
+  }
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/components/em-swimlane.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane.js b/tez-ui2/src/main/webapp/app/components/em-swimlane.js
index 3f22c50..c05461f 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane.js
@@ -44,6 +44,15 @@ export default Ember.Component.extend({
 
   eventBars: [],
 
+  tooltipContents: null,
+
+  zoom: 100,
+
+  didInsertElement: Ember.observer("zoom", function () {
+    var zoom = this.get("zoom");
+    this.$(".zoom-panel").css("width", `${zoom}%`);
+  }),
+
   timeWindow: Ember.computed("startTime", "endTime", function () {
     return Math.max(0, this.get("endTime") - this.get("startTime"));
   }),
@@ -111,6 +120,18 @@ export default Ember.Component.extend({
     });
 
     return Ember.A(normalizedProcesses);
-  })
+  }),
+
+  actions: {
+    showTooltip: function (type, process, options) {
+      this.set("tooltipContents", process.getTooltipContents(type, options));
+    },
+    hideTooltip: function () {
+      this.set("tooltipContents", null);
+    },
+    click: function (type, process, options) {
+      this.sendAction("click", type, process, options);
+    }
+  }
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/components/em-tooltip.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-tooltip.js b/tez-ui2/src/main/webapp/app/components/em-tooltip.js
new file mode 100644
index 0000000..093ca3d
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/components/em-tooltip.js
@@ -0,0 +1,158 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+const TIP_PADDING = 10, // As in em-tooltip.css
+      FADE_TIME = 150;
+
+export default Ember.Component.extend({
+
+  title: null,
+  description: null,
+  properties: null,
+  contents: null,
+
+  classNames: ["em-tooltip"],
+  classNameBindings: ["aboveOrBelow"],
+
+  x: 0,
+  y: 0,
+
+  _contents: null,
+  show: false,
+  aboveOrBelow: null,
+
+  window: null,
+  tip: null,
+  bubbles: null,
+
+  _contentObserver: Ember.on("init", Ember.observer("title", "description", "properties", "contents", function () {
+    var contents,
+        tip = this.get("tip");
+
+    if(this.get("title") || this.get("description") || this.get("properties")){
+      contents = [{
+        title: this.get("title"),
+        description: this.get("description"),
+        properties: this.get("properties"),
+      }];
+    }
+    else if(Array.isArray(this.get("contents"))){
+      contents = this.get("contents");
+    }
+
+    this.set("show", false);
+    if(contents) {
+      if(tip) {
+        tip.hide();
+      }
+      this.set("_contents", contents);
+
+      Ember.run.later(this, function () {
+        this.set("bubbles", this.$(".bubble"));
+        this.set("show", true);
+        this.renderTip();
+      });
+    }
+    else if(tip){
+      tip.stop(true).fadeOut(FADE_TIME);
+    }
+  })),
+
+  didInsertElement: function () {
+    this.setProperties({
+      window: Ember.$(window),
+      tip: this.$(),
+    });
+    Ember.$(document).on("mousemove", this, this.onMouseMove);
+  },
+
+  willDestroyElement: function () {
+    Ember.$(document).off("mousemove", this.onMouseMove);
+  },
+
+  onMouseMove: function (event) {
+    event.data.setProperties({
+      x: event.clientX,
+      y: event.clientY
+    });
+
+    if(Ember.get(event, "data.tip")) {
+      event.data.renderTip();
+    }
+  },
+
+  getBubbleOffset: function (x, bubbleElement, winWidth) {
+    var bubbleWidth = bubbleElement.width(),
+        bubbleOffset = (bubbleWidth - TIP_PADDING) >> 1;
+
+    if(bubbleWidth < 0) {
+      bubbleWidth = 0;
+    }
+
+    if(x - bubbleOffset < 0) {
+      bubbleOffset = x;
+    }
+    else if(x + TIP_PADDING + bubbleOffset > winWidth) {
+      bubbleOffset = x - (winWidth - bubbleWidth);
+    }
+
+    return -bubbleOffset;
+  },
+
+  renderTip: function () {
+    if(this.get("show")) {
+      let x = this.get("x"),
+          y = this.get("y"),
+
+          winHeight = this.get("window").height(),
+          winWidth = this.get("window").width(),
+
+          showAbove = y < (winHeight >> 1),
+
+          that = this,
+          tip = this.get("tip");
+
+      if(!showAbove) {
+        y -= tip.height();
+        this.set("aboveOrBelow", "below");
+      }
+      else {
+        this.set("aboveOrBelow", "above");
+      }
+
+      tip.css({
+        left: `${x}px`,
+        top: `${y}px`,
+      });
+
+      tip.fadeIn({
+        duration: FADE_TIME,
+        start: function () {
+          that.get("bubbles").each(function () {
+            var bubble = Ember.$(this),
+                bubbleOffset = that.getBubbleOffset(x, bubble, winWidth);
+            bubble.css("left", `${bubbleOffset}px`);
+          });
+        }
+      });
+    }
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js b/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js
index 1699708..cf69e1f 100644
--- a/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js
+++ b/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js
@@ -25,32 +25,96 @@ import VertexProcess from '../../utils/vertex-process';
 import fullscreen from 'em-tgraph/utils/fullscreen';
 
 export default MultiTableController.extend({
+
+  zoom: 100,
+
   breadcrumbs: [{
     text: "Vertex Swimlane",
     routeName: "dag.swimlane",
   }],
 
-  columns: ColumnDefinition.make([]),
-
-  actions: {
-    toggleFullscreen: function () {
-      var swimlaneElement = Ember.$(".swimlane-page")[0];
-      if(swimlaneElement){
-        fullscreen.toggle(swimlaneElement);
-      }
+  columns: ColumnDefinition.make([{
+    id: 'entityID',
+    headerTitle: 'Vertex Id',
+    contentPath: 'entityID'
+  },{
+    id: 'status',
+    headerTitle: 'Status',
+    contentPath: 'status',
+  },{
+    id: 'progress',
+    headerTitle: 'Progress',
+    contentPath: 'progress',
+    cellDefinition: {
+      type: 'number',
+      format: '0%'
+    }
+  },{
+    id: 'startTime',
+    headerTitle: 'Start Time',
+    contentPath: 'startTime',
+    cellDefinition: {
+      type: 'date'
+    }
+  },{
+    id: 'endTime',
+    headerTitle: 'End Time',
+    contentPath: 'endTime',
+    cellDefinition: {
+      type: 'date'
+    }
+  },{
+    id: 'duration',
+    headerTitle: 'Duration',
+    contentPath: 'duration',
+    cellDefinition: {
+      type: 'duration'
     }
-  },
+  },{
+    id: 'firstTaskStartTime',
+    headerTitle: 'First Task Start Time',
+    contentPath: 'firstTaskStartTime',
+    cellDefinition: {
+      type: 'date'
+    }
+  },{
+    id: 'totalTasks',
+    headerTitle: 'Tasks',
+    contentPath: 'totalTasks',
+  },{
+    id: 'succeededTasks',
+    headerTitle: 'Succeeded Tasks',
+    contentPath: 'succeededTasks',
+  },{
+    id: 'runningTasks',
+    headerTitle: 'Running Tasks',
+    contentPath: 'runningTasks',
+  },{
+    id: 'pendingTasks',
+    headerTitle: 'Pending Tasks',
+    contentPath: 'pendingTasks',
+  },{
+    id: 'processorClassName',
+    headerTitle: 'Processor Class',
+    contentPath: 'processorClassName',
+  }]),
 
   processes: Ember.computed("model", function () {
     var processes = [],
         processHash = {},
 
-        dagPlanEdges = this.get("model.firstObject.dag.edges");
+        dagPlanEdges = this.get("model.firstObject.dag.edges"),
+
+        that = this,
+        getVisibleProps = function () {
+          return that.get("visibleColumns");
+        };
 
     // Create process instances for each vertices
     this.get("model").forEach(function (vertex) {
       var process = VertexProcess.create({
         vertex: vertex,
+        getVisibleProps: getVisibleProps,
         blockers: Ember.A()
       });
       processHash[vertex.get("name")] = process;
@@ -74,5 +138,17 @@ export default MultiTableController.extend({
   }, {
     fromEvent: "BLOCKING_VERTICES_COMPLETE",
     toEvent: "VERTEX_TASK_FINISH",
-  }]
+  }],
+
+  actions: {
+    toggleFullscreen: function () {
+      var swimlaneElement = Ember.$(".swimlane-page")[0];
+      if(swimlaneElement){
+        fullscreen.toggle(swimlaneElement);
+      }
+    },
+    click: function (type, process) {
+      this.transitionToRoute('vertex.index', process.get('vertex.entityID'));
+    }
+  }
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/styles/app.less
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/styles/app.less b/tez-ui2/src/main/webapp/app/styles/app.less
index 0202486..7275650 100644
--- a/tez-ui2/src/main/webapp/app/styles/app.less
+++ b/tez-ui2/src/main/webapp/app/styles/app.less
@@ -30,6 +30,7 @@
 @import "caller-info";
 @import "date-formatter";
 @import "em-swimlane";
+@import "em-tooltip";
 
 // Modals
 @import "column-selector";

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/styles/em-swimlane.less
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/styles/em-swimlane.less b/tez-ui2/src/main/webapp/app/styles/em-swimlane.less
index a982712..2deb4db 100644
--- a/tez-ui2/src/main/webapp/app/styles/em-swimlane.less
+++ b/tez-ui2/src/main/webapp/app/styles/em-swimlane.less
@@ -28,10 +28,13 @@
     border-right: 1px solid @border-color;
   }
   .process-visuals {
+    .force-scrollbar;
+
     position: absolute;
     left: 100px;
     right: 0px;
     top: 0px;
+    overflow: auto;
   }
 }
 
@@ -51,9 +54,11 @@
   position: relative;
   height: 30px;
 
-  margin-left: 10px;
+  margin: 0px 10px;
 
-  cursor: pointer;
+  .process-line, .event-bar, .event-bubble {
+    cursor: pointer;
+  }
 
   .base-line {
     position: relative;
@@ -63,7 +68,7 @@
     border-top: 1px dotted @border-color;
   }
 
-  .event-window-line {
+  .process-line {
     position: absolute;
     top: unit(unit(@process-height) * 0.5 - 1, get-unit(@process-height));
     height: 3px;
@@ -78,48 +83,46 @@
     border: 1px solid;
     margin-right: -1px;
   }
-}
 
-.em-swimlane-event {
-  position: absolute;
-  top: unit(unit(@process-height) * 0.5, get-unit(@process-height));
-
-  .event-line {
+  .em-swimlane-event {
     position: absolute;
-    top: -9px;
-    height: 18px;
-    border-left: 1px solid;
-  }
+    top: unit(unit(@process-height) * 0.5, get-unit(@process-height));
 
-  .event-bubble {
-    position: absolute;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    left: 0;
-
-    -webkit-transition: top .2s, right .2s, bottom .2s, left .2s, border-width .2s; /* Safari */
-    transition: top .2s, right .2s, bottom .2s, left .2s, border-width .2s;
-    transition-timing-function: cubic-bezier(1.44);
-
-    border-radius: 7px;
-    border: 0px solid;
-    background-color: white;
-  }
-}
+    .event-line {
+      position: absolute;
+      top: -9px;
+      height: 18px;
+      border-left: 1px solid;
+    }
 
-.em-swimlane-blocking-event {
-  position: absolute;
-  top: unit(unit(@process-height) * 0.5, get-unit(@process-height));
+    .event-bubble {
+      position: absolute;
+      top: 0;
+      right: 0;
+      bottom: 0;
+      left: 0;
+
+      -webkit-transition: top .2s, right .2s, bottom .2s, left .2s, border-width .2s; /* Safari */
+      transition: top .2s, right .2s, bottom .2s, left .2s, border-width .2s;
+      transition-timing-function: cubic-bezier(1.44);
+
+      border-radius: 7px;
+      border: 0px solid;
+      background-color: white;
+    }
+  }
 
-  .event-line {
+  .em-swimlane-blocking-event {
     position: absolute;
-    top: 0px;
-    border-left: 1px solid;
+    top: unit(unit(@process-height) * 0.5, get-unit(@process-height));
+
+    .event-line {
+      position: absolute;
+      top: 0px;
+      border-left: 1px solid;
+    }
   }
-}
 
-.em-swimlane-process-visual {
   &:hover {
     .event-bubble {
       top: -7px;

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/styles/em-tooltip.less
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/styles/em-tooltip.less b/tez-ui2/src/main/webapp/app/styles/em-tooltip.less
new file mode 100644
index 0000000..ab2a19e
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/styles/em-tooltip.less
@@ -0,0 +1,117 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.em-tooltip {
+  .no-select;
+  .no-mouse;
+
+  position: fixed;
+  width: 820px; //2 * (td width + padding)
+
+  top: 10px;
+  left: 20px;
+
+  .bubble-container {
+    margin-bottom: 5px;
+
+    .bubble {
+      display: inline-block;
+      max-width: 820px;
+      margin-left: -11px; // Border radius + arrow margin-left
+
+      position: relative;
+      padding: 10px;
+
+      font-family: helvetica;
+      background: rgba(0, 0, 0, 0.8);
+      color: #fff;
+      border-radius: 5px;
+
+      .tip-title, .tip-desc, .tip-props {
+        border-top: 1px solid rgba(255, 255, 255, 0.4);
+        text-align: center;
+
+        padding: 0px 2px;
+
+        &:first-child {
+          border-top: none;
+        }
+      }
+
+      .tip-title {
+        font-size: 1.1em;
+      }
+
+      .tip-props {
+        table {
+          display: inline;
+          table-layout:fixed;
+
+          td {
+            overflow: hidden;
+            white-space: nowrap;
+            text-overflow: ellipsis;
+            max-width: 400px;
+
+            text-align: right;
+          }
+          td:nth-child(1) {
+            padding-right: 10px;
+            text-align: left;
+          }
+          td:nth-child(2) {
+            text-align: right;
+            padding-left: 10px;
+            border-left: 1px solid rgba(255, 255, 255, 0.4);
+          }
+        }
+      }
+    }
+  }
+
+  &.below:after, &.above:before {
+    display: block;
+    box-sizing: border-box;
+
+    font-size: 12px;
+    line-height: 9px;
+
+    color: rgba(0, 0, 0, 0.8);
+    margin-left: -6px; // Half of font size
+  }
+
+  &.above {
+    margin-top: 20px;
+
+    &:before {
+      margin-bottom: -2px;
+      content: "\25B2";
+    }
+  }
+
+  &.below {
+    .bubble-container {
+      margin-top: 5px;
+      margin-bottom: 0px;
+    }
+
+    &:after {
+      content: "\25BC";
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/styles/swimlane-page.less
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/styles/swimlane-page.less b/tez-ui2/src/main/webapp/app/styles/swimlane-page.less
index e31371f..c9ae374 100644
--- a/tez-ui2/src/main/webapp/app/styles/swimlane-page.less
+++ b/tez-ui2/src/main/webapp/app/styles/swimlane-page.less
@@ -41,6 +41,23 @@
     }
   }
 
+  .zoom-range {
+    .no-wrap;
+    display: inline-block;
+    width: 300px;
+
+    margin-right: 5px;
+    padding-right: 5px;
+    border-right: 1px solid @border-color;
+
+    input {
+      width: 200px;
+      display: inline-block;
+      margin-top: 5px;
+      vertical-align: text-bottom;
+    }
+  }
+
   .fa-compress {
     display: none;
   }

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs
index 9b6330f..e5ddfa9 100644
--- a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs
@@ -17,4 +17,4 @@
 }}
 
 <div class="event-line"></div>
-<div class="event-bubble" {{action "showTooltip" on="mouseEnter"}}></div>
\ No newline at end of file
+<div class="event-bubble"></div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-line.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-line.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-line.hbs
new file mode 100644
index 0000000..bd790fa
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-line.hbs
@@ -0,0 +1,19 @@
+{{!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+}}
+
+<div class="process-line"></div>

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs
index 6e4603d..cbb025c 100644
--- a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs
@@ -17,9 +17,23 @@
 }}
 
 <div class="base-line"></div>
-<div class="event-window-line"></div>
+{{em-swimlane-process-line
+  process=process
+  startEvent=startEvent
+  endEvent=endEvent
+  showTooltip="showTooltip"
+  hideTooltip="hideTooltip"
+  click="click"
+}}
 {{#each process.blocking as |blocking|}}
-  {{em-swimlane-blocking-event process=process blocking=blocking events=normalizedEvents}}
+  {{em-swimlane-blocking-event
+    process=process
+    blocking=blocking
+    events=normalizedEvents
+    showTooltip="showTooltip"
+    hideTooltip="hideTooltip"
+    click="click"
+  }}
 {{/each}}
 {{#each eventBars as |bar index|}}
   {{em-swimlane-event-bar
@@ -27,8 +41,18 @@
     bar=bar
     barIndex=index
     bars=eventBars
-    process=process}}
+    process=process
+    showTooltip="showTooltip"
+    hideTooltip="hideTooltip"
+    click="click"
+  }}
 {{/each}}
 {{#each normalizedEvents as |event|}}
-  {{em-swimlane-event process=process event=event}}
+  {{em-swimlane-event
+    process=process
+    event=event
+    showTooltip="showTooltip"
+    hideTooltip="hideTooltip"
+    click="click"
+  }}
 {{/each}}

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs
index a4777f0..ad0cbe2 100644
--- a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs
@@ -18,17 +18,27 @@
 
 <div class="process-names">
   {{#each normalizedProcesses as |process|}}
-    {{em-swimlane-process-name process=process definition=processDefinition}}
-  {{/each}}
-</div><div class="process-visuals">
-  {{#each normalizedProcesses as |process|}}
-    {{em-swimlane-process-visual
+    {{em-swimlane-process-name
       process=process
-      definition=processDefinition
-      eventBars=eventBars
-      startTime=startTime
-      endTime=endTime
-      timeWindow=timeWindow
+      click="click"
     }}
   {{/each}}
+</div><div class="process-visuals">
+  <div class="zoom-panel">
+    {{#each normalizedProcesses as |process|}}
+      {{em-swimlane-process-visual
+        process=process
+        definition=processDefinition
+        eventBars=eventBars
+        startTime=startTime
+        endTime=endTime
+        timeWindow=timeWindow
+        showTooltip="showTooltip"
+        hideTooltip="hideTooltip"
+        click="click"
+      }}
+    {{/each}}
+  </div>
 </div>
+
+{{em-tooltip contents=tooltipContents}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/templates/components/em-tooltip.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-tooltip.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-tooltip.hbs
new file mode 100644
index 0000000..65f33db
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-tooltip.hbs
@@ -0,0 +1,54 @@
+{{!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+}}
+
+{{#each _contents as |content|}}
+
+  <div class="bubble-container">
+    <div class="bubble">
+      {{#if content.title}}{{show_}}
+        <div class="tip-title">{{content.title}}</div>
+      {{/if}}
+      {{#if content.description}}
+        <div class="tip-desc">{{content.description}}</div>
+      {{/if}}
+      {{#if content.properties}}
+        <div class="tip-props">
+          <table>
+            {{#each content.properties as |prop|}}
+              <tr>
+                <td>
+                  {{prop.name}}
+                </td>
+                <td>
+                  {{txt prop.value
+                  type=prop.type
+                  format=prop.format
+                  timeZone=prop.timeZone
+                  valueFormat=prop.valueFormat
+                  valueTimeZone=prop.valueTimeZone
+                  valueUnit=prop.valueUnit}}
+                </td>
+              </tr>
+            {{/each}}
+          </table>
+        </div>
+      {{/if}}
+    </div>
+  </div>
+
+{{/each}}

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs b/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs
index a2b2cbe..6d9b885 100644
--- a/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs
@@ -19,6 +19,11 @@
 {{#if loaded}}
   <div class="swimlane-page">
     <div class="button-panel">
+      <div class="zoom-range">
+        {{zoom}}%
+        {{input type="range" value=zoom min=100 max=1000}}
+      </div>
+      <i class='fa fa-cog fa-border' {{action 'openColumnSelector'}} title="Customize vertex tooltip"></i>
       <i class='fa fa-expand fa-border' {{action 'toggleFullscreen'}} title="Toggle fullscreen"></i>
       <i class='fa fa-compress fa-border' {{action 'toggleFullscreen'}} title="Toggle fullscreen"></i>
     </div>
@@ -29,6 +34,8 @@
       eventBars=eventBars
       startTime=model.firstObject.dag.startTime
       endTime=model.firstObject.dag.endTime
+      zoom=zoom
+      click="click"
     }}
   </div>
 {{else}}

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/utils/process.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/utils/process.js b/tez-ui2/src/main/webapp/app/utils/process.js
index eba3e72..d0f06a7 100644
--- a/tez-ui2/src/main/webapp/app/utils/process.js
+++ b/tez-ui2/src/main/webapp/app/utils/process.js
@@ -96,6 +96,13 @@ export default Ember.Object.extend({
     parentHash[currentId] = false;
 
     return blockers;
+  },
+
+  getTooltipContents: function (type/*, options*/) {
+    return [{
+      title: this.get("name"),
+      description: "Mouse on : " + type
+    }];
   }
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/app/utils/vertex-process.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/utils/vertex-process.js b/tez-ui2/src/main/webapp/app/utils/vertex-process.js
index e48532d..b99c822 100644
--- a/tez-ui2/src/main/webapp/app/utils/vertex-process.js
+++ b/tez-ui2/src/main/webapp/app/utils/vertex-process.js
@@ -36,6 +36,8 @@ export default Process.extend({
 
   blockingEventName: "VERTEX_FINISHED",
 
+  getVisibleProps: null,
+
   events: Ember.computed(
     "vertex.events.@each.timestamp",
     "vertex.firstTaskStartTime",
@@ -45,7 +47,7 @@ export default Process.extend({
       var events = this.get("vertex.events").map(function (event) {
         return {
           name: event.eventtype,
-          test: EVENT_TEXTS[event.eventtype],
+          text: EVENT_TEXTS[event.eventtype],
           time: event.timestamp
         };
       }),
@@ -57,7 +59,7 @@ export default Process.extend({
         let type = "VERTEX_TASK_START";
         events.push({
           name: type,
-          test: EVENT_TEXTS[type],
+          text: EVENT_TEXTS[type],
           time: firstTaskStartTime
         });
       }
@@ -66,7 +68,7 @@ export default Process.extend({
         let type = "VERTEX_TASK_FINISH";
         events.push({
           name: type,
-          test: EVENT_TEXTS[type],
+          text: EVENT_TEXTS[type],
           time: lastTaskFinishTime
         });
       }
@@ -75,7 +77,7 @@ export default Process.extend({
         let type = "BLOCKING_VERTICES_COMPLETE";
         events.push({
           name: type,
-          test: EVENT_TEXTS[type],
+          text: EVENT_TEXTS[type],
           time: unblockTime
         });
       }
@@ -106,4 +108,45 @@ export default Process.extend({
     return time;
   }),
 
+  getTooltipContents: function (type, options) {
+    var contents,
+        that = this;
+
+    switch(type) {
+      case "event-bar":
+      case "process-line":
+        let properties = this.getVisibleProps().map(function (definition) {
+          return {
+            name: definition.get("headerTitle"),
+            value: that.get("vertex").get(definition.get("contentPath")),
+            type: Ember.get(definition, "cellDefinition.type"),
+            format: Ember.get(definition, "cellDefinition.format")
+          };
+        });
+
+        contents = [{
+          title: this.get("name"),
+          properties: properties
+        }];
+      break;
+      case "event":
+        contents = options.events.map(function (event) {
+          return {
+            title: event.text,
+            properties: [{
+              name: "Type",
+              value: event.name,
+            }, {
+              name: "Time",
+              value: event.time,
+              type: "date"
+            }]
+          };
+        });
+      break;
+    }
+
+    return contents;
+  }
+
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-line-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-line-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-line-test.js
new file mode 100644
index 0000000..0dd8ead
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-line-test.js
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+import wait from 'ember-test-helpers/wait';
+import Ember from 'ember';
+
+import Process from 'tez-ui/utils/process';
+
+moduleForComponent('em-swimlane-process-line', 'Integration | Component | em swimlane process line', {
+  integration: true
+});
+
+test('Basic creation test', function(assert) {
+  this.set("process", Process.create());
+
+  this.render(hbs`{{em-swimlane-process-line process=process}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-swimlane-process-line process=process}}
+      template block text
+    {{/em-swimlane-process-line}}
+  `);
+
+  assert.equal(this.$().text().trim(), '');
+});
+
+test('start-end event test', function(assert) {
+  var startEvent = Ember.Object.create({
+        pos: 50
+      }),
+      endEvent = Ember.Object.create({
+        pos: 70
+      });
+
+  this.set("process", Process.create());
+  this.set("startEvent", startEvent);
+  this.set("endEvent", endEvent);
+
+  this.render(hbs`{{em-swimlane-process-line process=process startEvent=startEvent endEvent=endEvent}}`);
+
+  return wait().then(() => {
+    assert.equal(this.$(".process-line").eq(0).attr("style").trim(), "left: 50%; right: 30%;", "process-line");
+  });
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js
index ffc7b56..4eb6b1b 100644
--- a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js
@@ -60,11 +60,10 @@ test('Events test', function(assert) {
   return wait().then(() => {
     var events = this.$(".em-swimlane-event");
 
-
     assert.equal(events.length, 2);
     assert.equal(events.eq(0).attr("style").trim(), "left: 50%;", "em-swimlane-event 1 left");
     assert.equal(events.eq(1).attr("style").trim(), "left: 70%;", "em-swimlane-event 2 left");
 
-    assert.equal(this.$(".event-window-line").eq(0).attr("style").trim(), "left: 50%; right: 30%;", "event-window-line");
+    assert.equal(this.$(".process-line").eq(0).attr("style").trim(), "left: 50%; right: 30%;", "process-line");
   });
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js
index 59e543d..e0ab7c7 100644
--- a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js
@@ -84,3 +84,10 @@ test('Normalization (Blocker based sorting) test - On a graph', function(assert)
   assert.equal(names.eq(3).text().trim(), p3.name);
   assert.equal(names.eq(4).text().trim(), p5.name);
 });
+
+test('Zoom test', function(assert) {
+  this.set("processes", [Process.create()]);
+
+  this.render(hbs`{{em-swimlane processes=processes zoom=500}}`);
+  assert.equal(this.$(".zoom-panel").attr("style").trim(), "width: 500%;");
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/tests/integration/components/em-tooltip-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-tooltip-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-tooltip-test.js
new file mode 100644
index 0000000..73a957b
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-tooltip-test.js
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+moduleForComponent('em-tooltip', 'Integration | Component | em tooltip', {
+  integration: true
+});
+
+test('Basic creation test', function(assert) {
+
+  this.render(hbs`{{em-tooltip}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-tooltip}}
+      template block text
+    {{/em-tooltip}}
+  `);
+
+  assert.equal(this.$().text().trim(), '');
+});
+
+test('Title test', function(assert) {
+  this.set("title", "TestTitle");
+  this.render(hbs`{{em-tooltip title=title}}`);
+
+  assert.equal(this.$().text().trim(), 'TestTitle');
+});
+
+test('Description test', function(assert) {
+  this.set("desc", "TestDesc");
+  this.render(hbs`{{em-tooltip description=desc}}`);
+
+  assert.equal(this.$().text().trim(), 'TestDesc');
+});
+
+test('Properties test', function(assert) {
+  this.set("properties", [{
+    name: "p1", value: "v1"
+  }, {
+    name: "p2", value: "v2"
+  }]);
+  this.render(hbs`{{em-tooltip properties=properties}}`);
+
+  assert.equal(this.$("tr").length, 2);
+});
+
+test('Contents test', function(assert) {
+  this.set("contents", [{
+    title: "p1",
+    properties: [{}, {}]
+  }, {
+    title: "p2",
+    properties: [{}, {}, {}]
+  }]);
+
+  this.render(hbs`{{em-tooltip contents=contents}}`);
+
+  assert.equal(this.$(".bubble").length, 2);
+  assert.equal(this.$("tr").length, 2 + 3);
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js b/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js
index ffab868..6069d7b 100644
--- a/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js
@@ -29,6 +29,7 @@ test('Basic creation test', function(assert) {
   assert.ok(process.startEvent);
   assert.ok(process.endEvent);
   assert.ok(process.getAllBlockers);
+  assert.ok(process.getTooltipContents);
 });
 
 test('getColor test', function(assert) {

http://git-wip-us.apache.org/repos/asf/tez/blob/182c9970/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js b/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js
index 85c0d0f..05ffc40 100644
--- a/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js
@@ -34,6 +34,8 @@ test('Basic creation test', function(assert) {
 
   assert.ok(process.events);
   assert.ok(process.unblockTime);
+
+  assert.ok(process.getTooltipContents);
 });
 
 test('unblockTime test', function(assert) {
@@ -96,3 +98,81 @@ test('events test', function(assert) {
   assert.equal(process.get("events.length"), 5);
   assert.equal(process.get("events.4.time"), 30);
 });
+
+test('getTooltipContents-event test', function(assert) {
+  var process = VertexProcess.create();
+
+  var eventTooltip = process.getTooltipContents("event", {
+    events: [{
+      text: "TestEventText1",
+      name: "TestEventName1",
+      time: 10
+    }, {
+      text: "TestEventText2",
+      name: "TestEventName2",
+      time: 20
+    }]
+  });
+
+  assert.equal(eventTooltip.length, 2);
+
+  assert.equal(eventTooltip[0].title, "TestEventText1");
+  assert.equal(eventTooltip[0].properties.length, 2);
+  assert.equal(eventTooltip[0].properties[0].name, "Type");
+  assert.equal(eventTooltip[0].properties[0].value, "TestEventName1");
+  assert.equal(eventTooltip[0].properties[1].name, "Time");
+  assert.equal(eventTooltip[0].properties[1].value, 10);
+
+  assert.equal(eventTooltip[1].title, "TestEventText2");
+  assert.equal(eventTooltip[1].properties.length, 2);
+  assert.equal(eventTooltip[1].properties[0].name, "Type");
+  assert.equal(eventTooltip[1].properties[0].value, "TestEventName2");
+  assert.equal(eventTooltip[1].properties[1].name, "Time");
+  assert.equal(eventTooltip[1].properties[1].value, 20);
+
+});
+
+test('getTooltipContents-process test', function(assert) {
+  var process = VertexProcess.create({
+    name: "TestName",
+    vertex: Ember.Object.create({
+      prop1: "val1",
+      prop2: "val2",
+      prop3: "val3"
+    }),
+    getVisibleProps: function () {
+      return [Ember.Object.create({
+        id: "prop1",
+        headerTitle: "Prop 1",
+        contentPath: "prop1"
+      }), Ember.Object.create({
+        id: "prop2",
+        headerTitle: "Prop 2",
+        contentPath: "prop2"
+      })];
+    }
+  });
+
+  var processTooltip = process.getTooltipContents("event-bar")[0];
+  assert.equal(processTooltip.title, "TestName");
+
+  assert.equal(processTooltip.properties.length, 2);
+
+  assert.equal(processTooltip.properties[0].name, "Prop 1");
+  assert.equal(processTooltip.properties[0].value, "val1");
+
+  assert.equal(processTooltip.properties[1].name, "Prop 2");
+  assert.equal(processTooltip.properties[1].value, "val2");
+
+  processTooltip = process.getTooltipContents("process-line")[0];
+  assert.equal(processTooltip.title, "TestName");
+
+  assert.equal(processTooltip.properties.length, 2);
+
+  assert.equal(processTooltip.properties[0].name, "Prop 1");
+  assert.equal(processTooltip.properties[0].value, "val1");
+
+  assert.equal(processTooltip.properties[1].name, "Prop 2");
+  assert.equal(processTooltip.properties[1].value, "val2");
+
+});