_binary From 79ac3180ff3da06cb9555c0002e041236aefc7a7 Mon Sep 17 00:00:00 2001
From: epinapala <epinapala@ebay.com>
Date: Sat, 13 Dec 2014 01:13:40 -0800
Subject: [PATCH] - Add PhantomJS tests back with refactoring. - Formatting
 changes suggested by professor. - Added username & password form to get input
 from user before running PhantomJS tests. - Minor fixes when help article
 load fails. - Minor changes to default.db and createdb.php

---
 configs/createdb.php                |  19 +--
 data/default.db                     | Bin 142336 -> 143360 bytes
 locale/ar/configure.ini             |   7 +-
 locale/bn/configure.ini             |   7 +-
 locale/de/configure.ini             |   7 +-
 locale/en-US/configure.ini          |   7 +-
 locale/es/configure.ini             |   7 +-
 locale/fa/configure.ini             |   7 +-
 locale/fr-FR/configure.ini          |   7 +-
 locale/he/configure.ini             |   7 +-
 locale/hi/configure.ini             |   7 +-
 locale/in-ID/configure.ini          |   7 +-
 locale/it/configure.ini             |   7 +-
 locale/ja/configure.ini             |   7 +-
 locale/kn/configure.ini             |   7 +-
 locale/ko/configure.ini             |   7 +-
 locale/pl/configure.ini             |   7 +-
 locale/pt/configure.ini             |   7 +-
 locale/ru/configure.ini             |   7 +-
 locale/te/configure.ini             |   7 +-
 locale/th/configure.ini             |   7 +-
 locale/tr/configure.ini             |   7 +-
 locale/vi-VN/configure.ini          |   7 +-
 locale/zh-CN/configure.ini          |   7 +-
 scripts/help.js                     |  32 +++--
 tests/YioopPhantomRunner.php        |  72 +++++++++++
 tests/index.php                     |  71 ++++++++++-
 tests/mobile_ui_tests.js            | 184 ++++++++++++++++++++++++++++
 tests/phantomjs_runner.js           | 209 +++++++++++++++++++++++++++++++
 tests/phantomjs_ui_test.php         | 238 ++++++++++++++++++++++++++++++++++++
 tests/web_ui_tests.js               | 151 +++++++++++++++++++++++
 views/elements/api_element.php      |   1 +
 views/helpers/helpbutton_helper.php |   4 +-
 33 files changed, 1068 insertions(+), 67 deletions(-)
 create mode 100644 tests/YioopPhantomRunner.php
 create mode 100644 tests/mobile_ui_tests.js
 create mode 100644 tests/phantomjs_runner.js
 create mode 100644 tests/phantomjs_ui_test.php
 create mode 100644 tests/web_ui_tests.js

diff --git a/configs/createdb.php b/configs/createdb.php
index b4d2410..a8a06eb 100755
--- a/configs/createdb.php
+++ b/configs/createdb.php
@@ -629,10 +629,11 @@ toc=true
 
 title=Browse Groups
 
-END_HEAD_VARS<br />
-Manage Groups is available to only those with a standard user role.
-<br />
-'''Groups''' are collections of users that have access to a group feed and a set of wiki pages. Any User with a &quot;Standard&quot; role can create or edit groups. The Create/Join Group form takes the name of a group, by which the user can select an existing group to Join. If you enter a name that currently does not exist, You will have the option to create a New group, which will be done on the Create Group form.
+END_HEAD_VARS==Creating or Joining a group==
+You can create or Join a Group all in one place using this Text field. Simply enter the Group Name You want to create or Join. If the Group Name already exists, you will simply join the group. If the group name doesn't exist, you will be presented with more options to customize and create your new Group.
+==Browse Existing Groups==
+You can use the [Browse] hyper link to browse the existing Groups. You will then be presented with a web form to narrow your search followed by a list of all visible groups to you beneath.
+{{right|[[https://www.seekquarry.com/?c=static&p=Documentation#Managing%20Users,%20Roles,%20and%20Groups| Learn More..]]}}
 EOD;
 $help_pages["Machine_Information"] = <<<EOD
 page_type=standard
@@ -646,9 +647,10 @@ title=Machine Information
 END_HEAD_VARS<br />
 '''Machine Information listings:'''
 <br />
-This shows the currently known about machines. This list always begins with the Name Server itself and a toggle to control whether or not the News Updater process is running on the Name Server. This allows you to control whether or not Yioop attempts to update its RSS (or Atom) search sources on an hourly basis.There is also a link to the log file of the News Updater process. Under the Name Server information is a dropdown that can be used to control the number of current machine statuses that are displayed for all other machines that have been added. It also might have next and previous arrow links to go through the currently available machines.
+This shows the currently known about machines. This list always begins with the '''Name Server''' itself and a toggle to control whether or not the News Updater process is running on the Name Server. This allows you to control whether or not Yioop attempts to update its RSS (or Atom) search sources on an hourly basis.
+<br />There is also a link to the log file of the News Updater process. Under the Name Server information is a dropdown that can be used to control the number of current machine statuses that are displayed for all other machines that have been added. It also might have next and previous arrow links to go through the currently available machines.
 <br />
-For more information on prerequisites for crawling Visit full documentation [[https://www.seekquarry.com/?c=main&amp;p=documentation#prereqs| here]]
+{{right|[[https://www.seekquarry.com/?c=static&p=Documentation#GUI%20for%20Managing%20Machines%20and%20Servers| Learn More..]]}}
 EOD;
 $help_pages["Manage_Machines"] = <<<EOD
 page_type=standard
@@ -659,14 +661,15 @@ toc=true
 
 title=Manage Machines
 
-END_HEAD_VARS<br />
-'''Add Machine:'''
+END_HEAD_VARS'''Add Machine:'''
 <br /><br />
 The Add machine form allows you to add a new machine to be controlled by this Yioop instance. The '''Machine Name''' field lets you give this machine an easy to remember name. The Machine URL field should be filled in with the URL to the installed Yioop instance.
 <br />
 The '''Mirror''' check-box says whether you want the given Yioop installation to act as a mirror for another Yioop installation. Checking it will reveal a drop-down menu that allows you to choose which installation among-st the previously entered machines you want to mirror. The '''Has Queue Server''' check-box is used to say whether the given Yioop installation will be running a queue server or not.
 <br />
 Finally, the '''Number of Fetchers''' drop down allows you to say how many fetcher instances you want to be able to manage for that machine.
+
+{{right|[[https://www.seekquarry.com/?c=static&p=Documentation#GUI%20for%20Managing%20Machines%20and%20Servers|Learn More..]]}}
 EOD;
 $help_pages["Discover_Groups"] = <<<EOD
 page_type=standard
diff --git a/data/default.db b/data/default.db
index ee2f8a628da9a05b9fecb2292362fd8b8bbc792f..329b350d036bbb366cd3ec1ffa27fc0feb1a6f50 100644
GIT binary patch
diff --git a/locale/ar/configure.ini b/locale/ar/configure.ini
index 6cfea09..7d12959 100755
--- a/locale/ar/configure.ini
+++ b/locale/ar/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = "قبل ساعات %s"
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/bn/configure.ini b/locale/bn/configure.ini
index 412a773..4fde9fa 100755
--- a/locale/bn/configure.ini
+++ b/locale/bn/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/de/configure.ini b/locale/de/configure.ini
index 8c7f003..60fda5c 100755
--- a/locale/de/configure.ini
+++ b/locale/de/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/en-US/configure.ini b/locale/en-US/configure.ini
index 0b61028..cf5986f 100755
--- a/locale/en-US/configure.ini
+++ b/locale/en-US/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = "%s hours ago"
 ; helpbutton_helper.php line: 91
 wiki_question_mark = "?"
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = "Edit"
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = "Unable to load Help article!"
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = "Read"
 ;
 ; images_helper.php line: 59
diff --git a/locale/es/configure.ini b/locale/es/configure.ini
index fa65e11..78702a1 100755
--- a/locale/es/configure.ini
+++ b/locale/es/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/fa/configure.ini b/locale/fa/configure.ini
index 6bb9564..ebfa23d 100755
--- a/locale/fa/configure.ini
+++ b/locale/fa/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = "%s ساعت پیش"
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/fr-FR/configure.ini b/locale/fr-FR/configure.ini
index ff60abd..c714601 100755
--- a/locale/fr-FR/configure.ini
+++ b/locale/fr-FR/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = "il y a %s heures"
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/he/configure.ini b/locale/he/configure.ini
index 7c2d1eb..0c628a7 100755
--- a/locale/he/configure.ini
+++ b/locale/he/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/hi/configure.ini b/locale/hi/configure.ini
index 7f4b374..bf4fe33 100755
--- a/locale/hi/configure.ini
+++ b/locale/hi/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/in-ID/configure.ini b/locale/in-ID/configure.ini
index c4374fb..57d56ee 100755
--- a/locale/in-ID/configure.ini
+++ b/locale/in-ID/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/it/configure.ini b/locale/it/configure.ini
index 6036be2..f1f5b5d 100755
--- a/locale/it/configure.ini
+++ b/locale/it/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/ja/configure.ini b/locale/ja/configure.ini
index 6446862..7075281 100755
--- a/locale/ja/configure.ini
+++ b/locale/ja/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/kn/configure.ini b/locale/kn/configure.ini
index 4bd8395..c3bb5fa 100755
--- a/locale/kn/configure.ini
+++ b/locale/kn/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/ko/configure.ini b/locale/ko/configure.ini
index ef3f5b7..8288fad 100755
--- a/locale/ko/configure.ini
+++ b/locale/ko/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/pl/configure.ini b/locale/pl/configure.ini
index 5dde3aa..0a4a0f2 100755
--- a/locale/pl/configure.ini
+++ b/locale/pl/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/pt/configure.ini b/locale/pt/configure.ini
index 6b35073..ba24782 100755
--- a/locale/pt/configure.ini
+++ b/locale/pt/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/ru/configure.ini b/locale/ru/configure.ini
index 4d8a50a..fe9d2e9 100755
--- a/locale/ru/configure.ini
+++ b/locale/ru/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/te/configure.ini b/locale/te/configure.ini
index ac4bd85..42e471a 100755
--- a/locale/te/configure.ini
+++ b/locale/te/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/th/configure.ini b/locale/th/configure.ini
index 79fcf29..a66527d 100755
--- a/locale/th/configure.ini
+++ b/locale/th/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/tr/configure.ini b/locale/tr/configure.ini
index fc9d9b5..3cfefcc 100755
--- a/locale/tr/configure.ini
+++ b/locale/tr/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/vi-VN/configure.ini b/locale/vi-VN/configure.ini
index 15d7504..4ec774d 100755
--- a/locale/vi-VN/configure.ini
+++ b/locale/vi-VN/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = ""
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/locale/zh-CN/configure.ini b/locale/zh-CN/configure.ini
index 03526b2..3539c78 100755
--- a/locale/zh-CN/configure.ini
+++ b/locale/zh-CN/configure.ini
@@ -3484,10 +3484,13 @@ feeds_helper_view_hourdate = "%s小时前"
 ; helpbutton_helper.php line: 91
 wiki_question_mark = ""
 ;
-; helpbutton_helper.php line: 124
+; helpbutton_helper.php line: 125
 wiki_view_edit = ""
 ;
-; helpbutton_helper.php line: 125
+; helpbutton_helper.php line: 126
+wiki_view_not_available = ""
+;
+; helpbutton_helper.php line: 128
 wiki_view_read = ""
 ;
 ; images_helper.php line: 59
diff --git a/scripts/help.js b/scripts/help.js
index 5d63b5f..fc8ce05 100644
--- a/scripts/help.js
+++ b/scripts/help.js
@@ -300,7 +300,7 @@ function displayHelpForId(help_point, is_mobile, target_controller,
         csrf_token_key + '=' + csrf_token_value + "&" +
         "page_name=" + help_point.getAttribute("data-pagename"),
         'json',
-        function (data)
+        function(data)
         {
             elt("help-frame-body").innerHTML = parseWikiContent(
                 data.wiki_content,
@@ -310,21 +310,27 @@ function displayHelpForId(help_point, is_mobile, target_controller,
                 csrf_token_key,
                 csrf_token_value
             );
-            elt('page_name').innerHTML = data.page_title + ' [<a href="' +
-            getEditLink(
-                target_controller,
-                current_action,
-                csrf_token_key,
-                csrf_token_value,
-                help_group_id,
-                data.page_title,
-                back_params) + '">' +
-            tl["wiki_view_edit"] + '</a>]';
+            elt('page_name').innerHTML = data.page_view_title ||
+            data.page_title;
+            if(data.can_edit) {
+                elt('page_name').innerHTML += ' [<a href="' +
+                getEditLink(
+                    target_controller,
+                    current_action,
+                    csrf_token_key,
+                    csrf_token_value,
+                    help_group_id,
+                    data.page_title,
+                    back_params) + '">' +
+                tl["wiki_view_edit"] + '</a>]';
+            }
             toggleHelp('help-frame', is_mobile, target_controller);
         },
-        function (status)
+        function(status)
         {
-            toggleHelp('help-frame', is_mobile, target_controller);
+            doMessage("<h2 class='red'>" + tl["wiki_view_not_available"] +
+            "</h2>");
+            //toggleHelp('help-frame', is_mobile, target_controller);
         });
 }
 /*
diff --git a/tests/YioopPhantomRunner.php b/tests/YioopPhantomRunner.php
new file mode 100644
index 0000000..f845080
--- /dev/null
+++ b/tests/YioopPhantomRunner.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ * Copyright (C) 2009 - 2014  Chris Pollett chris@pollett.org
+ * LICENSE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * END LICENSE
+ *
+ * @author Eswara Rajesh Pinapala epinapala@live.com
+ * @package seek_quarry
+ * @subpackage test
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2014
+ * @filesource
+ */
+/**
+ * Used to run PhantomJs scripts from the command line.
+ *
+ * @author Eswara Rajesh Pinapala
+ * @package seek_quarry
+ * @subpackage test
+ */
+class YioopPhantomRunner
+{
+    private $phantomjs_bin_path = 'phantomjs';
+    public function __construct($bin_path = null)
+    {
+        if($bin_path !== null) {
+            $this->phantomjs_bin_path = $bin_path;
+        }
+        $version = $this->execute("-v");
+        if(!$version){
+            throw new Exception("PhantomJS binary not found.");
+        }
+    }
+    public function execute($script, $decode_json = false)
+    {
+        $shell_result = shell_exec(
+            escapeshellcmd("{$this->phantomjs_bin_path} " . implode(' ',
+                    func_get_args())));
+        if($shell_result === null) {
+            return false;
+        }
+        if($decode_json) {
+            if(substr($shell_result, 0, 1) !== '{') {
+                //return if the result is not a JSON.
+                return $shell_result;
+            } else {
+                //If the result is a JSON, decode JSON into a PHP array.
+                $json = json_decode($shell_result, true);
+                if($json === null) {
+                    return false;
+                }
+                return $json;
+            }
+        } else {
+            return $shell_result;
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/tests/index.php b/tests/index.php
index b6b6448..d3085b1 100644
--- a/tests/index.php
+++ b/tests/index.php
@@ -88,12 +88,16 @@ require_once BASE_DIR."/lib/unit_test.php";
  */
 require_once BASE_DIR."/lib/javascript_unit_test.php";
 /**
+ * Load the YioopPhantomRunner that is used to execute PhantomJs Shell.
+ */
+require_once 'YioopPhantomRunner.php';
+/**
  * Do not send output to log files
  * @ignore
  */
 define("LOG_TO_FILES", false);
 $allowed_activities =
-    array("listTests", "runAllTests", "runTestBasedOnRequest");
+    array("listTests", "runAllTests", "runTestBasedOnRequest","runPhantomTests");
 if(isset($_REQUEST['activity']) &&
     in_array($_REQUEST['activity'], $allowed_activities)) {
     $activity = $_REQUEST['activity'];
@@ -105,6 +109,71 @@ if(isset($_REQUEST['activity']) &&
 <?php
 $activity();
 /**
+ * This function runs the phantomJS tests by calling th phantomJs shell to
+ * execute the PhantomJs tests written in JavaScript.
+ */
+function runPhantomTests()
+{
+    ob_clean();
+    $path_url = str_replace(basename(__DIR__) . "/", "", getFullURL(true));
+    $mode = "";
+    $resp_code = "";
+    $u = $_REQUEST['u'];
+    $p = $_REQUEST['p'];
+    if(isset($_REQUEST['mode'])) {
+        if(isset($_REQUEST['debug']) && $_REQUEST['debug'] == "true") {
+            $debug = true;
+        }else{
+            $debug = "false";
+        }
+        $mode = htmlentities($_REQUEST['mode'], ENT_QUOTES, "UTF-8");
+        $resp = array();
+        if(!in_array($mode, array("web", "mobile"))) {
+            $resp_code = "HTTP/1.1 400 Bad Request";
+        } else {
+            try {
+                $yioop_phantom_runner = new YioopPhantomRunner();
+                $test_results = ($yioop_phantom_runner->execute(
+                    "phantomjs_runner.js", $path_url, $mode, $debug, $u, $p));
+                if(!$test_results) {
+                    $resp_code = "HTTP/1.1 500";
+                } else {
+                    $resp['results'] = $test_results;
+                    $resp_code = "HTTP/1.1 200 OK";
+                    echo(json_encode($resp));
+                }
+            }catch(Exception $e){
+                $resp_code = "HTTP/1.1 500";
+                $resp['error'] = $e->getMessage();
+                echo(json_encode($resp));
+            }
+        }
+    } else {
+        $resp_code = "HTTP/1.1 500";
+        $resp['error'] = "Bad Request";
+        echo(json_encode($resp));
+    }
+    header($resp_code);
+    header("Content-Type : application/json");
+    exit();
+}
+
+/**
+ * This is a utility function to get the Full URL of the current page.
+ */
+function getFullURL($stripQueryParams = false)
+{
+    $pageURL = (@$_SERVER["HTTPS"] == "on") ? "https://" : "http://";
+    if(!in_array($_SERVER["SERVER_PORT"], array("80", "443"))) {
+        $pageURL .= $_SERVER["SERVER_NAME"] . ":" .
+            $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
+    } else {
+        $pageURL .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
+    }
+    //return full URL with query params stripped, if requested.
+    return $stripQueryParams ? strtok($pageURL, '?') : $pageURL;
+}
+/**
  * This function is responsible for listing out HTML links to the available
  * unit tests a user can run
  */
diff --git a/tests/mobile_ui_tests.js b/tests/mobile_ui_tests.js
new file mode 100644
index 0000000..7af847b
--- /dev/null
+++ b/tests/mobile_ui_tests.js
@@ -0,0 +1,184 @@
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ *
+ * Copyright (C) 2009 - 2014  Chris Pollett chris@pollett.org
+ *
+ * LICENSE:
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * END LICENSE
+ *
+ * @author Eswara Rajesh Pinapala epinapala@live.com
+ * @package seek_quarry
+ * @subpackage javascript
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2014
+ * @filesource
+ */
+/**
+ * Set a mobile user agent for PhantomJS, so that all the pages that phantomJs
+ * visits will be treated as mobile web pages.
+ */
+page.settings.userAgent = 'Mozilla/5.0 (Linux; U; Android 2.2.1; en-ca;'
++ ' LG-P505R Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko)' +
+' Version/4.0 Mobile Safari/533.1';
+/**
+ * Below is a list of step functions executed withe a delay of 2s by
+ * the interval function in "phantomjs_runner.js". Each function returns a
+ * result object, which contains the test name, test status(PASS/FAIL) &
+ * test ack(true/false). Note that result also contains the element of the page
+ * that is being asserted for existence, however this is stripped off later
+ * by "interval". All the cumulative steps are failed if any one test fails.
+ */
+var steps = [
+    /**
+     * Test if Yioop Home page is accessible.
+     */
+        function testHomePage()
+    {
+        page.open(page_url, function()
+        {
+            return true;
+        });
+    },
+    /**
+     * Test is SignIn link appears on the top of the page.
+     * If exists, Click on the sign in link. Page.evaluate does this action
+     * once the link existence is confirmed.
+     */
+        function testSignInLink()
+    {
+        var result = page.assertExists('body > div.landing-top-bar'
+            + ' > div.user-nav > ul > li:nth-child(2) > a',
+            "Signin link exists", page);
+        if(result.ack) {
+            page.evaluate(function()
+            {
+                var ev = document.createEvent("MouseEvents");
+                ev.initEvent("click", true, true);
+                document.querySelector("a[href='./?c=admin']")
+                    .dispatchEvent(ev);
+            });
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    },
+    /**
+     * Now that the click was expected in the last step function, now we assume
+     * the user lands on the login form, So we assert the same, If the user
+     * does land on login form, enter username/password and submit the form.
+     * Username and password can be changed in "phantomjs_runner.js".
+     */
+        function testLoginFormExists()
+    {
+        var result = page.assertExists('body > div.landing.non-search'
+        + ' > form', "Login Form exists", page);
+        if(result.ack) {
+            var creds = {};
+            creds.username = yioop_username;
+            creds.password = yioop_password;
+            //Enter Credentials
+            page.evaluate(function(creds)
+            {
+                document.getElementById("username").value = creds.username;
+                document.getElementById("password").value = creds.password;
+                document.querySelector('body > div.landing.non-search'
+                +
+                ' > form').submit();
+                return;
+            }, creds);
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    },
+    /**
+     * Now assuming that the login form is submitted successfully, we check for
+     * the ManageGroups dropdown option on the mobile web page, test is it
+     * exists.
+     */
+        function testDropDownExists()
+    {
+        var result = page.assertExists('#activity',
+            "Menu Dropdown exists", page);
+        if(result.ack) {
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    },
+    /**
+     * If manageGroups exists, select it.
+     */
+        function testManageGroups()
+    {
+        var selector = '#activity';
+        var result = {};
+        result.ack = false;
+        result.status = "FAIL";
+        result.msg = "Manage Groups link option exists in Dropdown.";
+        return page.evaluate(function(selector, result, page_url)
+        {
+            var elm = document.querySelector(selector);
+            if(elm !== null) {
+                var num_options = elm.options.length;
+                for (i = 0; i < num_options; i++) {
+                    var option_text = elm.options[i].text;
+                    if(option_text === "Manage Groups") {
+                        result.ack = true;
+                        result.status = "PASS";
+                        document.location.href = elm.options[i].value;
+                    }
+                }
+            }
+            return result;
+        }, selector, result, page_url);
+    },
+    /**
+     * On manage Groups page help button is expected, click on it if
+     * it exists.
+     */
+        function testHelpButtonExists()
+    {
+        var result = page.assertExists('button[data-pagename="Browse Groups"]',
+            "Help Button exists On Manage groups Page", page);
+        if(result.ack) {
+            //click on help
+            page.click('button[data-pagename="Browse Groups"]');
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    },
+    /**
+     * If the help button exists and if it is functioning properly, clicking on
+     * the help button will pull out the help article with an Edit hyper link.
+     * check if Edit link exists, If it does- click on it.
+     */
+        function testEditLinkForHelpArticlsExists()
+    {
+        var result = page.assertExists('#page_name > a',
+            "Edit Link for Help article exists", page);
+        if(result.ack) {
+            page.click('#page_name > a');
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    }
+];
\ No newline at end of file
diff --git a/tests/phantomjs_runner.js b/tests/phantomjs_runner.js
new file mode 100644
index 0000000..c3fac63
--- /dev/null
+++ b/tests/phantomjs_runner.js
@@ -0,0 +1,209 @@
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ *
+ * Copyright (C) 2009 - 2014  Chris Pollett chris@pollett.org
+ *
+ * LICENSE:
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * END LICENSE
+ *
+ * @author Eswara Rajesh Pinapala epinapala@live.com
+ * @package seek_quarry
+ * @subpackage javascript
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2014
+ * @filesource
+ */
+var page = require('webpage').create(),
+    testindex = 0,
+    filename,
+    loadInProgress = false,
+    fs = require('fs'),
+    path = 'results.json',
+    results = {},
+    system = require('system'),
+    args = system.args,
+    DEBUG = false,
+    page_url = args[1],
+    mode = args[2],
+    yioop_username = args[4],
+    yioop_password = args[5] || "";
+//Check is debug mode is intended.
+if(args[3] === "true") {
+    DEBUG = true;
+}
+/**
+ * set viewport for debugging using slimerjs.
+ */
+page.viewportSize = {
+    width: 1440,
+    height: 768
+};
+/**
+ * Prototype JS Array to add a "has" method that checks if the array has
+ * the element passed as param.
+ * @param v element to be tested against an array of elements.
+ * @returns boolean exists or not.
+ */
+Array.prototype.has = function(v)
+{
+    for (i = 0; i < this.length; i++) {
+        if(this[i] == v) return true;
+    }
+    return false;
+};
+var supported_tests = ["web", "mobile"];
+if(supported_tests.has(mode)) {
+    filename = mode + "_ui_tests.js",
+        phantom.injectJs(filename);
+} else {
+    console.log("Invalid test case. Terminating PhantomJS.");
+    phantom.exit();
+}
+/**
+ * Helper Functions for running PhantomJS tests.
+ */
+/**
+ * utility method which prints message to console if debug var is true.
+ * @param String msg to be printed to console.
+ */
+function l(msg)
+{
+    !DEBUG || console.log("[Debug] : " + msg);
+}
+/**
+ * Returns the name of the function when a  function is passed.
+ * @param Function fun whose name to be extracted.
+ * @returns String ret function name.
+ */
+function functionName(fun)
+{
+    var ret = fun.toString();
+    ret = ret.substr('function '.length);
+    ret = ret.substr(0, ret.indexOf('('));
+    return ret;
+}
+/**
+ * A function thet converts Object to String and prints the result to
+ * console.
+ */
+function renderTestResults()
+{
+    console.log(JSON.stringify(results));
+}
+/**
+ * PhantomJs Tests setup.
+ **/
+/**
+ * On encountering a console message in the page being evaluated,
+ * redirect to PhantomJS console.
+ * @param Strine msg caught from console.
+ */
+page.onConsoleMessage = function(msg)
+{
+    console.log("Error:" + msg);
+};
+/**
+ * set the flag variable loadInProgress to true when load is in progress.
+ */
+page.onLoadStarted = function()
+{
+    loadInProgress = true;
+};
+/**
+ * set the flag variable loadInProgress to false when load is finished.
+ */
+page.onLoadFinished = function()
+{
+    loadInProgress = false;
+};
+/**
+ * This method clicks on a an element.
+ * @param String selector CSS selector of the element to be clicked on.
+ */
+page.click = function(selector)
+{
+    l("Clicking Element[ " + selector + "]");
+    this.evaluate(function(selector, l)
+    {
+        var e = document.createEvent("MouseEvents");
+        e.initEvent("click", true, true);
+        document.querySelector(selector).dispatchEvent(e);
+    }, selector, l);
+};
+/**
+ * This function takes in a CSS selector a description message. The page in
+ * context is searched for the css selector, and if element exists, a result
+ * object with ack= true , status= PASS and the reference to element along with
+ * the message are constructed and returned.
+ *
+ * @param String selector CSS selector for the element being tested.
+ * @param String message description of test
+ * @returns object res
+ */
+page.assertExists = function(selector, message)
+{
+    var res = {};
+    res.msg = message;
+    res.elm = null;
+    res.elm = this.evaluate(function(selector)
+    {
+        return document.querySelector(selector);
+    }, selector);
+    if(res.elm) {
+        res.status = "PASS";
+        res.ack = true;
+    } else {
+        res.status = "FAIL";
+        res.ack = false;
+    }
+    return res;
+};
+/**
+ * This function runs each step periodically delayed by 2 seconds.
+ * each function returns a result object with
+ * elm-DOM reference to the
+ * element on page which is being tested.
+ * ack - test passed true/false
+ * status - readable PASS or FAIL.
+ * msg - more like a description of test.
+ * the element is used by the test function, but then stripped off by this
+ * function before adding to result collection - results.
+ * Once all steps are completed, results are printed to console, followed by
+ * exiting PhantomJS.
+ */
+interval = setInterval(function()
+{
+    if(!loadInProgress && typeof steps[testindex] == "function") {
+        var func = steps[testindex];
+        var result = steps[testindex]();
+        var function_name = functionName(func);
+        l("Test #" + (testindex + 1) + ": " + function_name);
+        if(result) {
+            l(result.status + " - " + result.msg);
+            delete result.elm;
+            results[function_name] = (result);
+        }
+        testindex++;
+    }
+    if(typeof steps[testindex] != "function") {
+        l("All Tests complete!");
+        renderTestResults();
+        phantom.exit();
+    }
+}, 2000);
\ No newline at end of file
diff --git a/tests/phantomjs_ui_test.php b/tests/phantomjs_ui_test.php
new file mode 100644
index 0000000..d252ebc
--- /dev/null
+++ b/tests/phantomjs_ui_test.php
@@ -0,0 +1,238 @@
+<?php
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ * Copyright (C) 2009 - 2014  Chris Pollett chris@pollett.org
+ * LICENSE:
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * END LICENSE
+ *
+ * @author Eswara Rajesh Pinapala epinapala@live.com
+ * @package seek_quarry
+ * @subpackage test
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2014
+ * @filesource
+ */
+/**
+ * Used to test the UI using PhantomJs.
+ *
+ * @author Eswara Rajesh Pinapala
+ * @package seek_quarry
+ * @subpackage test
+ */
+class PhantomjsUiTest extends JavascriptUnitTest
+{
+    /**
+     * This test case runs the UI test cases in JS using PhantomJS. It then
+     * sends the result JSON to Javascript land to render test results.
+     */
+    function UITestCase()
+    {
+        ?>
+        <style>
+            .login {
+                width :500px;
+            }
+            .login ul {
+                padding: 0;
+                margin: 0;
+            }
+            .login li {
+                display: inline;
+            }
+            label {
+                display: block;
+                color: #999;
+            }
+        </style>
+        <fieldset class="login cf">
+            <span>
+                <h4>Please enter username & password of any user with
+                    "Admin" role in Yioop.</h4>
+            </span>
+            <form>
+                <ul>
+                    <li>
+                        <label for="u">Username</label>
+                        <input type="username" id="u" name="u"
+                               placeholder="Admin username" required="">
+                    </li>
+                    <br /><br />
+                    <li>
+                        <label for="p">Password</label>
+                        <input type="password" id="p" name="p"
+                               placeholder="Admin password" required="">
+                    </li>
+                    <br /><br />
+                    <li>
+                        <button type="button"
+                                onclick="triggerTests(); return false;">
+                            Run Tests
+                        </button>
+                    </li>
+                </ul>
+            </form>
+        </fieldset>
+        <br /><br />
+        <div id="web-ui-test">
+        </div>
+        <div id="mobile-ui-test">
+        </div>
+        <script type="text/javascript" src="../scripts/basic.js"></script>
+        <script type="text/javascript" src="../scripts/help.js"></script>
+        <script type="text/javascript">
+            var u, p;
+            /**
+             * This function is the main function that initiates the tests.
+             * Picks up the user name and password the user entered, and
+             * calls the web service to run tests.
+             *
+             */
+            function triggerTests()
+            {
+                u = document.getElementById("u").value;
+                p = document.getElementById("p").value;
+                runTests("web");
+                runTests("mobile");
+            }
+            /**
+             * This function makes a XHR POST request with callback functions.
+             * @param url String Url to do POST request on.
+             * @param postData String post params data.
+             * @param response_type String response type expected.
+             * @param success_callback Function callback function to call on
+             * success.
+             * @param error_handler Function callback function to call on
+             * failure.
+             *
+             */
+            function postToPageWithCallback(url, postData, response_type,
+                success_call_back,
+                error_handler)
+            {
+                var request = makeRequest();
+                request.open('POST', url, true);
+                request.setRequestHeader("Content-Type",
+                    "application/x-www-form-urlencoded");
+                request.onload = function()
+                {
+                    var status = request.status;
+                    if(status == 200) {
+                        success_call_back && success_call_back(
+                            JSON.parse(request.responseText));
+                    } else {
+                        error_handler && error_handler(status,
+                            JSON.parse(request.responseText));
+                    }
+                };
+                request.send(postData);
+            }
+            /**
+             * This function runs the tests for the mode requested.
+             * @param String mode Tests mode Web/Mobile.
+             */
+            function runTests(mode)
+            {
+                elt(mode + "-ui-test").innerHTML =
+                    '<div style="width:200px;">' + 'Loading ' +
+                    mode + '-UI ' + 'test Results<marquee ' +
+                    'behavior="alternate">.............' +
+                    '</marquee></div>';
+                postToPageWithCallback("?activity=runPhantomTests&mode=" +
+                    mode, "&u=" + u + "&p=" + p, "json",
+                    function(data)
+                    {
+                        renderResults(data.results, mode)
+                    },
+                    function(status, response)
+                    {
+                        elt(mode + "-ui-test").innerHTML =
+                            "Unable to run " +
+                            mode + " UI tests. \n Error: " + response.error;
+                        updateResultsSummary(0,
+                            Object.keys(data.results).length,
+                            'red', mode);
+                    }
+                );
+            }
+            /**
+             *This function takes in the results object, parses it to render
+             *  the results ui.
+             *  @param results Obj results object.
+             *  @param mode String tests mode Web/Mobile.
+             */
+            function renderResults(results, mode)
+            {
+                elt(mode + "-ui-test").innerHTML = "";
+                var h2 = document.createElement("h2");
+                h2.innerHTML = "UI test results - " + mode;
+                elt(mode + "-ui-test").appendChild(h2);
+                var success_count = 0;
+                var results_size = 0;
+                for (var key in results) {
+                    var test_result = results[key];
+                    var cell;
+                    var row;
+                    var table;
+                    var color;
+                    table = document.createElement('table');
+                    if(test_result.ack) {
+                        color = 'lightgreen';
+                        success_count++;
+                    } else {
+                        color = 'red';
+                        row = table.insertRow(0);
+                        cell = row.insertCell(0);
+                        cell.style.fontWeight = 'bold';
+                        cell.innerHTML = key;
+                        cell = row.insertCell(1);
+                        cell.setAttribute("style", "background-color: " +
+                        color + ";");
+                        cell.innerHTML = test_result.status;
+                        elt(mode + "-ui-test").appendChild(table);
+                    }
+                    results_size++;
+                }
+                updateResultsSummary(success_count, results_size,
+                    'lightgreen',
+                    mode);
+            }
+            /**
+             * This test updates the results summary UI.
+             * @param pass String passed tests count
+             * @param total String total tests count
+             * @param color String color to display
+             * @param mode String mode of tests, Web/Mobile.
+             */
+            function updateResultsSummary(pass, total, color, mode)
+            {
+                var cell;
+                var row;
+                var table;
+                table = document.createElement('table');
+                row = table.insertRow(0);
+                cell = row.insertCell(0);
+                cell.style.fontWeight = 'bold';
+                cell.innerHTML = "PhantomJS " + mode + " UI Tests";
+                cell = row.insertCell(1);
+                cell.setAttribute("style", "background-color: " +
+                color + ";");
+                cell.innerHTML = pass + "/" + total;
+                elt(mode + "-ui-test").appendChild(table);
+            }
+        </script>
+    <?php
+    }
+}
+?>
\ No newline at end of file
diff --git a/tests/web_ui_tests.js b/tests/web_ui_tests.js
new file mode 100644
index 0000000..274703d
--- /dev/null
+++ b/tests/web_ui_tests.js
@@ -0,0 +1,151 @@
+/**
+ * SeekQuarry/Yioop --
+ * Open Source Pure PHP Search Engine, Crawler, and Indexer
+ *
+ * Copyright (C) 2009 - 2014  Chris Pollett chris@pollett.org
+ *
+ * LICENSE:
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * END LICENSE
+ *
+ * @author Eswara Rajesh Pinapala epinapala@live.com
+ * @package seek_quarry
+ * @subpackage javascript
+ * @license http://www.gnu.org/licenses/ GPL3
+ * @link http://www.seekquarry.com/
+ * @copyright 2009 - 2014
+ * @filesource
+ */
+/**
+ * Below is a list of step functions executed withe a delay of 2s by
+ * the interval function in "phantomjs_runner.js". Each function returns a
+ * result object, which contains the test name, test status(PASS/FAIL) &
+ * test ack(true/false). Note that result also contains the element of the page
+ * that is being asserted for existence, however this is stripped off later
+ * by "interval". All the cumulative steps are failed if any one test fails.
+ */
+var steps = [
+    /**
+     * Test if Yioop Home page is accessible.
+     */
+    function testHomePage()
+    {
+        page.open(page_url, function()
+        {
+            return true;
+        });
+    },
+    /**
+     * Test is SignIn link appears on the top of the page.
+     * If exists, Click on the sign in link. Page.evaluate does this action
+     * once the link existence is confirmed.
+     */
+    function testSignInLink()
+    {
+        var result = page.assertExists('body > div.landing-top-bar' +
+            ' > div.user-nav > ul > li:nth-child(2) > a',
+            "Signin link exists", page);
+        if(result.ack) {
+            page.evaluate(function()
+            {
+                var ev = document.createEvent("MouseEvents");
+                ev.initEvent("click", true, true);
+                document.querySelector("a[href='./?c=admin']")
+                    .dispatchEvent(ev);
+            });
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    },
+    /**
+     * Now that the click was expected in the last step function, now we assume
+     * the user lands on the login form, So we assert the same, If the user
+     * does land on login form, enter username/password and submit the form.
+     * Username and password can be changed in "phantomjs_runner.js".
+     */
+    function testLoginFormExists()
+    {
+        var result = page.assertExists(
+            'body > div.landing.non-search > form', "Login Form exists", page);
+        if(result.ack) {
+            var creds = {};
+            creds.username = yioop_username;
+            creds.password = yioop_password;
+            //Enter Credentials
+            page.evaluate(function(creds)
+            {
+                document.getElementById("username").value = creds.username;
+                document.getElementById("password").value = creds.password;
+                document.querySelector(
+                    'body > div.landing.non-search > form').submit();
+                return;
+            }, creds);
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    },
+    /**
+     * Now assuming that the login form is submitted successfully, we check for
+     * the ManageGroups link on the page. If manageGroups exists, click on it.
+     */
+    function testManageGroupsLinkExists()
+    {
+        var result = page.assertExists('body > div.component-container >' +
+            ' div:nth-child(3) > ul > li:nth-child(1) > a',
+            "Manage groups Link exists", page);
+        if(result.ack) {
+            page.click('body > div.component-container > div:nth-child(3) >' +
+            ' ul > li:nth-child(1) > a');
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    },
+    /**
+     * On manage Groups page help button is expected, click on it if
+     * it exists.
+     */
+    function testHelpButtonExists()
+    {
+        var result = page.assertExists('button[data-pagename="Browse Groups"]',
+            "Help Button exists On Manage groups Page", page);
+        if(result.ack) {
+            //click on help
+            page.click('button[data-pagename="Browse Groups"]');
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    },
+    /**
+     * If the help button exists and if it is functioning properly, clicking on
+     * the help button will pull out the help article with an Edit hyper link.
+     * check if Edit link exists, If it does- click on it.
+     */
+    function testEditLinkForHelpArticlsExists()
+    {
+        var result = page.assertExists('#page_name > a',
+            "Edit Link for Help article exists", page);
+        if(result.ack) {
+            page.click('#page_name > a');
+        } else {
+            l("Failed Test");
+        }
+        return result;
+    }
+];
\ No newline at end of file
diff --git a/views/elements/api_element.php b/views/elements/api_element.php
index 9c5d0fe..e5ef9e5 100644
--- a/views/elements/api_element.php
+++ b/views/elements/api_element.php
@@ -86,6 +86,7 @@ class ApiElement extends Element implements CrawlConstants
             $out_array['page_id'] = $data['PAGE_ID'];
             $out_array['page_name'] = $data['PAGE_NAME'];
             $out_array['page_title'] = $data['PAGE_TITLE'];
+            $out_array['page_view_title'] = $data['HEAD']['title'];
             $http_code = "200 OK";
         } else {
             if(!$logged_in) {
diff --git a/views/helpers/helpbutton_helper.php b/views/helpers/helpbutton_helper.php
index 4468a68..44d600e 100644
--- a/views/helpers/helpbutton_helper.php
+++ b/views/helpers/helpbutton_helper.php
@@ -116,13 +116,15 @@ class HelpbuttonHelper extends Helper
      * help to work. This gets executed if there is atleast one help button
      * rendered on the page. This is executed only once with the help of
      * "isHelpInitialized" variable.
-     *
+
      */
     private function setupHelpParams()
     {
         $this->isHelpInitialized = true;
         $this->localizationdata = "{" .
             'wiki_view_edit :"' . tl('wiki_view_edit') . '",' .
+            'wiki_view_not_available :"' . tl('wiki_view_not_available') .
+            '",' .
             'wiki_view_read :"' . tl('wiki_view_read') . '"' .
             "}";
         $this->back_params = "{";
-- 
2.1.2

