From 572e20226f9d6458321f7899a973f9893995962b Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 11:39:44 -0700 Subject: [PATCH 01/10] Fixed exact text matching by adding trim method. closes #7 Random white space was ocassionally getting added, and that was screwing things up. Added our real invoiceOne to specs view so that we could get a quality failing, real life test. --- src/behave.js | 9 +++---- test/templates.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++- test/test.js | 44 ++++++++++++++++++++++++++---- 3 files changed, 111 insertions(+), 11 deletions(-) diff --git a/src/behave.js b/src/behave.js index a97bbd7..819ccad 100644 --- a/src/behave.js +++ b/src/behave.js @@ -1,6 +1,6 @@ // Little jQuery extension to have exact equals; $.expr[':'].textEquals = function(a, i, m) { - var match = $(a).text().match("^" + m[3] + "$") + var match = $(a).text().trim().match("^" + m[3] + "$") return match && match.length > 0; } @@ -32,10 +32,9 @@ Behave.find = function(identifier, type) { var searchOptions = type ? {specificOption: Behave.domTypes[type]} : Behave.domTypes; _.each(searchOptions, function(searchParams) { _.each(searchParams.elementTypes, function(elType) { - if (element.length) { - // Explicitly returning false kills iteration early in lodash. - return false; - } + // Explicitly returning false kills iteration early in lodash. + if (element.length) {return false;} + _.each(searchParams.attrOptions, function(attrOption) { switch (attrOption) { case 'contains': diff --git a/test/templates.js b/test/templates.js index f52a5bb..db57a7f 100644 --- a/test/templates.js +++ b/test/templates.js @@ -57,4 +57,71 @@ " \n" + " \n" + " \n" + - "" + ""; + + templates.invoiceOne = + "
\n" + + "
\n" + + "

Invoice # {{data.invoice.invoice_number}}

\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " \n" + + "
\n" + + "

\n" + + " {{data.invoice.amount_in_cents | fromCents | currency }}\n" + + "

\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " \n" + + "
\n" + + "

\n" + + "

\n" + + "  \n" + + " \n" + + "

\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " \n" + + "
\n" + + "

\n" + + " {{data.invoice.invoice_date | date}}\n" + + "

\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " \n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + " \n" + + "
"; diff --git a/test/test.js b/test/test.js index 113d478..68e1ba0 100644 --- a/test/test.js +++ b/test/test.js @@ -1,16 +1,17 @@ /* jshint immed: false */ -/* globals Replicator, describe, beforeEach, it, xdescribe, templates */ +/* globals Replicator, describe, beforeEach, it, xdescribe, templates, Behave */ +/* quotmark: true*/ (function () { 'use strict'; var $view; beforeEach(function() { - Behave.view = $view = $(templates.signup) + Behave.view = $view = $(templates.signup); }); describe('#find', function() { describe("with field type elements", function() { it("should, by default, look for field type elements", function() { - $view.append('div[name=subdomain]') + $view.append('div[name=subdomain]'); var jqSubdomain = $view.find("input[name='subdomain']"); var bSubdomain = Behave.find('subdomain'); bSubdomain.attr('ng-model').should.eql(jqSubdomain.attr('ng-model')); @@ -40,12 +41,45 @@ bSubdomain.attr('ng-model').should.eql(correspondingInput.attr('ng-model')); }); }); + + describe("searching by text", function() { + describe("with exact matching", function() { + beforeEach(function() { + Behave.view = $view = $(templates.invoiceOne); + }); + it("should only find things with the exact text", function() { + var jqResult = $view.find("a:contains('Back to All Invoices')"); + var bResultNotExact = Behave.find("Back to All"); + var bResultExact = Behave.find("Back to All Invoices"); + bResultExact.text().should == "Back to All Invoices"; + bResultExact.text().should.eql(jqResult.text()); + bResultNotExact.text().should.eql(''); + }); + }); + describe("when using rough match", function() { + beforeEach(function() { + $view.append("
Worked!
") + $view.append("
WorkedAgain!
") + $view.append("
Success: This is alert text that could be many things!
") + }); + it("should find multiple results if multiple things match", function() { + var bResult = Behave.find('~Worked!'); + var jqResult = $view.find(":contains('Worked!')"); + bResult.length.should.eql(2); + bResult.length.should.eql(jqResult.length); + }); + it("should find a match if any of the letters are contained in the text of another", function() { + var bResult = Behave.find('~Success!'); + }); + it("should not be case sensitive") + }); + }); describe("with clickable type elements", function() { var jqResult, bResult; beforeEach(function() { - $view.append(''); + $view.append(""); $view.append("Practice Url"); - jqResult = $view.find('button:contains(Subdomain)') + jqResult = $view.find('button:contains(Subdomain)'); }) it("should find only clickable type elements and search by containing text", function() { bResult = Behave.find('Subdomain', 'clickable'); From c84e1abec2e35d08d6fa728a1de9804779c1fec2 Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 13:04:57 -0700 Subject: [PATCH 02/10] Added rough matching. And some refactoring - Now using a "~" at the beginning of text will do a rough match - Now using try/catch for all finds, but rethrowing errors if they aren't stupid syntax errors that really should just return nothing. - Refactored findByClass and moved text matchign logic to a findByText function. --- src/behave.js | 54 +++++++++++++++++++++++++++++++++++++++++---------- test/test.js | 9 ++------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/behave.js b/src/behave.js index 819ccad..6efa3a3 100644 --- a/src/behave.js +++ b/src/behave.js @@ -1,3 +1,4 @@ +'use strict'; // Little jQuery extension to have exact equals; $.expr[':'].textEquals = function(a, i, m) { var match = $(a).text().trim().match("^" + m[3] + "$") @@ -38,15 +39,14 @@ Behave.find = function(identifier, type) { _.each(searchParams.attrOptions, function(attrOption) { switch (attrOption) { case 'contains': - var filter = ":textEquals("+ identifier + ")" - element = Behave.view.find(elType + filter); + element = findByText(identifier, elType) break; case 'class': - element = findByClass(identifier, elType, 'glyphicon-'); + element = findByClass(identifier, elType); break; default: var filter = "[" + attrOption + "='" + identifier + "']"; - element = Behave.view.find(elType + filter); + element = tryFind(elType + filter); } // Explicitly returning false kills iteration early in lodash. if (element.length) { @@ -61,7 +61,7 @@ Behave.find = function(identifier, type) { } // Fall back to jQuery if we can't find anything if (!element.length) { - element = Behave.view.find(identifier); + element = tryFind(identifier); } return element; }; @@ -71,6 +71,7 @@ Behave.fill = function(identifier) { // If id is already jQuery, just go with it. Useful if you've set a variable using Behave.find, and then want to // reuse that variable in a fill later on. var $el = identifier instanceof jQuery ? identifier : Behave.find(identifier, 'field'); + var fillWith = function(data) { if ($el.is('form') || $el.attr('type') === 'form') { if (!_.isObject(data)) { @@ -120,6 +121,23 @@ Behave.getAllEls = function(element, $els) { // PRIVATE FUNCTIONS +var tryFind = function(expression) { + var el = ''; + try { + console.log(expression); + el = Behave.view.find(expression); + } + catch (e) { + // Syntax errors occur sometimes when trying to do certain operations + // with ~'s and such. We just want it to return nothing in this case. + if ( !(_.contains(e.message, "Syntax error")) ) { + // Re throw if it's not a syntax errors + throw (e) + } + } + return $(el); +}; + var getClosestInput = function($el) { var sibling = $el.next(); if (sibling.is('input')) {return sibling} @@ -127,11 +145,27 @@ var getClosestInput = function($el) { return relatedInput.length ? relatedInput : $el; }; -var findByClass = function(identifier, elType, prefix) { - prefix = prefix || ''; +var findByClass = function(identifier, elType) { + var prefix = _.contains(['icon', 'div', 'span'], elType) ? 'glyphicon-' : ''; elType = elType || ''; - return Behave.view.find(elType + '.' + prefix + identifier).first(); -} + var expression = elType + '.' + prefix + identifier; + + return tryFind(expression).first(); +}; + +var findByText = function(identifier, elType) { + var filterMethod, filterString, expression; + filterMethod = ":textEquals"; + + if (identifier[0] === '~') { + identifier = identifier.slice(1); + filterMethod = ":contains"; + } + filterString = filterMethod + "('" + identifier + "')"; + expression = elType + filterString; + + return tryFind(expression); +}; var cleanVal = function(val) { if (!val) {return;} @@ -139,7 +173,7 @@ var cleanVal = function(val) { // Remove any spaces. val = val.replace(' ', ''); - if (val.indexOf('-') !== -1) { + if (_.contains(val, '-')) { // camelCasing attrs with dashes in them. var words = val.split('-'); words[1] = words[1][0].toUpperCase() + words[1].substring(1); diff --git a/test/test.js b/test/test.js index 68e1ba0..cc12a8c 100644 --- a/test/test.js +++ b/test/test.js @@ -63,8 +63,8 @@ $view.append("
Success: This is alert text that could be many things!
") }); it("should find multiple results if multiple things match", function() { - var bResult = Behave.find('~Worked!'); - var jqResult = $view.find(":contains('Worked!')"); + var bResult = Behave.find('~Worked'); + var jqResult = $view.find(":contains('Worked')"); bResult.length.should.eql(2); bResult.length.should.eql(jqResult.length); }); @@ -105,11 +105,6 @@ bResult.text().should.eql(jqResult.text()); }); }); - describe("with display type elements", function() { - xit("should return rough matches of text contained in display type elements", function() { - // TODO: Create good list of display elements, or maybe just use body:contains ? figure this out. - }); - }); describe("with icon type elements", function() { beforeEach(function() { $view.append("") From b67c4529a4954a13567621c150ed5a32fd55458d Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 13:05:39 -0700 Subject: [PATCH 03/10] Bumped to 2.4.1 --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 25aa663..76ce974 100644 --- a/bower.json +++ b/bower.json @@ -26,6 +26,6 @@ ], "dependencies": { "jQuery": "~2.0.3", - "lodash": "^2.2.1" + "lodash": "^2.4.1" } } From d68097615cc43211c2c396f99306c02d938c59b1 Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 15:36:31 -0700 Subject: [PATCH 04/10] Improvements! - Added better rough matching (not case sensitive anymore). - Added find all. - Now throws errors if it finds nothing or multiple items. - Added tryFind method. closes #9 closes #10 --- src/behave.js | 63 +++++++++++++++++++++++++++++++++++++-------------- test/test.js | 56 +++++++++++++++++++++++++++++---------------- 2 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/behave.js b/src/behave.js index 6efa3a3..d8f1b47 100644 --- a/src/behave.js +++ b/src/behave.js @@ -1,9 +1,18 @@ 'use strict'; -// Little jQuery extension to have exact equals; -$.expr[':'].textEquals = function(a, i, m) { - var match = $(a).text().trim().match("^" + m[3] + "$") +// Little jQuery extension for exact and rough text matching +$.expr[':'].textEquals = function(el, i, m) { + var textToMatch = m[3]; + var match = $(el).text().trim().match("^" + textToMatch + "$") return match && match.length > 0; -} +}; + +// This is similar to contains, except not case sensitive. +$.expr[':'].textRoughMatch = function(el, i, m) { + var textToMatch = m[3]; + var elText = $(el).text().toLowerCase(); + var res = elText.toLowerCase().indexOf(textToMatch.toLowerCase()) !== -1; + return res; +}; window.Behave = {}; Behave.view = $(document.body); @@ -21,14 +30,16 @@ Behave.domTypes = { attrOptions: ['type', 'class', 'test-me'] }, display: { - elementTypes: [''], // This is actually all elements, since there's no leading el type + // The empty string is actually all elements, since there's no leading el type + elementTypes: ['table', 'tr', 'td', 'th', ''], attrOptions: ['test-me', 'contains'] } -} +}; -Behave.getAllElsAttrOptions = ['name', 'for', 'placeholder', 'type', 'test-me'] +Behave.getAllElsAttrOptions = ['name', 'for', 'placeholder', 'type', 'test-me']; -Behave.find = function(identifier, type) { +Behave.find = function(identifier, type, opts) { + opts = opts || {}; var element = ''; var searchOptions = type ? {specificOption: Behave.domTypes[type]} : Behave.domTypes; _.each(searchOptions, function(searchParams) { @@ -39,14 +50,14 @@ Behave.find = function(identifier, type) { _.each(searchParams.attrOptions, function(attrOption) { switch (attrOption) { case 'contains': - element = findByText(identifier, elType) + element = findByText(identifier, elType); break; case 'class': element = findByClass(identifier, elType); break; default: var filter = "[" + attrOption + "='" + identifier + "']"; - element = tryFind(elType + filter); + element = tryToFind(elType + filter); } // Explicitly returning false kills iteration early in lodash. if (element.length) { @@ -54,18 +65,37 @@ Behave.find = function(identifier, type) { } }); }); - }) + }); if (element && element.is('label')) { element = getClosestInput(element); } // Fall back to jQuery if we can't find anything if (!element.length) { - element = tryFind(identifier); + element = tryToFind(identifier); + } + + // No element has been found, and we're in error mode. + if (!element.length && !opts.noErrors) { + throw new Error('Can\'t find element identified by ' + identifier); } + + // We found too many things + if (element.length > 1 && !opts.findMany) { + throw new Error('Matched multiple elements identified by ' + identifier + '. Use findAll if that\'s what you expect.') + } + return element; }; +Behave.tryFind = function(identifier, type) { + return Behave.find(identifier, type, {noErrors: true}); +}; + +Behave.findAll = function(identifier, type) { + return Behave.find(identifier, type, {findMany: true}); +}; + Behave.fill = function(identifier) { // If id is already jQuery, just go with it. Useful if you've set a variable using Behave.find, and then want to @@ -121,10 +151,9 @@ Behave.getAllEls = function(element, $els) { // PRIVATE FUNCTIONS -var tryFind = function(expression) { +var tryToFind = function(expression) { var el = ''; try { - console.log(expression); el = Behave.view.find(expression); } catch (e) { @@ -150,7 +179,7 @@ var findByClass = function(identifier, elType) { elType = elType || ''; var expression = elType + '.' + prefix + identifier; - return tryFind(expression).first(); + return tryToFind(expression).first(); }; var findByText = function(identifier, elType) { @@ -159,12 +188,12 @@ var findByText = function(identifier, elType) { if (identifier[0] === '~') { identifier = identifier.slice(1); - filterMethod = ":contains"; + filterMethod = ":textRoughMatch"; } filterString = filterMethod + "('" + identifier + "')"; expression = elType + filterString; - return tryFind(expression); + return tryToFind(expression); }; var cleanVal = function(val) { diff --git a/test/test.js b/test/test.js index cc12a8c..2879aab 100644 --- a/test/test.js +++ b/test/test.js @@ -9,6 +9,13 @@ Behave.view = $view = $(templates.signup); }); describe('#find', function() { + describe("when no element is found", function() { + it("should throw an error", function() { + (function() { + var el = Behave.find("TOTALLY NOT HERE") + }).should.throw("Can't find element identified by TOTALLY NOT HERE"); + }); + }); describe("with field type elements", function() { it("should, by default, look for field type elements", function() { $view.append('div[name=subdomain]'); @@ -42,6 +49,28 @@ }); }); + describe("when multiple things match", function() { + beforeEach(function() { + Behave.view.append("
Worked!
") + Behave.view.append("
WorkedAgain!
") + }); + it("should throw an error if multiple things match", function() { + (function() { + Behave.find('~Worked') + }).should.throw('Matched multiple elements identified by ~Worked. Use findAll if that\'s what you expect.'); + }); + describe("using find all", function() { + it("should be totally fine", function() { + (function() { + Behave.findAll('~Worked'); + }).should.not.throw(); + }); + it("should return the correct number of elements", function() { + Behave.findAll('~Worked').length.should.eql(2); + }); + }); + }); + describe("searching by text", function() { describe("with exact matching", function() { beforeEach(function() { @@ -49,7 +78,7 @@ }); it("should only find things with the exact text", function() { var jqResult = $view.find("a:contains('Back to All Invoices')"); - var bResultNotExact = Behave.find("Back to All"); + var bResultNotExact = Behave.tryFind("Back to All"); var bResultExact = Behave.find("Back to All Invoices"); bResultExact.text().should == "Back to All Invoices"; bResultExact.text().should.eql(jqResult.text()); @@ -58,20 +87,16 @@ }); describe("when using rough match", function() { beforeEach(function() { - $view.append("
Worked!
") - $view.append("
WorkedAgain!
") - $view.append("
Success: This is alert text that could be many things!
") + Behave.view.append("
Success: This is alert text that could be many things!
") }); - it("should find multiple results if multiple things match", function() { - var bResult = Behave.find('~Worked'); - var jqResult = $view.find(":contains('Worked')"); - bResult.length.should.eql(2); - bResult.length.should.eql(jqResult.length); + it("should find a substring match", function() { + var bResult = Behave.find('~Success'); + bResult.text().should.eql("Success: This is alert text that could be many things!"); }); - it("should find a match if any of the letters are contained in the text of another", function() { - var bResult = Behave.find('~Success!'); + it("should not be case sensitive", function() { + var bResult = Behave.find('~sUcCess'); + bResult.text().should.eql('Success: This is alert text that could be many things!'); }); - it("should not be case sensitive") }); }); describe("with clickable type elements", function() { @@ -87,13 +112,6 @@ bResult.is('button').should.eql(true); bResult.text().should.eql(jqResult.text()); }); - it("should default to doing an exact search", function() { - var bRoughResult = Behave.find('Subdo', 'clickable'); - var bExactResult = Behave.find('Subdomain', 'clickable'); - bRoughResult.is('button').should.eql(false); - bExactResult.is('button').should.eql(true); - bExactResult.text().should.eql(jqResult.text()); - }); it("should find things based on href", function() { bResult = Behave.find("www.test.com", 'clickable'); jqResult = $view.find("a[href='www.test.com']"); From 8106cf8ca9655c13e2091c6bcd30f11fe9bc1068 Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 15:39:21 -0700 Subject: [PATCH 05/10] Aliased tryFind and findAll to the window. Also super tiny refactors. --- src/behave.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/behave.js b/src/behave.js index d8f1b47..c704d0c 100644 --- a/src/behave.js +++ b/src/behave.js @@ -169,7 +169,7 @@ var tryToFind = function(expression) { var getClosestInput = function($el) { var sibling = $el.next(); - if (sibling.is('input')) {return sibling} + if (sibling.is('input')) {return sibling;} var relatedInput = sibling.find('input'); return relatedInput.length ? relatedInput : $el; }; @@ -214,5 +214,7 @@ var cleanVal = function(val) { // Set functions to the window for convenience window.find = Behave.find; -window.fill = Behave.fill +window.fill = Behave.fill; +window.findAll = Behave.findAll; +window.tryFind = Behave.tryFind; From 7fb9cab2bf505d26dcb6bdcc34dd0825c4cf1028 Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 15:39:44 -0700 Subject: [PATCH 06/10] Added jshint rc file. --- .jshintrc | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..6a67cd4 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,87 @@ +{ + // JSHint Configuration File for Testing + // See http://jshint.com/docs/ for more details + + "maxerr" : 50, // {int} Maximum error before stopping + + // Enforcing + "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) + "camelcase" : false, // true: Identifiers must be in camelCase + "curly" : true, // true: Require {} for every new block or scope + "eqeqeq" : true, // true: Require triple equals (===) for comparison + "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` + "indent" : 2, // {int} Number of spaces to use for indentation + "latedef" : true, // true: Require variables/functions to be defined before being used + "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` + "noempty" : true, // true: Prohibit use of empty blocks + "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) + "plusplus" : false, // true: Prohibit use of `++` & `--` + "quotmark" : "false", // Quotation mark consistency: + // false : do nothing (default) + // true : ensure whatever is used is consistent + // "single" : require single quotes + // "double" : require double quotes + "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused" : true, // true: Require all defined variables be used + "strict" : true, // true: Requires all functions run in ES5 Strict Mode + "globalstrict" : true, // true: Allow global "use strict" (also enables 'strict') + "trailing" : true, // true: Prohibit trailing whitespaces + "maxparams" : false, // {int} Max number of formal params allowed per function + "maxdepth" : false, // {int} Max depth of nested blocks (within functions) + "maxstatements" : false, // {int} Max number statements per function + "maxcomplexity" : false, // {int} Max cyclomatic complexity per function + "maxlen" : 120, // {int} Max number of characters per line + + // Relaxing + "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "boss" : false, // true: Tolerate assignments where comparisons would be expected + "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // true: Tolerate use of `== null` + "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) + "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) + "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) + // (ex: `for each`, multiple try/catch, function expression…) + "evil" : false, // true: Tolerate use of `eval` and `new Function()` + "expr" : false, // true: Tolerate `ExpressionStatement` as Programs + "funcscope" : false, // true: Tolerate defining variables inside control statements" + "iterator" : false, // true: Tolerate using the `__iterator__` property + "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block + "laxbreak" : false, // true: Tolerate possibly unsafe line breakings + "laxcomma" : false, // true: Tolerate comma-first style coding + "loopfunc" : false, // true: Tolerate functions being defined in loops + "multistr" : false, // true: Tolerate multi-line strings + "proto" : false, // true: Tolerate using the `__proto__` property + "scripturl" : false, // true: Tolerate script-targeted URLs + "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment + "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` + "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation + "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` + "validthis" : false, // true: Tolerate using this in a non-constructor function + + // Environments + "browser" : true, // Web Browser (window, document, etc) + "couch" : false, // CouchDB + "devel" : false, // Development/debugging (alert, confirm, etc) + "dojo" : false, // Dojo Toolkit + "jquery" : true, // jQuery + "mootools" : false, // MooTools + "node" : false, // Node.js + "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) + "prototypejs" : false, // Prototype and Scriptaculous + "rhino" : false, // Rhino + "worker" : false, // Web Workers + "wsh" : false, // Windows Scripting Host + "yui" : false, // Yahoo User Interface + + // Legacy + "nomen" : false, // true: Prohibit dangling `_` in variables + "onevar" : false, // true: Allow only one `var` statement per function + "passfail" : false, // true: Stop on first error + "white" : true, // true: Check against strict whitespace and indentation rules + + // Custom Globals + "predef" : [ "_" , "$", "angular", "moment", "Stripe", "analytics", "bowser", "Behave"] +} + From 3ebb6ed2ae6a143b6717bbf1d93dd9db9e00ee58 Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 16:57:55 -0700 Subject: [PATCH 07/10] Added bexpect to the window. This just delegates to jasmine's expect. Don't really think I like this. Couldn't get delegation to work with the name 'expect'. Was causing an infinite loop. Couldn't figure out why, so putting off for now. Anyway... closes #2 --- src/behave.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/behave.js b/src/behave.js index c704d0c..13be96d 100644 --- a/src/behave.js +++ b/src/behave.js @@ -149,6 +149,17 @@ Behave.getAllEls = function(element, $els) { return $els; }; +Behave.bexpect = function(identifier, opts) { + if (_.isObject(jasmine)) { + if (_.isString(identifier)) { + return jasmine.getGlobal().expect(Behave.find(identifier, opts)); + } + return jasmine.getGlobal().expect(identifier); + } + + throw new Error("It appears jasmine or expect isn't defined. Thus Behave can't delegate expect"); +}; + // PRIVATE FUNCTIONS var tryToFind = function(expression) { @@ -217,4 +228,5 @@ window.find = Behave.find; window.fill = Behave.fill; window.findAll = Behave.findAll; window.tryFind = Behave.tryFind; +window.bexpect = Behave.bexpect; From d57995f08a8e020ed4f1ca2e79ef3adc5d6c9eb5 Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 17:03:14 -0700 Subject: [PATCH 08/10] removed getAllEls. Won't be needed if finding works and there are good window aliases --- src/behave.js | 25 ------------------------- test/test.js | 26 -------------------------- 2 files changed, 51 deletions(-) diff --git a/src/behave.js b/src/behave.js index 13be96d..6cb45b2 100644 --- a/src/behave.js +++ b/src/behave.js @@ -36,8 +36,6 @@ Behave.domTypes = { } }; -Behave.getAllElsAttrOptions = ['name', 'for', 'placeholder', 'type', 'test-me']; - Behave.find = function(identifier, type, opts) { opts = opts || {}; var element = ''; @@ -126,29 +124,6 @@ Behave.fill = function(identifier) { return fillObject; }; -Behave.getAllEls = function(element, $els) { - element = element || Behave.view; - $els = $els || {}; - var kids = element.children; - if (kids.length) { - element.children().each(function() { - $els = Behave.getAllEls($(this), $els); - }); - } - _.each(Behave.getAllElsAttrOptions, function(attrOption) { - var attrVal = cleanVal(element.attr(attrOption)); - if (attrVal) { - element.reload = function() { - return Behave.find(attrVal); - } - } - attrVal && ($els[attrVal] = element) - }); - var elText = element.text(); - if(elText) {$els[cleanVal(elText)] = element;} - return $els; -}; - Behave.bexpect = function(identifier, opts) { if (_.isObject(jasmine)) { if (_.isString(identifier)) { diff --git a/test/test.js b/test/test.js index 2879aab..7548457 100644 --- a/test/test.js +++ b/test/test.js @@ -184,30 +184,4 @@ }); }) }); - describe("#getAllEls", function() { - var $els; - beforeEach(function() { - var form = $("
") - form.append("") - form.append("") - form.append("") - form.append("") - $view.append("
") - $view.append(form) - $els = Behave.getAllEls(); - }); - it("should create an object with jQ elements from the whole page", function() { - $els.accept_terms.attr('name').should.eql(Behave.find('accept_terms').attr('name')); - $els.first_name.attr('name').should.eql(Behave.find('first_name').attr('name')); - }); - it("should camelCase elements with attrs that are dash-cased", function() { - $els.couponContainer.should.be.an.Object - }); - it("should concatenate the text of label elements", function() { - $els.PracticeUrl.should.be.an.Object - }); - xit("should give each element a reload method", function() { - // Need to test this properly, but it works for our angular testing; - }); - }); }()); From d1b20da90787484d0a1eec162fdd6b7f4fa82ee1 Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 18:36:53 -0700 Subject: [PATCH 09/10] Added a choose function and a click function to help standardize the stupidness of angular. --- src/behave.js | 37 ++++++++++++++++++++++++++++++++++--- test/templates.js | 16 ++++++++++++++++ test/test.js | 14 ++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/src/behave.js b/src/behave.js index 6cb45b2..0365e1a 100644 --- a/src/behave.js +++ b/src/behave.js @@ -22,7 +22,7 @@ Behave.domTypes = { attrOptions: ['name', 'for', 'placeholder', 'contains', 'type', 'test-me'] }, clickable: { - elementTypes: ['button', 'a'], + elementTypes: ['button', 'a', 'select'], attrOptions: ['contains', 'href', 'test-me'] }, icon: { @@ -70,7 +70,7 @@ Behave.find = function(identifier, type, opts) { } // Fall back to jQuery if we can't find anything if (!element.length) { - element = tryToFind(identifier); + element = Behave.view.find(identifier); } // No element has been found, and we're in error mode. @@ -135,6 +135,37 @@ Behave.bexpect = function(identifier, opts) { throw new Error("It appears jasmine or expect isn't defined. Thus Behave can't delegate expect"); }; +Behave.click = function(identifier) { + if (identifier instanceof jQuery) { + if (identifier.is('input') && identifier.attr('type') === 'radio') { + return identifier.click().trigger('click'); + } else { + return identifier.trigger('click'); + } + return; + } + if (_.isString(identifier)) { + return Behave.find(identifier).trigger('click'); + } + throw new Error("The identifier passed to click was invalid. It must be either a string or jQuery object"); +}; + +Behave.choose = function(value) { + // If id is already jQuery, just go with it. Useful if you've set a variable using Behave.find, and then want to + // reuse that variable in a fill later on. + + var chooseFrom = function(dropDown) { + var $el = dropDown instanceof jQuery ? dropDown : Behave.find(dropDown); + return $el.val(value).trigger('change'); + }; + + var chooseObject = { + from: chooseFrom + }; + + return chooseObject; +}; + // PRIVATE FUNCTIONS var tryToFind = function(expression) { @@ -204,4 +235,4 @@ window.fill = Behave.fill; window.findAll = Behave.findAll; window.tryFind = Behave.tryFind; window.bexpect = Behave.bexpect; - +window.click = Behave.click; diff --git a/test/templates.js b/test/templates.js index db57a7f..774b3a9 100644 --- a/test/templates.js +++ b/test/templates.js @@ -125,3 +125,19 @@ " \n" + " \n" + ""; + + templates.dropdowns = + "
\n" + + "
\n" + + " \n" + + "
\n" + + "
\n" + + "
\n"; + diff --git a/test/test.js b/test/test.js index 7548457..6411bb2 100644 --- a/test/test.js +++ b/test/test.js @@ -184,4 +184,18 @@ }); }) }); + xdescribe("#click", function() { + it("would be annoying to test, but I manually tested this"); + }); + describe("#choose/from", function() { + beforeEach(function() { + Behave.view = $view = $(templates.dropdowns); + }); + it.only("should select a dropdown", function() { + var coupon = Behave.choose('InactiveCoupon').from('coupons'); + coupon.val().should.eql('InactiveCoupon'); + coupon = Behave.choose('ActiveCoupon').from('coupons'); + coupon.val().should.eql('ActiveCoupon'); + }); + }); }()); From ba34f54ed9c6451bcc71ffafdea91e0dc5fda977 Mon Sep 17 00:00:00 2001 From: Blake West Date: Fri, 17 Oct 2014 18:37:59 -0700 Subject: [PATCH 10/10] removed the it.only --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 6411bb2..69f9ea4 100644 --- a/test/test.js +++ b/test/test.js @@ -191,7 +191,7 @@ beforeEach(function() { Behave.view = $view = $(templates.dropdowns); }); - it.only("should select a dropdown", function() { + it("should select a dropdown", function() { var coupon = Behave.choose('InactiveCoupon').from('coupons'); coupon.val().should.eql('InactiveCoupon'); coupon = Behave.choose('ActiveCoupon').from('coupons');