From: Mike Taylor Date: Mon, 17 Mar 2014 12:35:51 +0000 (+0000) Subject: Merge branch 'master' of ssh://git.indexdata.com/home/git/private/mkws X-Git-Tag: 1.0.0~1318 X-Git-Url: http://git.indexdata.com/?a=commitdiff_plain;h=2fd469f776110c2ba03d2a16d603bf32a2b01cdf;hp=e1bf63bcbe91b792c14a7223f96d28f77b1a5726;p=mkws-moved-to-github.git Merge branch 'master' of ssh://git.indexdata.com/home/git/private/mkws --- diff --git a/examples/htdocs/jasmine-local-popup.html b/examples/htdocs/jasmine-local-popup.html index fe1752d..8678a01 100644 --- a/examples/htdocs/jasmine-local-popup.html +++ b/examples/htdocs/jasmine-local-popup.html @@ -58,5 +58,7 @@ This directory contains an embryonic MasterKey Widget Set, based initially on "jsdemo" though now far removed from those beginnnings. [...] + +
This is the mkwsMOTD div
diff --git a/examples/htdocs/jasmine-popup.html b/examples/htdocs/jasmine-popup.html index 3aac786..f979e63 100644 --- a/examples/htdocs/jasmine-popup.html +++ b/examples/htdocs/jasmine-popup.html @@ -52,5 +52,6 @@ This directory contains an embryonic MasterKey Widget Set, based initially on "jsdemo" though now far removed from those beginnnings. [...] +
This is the mkwsMOTD div
diff --git a/examples/htdocs/jasmine-pp2.html b/examples/htdocs/jasmine-pp2.html index 66a03f3..18f3d40 100644 --- a/examples/htdocs/jasmine-pp2.html +++ b/examples/htdocs/jasmine-pp2.html @@ -14,7 +14,7 @@ "show_record_url": false // URLs not configured for pp2 }; - + @@ -85,5 +85,6 @@ +
This is the mkwsMOTD div
diff --git a/examples/htdocs/jasmine.html b/examples/htdocs/jasmine.html index 647be4b..4de368e 100644 --- a/examples/htdocs/jasmine.html +++ b/examples/htdocs/jasmine.html @@ -10,7 +10,7 @@ perpage_default: 10 }; - + @@ -46,6 +46,7 @@ +
This is the mkwsMOTD div
diff --git a/notes/developers.txt b/notes/developers.txt index 82729f2..02fb7bd 100644 --- a/notes/developers.txt +++ b/notes/developers.txt @@ -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 + + + + Structure of mkws.js -------------------- diff --git a/test/Makefile b/test/Makefile index 4cc9211..dba1540 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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" diff --git a/test/bomb.pl b/test/bomb.pl index d78d2cc..f4b4cec 100755 --- a/test/bomb.pl +++ b/test/bomb.pl @@ -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 index 0000000..27c465b --- /dev/null +++ b/test/phantom/run-jasmine.js @@ -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 '); + 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\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); +}); diff --git a/test/spec/mkws-pazpar2.js b/test/spec/mkws-pazpar2.js index 41ad086..2b1f250 100644 --- a/test/spec/mkws-pazpar2.js +++ b/test/spec/mkws-pazpar2.js @@ -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 */ diff --git a/tools/htdocs/Makefile b/tools/htdocs/Makefile index 81b8af3..bcb753d 100644 --- a/tools/htdocs/Makefile +++ b/tools/htdocs/Makefile @@ -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 diff --git a/tools/htdocs/mkws.js b/tools/htdocs/mkws.js index e047215..6f57f6b 100644 --- a/tools/htdocs/mkws.js +++ b/tools/htdocs/mkws.js @@ -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(',');