diff options
author | Benjamin Jones <bjones@galois.com> | 2013-06-13 15:21:58 -0700 |
---|---|---|
committer | Benjamin Jones <bjones@galois.com> | 2013-06-13 15:21:58 -0700 |
commit | 032f5c5c7925f8edc4eca95b03f6fb29303b5dfb (patch) | |
tree | 3057ede1d0e15bdaab4886904da1c78e8cb41452 | |
parent | 296ca3e5a2166965df546e1db4250366403a7d47 (diff) | |
parent | 050b16e076ef08f57d9bbe19109b4f3529f78ade (diff) |
Merge branch 'master' of src.galois.com:/srv/git/FiveUI
33 files changed, 424 insertions, 372 deletions
diff --git a/src/batchtools/headless/src/main/java/com/galois/fiveui/BatchRunner.java b/src/batchtools/headless/src/main/java/com/galois/fiveui/BatchRunner.java index d953239..a57a9eb 100644 --- a/src/batchtools/headless/src/main/java/com/galois/fiveui/BatchRunner.java +++ b/src/batchtools/headless/src/main/java/com/galois/fiveui/BatchRunner.java @@ -56,10 +56,7 @@ public class BatchRunner { // Hard coded JS files, relative to the FiveUI root directory. private static final String J_QUERY_JS = "lib/jquery/jquery.js"; - private static final String PRELUDE_JS = "fiveui/injected/prelude.js"; private static final String MD5_JS = "lib/md5.js"; - private static final String JQUERY_PLUGIN_JS = "fiveui/injected/jquery-plugins.js"; - private static final String SEL_INJECTED_COMPUTE_JS = "selenium/selenium-injected-compute.js"; private static Logger logger = Logger.getLogger("com.galois.fiveui.BatchRunner"); @@ -197,9 +194,9 @@ public class BatchRunner { ", ruleSet=\"" + ruleSet.getName() + "\""; logger.debug("runRule: " + state); - contentScript += "return fiveui.selPort.query(type='ReportProblem')"; + contentScript += "return fiveui.selPort.query('ReportProblem')"; Object res =_exe.executeScript(contentScript); - + logger.debug("runRule: " + state); if (res.getClass() == String.class) { // we received an error via the expected mechanisms: logger.error("exception running rule: " + res); @@ -224,13 +221,14 @@ public class BatchRunner { // // Probably we should just pass along the Map<String, String> // and let the reporter deal with it. - String problemAsHTML = "Rule Name: " + problem.get("name") + " / " - + "Rule Desc: " + problem.get("descr") + " / " + String ruleName = problem.get("name"); + String ruleDescr = problem.get("descr"); + String problemAsHTML = "Rule Name: " + ruleName + " / " + + "Rule Desc: " + ruleDescr + " / " + "XPath: " + problem.get("xpath"); builder.add(new Result(ResType.Error, _driver, "", _driver.getCurrentUrl(), - ruleSet.getName(), - ruleSet.getDescription(), + ruleName, ruleDescr, problemAsHTML)); } @@ -268,11 +266,11 @@ public class BatchRunner { injected += Utils.readFile(_root + JS_SRC_ROOT + "fiveui/injected/jquery-plugins.js"); injected += Utils.readFile(_root + JS_SRC_ROOT + "selenium/selenium-injected-compute.js"); injected += Utils.readFile(_root + JS_SRC_ROOT + "fiveui/injected/compute.js"); + + String ruleStrList = ruleSet.toJS(); + String cmd = "fiveui.selPort.send('ForceEval', " + ruleStrList + ");"; - //injected += "return fiveui.selPort.send('SetRules', " + ruleSet + ");"; - injected += "fiveui.selPort.send('SetRules', " + ruleSet + ");"; - - return injected; + return injected + cmd; } /** diff --git a/src/batchtools/headless/src/test/java/com/galois/fiveui/BatchExecutorTest.java b/src/batchtools/headless/src/test/java/com/galois/fiveui/BatchExecutorTest.java index 861391f..665876e 100644 --- a/src/batchtools/headless/src/test/java/com/galois/fiveui/BatchExecutorTest.java +++ b/src/batchtools/headless/src/test/java/com/galois/fiveui/BatchExecutorTest.java @@ -46,7 +46,7 @@ import com.google.common.collect.ImmutableList; public class BatchExecutorTest { private static final String RUN_DESCRIPTION_DIR = "src/test/resources/runDescriptions/"; - private static Logger logger = Logger.getLogger("com.galois.fiveui.BatchExecutorTest"); + private static Logger logger = Logger.getLogger(BatchExecutorTest.class); private static NanoHTTPD httpServer; @BeforeClass diff --git a/src/batchtools/headless/src/test/java/com/galois/fiveui/HeadlessRunnerTest.java b/src/batchtools/headless/src/test/java/com/galois/fiveui/HeadlessRunnerTest.java new file mode 100644 index 0000000..f5f09dc --- /dev/null +++ b/src/batchtools/headless/src/test/java/com/galois/fiveui/HeadlessRunnerTest.java @@ -0,0 +1,154 @@ +/** + * + * Copyright (c) 2009-2013, + * + * Galois, Inc. (creswick) + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The names of the contributors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * + */ +package com.galois.fiveui; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.BindException; +import java.util.Collection; +import java.util.List; + +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +/** + * @author creswick + * + */ +@RunWith(Parameterized.class) +public class HeadlessRunnerTest { + private static Logger logger = + Logger.getLogger(HeadlessRunnerTest.class); + + static { + BasicConfigurator.configure(); + logger.setLevel(Level.DEBUG); + Logger root = Logger.getRootLogger(); + root.setLevel(Level.ERROR); + } + + /** + * Set up the tests via the parameterized runner: + * + * @return + * @throws Throwable + */ + @Parameters(name = "{0} {1}") + public static Collection<Object[]> buildtests() throws Throwable { + + List<Object[]> tests = Lists.newArrayList(); + + // webRoot, startingUrl, runDescr, oracle + Object[][] rawTests = new Object[][] { + { "../../../exampleData/sites/", + "src/test/resources/runDescriptions/headlessSample1.json", + ImmutableList.of(Result.error(null, "Headings are capitalized"), + Result.error(null, "Headings are capitalized"), + Result.error(null, "Disallow Empty Headers")) + }, + { + "../../../exampleData/sites/", + "src/test/resources/runDescriptions/headlessSample3.json", + ImmutableList.of(Result.error(null, "Generate Errors")) + } + }; + + for (Object[] descr : rawTests) { + tests.add(descr); + } + + return tests; + } + + private final String _webRoot; + + private final String _runDescrPath; + + private final ImmutableList<Result> _oracle; + + private NanoHTTPD _httpServer; + + public HeadlessRunnerTest(String webRoot, String runDescrPath, + ImmutableList<Result> oracle) { + super(); + this._webRoot = webRoot; + this._runDescrPath = runDescrPath; + this._oracle = oracle; + } + + @Before + public void setup() { + File dir = new File(_webRoot); + int port = 8000; + logger.info("Starting NanoHTTPD webserver in " + dir.getAbsolutePath() + " on port "+port); + try { + _httpServer = new NanoHTTPD(port, dir); + } catch (BindException e) { + logger.debug("assuming that local web server is already running"); + } catch (IOException e1) { + e1.printStackTrace(); + Assert.assertTrue("failed to start NanoHTTPD in current directory " + dir.getAbsolutePath(), false); + } + } + + @After + public void teardown() { + _httpServer.stop(); + } + + @Test + public void test() throws FileNotFoundException { + HeadlessRunDescription descr = + HeadlessRunDescription.parse(_runDescrPath); + BatchRunner runner = new BatchRunner(); + ImmutableList<Result> actual = runner.runHeadless(descr); + Assert.assertEquals("Expected results differ from actual.", _oracle, actual); + } + +} diff --git a/src/batchtools/headless/src/test/java/com/galois/fiveui/HeadlessTest.java b/src/batchtools/headless/src/test/java/com/galois/fiveui/RunDescriptionParseTest.java index 6db72ba..e19d4dc 100644 --- a/src/batchtools/headless/src/test/java/com/galois/fiveui/HeadlessTest.java +++ b/src/batchtools/headless/src/test/java/com/galois/fiveui/RunDescriptionParseTest.java @@ -19,7 +19,7 @@ import com.google.common.collect.ImmutableList; * @author bjones * */ -public class HeadlessTest { +public class RunDescriptionParseTest { private static final String RUN_DESCRIPTION_DIR = "src/test/resources/runDescriptions/"; private static Logger logger = Logger.getLogger("com.galois.fiveui.HeadlessTest"); @@ -67,7 +67,7 @@ public class HeadlessTest { "../../../../../rsTester/src/test/resources/ruleSets/headingGuidelines.json"; RuleSet ruleSetOracle = RuleSet.parseFile(ruleSetLoc); HeadlessAtom headlessAtomOracle = - new HeadlessAtom("http://testhost", ruleSetOracle); + new HeadlessAtom("http://localhost:8000/headings.html", ruleSetOracle); HeadlessRunDescription oracle = new HeadlessRunDescription(ImmutableList.of(headlessAtomOracle)); @@ -87,10 +87,10 @@ public class HeadlessTest { String jsonFileName = RUN_DESCRIPTION_DIR + "headlessSample2.json"; // manually build first HeadlessAtom String ruleSetLoc1 = - RUN_DESCRIPTION_DIR + "../ruleSets/emptyRuleSet.json"; + RUN_DESCRIPTION_DIR + "../ruleSets/alwaysErrors.json"; RuleSet ruleSetOracle1 = RuleSet.parseFile(ruleSetLoc1); HeadlessAtom headlessAtomOracle1 = - new HeadlessAtom("http://testhost1", ruleSetOracle1); + new HeadlessAtom("http://localhost:8000/headings.html", ruleSetOracle1); // manually build second HeadlessAtom String ruleSetLoc2 = RUN_DESCRIPTION_DIR + "../../../../../rsTester/src/test/resources/ruleSets/headingGuidelines.json"; diff --git a/src/batchtools/headless/src/test/resources/ruleSets/alwaysErrors.json b/src/batchtools/headless/src/test/resources/ruleSets/alwaysErrors.json new file mode 100644 index 0000000..f602290 --- /dev/null +++ b/src/batchtools/headless/src/test/resources/ruleSets/alwaysErrors.json @@ -0,0 +1,4 @@ +{ "name": "A rule set that always generates an error." +, "description": "Testing rule set; always errors." +, "rules": [ "makeError.js" ] +}
\ No newline at end of file diff --git a/src/batchtools/headless/src/test/resources/ruleSets/makeError.js b/src/batchtools/headless/src/test/resources/ruleSets/makeError.js new file mode 100644 index 0000000..d1705d3 --- /dev/null +++ b/src/batchtools/headless/src/test/resources/ruleSets/makeError.js @@ -0,0 +1,6 @@ +exports.name = "Generate Errors"; +exports.description = "Always report an error"; +exports.rule = function() { + var r = this; + r.report("error", null); +};
\ No newline at end of file diff --git a/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample1.json b/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample1.json index 8e4d6c6..6e67969 100644 --- a/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample1.json +++ b/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample1.json @@ -1,4 +1,4 @@ [{ - 'url': 'http://testhost', + 'url': 'http://localhost:8000/headings.html', 'ruleSet': '../../../../../rsTester/src/test/resources/ruleSets/headingGuidelines.json' }] diff --git a/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample2.json b/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample2.json index b5ea2ee..bd9adaf 100644 --- a/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample2.json +++ b/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample2.json @@ -1,9 +1,10 @@ [ { - 'url': 'http://testhost1', - 'ruleSet': '../ruleSets/emptyRuleSet.json' + "url": "http://localhost:8000/headings.html", + "ruleSet": "../ruleSets/alwaysErrors.json" }, { 'url': 'http://testhost2', 'ruleSet': '../../../../../rsTester/src/test/resources/ruleSets/headingGuidelines.json' -}] +} +] diff --git a/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample3.json b/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample3.json new file mode 100644 index 0000000..5be7735 --- /dev/null +++ b/src/batchtools/headless/src/test/resources/runDescriptions/headlessSample3.json @@ -0,0 +1,6 @@ +[ +{ + "url": "http://localhost:8000/headings.html", + "ruleSet": "../ruleSets/alwaysErrors.json" +} +] diff --git a/src/batchtools/rsTester/pom.xml b/src/batchtools/rsTester/pom.xml index 95d27e3..6034b4c 100644 --- a/src/batchtools/rsTester/pom.xml +++ b/src/batchtools/rsTester/pom.xml @@ -85,6 +85,11 @@ <dependencies> <dependency> + <groupId>org.mozilla</groupId> + <artifactId>rhino</artifactId> + <version>1.7R3</version> + </dependency> + <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.1</version> diff --git a/src/batchtools/rsTester/src/main/java/com/galois/fiveui/Result.java b/src/batchtools/rsTester/src/main/java/com/galois/fiveui/Result.java index 6543476..a367d1d 100644 --- a/src/batchtools/rsTester/src/main/java/com/galois/fiveui/Result.java +++ b/src/batchtools/rsTester/src/main/java/com/galois/fiveui/Result.java @@ -33,7 +33,7 @@ public class Result { private ResType _type; private String _msg; - private WebDriver _driver; + private transient WebDriver _driver; private String _url; private String _ruleName; private String _ruleDesc; @@ -66,13 +66,13 @@ public class Result { * An information restricted version of the other public constructor. This * constructor does not include URL, rule, or problem information. */ - public Result(ResType type, WebDriver driver, String msg) { + public Result(ResType type, WebDriver driver, String name) { super(); _type = type; - _msg = msg; + _msg = ""; _driver = driver; _url = ""; - _ruleName = ""; + _ruleName = name; _ruleDesc = ""; _prob = ""; } @@ -86,44 +86,44 @@ public class Result { * Result constructor, returns an "exception" type result. * * @param driver WebDriver the result came from - * @param res description of the result + * @param name name of the rule * @return a Result object */ - public static Result exception(WebDriver driver, String res) { - return new Result(ResType.Exception, driver, res); + public static Result exception(WebDriver driver, String name) { + return new Result(ResType.Exception, driver, name); } /** * Result constructor, returns a "pass" type result. * * @param driver WebDriver the result came from - * @param res description of the result + * @param name name of the rule * @return a Result object */ - public static Result pass(WebDriver driver, String res) { - return new Result(ResType.Pass, driver, res); + public static Result pass(WebDriver driver, String name) { + return new Result(ResType.Pass, driver, name); } /** * Result constructor, returns an "error" type result. * * @param driver WebDriver the result came from - * @param res description of the result + * @param name name of the rule * @return a Result object */ - public static Result error(WebDriver driver, String res) { - return new Result(ResType.Error, driver, res); + public static Result error(WebDriver driver, String name) { + return new Result(ResType.Error, driver, name); } /** * Result constructor, returns a "warning" type result. * * @param driver WebDriver the result came from - * @param res description of the result + * @param name name of the rule * @return a Result object */ - public static Result warning(WebDriver driver, String res) { - return new Result(ResType.Warning, driver, res); + public static Result warning(WebDriver driver, String name) { + return new Result(ResType.Warning, driver, name); } /********************************************************************************** @@ -158,16 +158,50 @@ public class Result { return _prob; } - /** + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((_ruleName == null) ? 0 : _ruleName.hashCode()); + result = prime * result + ((_type == null) ? 0 : _type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Result other = (Result) obj; + if (_ruleName == null) { + if (other._ruleName != null) + return false; + } else if (!_ruleName.equals(other._ruleName)) + return false; + if (_type != other._type) + return false; + return true; + } + + /** * Stringify the result, returning the type, driver name, and * full description. */ @Override public String toString() { - return getType() + " - " + _driver.toString().split(":")[0] + ": " + String drvStr = "Null"; + if (null != _driver){ + drvStr = _driver.toString(); + } + + return getType() + " - " + drvStr.split(":")[0] + ": " + _msg + "\n" + " |\\- " + _url + "\n" - + " |\\_ " + _ruleName + "\n" + + " |\\_ " + _ruleName + "\n" + " |\\_ " + _ruleDesc + "\n" + " \\_ " + _prob; } diff --git a/src/batchtools/rsTester/src/main/java/com/galois/fiveui/Rule.java b/src/batchtools/rsTester/src/main/java/com/galois/fiveui/Rule.java index 3f420cb..7d05958 100644 --- a/src/batchtools/rsTester/src/main/java/com/galois/fiveui/Rule.java +++ b/src/batchtools/rsTester/src/main/java/com/galois/fiveui/Rule.java @@ -17,9 +17,11 @@ */ package com.galois.fiveui; -import java.util.HashMap; +import java.text.ParseException; -import org.openqa.selenium.htmlunit.HtmlUnitDriver; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.Scriptable; import com.google.gson.Gson; @@ -30,26 +32,43 @@ public class Rule { * * @param str string representing a rule set * @return a RuleSet object + * @throws ParseException */ - @SuppressWarnings("unchecked") - public static final Rule parse(String str) { - HtmlUnitDriver driver = new HtmlUnitDriver(true); - String name = ""; - String desc = ""; - String ruleStr = ""; - HashMap<String, Object> res = null; - String stmt = "exports = {};\n"+str + "; return exports;"; + public static final Rule parse(String str) throws ParseException { + + Context cx = Context.enter(); + try { - driver.get("http://localhost:8000/test.html"); - res = (HashMap<String, Object>) driver.executeScript(stmt); - name = (String) res.get("name"); - desc = (String) res.get("description"); - ruleStr = res.get("rule").toString(); + Scriptable scope = cx.initStandardObjects(); + + cx.evaluateString(scope, "exports = {};"+str, "<rule>", 1, null); + + Object mExports = scope.get("exports", scope); + + if (mExports == Scriptable.NOT_FOUND) { + throw new ParseException("Could not parse rule: no Exports found", 0); + } else { + NativeObject exports = (NativeObject)mExports; + String name = ""; + String description = ""; + Object rule; + String ruleStr = ""; + if (exports.containsKey("name") ) { + name = exports.get("name").toString(); + } + if (exports.containsKey("description") ) { + description = exports.get("description").toString(); + } + if (exports.containsKey("rule") ) { + rule = exports.get("rule"); + ruleStr = Context.toString(rule); + } + + return new Rule(name, description, ruleStr); + } } finally { - driver.quit(); + Context.exit(); } - - return new Rule(name, desc, ruleStr); } private final String _name; @@ -77,10 +96,10 @@ public class Rule { @Override public String toString() { Gson gson = new Gson(); - + String ruleStr = getRule().replace("\"", "\\\""); return "exports.name = " + gson.toJson(getName()) + ";\n" + "exports.description = " + gson.toJson(getDescription()) + ";\n" + - "exports.rule = " + gson.toJson(getRule()) + ";\n"; + "exports.rule = \"" + ruleStr + "\";\n"; } /** diff --git a/src/batchtools/rsTester/src/main/java/com/galois/fiveui/RuleSet.java b/src/batchtools/rsTester/src/main/java/com/galois/fiveui/RuleSet.java index ed9bc99..568a346 100644 --- a/src/batchtools/rsTester/src/main/java/com/galois/fiveui/RuleSet.java +++ b/src/batchtools/rsTester/src/main/java/com/galois/fiveui/RuleSet.java @@ -19,6 +19,7 @@ package com.galois.fiveui; import java.io.File; import java.io.IOException; +import java.text.ParseException; import java.util.List; import com.google.common.collect.ImmutableCollection; @@ -72,6 +73,9 @@ public class RuleSet { } catch (IOException e) { System.err.println("Could not load rule from file: "+adjustedPath); System.err.println(" error: "+e); + } catch (ParseException e) { + System.err.println("Could not parse rule from: "+r); + System.err.println(e.getMessage()); } } @@ -114,6 +118,32 @@ public class RuleSet { return gson.toJson(this); } + public String toJS() { + Gson gson = new Gson(); + StringBuilder builder = new StringBuilder(); + builder.append("["); + + for (String r : this.rules) { + try { + String ruleStr = Utils.readFile(descDir + File.separator + r); + //ruleStr = "\""+ruleStr.replace("\"", "\\\"") + "\\\""; + ruleStr = gson.toJson(ruleStr); + + // XXX Gson doesn't seem to escape quotes or \n's enough: + //ruleStr = ruleStr.replace("\\", "\\\\"); + //ruleStr = ruleStr.replace("\\u003d", "="); + builder.append(ruleStr + ", "); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + builder.append("]"); + + return builder.toString(); + } + @Override public int hashCode() { final int prime = 31; diff --git a/src/batchtools/rsTester/src/test/java/com/galois/fiveui/BasicRuleSetParseTest.java b/src/batchtools/rsTester/src/test/java/com/galois/fiveui/BasicRuleSetParseTest.java index 5db53f8..3f561fc 100644 --- a/src/batchtools/rsTester/src/test/java/com/galois/fiveui/BasicRuleSetParseTest.java +++ b/src/batchtools/rsTester/src/test/java/com/galois/fiveui/BasicRuleSetParseTest.java @@ -65,8 +65,8 @@ public class BasicRuleSetParseTest { List<Object[]> tests = Lists.newArrayList(); Object[][] rawTests = new Object[][] { - { "ruleSets/emptyRuleSet.json", true }, - { "ruleSets/simpleRuleSet1.json", true }, +// { "ruleSets/emptyRuleSet.json", true }, + //{ "ruleSets/simpleRuleSet1.json", true }, { "ruleSets/headingGuidelines.json", true }, }; diff --git a/src/batchtools/rsTester/src/test/resources/ruleSets/headingGuidelines-caps.js b/src/batchtools/rsTester/src/test/resources/ruleSets/headingGuidelines-caps.js index 8c41123..6178ac1 100644 --- a/src/batchtools/rsTester/src/test/resources/ruleSets/headingGuidelines-caps.js +++ b/src/batchtools/rsTester/src/test/resources/ruleSets/headingGuidelines-caps.js @@ -3,6 +3,7 @@ exports.name = "Headings are capitalized"; exports.description = "Check to see if all headings use leading capital letters."; exports.rule = function() { + var r = this; var badHeadings = fiveui.query(':header').filter( function(idx) { @@ -14,6 +15,6 @@ exports.rule = function() { } }); $(badHeadings).map(function(idx, elt) { - report('Heading does not start with a capital letter.', elt); + r.report('Heading does not start with a capital letter.', elt); }); }; diff --git a/src/batchtools/rsTester/src/test/resources/ruleSets/headingGuidelines-noEmptyHdrs.js b/src/batchtools/rsTester/src/test/resources/ruleSets/headingGuidelines-noEmptyHdrs.js index a70f71c..466f46e 100644 --- a/src/batchtools/rsTester/src/test/resources/ruleSets/headingGuidelines-noEmptyHdrs.js +++ b/src/batchtools/rsTester/src/test/resources/ruleSets/headingGuidelines-noEmptyHdrs.js @@ -3,10 +3,11 @@ exports.name = "Disallow Empty Headers"; exports.description = "Heading elements should contain text."; exports.rule = function() { + var r = this; fiveui.query(':header').each( function(ix, elt) { if($(elt).text() == '') { - report('Heading does not contain text', elt); + r.report('Heading does not contain text', elt); } }); }; diff --git a/src/js/chrome/background.html b/src/js/chrome/background.html index 8ca54fd..5f9e5a2 100644 --- a/src/js/chrome/background.html +++ b/src/js/chrome/background.html @@ -26,7 +26,7 @@ <script src="backbone.js"></script> <script src="js/set.js"></script> <script src="js/background.js"></script> - <script src="js/url-pat.js"></script> + <script src="js/utils.js"></script> <script src="js/settings.js"></script> <script src="js/messenger.js"></script> <script src="js/state.js"></script> diff --git a/src/js/firefox/build.mk b/src/js/firefox/build.mk index 1807a45..0a20a80 100644 --- a/src/js/firefox/build.mk +++ b/src/js/firefox/build.mk @@ -58,8 +58,6 @@ quiet_cmd_compilejs = JSC $(call drop-prefix,$@) $(firefox-build)/data/main.js: \ $(firefox-dir)/lib/main.js \ $(fiveui-dir)/js/set.js \ - $(fiveui-dir)/js/url-pat.js \ - $(fiveui-dir)/js/url-pat.js \ $(fiveui-dir)/js/settings.js \ $(fiveui-dir)/js/messenger.js \ $(fiveui-dir)/js/state.js \ diff --git a/src/js/fiveui/css/options.css b/src/js/fiveui/css/options.css index 65540e2..f0ba264 100644 --- a/src/js/fiveui/css/options.css +++ b/src/js/fiveui/css/options.css @@ -155,7 +155,7 @@ ul.entries li.entry { border: 1px solid #CDCDCD; margin-top: 5px; padding: 10px; - background-color: #f0f0f0; + background-color: #EAEEF3; } li.entry .error { @@ -170,8 +170,14 @@ li.entry button { font-size: 0.6em; } +li.entry div.container { + float: left; + margin-right: 10px; +} + li.entry .editable { padding: 5px; + margin-left; 10px; border: 1px solid #CDCDCD; } diff --git a/src/js/fiveui/injected/compute.js b/src/js/fiveui/injected/compute.js index 8efc8f7..1bd546d 100644 --- a/src/js/fiveui/injected/compute.js +++ b/src/js/fiveui/injected/compute.js @@ -88,9 +88,10 @@ core.port.emit('ReportStats', stats); }; - core.hash = function(rule, name, node) { + core.hash = function(rule, message, node) { var prob = { - name: name, + name: rule.name, + msg: message, descr: rule.description, url: window.location.href, severity: 1, @@ -195,6 +196,9 @@ /* END of BSD licensed code */ + /** + * @param {!Array.<Rule>} rs A list of Rule objects. + */ core.evaluate = function(rs) { var theRule = null; var date = new Date(); @@ -206,8 +210,8 @@ }; fiveui.stats.numElts = 0; // reset stats element counter - var report = function(name, node) { - var prob = core.hash(theRule, name, node); + var report = function(message, node) { + var prob = core.hash(theRule, message, node); var query = $(node); if(!query.hasClass(prob.hash)) { query.addClass(prob.hash); @@ -289,25 +293,35 @@ }; var registerBackendListeners = function(port) { - port.on('SetRules', function(payload) { + var assembleRules = function(ruleStrList) { + var ruleList = []; - core.rules = []; - - for(var i=0; i<payload.length; ++i) { + for(var i=0; i<ruleStrList.length; ++i) { var moduleStr = [ '(function(){' , 'var exports = {};' - , payload[i] + , ruleStrList[i] , 'return exports;' , '})()' ].join('\n'); - core.rules.push(eval(moduleStr)); + var evaled = eval(moduleStr); + ruleList.push(evaled); } + return ruleList; + }; + + port.on('SetRules', function(payload) { + core.rules = assembleRules(payload); core.scheduleRules(); registerDomListeners(document); }); + + port.on('ForceEval', function(ruleStrList){ + var ruleList = assembleRules(ruleStrList); + core.evaluate(ruleList); + }); }; registerBackendListeners(core.port); diff --git a/src/js/fiveui/js/options.js b/src/js/fiveui/js/options.js index 598649f..5a4e48d 100644 --- a/src/js/fiveui/js/options.js +++ b/src/js/fiveui/js/options.js @@ -62,50 +62,6 @@ fiveui.options.init = function(port) { var update = new fiveui.UpdateManager(msg); ruleSets = new fiveui.RuleSets([], { url: msg }); - urlPats = new fiveui.UrlPats([], { url: msg }); - - - /** UrlPat list entries ****************************************************/ - - var urlPatEntries = jQuery('#urlPatEntries'); - var addUrlPat = jQuery('#addUrlPat'); - - addUrlPat.prop('disabled', true); - - addUrlPat.on('click', function() { - urlPats.add(new fiveui.UrlPatModel({}, { url : msg })); - }); - - // when a new rule set is sync'd, make sure that the add url pattern button is - // enabled. - ruleSets.on('sync', function() { - if(ruleSets.length > 0) { - addUrlPat.prop('disabled', false); - } - }); - - // when a rule set is destroyed, and the collection is now empty, disable the - // add url pattern button. - ruleSets.on('destroy', function(model,col) { - if(col.length <= 0) { - addUrlPat.prop('disabled', true); - } - }); - - // handle new url patterns being added to the collection. - urlPats.on('add', function(model) { - var view = new fiveui.UrlPatEntry({ - model: model, - rules: new fiveui.RulesView({ model: ruleSets }) - }); - urlPatEntries.append(view.$el); - - if(model.isNew()) { - view.edit(); - } else { - view.render(); - } - }); /** RuleSet list entries ***************************************************/ @@ -129,7 +85,6 @@ fiveui.options.init = function(port) { if(model.isNew()) { entry.edit(); } else { - addUrlPat.prop('disabled', false); entry.render(); } }); @@ -198,7 +153,7 @@ fiveui.options.init = function(port) { setClickHandler(jQuery('#rule-sets'), select('#tab-rule-sets')); setClickHandler(jQuery('#basics'), select('#tab-basics')); - // select the url patterns tab by default + // select the rule sets tab selectNav(jQuery('#rule-sets')); selectSection(jQuery('#tab-rule-sets')); @@ -209,12 +164,8 @@ fiveui.options.init = function(port) { jQuery('#windowDisplayDefault').prop('checked', def); }); - // pre-populate the rule set and url pattern lists - ruleSets.fetch({ - success:function() { - urlPats.fetch(); - } - }); + // pre-populate the rule sets + ruleSets.fetch(); }; @@ -347,8 +298,10 @@ fiveui.RuleSetEntry = Backbone.View.extend({ // setup the url pattern editor this.$addpat.button({ icons: { primary: 'ui-icon-plus' } }); - editable(this.$urlpat, 'http://example.com/*', - _.bind(this.$addpat.click, this.$addpat)) + editable(this.$urlpat, 'http://example.com/*', _.bind(function() { + this.$addpat.click(); + this.$urlpat.focus(); + }, this)); }, events: { @@ -360,9 +313,11 @@ fiveui.RuleSetEntry = Backbone.View.extend({ }, viewRsTemplate: _.template( - [ '<button class="remove">remove</button>' - , '<button class="edit">edit</button>' - , '<button class="reload">reload</button>' + [ '<div class="container">' + , ' <button class="remove">remove</button>' + , ' <button class="edit">edit</button>' + , ' <button class="reload">reload</button>' + , '</div>' , '<span class="title"><%= name %></span>' ].join('')), @@ -386,8 +341,10 @@ fiveui.RuleSetEntry = Backbone.View.extend({ }, editTemplate: _.template( - [ '<button class="remove">x</button>' - , '<button class="save">save</button>' + [ '<div class="container">' + , ' <button class="remove">x</button>' + , ' <button class="save">save</button>' + , '</div>' , '<span class="source"><%= source %></span>' ].join('')), diff --git a/src/js/fiveui/js/settings.js b/src/js/fiveui/js/settings.js index 96ec864..15ccf1b 100644 --- a/src/js/fiveui/js/settings.js +++ b/src/js/fiveui/js/settings.js @@ -200,7 +200,7 @@ _.extend(fiveui.Settings.prototype, { return _.find(this.getRuleSets(), function(rs) { var pat = _.find(rs.patterns, function(pat) { - var regex = fiveui.UrlPat.compile(pat); + var regex = fiveui.utils.compilePattern(pat); return regex.test(url); }); diff --git a/src/js/fiveui/js/update-manager.js b/src/js/fiveui/js/update-manager.js index 8013d2f..d134c0d 100644 --- a/src/js/fiveui/js/update-manager.js +++ b/src/js/fiveui/js/update-manager.js @@ -28,20 +28,6 @@ fiveui.UpdateManager = function(msg) { // fired when the rule set gets updated msg.register('updateRuleSet', function(newRuleSet) { manager.trigger('updateRuleSet.' + newRuleSet.id, false, newRuleSet); - - // update the associated url patterns - msg.send('getRuleSetPatIds', null, function(patIds) { - _.each(patIds, function(patId) { - manager.trigger('updateUrlPat.' + patId); - }); - }); - }); - - // fired when the url pat gets removed - msg.register('remUrlPat', function(id) { - var evt = 'remUrlPat.' + id; - manager.trigger(evt); - manager.off(evt); }); }; diff --git a/src/js/fiveui/js/url-pat.js b/src/js/fiveui/js/url-pat.js deleted file mode 100644 index d723021..0000000 --- a/src/js/fiveui/js/url-pat.js +++ /dev/null @@ -1,140 +0,0 @@ - -(function() { - -/** - * Create a new Url Pattern to map urls to Rule Sets. - * - * @constructor - * @param {!number} id New id for this UrlPat. - * @param {!string} regex The pattern that is used to match Urls. - * @param {!number} rule_id Unique id of the RuleSet to use for matching URLs. - */ -fiveui.UrlPat = function(id, regex, rule_id) { - this.id = id; - this.regex = regex; - this.rule_id = rule_id; -}; - -/** - * Create a Url Pattern from a JSON object. - * - * @param {!number} id The id to use for the restored object. - * @param {!Object} obj The object to take settings from. - * @return {!fiveui.UrlPat} A populated UrlPat object. - */ -fiveui.UrlPat.fromJSON = function(id, obj) { - return new fiveui.UrlPat(id, obj.regex, obj.rule_id); -}; - -/** - * Create a regular expression from a globbed pattern. - * - * @param {!string} str The globbed url. - * @return {!RegExp} A compiled regular expression. - */ -fiveui.UrlPat.compile = function(str) { - var regex = str.replace(/\./g, '\.') - .replace(/\*/g, '.*'); - return new RegExp(regex); -}; - -/** - * Test a string Url against the regular expression held in a Url Pattern. - * - * @param {!string} url The Url the string to test. - * @return {!boolean} If the Url matched the regular expression. - */ -fiveui.UrlPat.prototype.match = function(url) { - var pat = fiveui.UrlPat.compile(this.regex); - return pat.test(url); -}; - - - -fiveui.UrlPatModel = Backbone.Model.extend({ - - defaults: { - id: null, - regex: '', - rule_id: null, - }, - - sync:function(method, model, options) { - _.defaults(options, { - success:function() {}, - error:function() {} - }); - - var msg = model.url; - var id = model.get('id'); - - switch(method) { - case 'read': - msg.send('getUrlPat', id, function(pat) { - model.set(pat); - options.success(); - }); - break; - - case 'update': - msg.send('updateUrlPat', _.clone(model.attributes), options.success); - break; - - case 'create': - msg.send('addUrlPat', _.clone(model.attributes), options.success); - break; - - case 'delete': - msg.send('remUrlPat', id, function(res) { - if(res) { - options.success({}); - } else { - options.error({}); - } - }); - break; - } - } - -}, { - - fromUrlPat: function(pat, msg) { - return new fiveui.UrlPatModel({ - id: pat.id, - regex: pat.regex, - rule_id: pat.rule_id - }, { url : msg }); - } - -}); - - -fiveui.UrlPats = Backbone.Collection.extend({ - - model: fiveui.UrlPatModel, - - sync:function(method, collection, options) { - - _.defaults(options, { - success:function() {}, - error:function() {} - }); - - var msg = this.url; - - switch(method) { - - case 'read': - msg.send('getUrlPats', null, function(pats) { - options.success(_.map(pats, function(pat) { - return fiveui.UrlPatModel.fromUrlPat(pat, msg); - })); - }); - break; - } - - } - -}); - -})(); diff --git a/src/js/fiveui/js/utils.js b/src/js/fiveui/js/utils.js index 0ab3128..204d961 100644 --- a/src/js/fiveui/js/utils.js +++ b/src/js/fiveui/js/utils.js @@ -76,6 +76,19 @@ fiveui.utils.getNewId = function(list) { /** + * Create a regular expression from a globbed pattern. + * + * @param {!string} str The globbed url. + * @return {!RegExp} A compiled regular expression. + */ +fiveui.utils.compilePattern = function(str) { + var regex = str.replace(/\./g, '\.') + .replace(/\*/g, '.*'); + return new RegExp(regex); +}; + + +/** * Remove c-style comments * * There's probably a faster way to do this. diff --git a/src/js/fiveui/options.html b/src/js/fiveui/options.html index 0115368..1d07eb1 100644 --- a/src/js/fiveui/options.html +++ b/src/js/fiveui/options.html @@ -39,7 +39,6 @@ <script src="js/update-manager.js"></script> <script src="js/utils.js"></script> <script src="js/rules.js"></script> - <script src="js/url-pat.js"></script> <script src="js/platform-ajax.js"></script> <script src="js/platform-options.js"></script> </head> diff --git a/src/js/selenium/selenium-injected-compute.js b/src/js/selenium/selenium-injected-compute.js index dd208be..e6a8b59 100644 --- a/src/js/selenium/selenium-injected-compute.js +++ b/src/js/selenium/selenium-injected-compute.js @@ -19,11 +19,13 @@ * limitations under the License. */ + + /** * @return {{on: function(!string, function(*)), emit: function(!string, *)}} */ var obtainComputePort = function() { - fiveui.selPort = new fiveui.SeleniumPort(); + return fiveui.selPort; }; @@ -61,7 +63,8 @@ fiveui.SeleniumPort.prototype.emit = function(evt, obj) { * Send a message to the injected script. * * @param {!string} evt The event to fire. - * @param {?Object} obj The data to associate with the event. + * @param {?Array.<string>} obj The data to associate with the event + * (an array of strings, each representing a JS module) */ fiveui.SeleniumPort.prototype.send = function(evt, obj) { if (this._events[evt]) { @@ -81,8 +84,7 @@ fiveui.SeleniumPort.prototype.query = function (type) { if (!type) { msgs = this._messages; - } - else { + } else { for (i=0; i < this._messages.length; i += 1) { console.log(this._messages); if (this._messages[i].type === type) { @@ -97,3 +99,9 @@ fiveui.SeleniumPort.prototype.query = function (type) { // return the new messages to the backend: return msgs; }; + +// Define a port at the top level, so multiple contexts can access it. +fiveui.selPort = new fiveui.SeleniumPort(); +// fiveui.selPort.emit('ReportProblem', {name: 'foo'}); + +// this._messages = [{type: 'ReportProblem', payload: {name: 'bork'}}]; diff --git a/src/js/tests/PhantomJSJasmineRunner.html b/src/js/tests/PhantomJSJasmineRunner.html index e8e07e6..606a251 100644 --- a/src/js/tests/PhantomJSJasmineRunner.html +++ b/src/js/tests/PhantomJSJasmineRunner.html @@ -23,7 +23,6 @@ <script type="text/javascript" src="../fiveui/js/chan.js"></script> <script type="text/javascript" src="../fiveui/js/rules.js"></script> <script type="text/javascript" src="../fiveui/js/messenger.js"></script> - <script type="text/javascript" src="../fiveui/js/url-pat.js"></script> <script type="text/javascript" src="../fiveui/js/settings.js"></script> <script type="text/javascript" src="../fiveui/js/state.js"></script> <script type="text/javascript" src="../fiveui/injected/prelude.js"></script> diff --git a/src/js/tests/SpecRunner.html b/src/js/tests/SpecRunner.html index 3469bfd..606967e 100644 --- a/src/js/tests/SpecRunner.html +++ b/src/js/tests/SpecRunner.html @@ -21,7 +21,6 @@ <script type="text/javascript" src="../fiveui/js/chan.js"></script> <script type="text/javascript" src="../fiveui/js/rules.js"></script> <script type="text/javascript" src="../fiveui/js/messenger.js"></script> - <script type="text/javascript" src="../fiveui/js/url-pat.js"></script> <script type="text/javascript" src="../fiveui/js/settings.js"></script> <script type="text/javascript" src="../fiveui/js/state.js"></script> <script type="text/javascript" src="../fiveui/injected/prelude.js"></script> diff --git a/src/js/tests/build.mk b/src/js/tests/build.mk index 3ab79c0..8facda7 100644 --- a/src/js/tests/build.mk +++ b/src/js/tests/build.mk @@ -12,6 +12,8 @@ $(call strict-error,"phantomjs not found: unable to run javascript tests") else test: test-js + +.PHONY: test-js test-js: cd $(topdir)/src/js && $(phantomjs-cmd) \ lib/phantomjs_jasmine/phantomjs_jasminexml_runner.js \ diff --git a/src/js/tests/specs/messenger.js b/src/js/tests/specs/messenger.js index 12bd9e1..3e236fa 100644 --- a/src/js/tests/specs/messenger.js +++ b/src/js/tests/specs/messenger.js @@ -72,25 +72,4 @@ describe('fiveui.Messenger', function() { expect(m1got[0]).toBe(null); }); - - it('is able to send rules', function() { - var ruleIn = new fiveui.Rule(42, 'testRule', - 'see: http://test.description/', - 'function() { console.log("fail"); }'); - - var got = []; - m1.register('rule', function(r){ - got.push(r); - }); - - m2.send('rule', ruleIn); - expect(got.length).toBe(1); - - var ruleOut = got[0]; - expect(ruleIn.id).toBe(ruleOut.id); - expect(ruleIn.name).toBe(ruleOut.name); - expect(ruleIn.description).toBe(ruleOut.description); - expect(ruleIn.ruleStr).toBe(ruleOut.ruleStr); - }); - }); diff --git a/src/js/tests/specs/rules.js b/src/js/tests/specs/rules.js index b9db6b3..518cfc9 100644 --- a/src/js/tests/specs/rules.js +++ b/src/js/tests/specs/rules.js @@ -3,9 +3,11 @@ describe('fiveui.Rules', function() { it('round trips via JSON', function() { - var ruleIn = new fiveui.Rule(42, 'testRule', - 'see: http://test.description/', - 'function() { console.log("fail"); }'); + var ruleIn = new fiveui.Rule({ + id: 42, + name: 'testRule', + description: 'see: http://test.description/', + }); var jsonRule = JSON.stringify(ruleIn); @@ -14,7 +16,6 @@ describe('fiveui.Rules', function() { expect(ruleOut.id).toBe(ruleIn.id); expect(ruleOut.name).toBe(ruleIn.name); expect(ruleOut.description).toBe(ruleIn.description); - expect(ruleOut.ruleStr).toBe(ruleIn.ruleStr); }); @@ -25,10 +26,11 @@ describe('fiveui.RuleSet', function() { it('round trips via JSON, without deps', function() { - var rule1 = new fiveui.Rule(42, 'r1', 'desc1', 'rule txt1'); - var rule2 = new fiveui.Rule(43, 'r2', 'desc2', 'rule txt2'); - - var ruleSet = new fiveui.RuleSet(42, 'rule set', 'desc', '', [rule1, rule2]); + var ruleSet = new fiveui.RuleSet({ + id: 42, + name: 'rule set', + description: 'desc' + }); var jsonSet = JSON.stringify(ruleSet); var restoredSet = fiveui.RuleSet.fromJSON(42, JSON.parse(jsonSet)); @@ -45,11 +47,11 @@ describe('fiveui.RuleSet', function() { it('round trips via JSON, with deps', function() { - var rule1 = new fiveui.Rule(42, 'r1', 'desc1', 'rule txt1'); - var rule2 = new fiveui.Rule(43, 'r2', 'desc2', 'rule txt2'); - - var ruleSet = new fiveui.RuleSet(42, 'rule set', 'desc', [rule1, rule2], - '', ['dep1.js', 'dep2.js']); + var ruleSet = new fiveui.RuleSet({ + id: 42, + name: 'rule set', + description: 'desc', [rule1, rule2], + }); var jsonSet = JSON.stringify(ruleSet); var restoredSet = fiveui.RuleSet.fromJSON(42, JSON.parse(jsonSet)); diff --git a/src/js/tests/specs/settings.js b/src/js/tests/specs/settings.js index a89b532..088b94d 100644 --- a/src/js/tests/specs/settings.js +++ b/src/js/tests/specs/settings.js @@ -27,61 +27,32 @@ describe('fiveui.Settings', function() { expect(settings.get(key)).toEqual(value); }); - it('round trips rules through addUrl', function() { - // somewhat random rule id - var ruleId = Math.floor(Math.random() * 101); - var urlPat = 'http://.*'; - var urlId = settings.addUrl(urlPat, ruleId); - var result = settings.getUrlPat(urlId); - - expect(result.regex).toBe(urlPat); - expect(result.rule_id).toBe(ruleId); - }); - it('matches urls when there\s a valid pattern registered', function() { - var newId = 42; - settings.addUrl('http://.*', newId); - expect(settings.checkUrl('http://foo').rule_id).toBe(newId); + var rset = new fiveui.RuleSet({ patterns: ['http://.*'] }); + settings.addRuleSet(rset); + expect(settings.checkUrl('http://foo').id).toBe(0); }); it('doesn\'t match urls when there are no patterns registered', function() { - expect(settings.checkUrl('http://foo')).toBe(null); + expect(settings.checkUrl('http://foo')).toBe(undefined); }); it('removes rules successfully', function() { - var obj = { - id: 17, + var rsId = settings.addRuleSet(new fiveui.RuleSet({ name: 'rs', description: '', rules: [] - }; - - var rs = settings.addRuleSet(obj); + })); expect(rs).not.toBe(null); var rsCount1 = settings.getRuleSets().length; - settings.remRuleSet(rs.id); + settings.remRuleSet(rsId); + var rsCount2 = settings.getRuleSets().length; expect(rsCount1).toEqual(rsCount2 + 1); - rs = settings.getRuleSet(rs.id); + var rs = settings.getRuleSet(rsId); expect(rs).toBe(null); }); - it('is unable to remove rules that are in use', function() { - var obj = { - id: 17, - name: 'rs', - description: '', - rules: [] - }; - - var rs = settings.addRuleSet(obj); - expect(rs).not.toBe(null); - - var urlPatId = settings.addUrl('*', rs.id); - rs = settings.getRuleSet(rs.id); - expect(rs).not.toBe(null); - }); - }); |