_binary From 98ba69c66a6536c2bbb119f54e1d46f653671f3b Mon Sep 17 00:00:00 2001
From: epinapala <epinapala@ebay.com>
Date: Sun, 7 Dec 2014 18:06:07 -0800
Subject: [PATCH] - Refactor PhantomJS UI Tests. - Added back all the tests
 with cleanedup code. - Throw an exception when PhantomJS is not found

Signed-off-by: epinapala <epinapala@ebay.com>
---
 tests/YioopPhantomRunner.php |  72 +++++++++++++++++
 tests/index.php              |  66 +++++++++++++++-
 tests/mobile_ui_tests.js     | 151 ++++++++++++++++++++++++++++++++++++
 tests/phantomjs_runner.js    | 178 +++++++++++++++++++++++++++++++++++++++++++
 tests/phantomjs_ui_test.php  | 128 +++++++++++++++++++++++++++++++
 tests/web_ui_tests.js        | 121 +++++++++++++++++++++++++++++
 6 files changed, 715 insertions(+), 1 deletion(-)
 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/tests/YioopPhantomRunner.php b/tests/YioopPhantomRunner.php
new file mode 100644
index 0000000..e549d1c
--- /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 Not found, Skipping UI Tests");
+        }
+    }
+    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..f96cb21 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,66 @@ 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 = "";
+    $debug = false;
+    $test_filename = "test.js";
+    if(isset($_GET['mode'])) {
+        if(isset($_GET['debug']) && $_GET['debug'] == "true") {
+            $debug = true;
+        }
+        $mode = htmlentities($_GET['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));
+                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();
+            }
+        }
+    } else {
+        $resp_code = "HTTP/1.1 500";
+    }
+    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..8271d3f
--- /dev/null
+++ b/tests/mobile_ui_tests.js
@@ -0,0 +1,151 @@
+/**
+ * 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);
+            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) {
+            debugger;
+            //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..b455b05
--- /dev/null
+++ b/tests/phantomjs_runner.js
@@ -0,0 +1,178 @@
+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 = "root",
+    yioop_password = "";
+//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(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();
+    }
+}, 4000);
\ 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..ea5115e
--- /dev/null
+++ b/tests/phantomjs_ui_test.php
@@ -0,0 +1,128 @@
+<?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()
+    {
+        ?>
+        <script type="text/javascript" src="../scripts/basic.js"></script>
+        <script type="text/javascript" src="../scripts/help.js"></script>
+        <div id="web-ui-test">
+        </div>
+        <div id="mobile-ui-test">
+        </div>
+        <script type="text/javascript">
+            runTests("web");
+            runTests("mobile");
+            function runTests(mode)
+            {
+                elt(mode + "-ui-test").innerHTML = '<div style="width:200px;">' +
+                'Loading ' + mode + '-UI test Results<marquee ' +
+                'behavior="alternate">.............' + '</marquee></div>';
+                getPageWithCallback("?activity=runPhantomTests&mode=" +
+                    mode, "json",
+                    function (data)
+                    {
+                        renderResults(data.results, mode)
+                    },
+                    function (status)
+                    {
+                        elt(mode + "-ui-test").innerHTML = "Unable to run " +
+                        mode + " UI tests.";
+                        updateResultsSummary(0,
+                            Object.keys(data.results).length,
+                            'red', mode);
+                    });
+            }
+            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);
+            }
+            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..36326de
--- /dev/null
+++ b/tests/web_ui_tests.js
@@ -0,0 +1,121 @@
+/**
+ * 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) {
+            debugger;
+            //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
-- 
2.1.2

