Merge branch 'master' of ssh://git.indexdata.com/home/git/private/mkws
authorMike Taylor <mike@indexdata.com>
Mon, 17 Mar 2014 12:35:51 +0000 (12:35 +0000)
committerMike Taylor <mike@indexdata.com>
Mon, 17 Mar 2014 12:35:51 +0000 (12:35 +0000)
examples/htdocs/jasmine-local-popup.html
examples/htdocs/jasmine-popup.html
examples/htdocs/jasmine-pp2.html
examples/htdocs/jasmine.html
notes/developers.txt
test/Makefile
test/bomb.pl
test/phantom/run-jasmine.js [new file with mode: 0644]
test/spec/mkws-pazpar2.js
tools/htdocs/Makefile
tools/htdocs/mkws.js

index fe1752d..8678a01 100644 (file)
@@ -58,5 +58,7 @@ This directory contains an embryonic MasterKey Widget Set, based
 initially on "jsdemo" though now far removed from those beginnnings.
 [...]
     </pre>
+
+    <div id="testMOTD"><div id="mkwsMOTD">This is the mkwsMOTD div</div></div>
   </body>
 </html>
index 3aac786..f979e63 100644 (file)
@@ -52,5 +52,6 @@ This directory contains an embryonic MasterKey Widget Set, based
 initially on "jsdemo" though now far removed from those beginnnings.
 [...]
     </pre>
+     <div id="testMOTD"><div id="mkwsMOTD">This is the mkwsMOTD div</div></div>
   </body>
 </html>
index 66a03f3..18f3d40 100644 (file)
@@ -14,7 +14,7 @@
        "show_record_url": false // URLs not configured for pp2
       };
     </script>
-    <script type="text/javascript" src="//code.jquery.com/jquery-1.6.4.min.js"></script>
+    <script type="text/javascript" src="//code.jquery.com/jquery-1.7.2.min.js"></script>
     <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
     <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
     <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
@@ -85,5 +85,6 @@
       </tr>
     </table>
 
+    <div id="testMOTD"><div id="mkwsMOTD">This is the mkwsMOTD div</div></div>
   </body>
 </html>
index 647be4b..4de368e 100644 (file)
@@ -10,7 +10,7 @@
          perpage_default: 10
       };
     </script>
-    <script type="text/javascript" src="//code.jquery.com/jquery-1.6.4.min.js"></script>
+    <script type="text/javascript" src="//code.jquery.com/jquery-1.7.2.min.js"></script> 
     <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
     <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
     <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
@@ -46,6 +46,7 @@
 
   </head>
   <body>
+    <div id="testMOTD"><div id="mkwsMOTD">This is the mkwsMOTD div</div></div>
     <table width="100%" border="0">
       <tr>
         <td>
index 82729f2..02fb7bd 100644 (file)
@@ -4,6 +4,26 @@ These notes are collected by Heikki, mostly from skype chats with Wolfram
 and Mike. I collected them for my own use, but I hope they will turn out
 to be helpful to anyone who needs to get started with mkws.
 
+Environment
+-----------
+
+apt-get install yui-compressor
+get nodejs, sudo make install, ln -s /usr/local/bin/npm ~/bin/npm if needed
+
+cd .../mkws; make check
+
+
+Apache
+------
+You need to set up a local apache. 
+  * add 'mkws' in /etc/hosts to point to 127.0.0.2
+  * symlinked .../mkws/tools/apache2/mkws-heikki to /etc/apache/sites-available
+  * a2ensite mkws-heikki
+  * a2enmod rewrite
+  * a2enmod headers
+  * service apache2 reload
+  * Check that your browser sees somethig in http://mkws/ and 
+    http://mkws/jasmine-popup.html. If need be, enable javascript etc. 
 
 Libraries
 ---------
@@ -54,9 +74,18 @@ Most (all?) code work happens in mkws.js.
 Unit tests
 ----------
 
-if you want understand the test than you can look at mkws/test/spec/mkws-config.js
+Tests are based on jasmine. a general description of jasmine is on
+http://jasmine.github.io/1.3/introduction.html
+
+If you want understand the test than you can look at mkws/test/spec/mkws-config.js
 and mkws/test/spec/mkws-pazpar2.js . See also mkws/test/README.txt
 
+The test scripts are included from the test page, for example
+mkws/examples/htdocs/jasmine-popup.html has 
+<script type="text/javascript" src="test/spec/mkws-pazpar2.js"></script>
+
+
+
 
 Structure of mkws.js
 --------------------
index 4cc9211..dba1540 100644 (file)
@@ -4,13 +4,14 @@
 MIKE = PATH=$$PATH:/usr/local/lib/node-v0.10.24-linux-x64/bin
 
 PHANTOMJS_URL=https://mkws-dev.indexdata.com/jasmine-popup.html        
-PHANTOMJS_TIMEOUT=12   
+PHANTOMJS_TIMEOUT=16
 
 NPM_INSTALL_FLAGS=-q
 JASMINE_NODE=  ./node_modules/jasmine-node/bin/jasmine-node
-PHANTOMJS=     ./bomb.pl ./node_modules/phantomjs/bin/phantomjs
+PHANTOMJS=     ./node_modules/phantomjs/bin/phantomjs
 IMAGES=        ./images
 SCREENSHOT_WIDTH=      360 480 640 768 1024 1200 1440 2048
+PERL_SCRIPTS=  bomb.pl
 
 all: check
 
@@ -32,8 +33,8 @@ test: check
 terse:
        $(MIKE) jasmine-node --noColor --captureExceptions --forceexit spec
 
-phantomjs:
-       ${PHANTOMJS} phantom/evaluate.js ${PHANTOMJS_URL} ${PHANTOMJS_TIMEOUT}
+phantomjs p:
+       ./bomb.pl --timeout="${PHANTOMJS_TIMEOUT}.5" ${PHANTOMJS} phantom/run-jasmine.js ${PHANTOMJS_URL} ${PHANTOMJS_TIMEOUT}
 
 screenshot:
        ${PHANTOMJS} phantom/screenshot.js ${PHANTOMJS_URL} ${IMAGES}/screenshot.png 1200 1000
@@ -55,13 +56,19 @@ jsbeautifier jsb indent:
          jsbeautifier -j $$i > $@.tmp && mv -f $@.tmp $$i; \
        done
 
+perltidy:
+       @ls ${PERL_SCRIPTS} | xargs -n1 -P16 perl -c 2>/dev/null
+       @ls ${PERL_SCRIPTS} | xargs -n2 -P8 perltidy -b
+
+
 node_modules node-modules:
        npm install ${NPM_INSTALL_FLAGS}
 
 help:
        @echo "make [ all | check | clean | distclean ]"
        @echo "     [ phantomjs | screenshot ]"
-       @echo "     [ jsbeautifier | node-modules ]"
+       @echo "     [ jsbeautifier | perltidy ]"
+       @echo "     [ node-modules ]"
        @echo ""
-       @echo "DEBUG=1 make phantomjs PHANTOMJS_TIMEOUT=8 PHANTOM_URL=https://mkws-dev.indexdata.com/jasmine-popup.html"
+       @echo "DEBUG=1 make phantomjs PHANTOMJS_TIMEOUT=8 PHANTOMJS_URL=https://mkws-dev.indexdata.com/jasmine-popup.html"
 
index d78d2cc..f4b4cec 100755 (executable)
@@ -57,7 +57,7 @@ EOF
 GetOptions(
     "help"      => \$help,
     "debug=i"   => \$debug,
-    "timeout=i" => \$timeout,
+    "timeout=f" => \$timeout,
 ) or die usage;
 
 my @system = @ARGV;
diff --git a/test/phantom/run-jasmine.js b/test/phantom/run-jasmine.js
new file mode 100644 (file)
index 0000000..27c465b
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+    Fetch a mkws/jasmine based page into node.js, evaluate the page and check if test status
+    This should make it possible to run the test on the command line in jenkins.  e.g.:
+
+      phantomjs evaluate.js https://mkws-dev.indexdata.com/jasmine-local-popup.html
+*/
+
+var page = require('webpage').create(),
+    system = require('system');
+
+if (system.args.length === 1) {
+    console.log('Usage: screenshot.js <some URL>');
+    phantom.exit();
+}
+var url = system.args[1];
+
+var run_time = 8; // poll up to seconds
+if (system.args[2] && parseFloat(system.args[2]) > 0) {
+    run_time = parseFloat(system.args[2]);
+}
+
+page.viewportSize = {
+    width: 1200,
+    height: 1000
+};
+
+// 0: silent, 1: some infos,  2: display console.log() output
+var debug = 2;
+if (typeof system.env['DEBUG'] != 'undefined' && parseInt(system.env['DEBUG']) != NaN) {
+    debug = system.env['DEBUG'];
+    if (debug > 0) console.log("reset debug level to: " + debug);
+}
+
+/************************/
+
+function wait_for_jasmine(checkFx, readyFx, failFx, timeout) {
+    var max_timeout = timeout ? timeout : run_time * 1000,
+        start = new Date().getTime(),
+        result, condition = false;
+
+    var interval = setInterval(function () {
+        if (debug == 1) console.log(".");
+
+        // done
+        if (condition) {
+            clearInterval(interval);
+            result.time = (new Date().getTime() - start);
+            result.failed ? failFx(result) : readyFx(result);
+            phantom.exit(result.failed == 0 ? 0 : 2);
+        }
+
+        // timeout
+        else if (new Date().getTime() - start >= max_timeout) {
+            result.time = (new Date().getTime() - start);
+            failFx(result);
+            phantom.exit(1);
+        }
+
+        // checking
+        else {
+            result = checkFx();
+            if (result) condition = result.done;
+        }
+
+    }, 500); //< repeat check every N ms
+};
+
+function dump_html(file) {
+    // not yet implemented
+    var spawn = require('child_process').spawn,
+        lynx = spawn("lynx", ["-nolist", "-dump", file]);
+
+    lynx.stdout.on('data', function (data) {
+        console.log('lynx >> ' + data);
+    });
+
+    // lynx.stderr.on('data', function (data) { console.log('stderr: ' + data); });
+    // lynx.on('close', function (code) { console.log('child process exited with code ' + code) });
+};
+
+// redirect webkit console.log() output
+page.onConsoleMessage = function (message) {
+    if (debug >= 2) console.log(message);
+};
+
+// cat webkit alert()
+page.onAlert = function (msg) {
+    console.log("Alert: " + msg);
+};
+
+// display HTTP errors
+page.onResourceError = function (resourceError) {
+    // console.log('phantomjs error code: ' + resourceError.errorCode);
+    console.log(resourceError.errorString);
+    phantom.exit(3);
+};
+
+page.open(url, function (status) {
+    if (debug >= 1) console.log("fetch " + url + " with status: " + status);
+
+    if (status != 'success') {
+        console.log("Failed to fetch page, give up. Network error?");
+        phantom.exit(1);
+    }
+
+    if (debug >= 1) console.log("polling MKWS jasmine test status for " + run_time + " seconds");
+
+
+    var exit = wait_for_jasmine(function () {
+        return page.evaluate(function () {
+            if (!window || !window.$ || !window.mkws) {
+                console.log("No window object found");
+                return false;
+            }
+
+            var $ = window.$;
+            var error_msg = [""];
+            var passing = $(".passingAlert").text() || window.$(".failingAlert").text();
+
+            // extract failed tests
+            var list = $('.results > #details > .specDetail.failed');
+            if (list && list.length > 0) {
+                error_msg.push("==> " + list.length + ' test(s) FAILED:');
+                for (i = 0; i < list.length; ++i) {
+                    var el = list[i],
+                        desc = el.querySelector('.description'),
+                        msg = el.querySelector('.resultMessage.fail');
+                    error_msg.push($(desc).text());
+                    error_msg.push($(msg).text());
+                }
+            }
+
+            return {
+                mkws: window.mkws,
+                done: $('.symbolSummary .pending').length == 0,
+                html: $("html").html(),
+                duration: $(".duration").text(),
+                error_msg: error_msg,
+                failed: list.length,
+                passing: passing
+            };
+        })
+    },
+
+    function (result) {
+        if (debug < 1) return;
+
+        console.log("");
+        console.log("MKWS tests are successfully done in " + result.time / 1000 + " seconds. Hooray!");
+        console.log("jasmine duration: " + result.duration);
+        console.log("jasmine passing: " + result.passing);
+    },
+
+    function (result) {
+        var error_png = "./mkws-error.png";
+        var error_html = "./mkws-error.html";
+
+        var html = result.html + "\n\n<!-- mkws: " + JSON.stringify(result.mkws) + " -->\n";
+        var fs = require('fs');
+        fs.write(error_html, html, "wb");
+        dump_html(error_html);
+
+        console.log("MKWS tests failed after " + result.time / 1000 + " seconds");
+        console.log(result.error_msg.join("\n"));
+        console.log("keep screenshot in '" + error_png + "'");
+        page.render(error_png);
+
+        console.log("keep html DOM in '" + error_html + "'");
+        // console.log("you may run: lynx -nolist -dump " + error_html);
+    }, run_time * 1000);
+});
index 41ad086..2b1f250 100644 (file)
@@ -6,7 +6,7 @@
 
 // get references from mkws.js, lazy evaluation
 var debug = function (text) {
-        mkws.debug(text)
+        mkws.debug("Jasmine: " + text)
     }
 
     // Define empty mkws_config for simple applications that don't define it.
@@ -75,6 +75,25 @@ describe("Init jasmine config", function () {
     });
 });
 
+describe("Check MOTD before search", function () {
+    // Check that the MOTD has been moved into its container, and
+    // is visible before the search.
+    // the mkwsMOTD div was originally inside a testMOTD div, which should
+    // now be emtpy
+    // Note that the testMOTD is a regular div, and uses #testMOTD,
+    // since the automagic class-making does not apply to it.
+    it("MOTD is hidden", function () {
+        expect($(".mkwsMOTD").length).toBe(1);
+        expect($("#testMOTD").length).toBe(1);
+        expect($("#testMOTD").text()).toMatch("^ *$");
+    });
+
+    it("mkwsMOTDContainer has received the text", function () {
+        expect($(".mkwsMOTDContainer").length).toBe(1);
+        expect($(".mkwsMOTDContainer").text()).toMatch(/MOTD/);
+    });
+});
+
 describe("Check pazpar2 search", function () {
     it("pazpar2 was successfully initialized", function () {
         expect(mkws_config.error).toBe(undefined);
@@ -105,12 +124,20 @@ describe("Check pazpar2 search", function () {
 
         runs(function () {
             debug("Click on submit button");
-            var click = $("input.mkwsButton").trigger("click");
-            expect(click.length).toBe(1);
+            $("input.mkwsButton").trigger("click");
         })
     });
 });
 
+describe("Check MOTD after search", function () {
+    it("MOTD is hidden", function () {
+        expect($(".mkwsMOTD").length).toBe(1);
+        expect($(".mkwsMOTD").is(":hidden")).toBe(true);
+        debug("motd t=" + $(".mkwsMOTD").text());
+        debug("motd v=" + $(".mkwsMOTD").is(":visible"));
+    });
+});
+
 
 /*
  * This part runs in background. It should be rewritten with
@@ -125,10 +152,7 @@ describe("Check pazpar2 navigation", function () {
         function my_click(id, time) {
             setTimeout(function () {
                 debug("trigger click on id: " + id);
-                var click = $(id).trigger("click");
-
-                debug("next/prev: " + id + " click is success: " + click.length);
-                expect(click.length).toBe(1);
+                $(id).trigger("click");
             }, time * jasmine_config.second);
         }
 
@@ -161,11 +185,9 @@ describe("Check pazpar2 hit counter", function () {
 
         waitsFor(function () {
             hits = get_hit_counter();
-
             return hits > expected_hits;
         }, "Expect " + expected_hits + " hits", max_time * jasmine_config.second);
 
-
         runs(function () {
             debug("mkws pager found records: '" + hits + "'");
             expect($(".mkwsPager").length).toBe(1);
@@ -184,7 +206,6 @@ describe("Check Termlist", function () {
             return $("div.mkwsFacetSources").length == 1 ? true : false;
         }, "check for facet sources", 4 * jasmine_config.second);
 
-
         // everything displayed?
         runs(function () {
             var sources = $("div.mkwsFacetSources");
@@ -217,6 +238,7 @@ describe("Check Termlist", function () {
         var author_number = 2; // 2=first author
         // do not click on author with numbers, e.g.: "Bower, James M. Beeman, David, 1938-"
         // do not click on author names without a comma, e.g.: "Joe Barbara"
+        // because searching on such authors won't find anything.
         var terms = $("div.mkwsFacetAuthors div.term a");
         for (var i = 0; i < terms.length; i++) {
             var term = $(terms[i]).text();
@@ -227,10 +249,13 @@ describe("Check Termlist", function () {
                 break;
             }
         }
+        if ($("div.mkwsFacetAuthors div.term:nth-child(" + author_number + ") a").text().length == 0) {
+            debug("No good authors found. Not clicking on the bad ones");
+            return;
+        }
 
-        var click = $("div.mkwsFacetAuthors div.term:nth-child(" + author_number + ") a").trigger("click");
-        debug("limit author click is success: " + click.length);
-        expect(click.length).toBe(1);
+        debug("Clicking on author (" + author_number + ") " + $("div.mkwsFacetAuthors div.term:nth-child(" + author_number + ") a").text());
+        $("div.mkwsFacetAuthors div.term:nth-child(" + author_number + ") a").trigger("click");
 
         waitsFor(function () {
             return get_hit_counter() < hits_all_targets ? true : false;
@@ -257,10 +282,12 @@ describe("Check Termlist", function () {
                 break;
             }
         }
+        if ($("div.mkwsFacetSources div.term:nth-child(" + source_number + ") a").text().length == 0) {
+            debug("No good source found. Not clicking on the bad ones");
+            return;
+        }
 
-        var click = $("div.mkwsFacetSources div.term:nth-child(" + source_number + ") a").trigger("click");
-        debug("limit source click " + (source_number - 1) + " is success: " + click.length);
-        expect(click.length).toBe(1);
+        $("div.mkwsFacetSources div.term:nth-child(" + source_number + ") a").trigger("click");
 
         waitsFor(function () {
             if ($("div.mkwsNavi").length && $("div.mkwsNavi").text().match(/(Source|datenquelle|kilder): /i)) {
@@ -284,6 +311,29 @@ describe("Check Termlist", function () {
     });
 });
 
+
+describe("Check record list", function () {
+    it("got a record", function () {
+        var linkaddr = "div.mkwsRecords div.record:nth-child(1) a";
+        var waitcount = 0;
+
+        // wait for new records
+        $("div.mkwsRecords").bind("DOMSubtreeModified propertychange", function () {
+            waitcount++;
+            debug("DOM div.mkwsRecords changed");
+        });
+
+        waitsFor(function () {
+            return waitcount > 0 && $(linkaddr).length > 0;
+        }, "wait until we see a new record", 2.2 * jasmine_config.second);
+
+        runs(function () {
+            expect(waitcount).toBeGreaterThan(0);
+            $("div.mkwsRecords").unbind("DOMSubtreeModified");
+        });
+    });
+});
+
 describe("Show record", function () {
     var record_number = 1; // the Nth record in hit list
     it("show record author", function () {
@@ -309,14 +359,16 @@ describe("Show record", function () {
             return;
         }
 
-        var urls = $("div#mkwsRecords div.record:nth-child(" + record_number + ") div table tbody tr td a");
+        var urls = $("div.mkwsRecords div.record:nth-child(" + record_number + ") div table tbody tr td a");
         debug("number of extracted URL from record: " + urls.length);
+        // expect(urls.length).toBeGreaterThan(0); // LoC has records without links
         for (var i = 0; i < urls.length; i++) {
             var url = $(urls[i]);
-            debug("URL: " + url.attr('href'));
+            debug("URL: " + url.attr('href') + " text: " + url.text());
+
             expect(url.attr('href')).not.toBe(null);
             expect(url.attr('href')).toMatch(/^https?:\/\/[a-z0-9]+\.[0-9a-z].*\//i);
-            expect(url.attr('href')).toBe(url.text());
+            expect(url.text()).not.toBe("");
         }
     });
 });
@@ -330,9 +382,7 @@ describe("Check switch menu Records/Targets", function () {
     });
 
     it("switch to target view", function () {
-        var click = $("div.mkwsSwitch").children('a').eq(1).trigger("click");
-        debug("target view click is success: " + click.length);
-        expect(click.length).toBe(1);
+        $("div.mkwsSwitch").children('a').eq(1).trigger("click");
 
         // now the target table must be visible
         expect($("div.mkwsBytarget").is(":visible")).toBe(true);
@@ -351,9 +401,7 @@ describe("Check switch menu Records/Targets", function () {
     });
 
     it("switch back to record view", function () {
-        var click = $("div.mkwsSwitch").children('a').eq(0).trigger("click");
-        debug("record view click is success: " + click.length);
-        expect(click.length).toBe(1);
+        $("div.mkwsSwitch").children('a').eq(0).trigger("click");
 
         // now the target table must be visible
         expect($("div.mkwsBytarget").is(":visible")).toBe(false);
@@ -376,9 +424,7 @@ describe("Check status client counter", function () {
             } else {
                 return false;
             }
-
         }, "wait for Active clients: 0/1", 4 * jasmine_config.second);
-
 /*
         runs(function () {
             var clients = $("div#mkwsStat span.clients");
@@ -386,9 +432,7 @@ describe("Check status client counter", function () {
             expect(clients.text()).toEqual("0/1");
         });
         */
-
     });
-
 });
 
 /* done */
index 81b8af3..bcb753d 100644 (file)
@@ -10,13 +10,11 @@ JQUERY_URL= http://code.jquery.com/jquery-1.10.0.min.js
 #JQUERY_URL=   http://code.jquery.com/jquery-1.9.1.min.js
 #JQUERY_URL=   http://code.jquery.com/jquery-1.8.3.min.js
 #JQUERY_URL=   http://code.jquery.com/jquery-1.7.2.min.js
-#JQUERY_URL=   http://code.jquery.com/jquery-1.6.4.min.js
-#JQUERY_URL=   http://code.jquery.com/jquery-1.4.4.min.js
 
 JQUERY_UI_URL= http://code.jquery.com/ui/1.10.3/jquery-ui.js
 #JQUERY_UI_URL=        http://code.jquery.com/ui/1.8.0/jquery-ui.min.js
 JQUERY_JSON_URL= https://jquery-json.googlecode.com/files/jquery.json-2.4.js
-HANDLEBARS_URL=        http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.1.2.js -o $@
+HANDLEBARS_URL=        http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.1.2.js
 PP2_URL=       http://git.indexdata.com/?p=pazpar2.git;a=blob_plain;f=js/pz2.js;hb=HEAD
 VERSION = $(shell tr -d '\012' < VERSION)
 
@@ -71,10 +69,12 @@ ${JQUERY_FILE}:
        rm -f $@.new
 
 ${JQUERY_JSON_FILE}:
-       curl -sSf ${JQUERY_JSON_URL} -o $@
+       curl -sSf ${JQUERY_JSON_URL} -o $@.tmp
+       mv -f $@.tmp $@
 
 ${HANDLEBARS_FILE}:
-       curl -sSf ${HANDLEBARS_URL} -o $@
+       curl -sSf ${HANDLEBARS_URL} -o $@.tmp
+       mv -f $@.tmp $@
 
 ${PP2_FILE}:
        curl -sSf "${PP2_URL}" -o $@.tmp
index e047215..6f57f6b 100644 (file)
@@ -1019,7 +1019,7 @@ function team($, teamName) {
     function findnode(selector, teamName) {
        teamName = teamName || m_teamName;
 
-       selector = selector.split(',').map(function(s) {
+       selector = $.map(selector.split(','), function(s, i) {
            return s + '.mkwsTeam_' + teamName;
        }).join(',');