diff --git a/README.md b/README.md
index 44114b01..516074fd 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ npm install yadda
```
### Browser based environments (e.g. QUnit)
```html
-
+
```
## Contributing
We're always happy to receive pull requests, but please read the [Contributor Notes](https://github.com/acuminous/yadda/wiki/Notes-for-Contributors) first.
diff --git a/component.json b/component.json
index cb1f8bac..8940da8c 100644
--- a/component.json
+++ b/component.json
@@ -1,6 +1,6 @@
{
"name": "yadda",
- "version": "0.13.0",
+ "version": "0.13.1",
"description": "A true BDD framework for JavaScript",
"repo": "acuminous/yadda",
"keywords": [
diff --git a/dist/yadda-0.13.1.js b/dist/yadda-0.13.1.js
new file mode 100644
index 00000000..e28a582b
--- /dev/null
+++ b/dist/yadda-0.13.1.js
@@ -0,0 +1,3015 @@
+require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= items.length) return callback(null, result);
+ iterate();
+ });
+ };
+ iterate();
+ }
+
+ function collect(items, iterator) {
+ var results = ensure_array();
+ for (var i = 0; i < items.length; i++) {
+ results.push(iterator(items[i]));
+ }
+ return results;
+ }
+
+ function inject(items, default_value, iterator) {
+ var result = default_value;
+ for (var i = 0; i < items.length; i++) {
+ result = iterator(result, items[i]);
+ }
+ return result;
+ }
+
+ function push_all(items, more_items) {
+ more_items = more_items ? [].concat(more_items) : [];
+ for (var i = 0; i < more_items.length; i++) {
+ items.push(more_items[i]);
+ }
+ }
+
+ function find_all(items, test) {
+ var results = ensure_array();
+ for (var i = 0; i < items.length; i++) {
+ if (test(items[i])) {
+ results.push(items[i]);
+ }
+ }
+ return results;
+ }
+
+ function find(items, test) {
+ var result;
+ for (var i = 0; i < items.length; i++) {
+ if (test(items[i])) {
+ result = items[i];
+ break;
+ }
+ }
+ return result;
+ }
+
+ function last(items) {
+ return items[items.length - 1];
+ }
+
+ function naked(items) {
+ return [].concat(items);
+ }
+
+ return ensure_array(obj);
+};
+
+},{"./fn":16}],2:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var LevenshteinDistanceScore = require('./LevenshteinDistanceScore');
+var $ = require('./Array');
+
+// Understands appropriateness of macros in relation to a specific step
+var Competition = function(step, macros) {
+
+ var results = [];
+
+ this.validate = function() {
+ if (is_undefined()) return { step: step, valid: false, reason: 'Undefined Step' };
+ if (is_ambiguous()) return { step: step, valid: false, reason: 'Ambiguous Step (Patterns [' + winning_patterns() + '] are all equally good candidates)' };
+ return { step: step, valid: true };
+ };
+
+ this.clear_winner = function() {
+ if (is_undefined()) throw new Error('Undefined Step: [' + step + ']');
+ if (is_ambiguous()) throw new Error('Ambiguous Step: [' + step + ']. Patterns [' + winning_patterns() + '] match equally well.');
+ return this.winner();
+ };
+
+ function is_undefined() {
+ return results.length === 0;
+ }
+
+ function is_ambiguous() {
+ return (results.length > 1) && results[0].score.equals(results[1].score);
+ }
+
+ this.winner = function() {
+ return results[0].macro;
+ };
+
+ function winning_patterns() {
+ return results.find_all(by_winning_score).collect(macro_signatures).join(', ');
+ }
+
+ function rank(step, macros) {
+ results = macros.collect(function(macro) {
+ return {
+ macro: macro,
+ score: new LevenshteinDistanceScore(step, macro.levenshtein_signature())
+ };
+ }).sort( by_ascending_score );
+ }
+
+ function by_ascending_score(a, b) {
+ return b.score.beats(a.score);
+ }
+
+ function by_winning_score(result) {
+ return result.score.equals(results[0].score);
+ }
+
+ function macro_signatures(result) {
+ return result.macro.toString();
+ }
+
+ rank(step, $(macros));
+};
+
+module.exports = Competition;
+
+},{"./Array":1,"./LevenshteinDistanceScore":9}],3:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+// Constructs an object that macros will be bound to before execution
+var Context = function(properties) {
+
+ // I was previously getting some weird errors using instanceof to determine if
+ // the "other" object was a context object. Using pTFUHht733hM6wfnruGLgAu6Uqvy7MVp instead
+ this.pTFUHht733hM6wfnruGLgAu6Uqvy7MVp = true;
+ this.properties = {};
+
+ this.merge = function(other) {
+ if (other && other.pTFUHht733hM6wfnruGLgAu6Uqvy7MVp) return this.merge(other.properties);
+ return new Context(this.properties)._merge(other);
+ };
+
+ this._merge = function(other) {
+ for (var key in other) { this.properties[key] = other[key]; }
+ return this;
+ };
+
+ this._merge(properties);
+};
+
+module.exports = Context;
+
+},{}],4:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('./Array');
+var RegularExpression = require('./RegularExpression');
+
+// Understands term definitions
+var Dictionary = function(prefix) {
+
+ /* jslint shadow: true */
+ var prefix = prefix || '$';
+ var terms = {};
+ var term_pattern = new RegularExpression(new RegExp('(?:^|[^\\\\])\\' + prefix + '(\\w+)', 'g'));
+ var _this = this;
+
+ this.define = function(term, definition) {
+ if (this.is_defined(term)) throw new Error('Duplicate definition: [' + term + ']');
+ terms[term] = normalise(definition);
+ return this;
+ };
+
+ this.is_defined = function(term) {
+ return terms[term];
+ };
+
+ this.expand = function(term, already_expanding) {
+ if (!is_expandable(term)) return term;
+ return expand_sub_terms(term, $(already_expanding));
+ };
+
+ this.merge = function(other) {
+ if (other._prefix() != this._prefix()) throw new Error('Cannot merge dictionaries with different prefixes');
+ return new Dictionary(prefix)._merge(this)._merge(other);
+ };
+
+ this._merge = function(other) {
+ other.each_term(this.define.bind(this));
+ return this;
+ };
+
+ this._prefix = function() {
+ return prefix;
+ };
+
+ this.each_term = function(callback) {
+ for (var key in terms) {
+ callback(key, terms[key]);
+ }
+ };
+
+ var expand_sub_terms = function(term, already_expanding) {
+ return get_sub_terms(term).each(function(sub_term) {
+ if (already_expanding.in_array(sub_term)) throw new Error('Circular Definition: [' + already_expanding.join(', ') + ']');
+ var sub_term_definition = expand_sub_term(sub_term, already_expanding);
+ term = term.replace(prefix + sub_term, sub_term_definition);
+ return term;
+ });
+ };
+
+ var get_sub_terms = function(term) {
+ return term_pattern.groups(term);
+ };
+
+ var expand_sub_term = function(sub_term, already_expanding) {
+ var definition = terms[sub_term] || '(.+)';
+ return is_expandable(definition) ? _this.expand(definition, already_expanding.concat(sub_term)) : definition;
+ };
+
+ var normalise = function(definition) {
+ return definition.toString().replace(/^\/|\/$/g, '');
+ };
+
+ var is_expandable = function(definition) {
+ return term_pattern.test(definition);
+ };
+};
+
+
+module.exports = Dictionary;
+
+},{"./Array":1,"./RegularExpression":13}],5:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('./Array');
+var fn = require('./fn');
+var event_bus = new EventBus();
+
+// A communication channel between event emitters and event listeners
+function EventBus() {
+
+ var event_handlers = $();
+ var _this = this;
+
+ this.send = function(event_name, event_data, next) {
+ if (arguments.length == 1) return this.send(event_name, {});
+ if (arguments.length == 2 && fn.is_function(event_data)) return this.send(event_name, {}, event_data);
+ notify_handlers(event_name, event_data);
+ next && next();
+ return this;
+ };
+
+ this.on = function(event_pattern, callback) {
+ event_handlers.push({ pattern: event_pattern, callback: callback });
+ return this;
+ };
+
+ var notify_handlers = function(event_name, event_data) {
+ find_handlers(event_name).each(function(callback) {
+ callback({ name: event_name, data: event_data });
+ });
+ };
+
+ var find_handlers = function(event_name) {
+ return event_handlers.find_all(function(handler) {
+ return new RegExp(handler.pattern).test(event_name);
+ }).collect(function(handler) {
+ return handler.callback;
+ });
+ };
+}
+
+function instance() {
+ return event_bus;
+}
+
+module.exports = {
+ instance: instance,
+ ON_SCENARIO: '__ON_SCENARIO__',
+ ON_STEP: '__ON_STEP__',
+ ON_EXECUTE: '__ON_EXECUTE__'
+};
+
+},{"./Array":1,"./fn":16}],6:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var FileSearch = require('./FileSearch');
+
+var FeatureFileSearch = function(directories) {
+ this.constructor(directories, /.*\.(?:feature|spec|specification)$/);
+};
+FeatureFileSearch.prototype = new FileSearch();
+
+module.exports = FeatureFileSearch;
+
+},{"./FileSearch":7}],7:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var shims = require('./shims/index');
+var path = shims.path;
+var process = shims.process;
+var fs = shims.fs;
+var $ = require('./Array');
+
+// Searches for files in the given directories and their sub-directories, matching at least one of the given patterns
+var FileSearch = function(directories, patterns) {
+
+ /* jslint shadow: true */
+ var patterns = patterns || /.*/;
+
+ this.each = function(fn) {
+ this.list().forEach(fn);
+ };
+
+ this.list = function() {
+ return $(directories).inject($(), function(files, directory) {
+ return files.concat(list_files(directory).find_all(by_pattern));
+ });
+ };
+
+ var list_files = function(directory) {
+ return $(list_immediate_files(directory).concat(list_sub_directory_files(directory)));
+ };
+
+ var list_immediate_files = function(directory) {
+ return ls(directory).find_all(by_file);
+ };
+
+ var list_sub_directory_files = function(directory) {
+ return ls(directory).find_all(by_directory).inject($(), function(files, directory) {
+ return files.concat(list_files(directory));
+ });
+ };
+
+ var ls = function(directory) {
+ if (!fs.existsSync(directory)) return $();
+ return $(fs.readdirSync(directory)).collect(function(file) {
+ return path.join(directory, file);
+ });
+ };
+
+ var by_file = function(file) {
+ return !by_directory(file);
+ };
+
+ var by_directory = function(file) {
+ return fs.statSync(file).isDirectory();
+ };
+
+ var by_pattern = function(filename) {
+ return $(patterns).find(function(pattern) {
+ return new RegExp(pattern).test(filename);
+ });
+ };
+};
+
+module.exports = FileSearch;
+
+},{"./Array":1,"./shims/index":37}],8:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Competition = require('./Competition');
+var Context = require('./Context');
+var EventBus = require('./EventBus');
+var $ = require('./Array');
+var fn = require('./fn');
+
+// Understands a scenario
+var Interpreter = function(libraries) {
+
+ /* jslint shadow: true */
+ var libraries = $(libraries);
+ var event_bus = EventBus.instance();
+ var _this = this;
+
+ this.requires = function(libraries) {
+ libraries.push_all(libraries);
+ return this;
+ };
+
+ this.validate = function(scenario) {
+ var results = $(scenario).collect(function(step) {
+ return _this.rank_macros(step).validate();
+ });
+ if (results.find(by_invalid_step)) throw new Error('Scenario cannot be interpreted\n' + results.collect(validation_report).join('\n'));
+ };
+
+ function by_invalid_step(result) {
+ return !result.valid;
+ }
+
+ function validation_report(result) {
+ return result.step + (result.valid ? '' : ' <-- ' + result.reason);
+ }
+
+ this.interpret = function(scenario, scenario_context, next) {
+ scenario_context = new Context().merge(scenario_context);
+ event_bus.send(EventBus.ON_SCENARIO, { scenario: scenario, ctx: scenario_context.properties });
+ var iterator = make_step_iterator(scenario_context, next);
+ $(scenario).each_async(iterator, next);
+ };
+
+ var make_step_iterator = function(scenario_context, next) {
+ var iterator = function(step, index, callback) {
+ _this.interpret_step(step, scenario_context, callback);
+ };
+ return next ? iterator : fn.asynchronize(null, iterator);
+ };
+
+ this.interpret_step = function(step, scenario_context, next) {
+ var context = new Context().merge(scenario_context);
+ event_bus.send(EventBus.ON_STEP, { step: step, ctx: context.properties });
+ this.rank_macros(step).clear_winner().interpret(step, context || {}, next);
+ };
+
+ this.rank_macros = function(step) {
+ return new Competition(step, compatible_macros(step));
+ };
+
+ var compatible_macros = function(step) {
+ return libraries.inject([], function(macros, library) {
+ return macros.concat(library.find_compatible_macros(step));
+ });
+ };
+};
+
+module.exports = Interpreter;
+
+},{"./Array":1,"./Competition":2,"./Context":3,"./EventBus":5,"./fn":16}],9:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+// Understands similarity of two strings
+var LevenshteinDistanceScore = function(s1, s2) {
+
+ this.value;
+ this.type = 'LevenshteinDistanceScore';
+ var distance_table;
+ var _this = this;
+
+ var initialise = function() {
+
+ /* jslint shadow: true */
+
+ var x = s1.length;
+ var y = s2.length;
+
+ distance_table = new Array(x + 1);
+
+ for (var i = 0; i <= x; i++) {
+ distance_table[i] = new Array(y + 1);
+ }
+
+ for (var i = 0; i <= x; i++) {
+ for (var j = 0; j <= y; j++) {
+ distance_table[i][j] = 0;
+ }
+ }
+
+ for (var i = 0; i <= x; i++) {
+ distance_table[i][0] = i;
+ }
+
+ for (var j = 0; j <= y; j++) {
+ distance_table[0][j] = j;
+ }
+ };
+
+ var score = function() {
+
+ /*jslint boss: true */
+ if (s1 == s2) return _this.value = 0;
+
+ for (var j = 0; j < s2.length; j++) {
+ for (var i = 0; i < s1.length; i++) {
+ if (s1[i] == s2[j]) {
+ distance_table[i+1][j+1] = distance_table[i][j];
+ } else {
+ var deletion = distance_table[i][j+1] + 1;
+ var insertion = distance_table[i+1][j] + 1;
+ var substitution = distance_table[i][j] + 1;
+ distance_table[i+1][j+1] = Math.min(substitution, deletion, insertion);
+ }
+ }
+ }
+ _this.value = distance_table[s1.length][s2.length];
+ };
+
+ this.beats = function(other) {
+ return this.value < other.value;
+ };
+
+ this.equals = function(other) {
+ if (!other) return false;
+ return (this.type == other.type && this.value == other.value);
+ };
+
+ initialise();
+ score();
+};
+
+module.exports = LevenshteinDistanceScore;
+
+},{}],10:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Macro = require('./Macro');
+var Dictionary = require('./Dictionary');
+var $ = require('./Array');
+
+// Understands how to index macros
+var Library = function(dictionary) {
+
+ /* jslint shadow: true */
+ var dictionary = dictionary || new Dictionary();
+ var macros = $();
+ var _this = this;
+
+ this.define = function(signatures, fn, macro_context) {
+ $(signatures).each(function(signature) {
+ define_macro(signature, fn, macro_context);
+ });
+ return this;
+ };
+
+ var define_macro = function(signature, fn, macro_context) {
+ if (_this.get_macro(signature)) throw new Error('Duplicate macro: [' + signature + ']');
+ macros.push(new Macro(signature, dictionary.expand(signature), fn, macro_context));
+ };
+
+ this.get_macro = function(signature) {
+ return macros.find(function(other_macro) {
+ return other_macro.is_identified_by(signature);
+ });
+ };
+
+ this.find_compatible_macros = function(step) {
+ return macros.find_all(function(macro) {
+ return macro.can_interpret(step);
+ });
+ };
+};
+
+module.exports = Library;
+
+},{"./Array":1,"./Dictionary":4,"./Macro":11}],11:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var fn = require('./fn');
+var Context = require('./Context');
+var RegularExpression = require('./RegularExpression');
+var EventBus = require('./EventBus');
+
+// Understands how to invoke a step
+var Macro = function(signature, signature_pattern, macro, macro_context) {
+
+ /* jslint shadow: true */
+ var signature_pattern = new RegularExpression(signature_pattern);
+ var macro = macro || fn.async_noop;
+ var event_bus = EventBus.instance();
+ var _this = this;
+
+ var init = function(signature, signature_pattern) {
+ _this.signature = normalise(signature);
+ };
+
+ this.is_identified_by = function(other_signature) {
+ return this.signature == normalise(other_signature);
+ };
+
+ this.can_interpret = function(step) {
+ return signature_pattern.test(step);
+ };
+
+ this.interpret = function(step, scenario_context, next) {
+ var context = new Context().merge(macro_context).merge(scenario_context);
+ var args = signature_pattern.groups(step);
+ event_bus.send(EventBus.ON_EXECUTE, { step: step, ctx: context.properties, pattern: signature_pattern.toString(), args: args });
+ fn.invoke(macro, context.properties, args.concat(next));
+ };
+
+ this.levenshtein_signature = function() {
+ return signature_pattern.without_expressions();
+ };
+
+ var normalise = function(signature) {
+ return new RegExp(signature).toString();
+ };
+
+ this.toString = function() {
+ return this.signature;
+ };
+
+ init(signature, signature_pattern);
+};
+
+module.exports = Macro;
+
+},{"./Context":3,"./EventBus":5,"./RegularExpression":13,"./fn":16}],12:[function(require,module,exports){
+(function (process,global,__dirname){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+/* jslint browser: true */
+/* jslint phantom: true */
+"use strict";
+
+module.exports = Platform;
+
+function Platform() {
+
+ function get_container() {
+ if (is_browser()) return window;
+ if (is_phantom()) return phantom;
+ if (is_node()) return global;
+ }
+
+ function is_node() {
+ return typeof process != 'undefined' &&
+ typeof GLOBAL != 'undefined' &&
+ typeof __dirname != 'undefined';
+ }
+
+ function is_browser() {
+ return typeof window != 'undefined';
+ }
+
+ function is_phantom() {
+ return typeof phantom != 'undefined';
+ }
+
+ return {
+ get_container: get_container,
+ is_node: is_node,
+ is_browser: is_browser,
+ is_phantom: is_phantom
+ };
+
+}
+
+}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/lib")
+},{"_process":43}],13:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* jslint node: true */
+ "use strict";
+
+var $ = require('./Array');
+
+// Wrapper for JavaScript Regular Expressions
+var RegularExpression = function(pattern_or_regexp) {
+
+ var groups_pattern = /(^|[^\\\\])\(.*?\)/g;
+ var sets_pattern = /(^|[^\\\\])\[.*?\]/g;
+ var repetitions_pattern = /(^|[^\\\\])\{.*?\}/g;
+ var regex_aliases_pattern = /(^|[^\\\\])\\./g;
+ var non_word_tokens_pattern = /[^\w\s]/g;
+ var regexp = new RegExp(pattern_or_regexp);
+
+ this.test = function(text) {
+ var result = regexp.test(text);
+ this.reset();
+ return result;
+ };
+
+ this.groups = function(text) {
+ var results = $();
+ var match = regexp.exec(text);
+ while (match) {
+ var groups = match.slice(1, match.length);
+ results.push(groups);
+ match = regexp.global && regexp.exec(text);
+ }
+ this.reset();
+ return results.flatten();
+ };
+
+ this.reset = function() {
+ regexp.lastIndex = 0;
+ return this;
+ };
+
+ this.without_expressions = function() {
+ return regexp.source.replace(groups_pattern, '$1')
+ .replace(sets_pattern, '$1')
+ .replace(repetitions_pattern, '$1')
+ .replace(regex_aliases_pattern, '$1')
+ .replace(non_word_tokens_pattern, '');
+ };
+
+ this.equals = function(other) {
+ return this.toString() == other.toString();
+ };
+
+ this.toString = function() {
+ return "/" + regexp.source + "/";
+ };
+};
+
+module.exports = RegularExpression;
+
+},{"./Array":1}],14:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = {
+
+ trim: function trim(text) {
+ return text.replace(/^\s+|\s+$/g, '');
+ },
+ rtrim: function rtrim(text) {
+ return text.replace(/\s+$/g, '');
+ },
+ isBlank: function isBlank(text) {
+ return /^\s*$/g.test(text);
+ },
+ isNotBlank: function isNotBlank(text) {
+ return !this.isBlank(text);
+ },
+ indentation: function indentation(text) {
+ var match = /^(\s*)/.exec(text);
+ return match && match[0].length || 0;
+ }
+};
+
+},{}],15:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*jslint node: true */
+"use strict";
+
+var Interpreter = require('./Interpreter');
+var Context = require('./Context');
+var fn = require('./fn');
+
+var Yadda = function(libraries, interpreter_context) {
+
+ if (!(this instanceof Yadda)) {
+ return new Yadda(libraries, interpreter_context);
+ }
+
+ this.interpreter = new Interpreter(libraries);
+ var _this = this;
+
+ this.requires = function(libraries) {
+ this.interpreter.requires(libraries);
+ return this;
+ };
+
+ this.yadda = function(scenario, scenario_context, next) {
+ if (arguments.length === 0) return this;
+ if (arguments.length === 2 && fn.is_function(scenario_context)) return this.yadda(scenario, {}, scenario_context);
+ this.interpreter.validate(scenario);
+ this.interpreter.interpret(scenario, new Context().merge(interpreter_context).merge(scenario_context), next);
+ };
+
+ // Not everyone shares my sense of humour re the recursive api :(
+ // See https://github.com/acuminous/yadda/issues/111
+ this.run = this.yadda;
+
+ this.toString = function() {
+ return "Yadda 0.13.1 Copyright 2010 Acuminous Ltd / Energized Work Ltd";
+ };
+};
+
+module.exports = Yadda;
+
+},{"./Context":3,"./Interpreter":8,"./fn":16}],16:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = (function() {
+
+ var slice = Array.prototype.slice;
+
+ function curry(ctx, fn) {
+ var args = slice.call(arguments, 2);
+ return function() {
+ return fn.apply(ctx, args.concat(slice.call(arguments)));
+ };
+ }
+
+ function invoke(fn, ctx, args) {
+ return fn.apply(ctx, args);
+ }
+
+ function is_function(object) {
+ var getType = {};
+ return object && getType.toString.call(object) === '[object Function]';
+ }
+
+ function noop() {}
+
+ function asynchronize(ctx, fn) {
+ return function() {
+ var next = slice.call(arguments, arguments.length - 1)[0];
+ var args = slice.call(arguments, 0, arguments.length - 2);
+ fn.apply(ctx, args);
+ if (next) next();
+ };
+ }
+
+ return {
+ noop: noop,
+ async_noop: asynchronize(null, noop),
+ asynchronize: asynchronize,
+ is_function: is_function,
+ curry: curry,
+ invoke: invoke
+ };
+
+
+})();
+
+},{}],17:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '[Ff]eature',
+ scenario: '(?:[Ss]cenario|[Ss]cenario [Oo]utline)',
+ examples: '(?:[Ee]xamples|[Ww]here)',
+ pending: '(?:[Pp]ending|[Tt]odo)',
+ only: '(?:[Oo]nly)',
+ background: '[Bb]ackground',
+ given: '(?:[Gg]iven|[Ww]ith|[Aa]nd|[Bb]ut|[Ee]xcept)',
+ when: '(?:[Ww]hen|[Ii]f|[Aa]nd|[Bb]ut)',
+ then: '(?:[Tt]hen|[Ee]xpect|[Aa]nd|[Bb]ut)',
+ _steps: ['given', 'when', 'then']
+ };
+
+ return new Language('English', vocabulary);
+})();
+
+},{"./Language":20}],18:[function(require,module,exports){
+/* -*- coding: utf-8 -*-
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Ff]onctionnalité)',
+ scenario: '(?:[Ss]cénario|[Pp]lan [Dd]u [Ss]cénario)',
+ examples: '(?:[Ee]xemples|[Ee]xemple|[Oo][uù])',
+ pending: '(?:[Ee]n attente|[Ee]n cours|[Tt]odo)',
+ only: '(?:[Ss]eulement])',
+ background: '(?:[Cc]ontexte)',
+ given: '(?:[Ss]oit|[ÉéEe]tant données|[ÉéEe]tant donnée|[ÉéEe]tant donnés|[ÉéEe]tant donné|[Aa]vec|[Ee]t|[Mm]ais|[Aa]ttendre)',
+ when: '(?:[Qq]uand|[Ll]orsqu\'|[Ll]orsque|[Ss]i|[Ee]t|[Mm]ais)',
+ then: '(?:[Aa]lors|[Aa]ttendre|[Ee]t|[Mm]ais)',
+
+ _steps: [
+ 'given', 'when', 'then',
+ 'soit', 'etantdonnees', 'etantdonnee', 'etantdonne',
+ 'quand', 'lorsque',
+ 'alors'
+ ],
+ // Also aliasing French verbs for given-when-then for signature-lookup
+ get soit() { return this.given; },
+ get etantdonnees() { return this.given; },
+ get etantdonnee() { return this.given; },
+ get etantdonne() { return this.given; },
+ get quand() { return this.when; },
+ get lorsque() { return this.when; },
+ get alors() { return this.then; }
+ };
+
+ return new Language('French', vocabulary);
+})();
+
+},{"./Language":20}],19:[function(require,module,exports){
+/*
+* Copyright 2010 Acuminous Ltd / Energized Work Ltd
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Ff]unktionalität|[Ff]eature|[Aa]spekt|[Uu]secase|[Aa]nwendungsfall)',
+ scenario: '(?:[Ss]zenario|[Ss]zenario( g|G)rundriss|[Gg]eschehnis)',
+ examples: '(?:[Bb]eispiele?)',
+ pending: '(?:[Tt]odo|[Oo]ffen)',
+ only: '(?:[Nn]ur|[Ee]inzig)',
+ background: '(?:[Gg]rundlage|[Hh]intergrund|[Ss]etup|[Vv]orausgesetzt)',
+ given: '(?:[Aa]ngenommen|[Gg]egeben( sei(en)?)?|[Mm]it|[Uu]nd|[Aa]ber|[Aa]ußer)',
+ when: '(?:[Ww]enn|[Ff]alls|[Uu]nd|[Aa]ber)',
+ then: '(?:[Dd]ann|[Ff]olglich|[Aa]ußer|[Uu]nd|[Aa]ber)',
+ _steps: ['given', 'when', 'then']
+ };
+
+ return new Language('German', vocabulary);
+})();
+
+},{"./Language":20}],20:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Library = require('../Library');
+var $ = require('../Array');
+
+module.exports = function(name, vocabulary) {
+
+ var _this = this;
+
+ this.library = function(dictionary) {
+ return _this.localise_library(new Library(dictionary));
+ };
+
+ this.localise_library = function(library) {
+ $(vocabulary._steps).each(function(keyword) {
+ library[keyword] = function(signatures, fn, ctx) {
+ return $(signatures).each(function(signature) {
+ signature = prefix_signature(_this.localise(keyword), signature);
+ return library.define(signature, fn, ctx);
+ });
+ };
+ });
+ return library;
+ };
+
+ var prefix_signature = function(prefix, signature) {
+ var regex_delimiters = new RegExp('^/|/$', 'g');
+ var start_of_signature = new RegExp(/^(?:\^)?/);
+ var one_or_more_spaces = '\\s+';
+ return signature.toString().replace(regex_delimiters, '').replace(start_of_signature, prefix + one_or_more_spaces);
+ };
+
+ this.localise = function(keyword) {
+ if (vocabulary[keyword] === undefined) throw new Error('Keyword "' + keyword + '" has not been translated into ' + name + '.');
+ return vocabulary[keyword];
+ };
+};
+
+},{"../Array":1,"../Library":10}],21:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '[Ee]genskap',
+ scenario: '[Ss]cenario',
+ examples: '[Ee]ksempler',
+ pending: '[Aa]vventer',
+ only: '[Bb]are',
+ background: '[Bb]akgrunn',
+ given: '(?:[Gg]itt|[Mm]ed|[Oo]g|[Mm]en|[Uu]nntatt)',
+ when: '(?:[Nn]år|[Oo]g|[Mm]en)',
+ then: '(?:[Ss]å|[Ff]forvent|[Oo]g|[Mm]en)',
+ _steps: ['given', 'when', 'then', 'gitt', 'når', 'så'],
+ // Also aliasing Norwegian verbs for given-when-then for signature-lookup
+ get gitt() { return this.given; },
+ get når() { return this.when; },
+ get så() { return this.then; }
+ };
+
+ return new Language('Norwegian', vocabulary);
+})();
+
+},{"./Language":20}],22:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Tt]ale|[Yy]arn)',
+ scenario: '(?:[Aa]dventure|[Ss]ortie)',
+ examples: '[Ww]herest',
+ pending: '[Bb]rig',
+ only: '[Bb]lack [Ss]pot',
+ background: '[Aa]ftground',
+ given: '(?:[Gg]iveth|[Ww]ith|[Aa]nd|[Bb]ut|[Ee]xcept)',
+ when: '(?:[Ww]hence|[Ii]f|[Aa]nd|[Bb]ut)',
+ then: '(?:[Tt]hence|[Ee]xpect|[Aa]nd|[Bb]ut)',
+ _steps: ['given', 'when', 'then', 'giveth', 'whence', 'thence'],
+ // Also aliasing Pirate verbs for given-when-then for signature-lookup
+ get giveth() { return this.given; },
+ get whence() { return this.when; },
+ get thence() { return this.then; }
+
+ };
+
+ return new Language('Pirate', vocabulary);
+})();
+
+},{"./Language":20}],23:[function(require,module,exports){
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Ww]łaściwość|[Ff]unkcja|[Aa]spekt|[Pp]otrzeba [Bb]iznesowa)',
+ scenario: '(?:[Ss]cenariusz|[Ss]zablon [Ss]cenariusza)',
+ examples: '[Pp]rzykłady',
+ pending: '(?:[Oo]czekujący|[Nn]iezweryfikowany|[Tt]odo)',
+ only: '[Tt]ylko',
+ background: '[Zz]ałożenia',
+ given: '(?:[Zz]akładając|[Mm]ając|[Oo]raz|[Ii]|[Aa]le)',
+ when: '(?:[Jj]eżeli|[Jj]eśli|[Gg]dy|[Kk]iedy|[Oo]raz|[Ii]|[Aa]le)',
+ then: '(?:[Ww]tedy|[Oo]raz|[Ii]|[Aa]le)',
+ _steps: [
+ 'given', 'when', 'then',
+ 'zakladajac', 'majac',
+ 'jezeli', 'jesli', 'gdy', 'kiedy',
+ 'wtedy'
+ ],
+ // Also aliasing Polish verbs for given-when-then for signature-lookup
+ get zakladajac() { return this.given; },
+ get majac() { return this.given; },
+ get jezeli() { return this.when; },
+ get jesli() { return this.when; },
+ get gdy() { return this.when; },
+ get kiedy() { return this.when; },
+ get wtedy() { return this.then; }
+ };
+
+ return new Language('Polish', vocabulary);
+})();
+
+},{"./Language":20}],24:[function(require,module,exports){
+/* -*- coding: utf-8 -*-
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function () {
+
+ var vocabulary = {
+ feature: '(?:[Ff]uncionalidade|[Cc]aracter[íi]stica)',
+ scenario: '(?:[Cc]en[aá]rio|[Cc]aso)',
+ examples: '(?:[Ee]xemplos|[Ee]xemplo)',
+ pending: '[Pp]endente',
+ only: '[S][óo]',
+ background: '[Ff]undo',
+ given: '(?:[Ss]eja|[Ss]ejam|[Dd]ado|[Dd]ada|[Dd]ados|[Dd]adas)',
+ when: '(?:[Qq]uando|[Ss]e|[Qq]ue|[Ee]|[Mm]as)',
+ then: '(?:[Ee]nt[aã]o|[Ee]|[Mm]as)',
+
+ _steps: [
+ 'given', 'when', 'then',
+ 'seja', 'sejam', 'dado', 'dada', 'dados', 'dadas',
+ 'quando', 'se',
+ 'entao'
+ ],
+
+ get seja() { return this.given; },
+ get sejam() { return this.given; },
+ get dado() { return this.given; },
+ get dada() { return this.given; },
+ get dados() { return this.given; },
+ get dadas() { return this.given; },
+ get quando() { return this.when; },
+ get se() { return this.when; },
+ get entao() { return this.then; }
+ };
+
+ return new Language('Portuguese', vocabulary);
+})();
+},{"./Language":20}],25:[function(require,module,exports){
+/* -*- coding: utf-8 -*-
+ * Author: Marat Dyatko
+ * https://github.com/vectart
+ *
+ * Inspired by Gherkin vocabulary
+ * https://github.com/cucumber/gherkin/blob/master/lib/gherkin/i18n.json
+ *
+ * Also considered syntax highlight of Cucumber Sublime bundle
+ * https://github.com/drewda/cucumber-sublime-bundle/blob/master/Cucumber%20Plain%20Text%20Feature.tmLanguage
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Фф]ункция|[Фф]ункционал|[Сс]войство)',
+ scenario: 'Сценарий',
+ examples: 'Примеры?',
+ pending: '(?:[Ww]ip|[Tt]odo)',
+ only: 'Только',
+ background: '(?:[Пп]редыстория|[Кк]онтекст)',
+ given: '(?:[Дд]опустим|[Дд]ано|[Пп]усть|[Ии]|[Н]о)(?:\\s[Яя])?',
+ when: '(?:[Ее]сли|[Кк]огда|[Ии]|[Н]о)(?:\\s[Яя])?',
+ then: '(?:[Тт]о|[Тт]огда|[Ии]|[Н]о)(?:\\s[Яя])?',
+ _steps: ['given', 'when', 'then']
+ };
+
+ return new Language('Russian', vocabulary);
+})();
+
+},{"./Language":20}],26:[function(require,module,exports){
+/* -*- coding: utf-8 -*-
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Ff]uncionalidad|[Cc]aracterística)',
+ scenario: '(?:[Ee]scenario|[Cc]aso)',
+ examples: '(?:[Ee]jemplos|[Ee]jemplo)',
+ pending: '[Pp]endiente',
+ only: '[S]ólo',
+ background: '[Ff]ondo',
+ given: '(?:[Ss]ea|[Ss]ean|[Dd]ado|[Dd]ada|[Dd]ados|[Dd]adas)',
+ when: '(?:[Cc]uando|[Ss]i|[Qq]ue)',
+ then: '(?:[Ee]ntonces)',
+
+ _steps: [
+ 'given', 'when', 'then',
+ 'sea', 'sean', 'dado', 'dada','dados', 'dadas',
+ 'cuando', 'si',
+ 'entonces'
+ ],
+
+ get sea() { return this.given; },
+ get sean() { return this.given; },
+ get dado() { return this.given; },
+ get dada() { return this.given; },
+ get dados() { return this.given; },
+ get dadas() { return this.given; },
+ get cuando() { return this.when; },
+ get si() { return this.when; },
+ get entonces() { return this.then; }
+ };
+
+ return new Language('Spanish', vocabulary);
+})();
+
+},{"./Language":20}],27:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = {
+ English: require('./English'),
+ French: require('./French'),
+ German: require('./German'),
+ Norwegian: require('./Norwegian'),
+ Pirate: require('./Pirate'),
+ Polish: require('./Polish'),
+ Spanish: require('./Spanish'),
+ Russian: require('./Russian'),
+ Portuguese: require('./Portuguese'),
+ default: require('./English'),
+ Language: require('./Language')
+};
+
+},{"./English":17,"./French":18,"./German":19,"./Language":20,"./Norwegian":21,"./Pirate":22,"./Polish":23,"./Portuguese":24,"./Russian":25,"./Spanish":26}],28:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* jslint node: true */
+ "use strict";
+
+var FeatureFileParser = function(language) {
+
+ // Requiring fs locally so it doesn't break component
+ var fs = require('fs');
+ var FeatureParser = require('./FeatureParser');
+ var parser = new FeatureParser(language);
+
+ this.parse = function(file, next) {
+ var text = fs.readFileSync(file, 'utf8');
+ var feature = parser.parse(text);
+ return next && next(feature) || feature;
+ };
+};
+
+module.exports = FeatureFileParser;
+
+},{"./FeatureParser":29,"fs":41}],29:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* jslint node: true */
+ "use strict";
+
+var $ = require('../Array');
+var fn = require('../fn');
+var StringUtils = require('../StringUtils');
+var Localisation = require('../localisation');
+
+var FeatureParser = function(language) {
+
+ /* jslint shadow: true */
+ var language = language || Localisation.default;
+
+ var FEATURE_REGEX = new RegExp('^\\s*' + language.localise('feature') + ':\\s*(.*)', 'i');
+ var SCENARIO_REGEX = new RegExp('^\\s*' + language.localise('scenario') + ':\\s*(.*)', 'i');
+ var BACKGROUND_REGEX = new RegExp('^\\s*' + language.localise('background') + ':\\s*(.*)', 'i');
+ var EXAMPLES_REGEX = new RegExp('^\\s*' + language.localise('examples') + ':', 'i');
+ var TEXT_REGEX = new RegExp('^(.*)$', 'i');
+ var SINGLE_LINE_COMMENT_REGEX = new RegExp('^\\s*#');
+ var MULTI_LINE_COMMENT_REGEX = new RegExp('^\\s*#{3,}');
+ var BLANK_REGEX = new RegExp('^\\s*$');
+ var DASH_REGEX = new RegExp('(^\\s*[\\|\u2506]?-{3,})');
+ var SIMPLE_ANNOTATION_REGEX = new RegExp('^\\s*@([^=]*)$');
+ var NVP_ANNOTATION_REGEX = new RegExp('^\\s*@([^=]*)=(.*)$');
+
+ var specification;
+ var comment;
+
+ this.parse = function(text, next) {
+ reset();
+ split(text).each(parse_line);
+ return next && next(specification.export()) || specification.export();
+ };
+
+ function reset() {
+ specification = new Specification();
+ comment = false;
+ }
+
+ function split(text) {
+ return $(text.split(/\r\n|\n/));
+ }
+
+ function parse_line(line, index) {
+ /* jslint boss: true */
+ var match;
+ try {
+ if (match = MULTI_LINE_COMMENT_REGEX.test(line)) return comment = !comment;
+ if (comment) return;
+ if (match = SINGLE_LINE_COMMENT_REGEX.test(line)) return;
+ if (match = SIMPLE_ANNOTATION_REGEX.exec(line)) return specification.handle('Annotation', { key: StringUtils.trim(match[1]), value: true });
+ if (match = NVP_ANNOTATION_REGEX.exec(line)) return specification.handle('Annotation', { key: StringUtils.trim(match[1]), value: StringUtils.trim(match[2]) });
+ if (match = FEATURE_REGEX.exec(line)) return specification.handle('Feature', match[1]);
+ if (match = SCENARIO_REGEX.exec(line)) return specification.handle('Scenario', match[1]);
+ if (match = BACKGROUND_REGEX.exec(line)) return specification.handle('Background', match[1]);
+ if (match = EXAMPLES_REGEX.exec(line)) return specification.handle('Examples');
+ if (match = BLANK_REGEX.test(line)) return specification.handle('Blank');
+ if (match = DASH_REGEX.exec(line)) return specification.handle('Dash', match[1]);
+ if (match = TEXT_REGEX.exec(line)) return specification.handle('Text', match[1]);
+ } catch (e) {
+ e.message = 'Error parsing line ' + (index + 1) + ', "' + line + '".\nOriginal error was: ' + e.message;
+ throw e;
+ }
+ }
+};
+
+var Handlers = function(handlers) {
+
+ /* jslint shadow: true */
+ var handlers = handlers || {};
+
+ this.register = function(event, handler) {
+ handlers[event] = handler;
+ };
+
+ this.unregister = function() {
+ $(Array.prototype.slice.call(arguments)).each(function(event) {
+ delete handlers[event];
+ });
+ };
+
+ this.find = function(event) {
+ if (!handlers[event.toLowerCase()]) throw new Error(event + ' is unexpected at this time');
+ return { handle: handlers[event.toLowerCase()] };
+ };
+};
+
+var Specification = function() {
+
+ var current_element = this;
+ var feature;
+ var annotations = new Annotations();
+ var handlers = new Handlers({
+ text: fn.noop,
+ blank: fn.noop,
+ annotation: stash_annotation,
+ feature: start_feature,
+ scenario: start_scenario,
+ background: start_background,
+ });
+
+ function stash_annotation(event, annotation) {
+ handlers.unregister('background');
+ annotations.stash(annotation.key, annotation.value);
+ }
+
+ function start_feature(event, title) {
+ /* jslint boss: true */
+ return feature = new Feature(title, annotations, new Annotations());
+ }
+
+ function start_scenario(event, title) {
+ feature = new Feature(title, new Annotations(), annotations);
+ return feature.on(event, title);
+ }
+
+ var start_background = start_scenario;
+
+ this.handle = function(event, data) {
+ current_element = current_element.on(event, data);
+ };
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ if (!feature) throw new Error('A feature must contain one or more scenarios');
+ return feature.export();
+ };
+};
+
+var Annotations = function() {
+
+ var annotations = {};
+
+ this.stash = function(key, value) {
+ if (/\s/.test(key)) throw new Error('Invalid annotation: ' + key);
+ annotations[key.toLowerCase()] = value;
+ };
+
+ this.export = function() {
+ return annotations;
+ };
+};
+
+var Feature = function(title, annotations, stashed_annotations) {
+
+ var description = [];
+ var scenarios = [];
+ var background = new NullBackground();
+ var handlers = new Handlers({
+ text: capture_description,
+ blank: end_description,
+ annotation: stash_annotation,
+ scenario: start_scenario,
+ background: start_background
+ });
+ var _this = this;
+
+ function start_background(event, title) {
+ background = new Background(title, _this);
+ stashed_annotations = new Annotations();
+ return background;
+ }
+
+ function stash_annotation(event, annotation) {
+ handlers.unregister('background');
+ stashed_annotations.stash(annotation.key, annotation.value);
+ }
+
+ function capture_description(event, text) {
+ description.push(StringUtils.trim(text));
+ }
+
+ function end_description() {
+ handlers.unregister('text');
+ handlers.register('blank', fn.noop);
+ }
+
+ function start_scenario(event, title) {
+ var scenario = new Scenario(title, background, stashed_annotations, _this);
+ scenarios.push(scenario);
+ stashed_annotations = new Annotations();
+ return scenario;
+ }
+
+ function validate() {
+ if (scenarios.length === 0) throw new Error('Feature requires one or more scenarios');
+ }
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ validate();
+ return {
+ title: title,
+ annotations: annotations.export(),
+ description: description,
+ scenarios: $(scenarios).collect(function(scenario) {
+ return scenario.export();
+ }).flatten().naked()
+ };
+ };
+};
+
+var Background = function(title, feature) {
+
+ var steps = [];
+ var handlers = new Handlers({
+ text: capture_step,
+ blank: fn.noop,
+ annotation: stash_annotation,
+ scenario: start_scenario
+ });
+ var _this = this;
+
+ function capture_step(event, text) {
+ steps.push(StringUtils.trim(text));
+ }
+
+ function stash_annotation(event, annotation) {
+ validate();
+ return feature.on(event, annotation);
+ }
+
+ function start_scenario(event, data) {
+ validate();
+ return feature.on(event, data);
+ }
+
+ function validate() {
+ if (steps.length === 0) throw new Error('Background requires one or more steps');
+ }
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ validate();
+ return {
+ steps: steps
+ };
+ };
+};
+
+var NullBackground = function() {
+ var handlers = new Handlers();
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ return {
+ steps: []
+ };
+ };
+};
+
+var Scenario = function(title, background, annotations, feature) {
+ var description = [];
+ var steps = [];
+ var examples;
+ var handlers = new Handlers({
+ text: capture_step,
+ blank: fn.noop,
+ annotation: start_scenario,
+ scenario: start_scenario,
+ examples: start_examples
+ });
+ var _this = this;
+
+ function capture_step(event, text) {
+ steps.push(StringUtils.trim(text));
+ }
+
+ function start_scenario(event, data) {
+ validate();
+ return feature.on(event, data);
+ }
+
+ function start_examples(event, data) {
+ validate();
+ /* jslint boss: true */
+ return examples = new Examples(_this);
+ }
+
+ function validate() {
+ if (steps.length === 0) throw new Error('Scenario requires one or more steps');
+ }
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ validate();
+ var result = {
+ title: title,
+ annotations: annotations.export(),
+ description: description,
+ steps: background.export().steps.concat(steps)
+ };
+ return examples ? examples.expand(result) : result;
+ };
+};
+
+var Examples = function(scenario) {
+
+ var headings = [];
+ var examples = $();
+ var annotations = new Annotations();
+ var handlers = new Handlers({
+ text: capture_headings,
+ blank: fn.noop,
+ scenario: start_scenario
+ });
+ var _this = this;
+
+ function capture_headings(event, data) {
+ handlers.register('annotation', stash_annotation);
+ handlers.register('text', capture_singleline_fields);
+ handlers.register('dash', enable_multiline_examples);
+ headings = split(data).collect(function(column) {
+ return { text: StringUtils.trim(column), indentation: StringUtils.indentation(column) };
+ }).naked();
+ }
+
+ function stash_annotation(event, annotation) {
+ handlers.unregister('blank', 'dash');
+ annotations.stash(annotation.key, annotation.value);
+ }
+
+ function capture_singleline_fields(event, data) {
+ handlers.unregister('dash');
+ handlers.register('blank', start_scenario);
+ examples.push({ annotations: annotations, fields: parse_fields(data, {}) });
+ annotations = new Annotations();
+ }
+
+ function enable_multiline_examples(event, data) {
+ handlers.register('text', start_capturing_multiline_fields);
+ handlers.register('dash', stop_capturing_multiline_fields);
+ }
+
+ function start_capturing_multiline_fields(event, data) {
+ handlers.register('text', continue_capturing_multiline_fields);
+ handlers.register('dash', stop_capturing_multiline_fields);
+ handlers.register('blank', start_scenario);
+ examples.push({ annotations: annotations, fields: parse_fields(data, {}) });
+ }
+
+ function continue_capturing_multiline_fields(event, data) {
+ parse_fields(data, examples.last().fields);
+ }
+
+ function stop_capturing_multiline_fields(event, data) {
+ handlers.register('text', start_capturing_multiline_fields);
+ annotations = new Annotations();
+ }
+
+ function parse_fields(row, fields) {
+ split(row, headings.length).each(function(field, index) {
+ var column = headings[index].text;
+ var indentation = headings[index].indentation;
+ var text = StringUtils.rtrim(field.substr(indentation));
+ if (StringUtils.isNotBlank(field) && StringUtils.indentation(field) < indentation) throw new Error('Indentation error');
+ fields[column] = (fields[column] || []).concat(text);
+ });
+ return fields;
+ }
+
+ function split(row, number_of_fields) {
+ var separator = row.indexOf('\u2506') >= 0 ? '\u2506' : '|';
+ var fields = $(row.split(separator));
+ if (number_of_fields !== undefined && number_of_fields != fields.length) {
+ throw new Error('Incorrect number of fields in example table. Expected ' + number_of_fields + ' but found ' + fields.length);
+ }
+ return fields;
+ }
+
+ function start_scenario(event, data) {
+ validate();
+ return scenario.on(event, data);
+ }
+
+ function validate() {
+ if (headings.length === 0) throw new Error('Examples table requires one or more headings');
+ if (examples.length === 0) throw new Error('Examples table requires one or more rows');
+ }
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.expand = function(scenario) {
+ validate();
+ return examples.collect(function(example) {
+ return {
+ title: substitute(example.fields, scenario.title),
+ annotations: shallow_merge(example.annotations.export(), scenario.annotations),
+ description: substitute_all(example, scenario.description),
+ steps: substitute_all(example.fields, scenario.steps)
+ };
+ }).naked();
+ };
+
+ function shallow_merge() {
+ var result = {};
+ $(Array.prototype.slice.call(arguments)).each(function(annotations) {
+ for (var key in annotations) {
+ result[key] = annotations[key];
+ }
+ });
+ return result;
+ }
+
+ function substitute_all(example, lines) {
+ return $(lines).collect(function(line) {
+ return substitute(example, line);
+ }).naked();
+ }
+
+ function substitute(example, line) {
+ for (var heading in example) {
+ line = line.replace(new RegExp('\\[\\s*' + heading + '\\s*\\]', 'g'), StringUtils.rtrim(example[heading].join('\n')));
+ }
+ return line;
+ }
+};
+
+module.exports = FeatureParser;
+
+},{"../Array":1,"../StringUtils":14,"../fn":16,"../localisation":27}],30:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('../Array');
+
+var StepParser = function() {
+
+ var NON_BLANK_REGEX = /[^\s]/;
+
+ this.parse = function(text, next) {
+ var steps = split(text).find_all(non_blanks);
+ return next && next(steps) || steps;
+ };
+
+ var split = function(text) {
+ return $(text.split(/\n/));
+ };
+
+ var non_blanks = function(text) {
+ return text && NON_BLANK_REGEX.test(text);
+ };
+};
+
+module.exports = StepParser;
+
+},{"../Array":1}],31:[function(require,module,exports){
+/* jslint node: true */
+"use strict";
+
+module.exports = {
+ StepParser: require('./StepParser'),
+ FeatureParser: require('./FeatureParser'),
+ FeatureFileParser: require('./FeatureFileParser')
+};
+
+},{"./FeatureFileParser":28,"./FeatureParser":29,"./StepParser":30}],32:[function(require,module,exports){
+(function (global){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+if (!module.client) {
+ var fs = require('fs');
+ global.process = global.process || {
+ cwd: function() {
+ return fs.workingDirectory;
+ }
+ };
+}
+
+
+module.exports = function(yadda, casper) {
+
+ var EventBus = require('yadda').EventBus;
+
+ yadda.interpreter.interpret_step = function(step, ctx, next) {
+
+ var _this = this;
+ casper.then(function() {
+ casper.test.info(step);
+ EventBus.instance().send(EventBus.ON_STEP, { step: step, ctx: ctx });
+ _this.rank_macros(step).clear_winner().interpret(step, ctx, next);
+ });
+ };
+
+ casper.yadda = function(script, ctx) {
+ if (script === undefined) return this;
+ yadda.run(script, ctx);
+ };
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"fs":41,"yadda":"yadda"}],33:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = {
+ casper: require('./CasperPlugin'),
+ mocha: {
+ ScenarioLevelPlugin: require('./mocha/ScenarioLevelPlugin'),
+ StepLevelPlugin: require('./mocha/StepLevelPlugin')
+ },
+ get jasmine() {
+ return this.mocha;
+ }
+};
+
+},{"./CasperPlugin":32,"./mocha/ScenarioLevelPlugin":35,"./mocha/StepLevelPlugin":36}],34:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Localisation = require('../../localisation');
+var Platform = require('../../Platform');
+var FeatureFileParser = require('../../parsers/FeatureFileParser');
+var $ = require('../../Array');
+
+module.exports.create = function(options) {
+
+ /* jslint shadow: true */
+ var platform = new Platform();
+ var language = options.language || Localisation.default;
+ var parser = options.parser || new FeatureFileParser(language);
+ var container = options.container || platform.get_container();
+
+ function featureFiles(files, iterator) {
+ $(files).each(function(file) {
+ features(parser.parse(file), iterator);
+ });
+ }
+
+ function features(features, iterator) {
+ $(features).each(function(feature) {
+ describe(feature.title, feature, iterator);
+ });
+ }
+
+ function describe(title, subject, iterator) {
+ var _describe = getDescribe(subject.annotations);
+ _describe(title, function() {
+ iterator(subject);
+ });
+ }
+
+ function it_async(title, subject, iterator) {
+ var _it = getIt(subject.annotations);
+ _it(title, function(done) {
+ iterator(this, subject, done);
+ });
+ }
+
+ function it_sync(title, subject, iterator) {
+ var _it = getIt(subject.annotations);
+ _it(title, function() {
+ iterator(this, subject);
+ });
+ }
+
+ function getIt(annotations, next) {
+ if (has_annotation(annotations, 'pending')) return container.xit;
+ if (has_annotation(annotations, 'only')) return container.it.only || container.fit || container.iit;
+ return container.it;
+ }
+
+ function getDescribe(annotations, next) {
+ if (has_annotation(annotations, 'pending')) return container.xdescribe;
+ if (has_annotation(annotations, 'only')) return container.describe.only || container.fdescribe || container.ddescribe;
+ return container.describe;
+ }
+
+ function has_annotation(annotations, name) {
+ var regexp = new RegExp('^' + language.localise(name) + '$', 'i');
+ for (var key in annotations) {
+ if (regexp.test(key)) return true;
+ }
+ }
+
+ return {
+ featureFiles: featureFiles,
+ features: features,
+ describe: describe,
+ it_async: it_async,
+ it_sync: it_sync
+ };
+};
+
+},{"../../Array":1,"../../Platform":12,"../../localisation":27,"../../parsers/FeatureFileParser":28}],35:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('../../Array');
+var Platform = require('../../Platform');
+var BasePlugin = require('./BasePlugin');
+
+module.exports.init = function(options) {
+
+ /* jslint shadow: true */
+ var options = options || {};
+ var platform = new Platform();
+ var container = options.container || platform.get_container();
+
+ var base_plugin = BasePlugin.create(options);
+
+ function scenarios(scenarios, iterator) {
+ $(scenarios).each(function(scenario) {
+ var itFn = iterator.length == 1 ? base_plugin.it_sync : base_plugin.it_async;
+ itFn(scenario.title, scenario, iterator);
+ });
+ }
+
+ container.featureFiles = container.featureFile = base_plugin.featureFiles;
+ container.features = container.feature = base_plugin.features;
+ container.scenarios = container.scenario = scenarios;
+};
+
+},{"../../Array":1,"../../Platform":12,"./BasePlugin":34}],36:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('../../Array');
+var Platform = require('../../Platform');
+var BasePlugin = require('./BasePlugin');
+
+module.exports.init = function(options) {
+
+ /* jslint shadow: true */
+ var options = options || {};
+ var platform = new Platform();
+ var container = options.container || platform.get_container();
+
+ var base_plugin = BasePlugin.create(options);
+
+ function scenarios(scenarios, iterator) {
+ $(scenarios).each(function(scenario) {
+ base_plugin.describe(scenario.title, scenario, iterator);
+ });
+ }
+
+ function steps(steps, iterator) {
+
+ var abort = false;
+
+ $(steps).each(function(step) {
+ var stepFn = iterator.length == 1 ? step_sync : step_async;
+ stepFn(step, iterator);
+ });
+
+ function step_async(step, iterator) {
+ base_plugin.it_async(step, step, function(context, step, done) {
+ if (abort) {
+ context.skip && context.skip();
+ return done();
+ }
+ abort = true;
+ iterator(step, function(err) {
+ if (err) return done(err);
+ abort = false;
+ done();
+ });
+ });
+ }
+
+ function step_sync(step, iterator) {
+ base_plugin.it_sync(step, step, function(context, step) {
+ if (abort) return context.skip && context.skip();
+ abort = true;
+ iterator(step);
+ abort = false;
+ });
+ }
+ }
+
+ container.featureFiles = container.featureFile = base_plugin.featureFiles;
+ container.features = container.feature = base_plugin.features;
+ container.scenarios = container.scenario = scenarios;
+ container.steps = steps;
+};
+
+},{"../../Array":1,"../../Platform":12,"./BasePlugin":34}],37:[function(require,module,exports){
+(function (process){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Platform = require('../Platform');
+
+module.exports = (function() {
+
+ var platform = new Platform();
+
+ var shims = {
+ node: function() {
+ return {
+ fs: require('fs'),
+ path: require('path'),
+ process: process
+ };
+ },
+ phantom: function() {
+ return {
+ fs: require('./phantom-fs'),
+ path: require('./phantom-path'),
+ process: require('./phantom-process')
+ };
+ },
+ };
+
+ function get_shim() {
+ if (platform.is_phantom()) return shims.phantom();
+ if (platform.is_node()) return shims.node();
+ return {};
+ }
+
+ return get_shim();
+})();
+
+}).call(this,require('_process'))
+},{"../Platform":12,"./phantom-fs":38,"./phantom-path":39,"./phantom-process":40,"_process":43,"fs":41,"path":42}],38:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = (function() {
+ if (module.client) return {}; // Running in browser, not via node
+
+ var fs = require('fs');
+
+ fs.existsSync = fs.existsSync || fs.exists;
+
+ fs.readdirSync = fs.readdirSync || function(path) {
+ return fs.list(path).filter(function(name) {
+ return name != '.' && name != '..';
+ });
+ };
+
+ fs.statSync = fs.statSync || function(path) {
+ return {
+ isDirectory: function() {
+ return fs.isDirectory(path);
+ }
+ };
+ };
+
+ return fs;
+})();
+
+},{"fs":41}],39:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = (function() {
+ if (module.client) return {}; // Running in browser, not via node
+
+ var fs = require('fs');
+ var path = {};
+
+ try {
+ path = require('path');
+ } catch (e) {
+ // meh
+ }
+
+ path.join = path.join || function() {
+ return Array.prototype.join.call(arguments, fs.separator);
+ };
+
+ path.relative = path.relative || function(from, to) {
+ return from + fs.separator + to;
+ };
+
+ return path;
+
+})();
+
+},{"fs":41,"path":42}],40:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/* jslint node: true */
+"use strict";
+
+module.exports = (function() {
+ if (module.client) return {}; // Running in browser, not via node
+
+ var fs = require('fs');
+ var process = typeof process != 'undefined' ? process : {};
+
+ process.cwd = function() {
+ return fs.workingDirectory;
+ };
+
+ return process;
+
+})();
+
+},{"fs":41}],41:[function(require,module,exports){
+
+},{}],42:[function(require,module,exports){
+(function (process){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// resolves . and .. elements in a path array with directory names there
+// must be no slashes, empty elements, or device names (c:\) in the array
+// (so also no leading and trailing slashes - it does not distinguish
+// relative and absolute paths)
+function normalizeArray(parts, allowAboveRoot) {
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = parts.length - 1; i >= 0; i--) {
+ var last = parts[i];
+ if (last === '.') {
+ parts.splice(i, 1);
+ } else if (last === '..') {
+ parts.splice(i, 1);
+ up++;
+ } else if (up) {
+ parts.splice(i, 1);
+ up--;
+ }
+ }
+
+ // if the path is allowed to go above the root, restore leading ..s
+ if (allowAboveRoot) {
+ for (; up--; up) {
+ parts.unshift('..');
+ }
+ }
+
+ return parts;
+}
+
+// Split a filename into [root, dir, basename, ext], unix version
+// 'root' is just a slash, or nothing.
+var splitPathRe =
+ /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
+var splitPath = function(filename) {
+ return splitPathRe.exec(filename).slice(1);
+};
+
+// path.resolve([from ...], to)
+// posix version
+exports.resolve = function() {
+ var resolvedPath = '',
+ resolvedAbsolute = false;
+
+ for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
+ var path = (i >= 0) ? arguments[i] : process.cwd();
+
+ // Skip empty and invalid entries
+ if (typeof path !== 'string') {
+ throw new TypeError('Arguments to path.resolve must be strings');
+ } else if (!path) {
+ continue;
+ }
+
+ resolvedPath = path + '/' + resolvedPath;
+ resolvedAbsolute = path.charAt(0) === '/';
+ }
+
+ // At this point the path should be resolved to a full absolute path, but
+ // handle relative paths to be safe (might happen when process.cwd() fails)
+
+ // Normalize the path
+ resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
+ return !!p;
+ }), !resolvedAbsolute).join('/');
+
+ return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
+};
+
+// path.normalize(path)
+// posix version
+exports.normalize = function(path) {
+ var isAbsolute = exports.isAbsolute(path),
+ trailingSlash = substr(path, -1) === '/';
+
+ // Normalize the path
+ path = normalizeArray(filter(path.split('/'), function(p) {
+ return !!p;
+ }), !isAbsolute).join('/');
+
+ if (!path && !isAbsolute) {
+ path = '.';
+ }
+ if (path && trailingSlash) {
+ path += '/';
+ }
+
+ return (isAbsolute ? '/' : '') + path;
+};
+
+// posix version
+exports.isAbsolute = function(path) {
+ return path.charAt(0) === '/';
+};
+
+// posix version
+exports.join = function() {
+ var paths = Array.prototype.slice.call(arguments, 0);
+ return exports.normalize(filter(paths, function(p, index) {
+ if (typeof p !== 'string') {
+ throw new TypeError('Arguments to path.join must be strings');
+ }
+ return p;
+ }).join('/'));
+};
+
+
+// path.relative(from, to)
+// posix version
+exports.relative = function(from, to) {
+ from = exports.resolve(from).substr(1);
+ to = exports.resolve(to).substr(1);
+
+ function trim(arr) {
+ var start = 0;
+ for (; start < arr.length; start++) {
+ if (arr[start] !== '') break;
+ }
+
+ var end = arr.length - 1;
+ for (; end >= 0; end--) {
+ if (arr[end] !== '') break;
+ }
+
+ if (start > end) return [];
+ return arr.slice(start, end - start + 1);
+ }
+
+ var fromParts = trim(from.split('/'));
+ var toParts = trim(to.split('/'));
+
+ var length = Math.min(fromParts.length, toParts.length);
+ var samePartsLength = length;
+ for (var i = 0; i < length; i++) {
+ if (fromParts[i] !== toParts[i]) {
+ samePartsLength = i;
+ break;
+ }
+ }
+
+ var outputParts = [];
+ for (var i = samePartsLength; i < fromParts.length; i++) {
+ outputParts.push('..');
+ }
+
+ outputParts = outputParts.concat(toParts.slice(samePartsLength));
+
+ return outputParts.join('/');
+};
+
+exports.sep = '/';
+exports.delimiter = ':';
+
+exports.dirname = function(path) {
+ var result = splitPath(path),
+ root = result[0],
+ dir = result[1];
+
+ if (!root && !dir) {
+ // No dirname whatsoever
+ return '.';
+ }
+
+ if (dir) {
+ // It has a dirname, strip trailing slash
+ dir = dir.substr(0, dir.length - 1);
+ }
+
+ return root + dir;
+};
+
+
+exports.basename = function(path, ext) {
+ var f = splitPath(path)[2];
+ // TODO: make this comparison case-insensitive on windows?
+ if (ext && f.substr(-1 * ext.length) === ext) {
+ f = f.substr(0, f.length - ext.length);
+ }
+ return f;
+};
+
+
+exports.extname = function(path) {
+ return splitPath(path)[3];
+};
+
+function filter (xs, f) {
+ if (xs.filter) return xs.filter(f);
+ var res = [];
+ for (var i = 0; i < xs.length; i++) {
+ if (f(xs[i], i, xs)) res.push(xs[i]);
+ }
+ return res;
+}
+
+// String.prototype.substr - negative index don't work in IE8
+var substr = 'ab'.substr(-1) === 'b'
+ ? function (str, start, len) { return str.substr(start, len) }
+ : function (str, start, len) {
+ if (start < 0) start = str.length + start;
+ return str.substr(start, len);
+ }
+;
+
+}).call(this,require('_process'))
+},{"_process":43}],43:[function(require,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = setTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ currentQueue[queueIndex].run();
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ clearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ setTimeout(drainQueue, 0);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+// TODO(shtylman)
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}],"yadda":[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var api = {
+ Yadda: require('./Yadda'),
+ EventBus: require('./EventBus'),
+ Interpreter: require('./Interpreter'),
+ Context: require('./Context'),
+ Library: require('./Library'),
+ Dictionary: require('./Dictionary'),
+ FeatureFileSearch: require('./FeatureFileSearch'),
+ FileSearch: require('./FileSearch'),
+ Platform: require('./Platform'),
+ localisation: require('./localisation/index'),
+ parsers: require('./parsers/index'),
+ plugins: require('./plugins/index'),
+ shims: require('./shims/index'),
+ createInstance: function() {
+ // Not everyone shares my sense of humour re the recursive api :(
+ // See https://github.com/acuminous/yadda/issues/111
+ return api.Yadda.apply(null, Array.prototype.slice.call(arguments, 0));
+ }
+};
+
+module.exports = api;
+
+},{"./Context":3,"./Dictionary":4,"./EventBus":5,"./FeatureFileSearch":6,"./FileSearch":7,"./Interpreter":8,"./Library":10,"./Platform":12,"./Yadda":15,"./localisation/index":27,"./parsers/index":31,"./plugins/index":33,"./shims/index":37}]},{},["yadda"]);
diff --git a/dist/yadda-umd-0.13.1.js b/dist/yadda-umd-0.13.1.js
new file mode 100644
index 00000000..e28a582b
--- /dev/null
+++ b/dist/yadda-umd-0.13.1.js
@@ -0,0 +1,3015 @@
+require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= items.length) return callback(null, result);
+ iterate();
+ });
+ };
+ iterate();
+ }
+
+ function collect(items, iterator) {
+ var results = ensure_array();
+ for (var i = 0; i < items.length; i++) {
+ results.push(iterator(items[i]));
+ }
+ return results;
+ }
+
+ function inject(items, default_value, iterator) {
+ var result = default_value;
+ for (var i = 0; i < items.length; i++) {
+ result = iterator(result, items[i]);
+ }
+ return result;
+ }
+
+ function push_all(items, more_items) {
+ more_items = more_items ? [].concat(more_items) : [];
+ for (var i = 0; i < more_items.length; i++) {
+ items.push(more_items[i]);
+ }
+ }
+
+ function find_all(items, test) {
+ var results = ensure_array();
+ for (var i = 0; i < items.length; i++) {
+ if (test(items[i])) {
+ results.push(items[i]);
+ }
+ }
+ return results;
+ }
+
+ function find(items, test) {
+ var result;
+ for (var i = 0; i < items.length; i++) {
+ if (test(items[i])) {
+ result = items[i];
+ break;
+ }
+ }
+ return result;
+ }
+
+ function last(items) {
+ return items[items.length - 1];
+ }
+
+ function naked(items) {
+ return [].concat(items);
+ }
+
+ return ensure_array(obj);
+};
+
+},{"./fn":16}],2:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var LevenshteinDistanceScore = require('./LevenshteinDistanceScore');
+var $ = require('./Array');
+
+// Understands appropriateness of macros in relation to a specific step
+var Competition = function(step, macros) {
+
+ var results = [];
+
+ this.validate = function() {
+ if (is_undefined()) return { step: step, valid: false, reason: 'Undefined Step' };
+ if (is_ambiguous()) return { step: step, valid: false, reason: 'Ambiguous Step (Patterns [' + winning_patterns() + '] are all equally good candidates)' };
+ return { step: step, valid: true };
+ };
+
+ this.clear_winner = function() {
+ if (is_undefined()) throw new Error('Undefined Step: [' + step + ']');
+ if (is_ambiguous()) throw new Error('Ambiguous Step: [' + step + ']. Patterns [' + winning_patterns() + '] match equally well.');
+ return this.winner();
+ };
+
+ function is_undefined() {
+ return results.length === 0;
+ }
+
+ function is_ambiguous() {
+ return (results.length > 1) && results[0].score.equals(results[1].score);
+ }
+
+ this.winner = function() {
+ return results[0].macro;
+ };
+
+ function winning_patterns() {
+ return results.find_all(by_winning_score).collect(macro_signatures).join(', ');
+ }
+
+ function rank(step, macros) {
+ results = macros.collect(function(macro) {
+ return {
+ macro: macro,
+ score: new LevenshteinDistanceScore(step, macro.levenshtein_signature())
+ };
+ }).sort( by_ascending_score );
+ }
+
+ function by_ascending_score(a, b) {
+ return b.score.beats(a.score);
+ }
+
+ function by_winning_score(result) {
+ return result.score.equals(results[0].score);
+ }
+
+ function macro_signatures(result) {
+ return result.macro.toString();
+ }
+
+ rank(step, $(macros));
+};
+
+module.exports = Competition;
+
+},{"./Array":1,"./LevenshteinDistanceScore":9}],3:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+// Constructs an object that macros will be bound to before execution
+var Context = function(properties) {
+
+ // I was previously getting some weird errors using instanceof to determine if
+ // the "other" object was a context object. Using pTFUHht733hM6wfnruGLgAu6Uqvy7MVp instead
+ this.pTFUHht733hM6wfnruGLgAu6Uqvy7MVp = true;
+ this.properties = {};
+
+ this.merge = function(other) {
+ if (other && other.pTFUHht733hM6wfnruGLgAu6Uqvy7MVp) return this.merge(other.properties);
+ return new Context(this.properties)._merge(other);
+ };
+
+ this._merge = function(other) {
+ for (var key in other) { this.properties[key] = other[key]; }
+ return this;
+ };
+
+ this._merge(properties);
+};
+
+module.exports = Context;
+
+},{}],4:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('./Array');
+var RegularExpression = require('./RegularExpression');
+
+// Understands term definitions
+var Dictionary = function(prefix) {
+
+ /* jslint shadow: true */
+ var prefix = prefix || '$';
+ var terms = {};
+ var term_pattern = new RegularExpression(new RegExp('(?:^|[^\\\\])\\' + prefix + '(\\w+)', 'g'));
+ var _this = this;
+
+ this.define = function(term, definition) {
+ if (this.is_defined(term)) throw new Error('Duplicate definition: [' + term + ']');
+ terms[term] = normalise(definition);
+ return this;
+ };
+
+ this.is_defined = function(term) {
+ return terms[term];
+ };
+
+ this.expand = function(term, already_expanding) {
+ if (!is_expandable(term)) return term;
+ return expand_sub_terms(term, $(already_expanding));
+ };
+
+ this.merge = function(other) {
+ if (other._prefix() != this._prefix()) throw new Error('Cannot merge dictionaries with different prefixes');
+ return new Dictionary(prefix)._merge(this)._merge(other);
+ };
+
+ this._merge = function(other) {
+ other.each_term(this.define.bind(this));
+ return this;
+ };
+
+ this._prefix = function() {
+ return prefix;
+ };
+
+ this.each_term = function(callback) {
+ for (var key in terms) {
+ callback(key, terms[key]);
+ }
+ };
+
+ var expand_sub_terms = function(term, already_expanding) {
+ return get_sub_terms(term).each(function(sub_term) {
+ if (already_expanding.in_array(sub_term)) throw new Error('Circular Definition: [' + already_expanding.join(', ') + ']');
+ var sub_term_definition = expand_sub_term(sub_term, already_expanding);
+ term = term.replace(prefix + sub_term, sub_term_definition);
+ return term;
+ });
+ };
+
+ var get_sub_terms = function(term) {
+ return term_pattern.groups(term);
+ };
+
+ var expand_sub_term = function(sub_term, already_expanding) {
+ var definition = terms[sub_term] || '(.+)';
+ return is_expandable(definition) ? _this.expand(definition, already_expanding.concat(sub_term)) : definition;
+ };
+
+ var normalise = function(definition) {
+ return definition.toString().replace(/^\/|\/$/g, '');
+ };
+
+ var is_expandable = function(definition) {
+ return term_pattern.test(definition);
+ };
+};
+
+
+module.exports = Dictionary;
+
+},{"./Array":1,"./RegularExpression":13}],5:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('./Array');
+var fn = require('./fn');
+var event_bus = new EventBus();
+
+// A communication channel between event emitters and event listeners
+function EventBus() {
+
+ var event_handlers = $();
+ var _this = this;
+
+ this.send = function(event_name, event_data, next) {
+ if (arguments.length == 1) return this.send(event_name, {});
+ if (arguments.length == 2 && fn.is_function(event_data)) return this.send(event_name, {}, event_data);
+ notify_handlers(event_name, event_data);
+ next && next();
+ return this;
+ };
+
+ this.on = function(event_pattern, callback) {
+ event_handlers.push({ pattern: event_pattern, callback: callback });
+ return this;
+ };
+
+ var notify_handlers = function(event_name, event_data) {
+ find_handlers(event_name).each(function(callback) {
+ callback({ name: event_name, data: event_data });
+ });
+ };
+
+ var find_handlers = function(event_name) {
+ return event_handlers.find_all(function(handler) {
+ return new RegExp(handler.pattern).test(event_name);
+ }).collect(function(handler) {
+ return handler.callback;
+ });
+ };
+}
+
+function instance() {
+ return event_bus;
+}
+
+module.exports = {
+ instance: instance,
+ ON_SCENARIO: '__ON_SCENARIO__',
+ ON_STEP: '__ON_STEP__',
+ ON_EXECUTE: '__ON_EXECUTE__'
+};
+
+},{"./Array":1,"./fn":16}],6:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var FileSearch = require('./FileSearch');
+
+var FeatureFileSearch = function(directories) {
+ this.constructor(directories, /.*\.(?:feature|spec|specification)$/);
+};
+FeatureFileSearch.prototype = new FileSearch();
+
+module.exports = FeatureFileSearch;
+
+},{"./FileSearch":7}],7:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var shims = require('./shims/index');
+var path = shims.path;
+var process = shims.process;
+var fs = shims.fs;
+var $ = require('./Array');
+
+// Searches for files in the given directories and their sub-directories, matching at least one of the given patterns
+var FileSearch = function(directories, patterns) {
+
+ /* jslint shadow: true */
+ var patterns = patterns || /.*/;
+
+ this.each = function(fn) {
+ this.list().forEach(fn);
+ };
+
+ this.list = function() {
+ return $(directories).inject($(), function(files, directory) {
+ return files.concat(list_files(directory).find_all(by_pattern));
+ });
+ };
+
+ var list_files = function(directory) {
+ return $(list_immediate_files(directory).concat(list_sub_directory_files(directory)));
+ };
+
+ var list_immediate_files = function(directory) {
+ return ls(directory).find_all(by_file);
+ };
+
+ var list_sub_directory_files = function(directory) {
+ return ls(directory).find_all(by_directory).inject($(), function(files, directory) {
+ return files.concat(list_files(directory));
+ });
+ };
+
+ var ls = function(directory) {
+ if (!fs.existsSync(directory)) return $();
+ return $(fs.readdirSync(directory)).collect(function(file) {
+ return path.join(directory, file);
+ });
+ };
+
+ var by_file = function(file) {
+ return !by_directory(file);
+ };
+
+ var by_directory = function(file) {
+ return fs.statSync(file).isDirectory();
+ };
+
+ var by_pattern = function(filename) {
+ return $(patterns).find(function(pattern) {
+ return new RegExp(pattern).test(filename);
+ });
+ };
+};
+
+module.exports = FileSearch;
+
+},{"./Array":1,"./shims/index":37}],8:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Competition = require('./Competition');
+var Context = require('./Context');
+var EventBus = require('./EventBus');
+var $ = require('./Array');
+var fn = require('./fn');
+
+// Understands a scenario
+var Interpreter = function(libraries) {
+
+ /* jslint shadow: true */
+ var libraries = $(libraries);
+ var event_bus = EventBus.instance();
+ var _this = this;
+
+ this.requires = function(libraries) {
+ libraries.push_all(libraries);
+ return this;
+ };
+
+ this.validate = function(scenario) {
+ var results = $(scenario).collect(function(step) {
+ return _this.rank_macros(step).validate();
+ });
+ if (results.find(by_invalid_step)) throw new Error('Scenario cannot be interpreted\n' + results.collect(validation_report).join('\n'));
+ };
+
+ function by_invalid_step(result) {
+ return !result.valid;
+ }
+
+ function validation_report(result) {
+ return result.step + (result.valid ? '' : ' <-- ' + result.reason);
+ }
+
+ this.interpret = function(scenario, scenario_context, next) {
+ scenario_context = new Context().merge(scenario_context);
+ event_bus.send(EventBus.ON_SCENARIO, { scenario: scenario, ctx: scenario_context.properties });
+ var iterator = make_step_iterator(scenario_context, next);
+ $(scenario).each_async(iterator, next);
+ };
+
+ var make_step_iterator = function(scenario_context, next) {
+ var iterator = function(step, index, callback) {
+ _this.interpret_step(step, scenario_context, callback);
+ };
+ return next ? iterator : fn.asynchronize(null, iterator);
+ };
+
+ this.interpret_step = function(step, scenario_context, next) {
+ var context = new Context().merge(scenario_context);
+ event_bus.send(EventBus.ON_STEP, { step: step, ctx: context.properties });
+ this.rank_macros(step).clear_winner().interpret(step, context || {}, next);
+ };
+
+ this.rank_macros = function(step) {
+ return new Competition(step, compatible_macros(step));
+ };
+
+ var compatible_macros = function(step) {
+ return libraries.inject([], function(macros, library) {
+ return macros.concat(library.find_compatible_macros(step));
+ });
+ };
+};
+
+module.exports = Interpreter;
+
+},{"./Array":1,"./Competition":2,"./Context":3,"./EventBus":5,"./fn":16}],9:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+// Understands similarity of two strings
+var LevenshteinDistanceScore = function(s1, s2) {
+
+ this.value;
+ this.type = 'LevenshteinDistanceScore';
+ var distance_table;
+ var _this = this;
+
+ var initialise = function() {
+
+ /* jslint shadow: true */
+
+ var x = s1.length;
+ var y = s2.length;
+
+ distance_table = new Array(x + 1);
+
+ for (var i = 0; i <= x; i++) {
+ distance_table[i] = new Array(y + 1);
+ }
+
+ for (var i = 0; i <= x; i++) {
+ for (var j = 0; j <= y; j++) {
+ distance_table[i][j] = 0;
+ }
+ }
+
+ for (var i = 0; i <= x; i++) {
+ distance_table[i][0] = i;
+ }
+
+ for (var j = 0; j <= y; j++) {
+ distance_table[0][j] = j;
+ }
+ };
+
+ var score = function() {
+
+ /*jslint boss: true */
+ if (s1 == s2) return _this.value = 0;
+
+ for (var j = 0; j < s2.length; j++) {
+ for (var i = 0; i < s1.length; i++) {
+ if (s1[i] == s2[j]) {
+ distance_table[i+1][j+1] = distance_table[i][j];
+ } else {
+ var deletion = distance_table[i][j+1] + 1;
+ var insertion = distance_table[i+1][j] + 1;
+ var substitution = distance_table[i][j] + 1;
+ distance_table[i+1][j+1] = Math.min(substitution, deletion, insertion);
+ }
+ }
+ }
+ _this.value = distance_table[s1.length][s2.length];
+ };
+
+ this.beats = function(other) {
+ return this.value < other.value;
+ };
+
+ this.equals = function(other) {
+ if (!other) return false;
+ return (this.type == other.type && this.value == other.value);
+ };
+
+ initialise();
+ score();
+};
+
+module.exports = LevenshteinDistanceScore;
+
+},{}],10:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Macro = require('./Macro');
+var Dictionary = require('./Dictionary');
+var $ = require('./Array');
+
+// Understands how to index macros
+var Library = function(dictionary) {
+
+ /* jslint shadow: true */
+ var dictionary = dictionary || new Dictionary();
+ var macros = $();
+ var _this = this;
+
+ this.define = function(signatures, fn, macro_context) {
+ $(signatures).each(function(signature) {
+ define_macro(signature, fn, macro_context);
+ });
+ return this;
+ };
+
+ var define_macro = function(signature, fn, macro_context) {
+ if (_this.get_macro(signature)) throw new Error('Duplicate macro: [' + signature + ']');
+ macros.push(new Macro(signature, dictionary.expand(signature), fn, macro_context));
+ };
+
+ this.get_macro = function(signature) {
+ return macros.find(function(other_macro) {
+ return other_macro.is_identified_by(signature);
+ });
+ };
+
+ this.find_compatible_macros = function(step) {
+ return macros.find_all(function(macro) {
+ return macro.can_interpret(step);
+ });
+ };
+};
+
+module.exports = Library;
+
+},{"./Array":1,"./Dictionary":4,"./Macro":11}],11:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var fn = require('./fn');
+var Context = require('./Context');
+var RegularExpression = require('./RegularExpression');
+var EventBus = require('./EventBus');
+
+// Understands how to invoke a step
+var Macro = function(signature, signature_pattern, macro, macro_context) {
+
+ /* jslint shadow: true */
+ var signature_pattern = new RegularExpression(signature_pattern);
+ var macro = macro || fn.async_noop;
+ var event_bus = EventBus.instance();
+ var _this = this;
+
+ var init = function(signature, signature_pattern) {
+ _this.signature = normalise(signature);
+ };
+
+ this.is_identified_by = function(other_signature) {
+ return this.signature == normalise(other_signature);
+ };
+
+ this.can_interpret = function(step) {
+ return signature_pattern.test(step);
+ };
+
+ this.interpret = function(step, scenario_context, next) {
+ var context = new Context().merge(macro_context).merge(scenario_context);
+ var args = signature_pattern.groups(step);
+ event_bus.send(EventBus.ON_EXECUTE, { step: step, ctx: context.properties, pattern: signature_pattern.toString(), args: args });
+ fn.invoke(macro, context.properties, args.concat(next));
+ };
+
+ this.levenshtein_signature = function() {
+ return signature_pattern.without_expressions();
+ };
+
+ var normalise = function(signature) {
+ return new RegExp(signature).toString();
+ };
+
+ this.toString = function() {
+ return this.signature;
+ };
+
+ init(signature, signature_pattern);
+};
+
+module.exports = Macro;
+
+},{"./Context":3,"./EventBus":5,"./RegularExpression":13,"./fn":16}],12:[function(require,module,exports){
+(function (process,global,__dirname){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+/* jslint browser: true */
+/* jslint phantom: true */
+"use strict";
+
+module.exports = Platform;
+
+function Platform() {
+
+ function get_container() {
+ if (is_browser()) return window;
+ if (is_phantom()) return phantom;
+ if (is_node()) return global;
+ }
+
+ function is_node() {
+ return typeof process != 'undefined' &&
+ typeof GLOBAL != 'undefined' &&
+ typeof __dirname != 'undefined';
+ }
+
+ function is_browser() {
+ return typeof window != 'undefined';
+ }
+
+ function is_phantom() {
+ return typeof phantom != 'undefined';
+ }
+
+ return {
+ get_container: get_container,
+ is_node: is_node,
+ is_browser: is_browser,
+ is_phantom: is_phantom
+ };
+
+}
+
+}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {},"/lib")
+},{"_process":43}],13:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* jslint node: true */
+ "use strict";
+
+var $ = require('./Array');
+
+// Wrapper for JavaScript Regular Expressions
+var RegularExpression = function(pattern_or_regexp) {
+
+ var groups_pattern = /(^|[^\\\\])\(.*?\)/g;
+ var sets_pattern = /(^|[^\\\\])\[.*?\]/g;
+ var repetitions_pattern = /(^|[^\\\\])\{.*?\}/g;
+ var regex_aliases_pattern = /(^|[^\\\\])\\./g;
+ var non_word_tokens_pattern = /[^\w\s]/g;
+ var regexp = new RegExp(pattern_or_regexp);
+
+ this.test = function(text) {
+ var result = regexp.test(text);
+ this.reset();
+ return result;
+ };
+
+ this.groups = function(text) {
+ var results = $();
+ var match = regexp.exec(text);
+ while (match) {
+ var groups = match.slice(1, match.length);
+ results.push(groups);
+ match = regexp.global && regexp.exec(text);
+ }
+ this.reset();
+ return results.flatten();
+ };
+
+ this.reset = function() {
+ regexp.lastIndex = 0;
+ return this;
+ };
+
+ this.without_expressions = function() {
+ return regexp.source.replace(groups_pattern, '$1')
+ .replace(sets_pattern, '$1')
+ .replace(repetitions_pattern, '$1')
+ .replace(regex_aliases_pattern, '$1')
+ .replace(non_word_tokens_pattern, '');
+ };
+
+ this.equals = function(other) {
+ return this.toString() == other.toString();
+ };
+
+ this.toString = function() {
+ return "/" + regexp.source + "/";
+ };
+};
+
+module.exports = RegularExpression;
+
+},{"./Array":1}],14:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = {
+
+ trim: function trim(text) {
+ return text.replace(/^\s+|\s+$/g, '');
+ },
+ rtrim: function rtrim(text) {
+ return text.replace(/\s+$/g, '');
+ },
+ isBlank: function isBlank(text) {
+ return /^\s*$/g.test(text);
+ },
+ isNotBlank: function isNotBlank(text) {
+ return !this.isBlank(text);
+ },
+ indentation: function indentation(text) {
+ var match = /^(\s*)/.exec(text);
+ return match && match[0].length || 0;
+ }
+};
+
+},{}],15:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*jslint node: true */
+"use strict";
+
+var Interpreter = require('./Interpreter');
+var Context = require('./Context');
+var fn = require('./fn');
+
+var Yadda = function(libraries, interpreter_context) {
+
+ if (!(this instanceof Yadda)) {
+ return new Yadda(libraries, interpreter_context);
+ }
+
+ this.interpreter = new Interpreter(libraries);
+ var _this = this;
+
+ this.requires = function(libraries) {
+ this.interpreter.requires(libraries);
+ return this;
+ };
+
+ this.yadda = function(scenario, scenario_context, next) {
+ if (arguments.length === 0) return this;
+ if (arguments.length === 2 && fn.is_function(scenario_context)) return this.yadda(scenario, {}, scenario_context);
+ this.interpreter.validate(scenario);
+ this.interpreter.interpret(scenario, new Context().merge(interpreter_context).merge(scenario_context), next);
+ };
+
+ // Not everyone shares my sense of humour re the recursive api :(
+ // See https://github.com/acuminous/yadda/issues/111
+ this.run = this.yadda;
+
+ this.toString = function() {
+ return "Yadda 0.13.1 Copyright 2010 Acuminous Ltd / Energized Work Ltd";
+ };
+};
+
+module.exports = Yadda;
+
+},{"./Context":3,"./Interpreter":8,"./fn":16}],16:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = (function() {
+
+ var slice = Array.prototype.slice;
+
+ function curry(ctx, fn) {
+ var args = slice.call(arguments, 2);
+ return function() {
+ return fn.apply(ctx, args.concat(slice.call(arguments)));
+ };
+ }
+
+ function invoke(fn, ctx, args) {
+ return fn.apply(ctx, args);
+ }
+
+ function is_function(object) {
+ var getType = {};
+ return object && getType.toString.call(object) === '[object Function]';
+ }
+
+ function noop() {}
+
+ function asynchronize(ctx, fn) {
+ return function() {
+ var next = slice.call(arguments, arguments.length - 1)[0];
+ var args = slice.call(arguments, 0, arguments.length - 2);
+ fn.apply(ctx, args);
+ if (next) next();
+ };
+ }
+
+ return {
+ noop: noop,
+ async_noop: asynchronize(null, noop),
+ asynchronize: asynchronize,
+ is_function: is_function,
+ curry: curry,
+ invoke: invoke
+ };
+
+
+})();
+
+},{}],17:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '[Ff]eature',
+ scenario: '(?:[Ss]cenario|[Ss]cenario [Oo]utline)',
+ examples: '(?:[Ee]xamples|[Ww]here)',
+ pending: '(?:[Pp]ending|[Tt]odo)',
+ only: '(?:[Oo]nly)',
+ background: '[Bb]ackground',
+ given: '(?:[Gg]iven|[Ww]ith|[Aa]nd|[Bb]ut|[Ee]xcept)',
+ when: '(?:[Ww]hen|[Ii]f|[Aa]nd|[Bb]ut)',
+ then: '(?:[Tt]hen|[Ee]xpect|[Aa]nd|[Bb]ut)',
+ _steps: ['given', 'when', 'then']
+ };
+
+ return new Language('English', vocabulary);
+})();
+
+},{"./Language":20}],18:[function(require,module,exports){
+/* -*- coding: utf-8 -*-
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Ff]onctionnalité)',
+ scenario: '(?:[Ss]cénario|[Pp]lan [Dd]u [Ss]cénario)',
+ examples: '(?:[Ee]xemples|[Ee]xemple|[Oo][uù])',
+ pending: '(?:[Ee]n attente|[Ee]n cours|[Tt]odo)',
+ only: '(?:[Ss]eulement])',
+ background: '(?:[Cc]ontexte)',
+ given: '(?:[Ss]oit|[ÉéEe]tant données|[ÉéEe]tant donnée|[ÉéEe]tant donnés|[ÉéEe]tant donné|[Aa]vec|[Ee]t|[Mm]ais|[Aa]ttendre)',
+ when: '(?:[Qq]uand|[Ll]orsqu\'|[Ll]orsque|[Ss]i|[Ee]t|[Mm]ais)',
+ then: '(?:[Aa]lors|[Aa]ttendre|[Ee]t|[Mm]ais)',
+
+ _steps: [
+ 'given', 'when', 'then',
+ 'soit', 'etantdonnees', 'etantdonnee', 'etantdonne',
+ 'quand', 'lorsque',
+ 'alors'
+ ],
+ // Also aliasing French verbs for given-when-then for signature-lookup
+ get soit() { return this.given; },
+ get etantdonnees() { return this.given; },
+ get etantdonnee() { return this.given; },
+ get etantdonne() { return this.given; },
+ get quand() { return this.when; },
+ get lorsque() { return this.when; },
+ get alors() { return this.then; }
+ };
+
+ return new Language('French', vocabulary);
+})();
+
+},{"./Language":20}],19:[function(require,module,exports){
+/*
+* Copyright 2010 Acuminous Ltd / Energized Work Ltd
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Ff]unktionalität|[Ff]eature|[Aa]spekt|[Uu]secase|[Aa]nwendungsfall)',
+ scenario: '(?:[Ss]zenario|[Ss]zenario( g|G)rundriss|[Gg]eschehnis)',
+ examples: '(?:[Bb]eispiele?)',
+ pending: '(?:[Tt]odo|[Oo]ffen)',
+ only: '(?:[Nn]ur|[Ee]inzig)',
+ background: '(?:[Gg]rundlage|[Hh]intergrund|[Ss]etup|[Vv]orausgesetzt)',
+ given: '(?:[Aa]ngenommen|[Gg]egeben( sei(en)?)?|[Mm]it|[Uu]nd|[Aa]ber|[Aa]ußer)',
+ when: '(?:[Ww]enn|[Ff]alls|[Uu]nd|[Aa]ber)',
+ then: '(?:[Dd]ann|[Ff]olglich|[Aa]ußer|[Uu]nd|[Aa]ber)',
+ _steps: ['given', 'when', 'then']
+ };
+
+ return new Language('German', vocabulary);
+})();
+
+},{"./Language":20}],20:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Library = require('../Library');
+var $ = require('../Array');
+
+module.exports = function(name, vocabulary) {
+
+ var _this = this;
+
+ this.library = function(dictionary) {
+ return _this.localise_library(new Library(dictionary));
+ };
+
+ this.localise_library = function(library) {
+ $(vocabulary._steps).each(function(keyword) {
+ library[keyword] = function(signatures, fn, ctx) {
+ return $(signatures).each(function(signature) {
+ signature = prefix_signature(_this.localise(keyword), signature);
+ return library.define(signature, fn, ctx);
+ });
+ };
+ });
+ return library;
+ };
+
+ var prefix_signature = function(prefix, signature) {
+ var regex_delimiters = new RegExp('^/|/$', 'g');
+ var start_of_signature = new RegExp(/^(?:\^)?/);
+ var one_or_more_spaces = '\\s+';
+ return signature.toString().replace(regex_delimiters, '').replace(start_of_signature, prefix + one_or_more_spaces);
+ };
+
+ this.localise = function(keyword) {
+ if (vocabulary[keyword] === undefined) throw new Error('Keyword "' + keyword + '" has not been translated into ' + name + '.');
+ return vocabulary[keyword];
+ };
+};
+
+},{"../Array":1,"../Library":10}],21:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '[Ee]genskap',
+ scenario: '[Ss]cenario',
+ examples: '[Ee]ksempler',
+ pending: '[Aa]vventer',
+ only: '[Bb]are',
+ background: '[Bb]akgrunn',
+ given: '(?:[Gg]itt|[Mm]ed|[Oo]g|[Mm]en|[Uu]nntatt)',
+ when: '(?:[Nn]år|[Oo]g|[Mm]en)',
+ then: '(?:[Ss]å|[Ff]forvent|[Oo]g|[Mm]en)',
+ _steps: ['given', 'when', 'then', 'gitt', 'når', 'så'],
+ // Also aliasing Norwegian verbs for given-when-then for signature-lookup
+ get gitt() { return this.given; },
+ get når() { return this.when; },
+ get så() { return this.then; }
+ };
+
+ return new Language('Norwegian', vocabulary);
+})();
+
+},{"./Language":20}],22:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Tt]ale|[Yy]arn)',
+ scenario: '(?:[Aa]dventure|[Ss]ortie)',
+ examples: '[Ww]herest',
+ pending: '[Bb]rig',
+ only: '[Bb]lack [Ss]pot',
+ background: '[Aa]ftground',
+ given: '(?:[Gg]iveth|[Ww]ith|[Aa]nd|[Bb]ut|[Ee]xcept)',
+ when: '(?:[Ww]hence|[Ii]f|[Aa]nd|[Bb]ut)',
+ then: '(?:[Tt]hence|[Ee]xpect|[Aa]nd|[Bb]ut)',
+ _steps: ['given', 'when', 'then', 'giveth', 'whence', 'thence'],
+ // Also aliasing Pirate verbs for given-when-then for signature-lookup
+ get giveth() { return this.given; },
+ get whence() { return this.when; },
+ get thence() { return this.then; }
+
+ };
+
+ return new Language('Pirate', vocabulary);
+})();
+
+},{"./Language":20}],23:[function(require,module,exports){
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Ww]łaściwość|[Ff]unkcja|[Aa]spekt|[Pp]otrzeba [Bb]iznesowa)',
+ scenario: '(?:[Ss]cenariusz|[Ss]zablon [Ss]cenariusza)',
+ examples: '[Pp]rzykłady',
+ pending: '(?:[Oo]czekujący|[Nn]iezweryfikowany|[Tt]odo)',
+ only: '[Tt]ylko',
+ background: '[Zz]ałożenia',
+ given: '(?:[Zz]akładając|[Mm]ając|[Oo]raz|[Ii]|[Aa]le)',
+ when: '(?:[Jj]eżeli|[Jj]eśli|[Gg]dy|[Kk]iedy|[Oo]raz|[Ii]|[Aa]le)',
+ then: '(?:[Ww]tedy|[Oo]raz|[Ii]|[Aa]le)',
+ _steps: [
+ 'given', 'when', 'then',
+ 'zakladajac', 'majac',
+ 'jezeli', 'jesli', 'gdy', 'kiedy',
+ 'wtedy'
+ ],
+ // Also aliasing Polish verbs for given-when-then for signature-lookup
+ get zakladajac() { return this.given; },
+ get majac() { return this.given; },
+ get jezeli() { return this.when; },
+ get jesli() { return this.when; },
+ get gdy() { return this.when; },
+ get kiedy() { return this.when; },
+ get wtedy() { return this.then; }
+ };
+
+ return new Language('Polish', vocabulary);
+})();
+
+},{"./Language":20}],24:[function(require,module,exports){
+/* -*- coding: utf-8 -*-
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function () {
+
+ var vocabulary = {
+ feature: '(?:[Ff]uncionalidade|[Cc]aracter[íi]stica)',
+ scenario: '(?:[Cc]en[aá]rio|[Cc]aso)',
+ examples: '(?:[Ee]xemplos|[Ee]xemplo)',
+ pending: '[Pp]endente',
+ only: '[S][óo]',
+ background: '[Ff]undo',
+ given: '(?:[Ss]eja|[Ss]ejam|[Dd]ado|[Dd]ada|[Dd]ados|[Dd]adas)',
+ when: '(?:[Qq]uando|[Ss]e|[Qq]ue|[Ee]|[Mm]as)',
+ then: '(?:[Ee]nt[aã]o|[Ee]|[Mm]as)',
+
+ _steps: [
+ 'given', 'when', 'then',
+ 'seja', 'sejam', 'dado', 'dada', 'dados', 'dadas',
+ 'quando', 'se',
+ 'entao'
+ ],
+
+ get seja() { return this.given; },
+ get sejam() { return this.given; },
+ get dado() { return this.given; },
+ get dada() { return this.given; },
+ get dados() { return this.given; },
+ get dadas() { return this.given; },
+ get quando() { return this.when; },
+ get se() { return this.when; },
+ get entao() { return this.then; }
+ };
+
+ return new Language('Portuguese', vocabulary);
+})();
+},{"./Language":20}],25:[function(require,module,exports){
+/* -*- coding: utf-8 -*-
+ * Author: Marat Dyatko
+ * https://github.com/vectart
+ *
+ * Inspired by Gherkin vocabulary
+ * https://github.com/cucumber/gherkin/blob/master/lib/gherkin/i18n.json
+ *
+ * Also considered syntax highlight of Cucumber Sublime bundle
+ * https://github.com/drewda/cucumber-sublime-bundle/blob/master/Cucumber%20Plain%20Text%20Feature.tmLanguage
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Фф]ункция|[Фф]ункционал|[Сс]войство)',
+ scenario: 'Сценарий',
+ examples: 'Примеры?',
+ pending: '(?:[Ww]ip|[Tt]odo)',
+ only: 'Только',
+ background: '(?:[Пп]редыстория|[Кк]онтекст)',
+ given: '(?:[Дд]опустим|[Дд]ано|[Пп]усть|[Ии]|[Н]о)(?:\\s[Яя])?',
+ when: '(?:[Ее]сли|[Кк]огда|[Ии]|[Н]о)(?:\\s[Яя])?',
+ then: '(?:[Тт]о|[Тт]огда|[Ии]|[Н]о)(?:\\s[Яя])?',
+ _steps: ['given', 'when', 'then']
+ };
+
+ return new Language('Russian', vocabulary);
+})();
+
+},{"./Language":20}],26:[function(require,module,exports){
+/* -*- coding: utf-8 -*-
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Language = require('./Language');
+
+module.exports = (function() {
+
+ var vocabulary = {
+ feature: '(?:[Ff]uncionalidad|[Cc]aracterística)',
+ scenario: '(?:[Ee]scenario|[Cc]aso)',
+ examples: '(?:[Ee]jemplos|[Ee]jemplo)',
+ pending: '[Pp]endiente',
+ only: '[S]ólo',
+ background: '[Ff]ondo',
+ given: '(?:[Ss]ea|[Ss]ean|[Dd]ado|[Dd]ada|[Dd]ados|[Dd]adas)',
+ when: '(?:[Cc]uando|[Ss]i|[Qq]ue)',
+ then: '(?:[Ee]ntonces)',
+
+ _steps: [
+ 'given', 'when', 'then',
+ 'sea', 'sean', 'dado', 'dada','dados', 'dadas',
+ 'cuando', 'si',
+ 'entonces'
+ ],
+
+ get sea() { return this.given; },
+ get sean() { return this.given; },
+ get dado() { return this.given; },
+ get dada() { return this.given; },
+ get dados() { return this.given; },
+ get dadas() { return this.given; },
+ get cuando() { return this.when; },
+ get si() { return this.when; },
+ get entonces() { return this.then; }
+ };
+
+ return new Language('Spanish', vocabulary);
+})();
+
+},{"./Language":20}],27:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = {
+ English: require('./English'),
+ French: require('./French'),
+ German: require('./German'),
+ Norwegian: require('./Norwegian'),
+ Pirate: require('./Pirate'),
+ Polish: require('./Polish'),
+ Spanish: require('./Spanish'),
+ Russian: require('./Russian'),
+ Portuguese: require('./Portuguese'),
+ default: require('./English'),
+ Language: require('./Language')
+};
+
+},{"./English":17,"./French":18,"./German":19,"./Language":20,"./Norwegian":21,"./Pirate":22,"./Polish":23,"./Portuguese":24,"./Russian":25,"./Spanish":26}],28:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* jslint node: true */
+ "use strict";
+
+var FeatureFileParser = function(language) {
+
+ // Requiring fs locally so it doesn't break component
+ var fs = require('fs');
+ var FeatureParser = require('./FeatureParser');
+ var parser = new FeatureParser(language);
+
+ this.parse = function(file, next) {
+ var text = fs.readFileSync(file, 'utf8');
+ var feature = parser.parse(text);
+ return next && next(feature) || feature;
+ };
+};
+
+module.exports = FeatureFileParser;
+
+},{"./FeatureParser":29,"fs":41}],29:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* jslint node: true */
+ "use strict";
+
+var $ = require('../Array');
+var fn = require('../fn');
+var StringUtils = require('../StringUtils');
+var Localisation = require('../localisation');
+
+var FeatureParser = function(language) {
+
+ /* jslint shadow: true */
+ var language = language || Localisation.default;
+
+ var FEATURE_REGEX = new RegExp('^\\s*' + language.localise('feature') + ':\\s*(.*)', 'i');
+ var SCENARIO_REGEX = new RegExp('^\\s*' + language.localise('scenario') + ':\\s*(.*)', 'i');
+ var BACKGROUND_REGEX = new RegExp('^\\s*' + language.localise('background') + ':\\s*(.*)', 'i');
+ var EXAMPLES_REGEX = new RegExp('^\\s*' + language.localise('examples') + ':', 'i');
+ var TEXT_REGEX = new RegExp('^(.*)$', 'i');
+ var SINGLE_LINE_COMMENT_REGEX = new RegExp('^\\s*#');
+ var MULTI_LINE_COMMENT_REGEX = new RegExp('^\\s*#{3,}');
+ var BLANK_REGEX = new RegExp('^\\s*$');
+ var DASH_REGEX = new RegExp('(^\\s*[\\|\u2506]?-{3,})');
+ var SIMPLE_ANNOTATION_REGEX = new RegExp('^\\s*@([^=]*)$');
+ var NVP_ANNOTATION_REGEX = new RegExp('^\\s*@([^=]*)=(.*)$');
+
+ var specification;
+ var comment;
+
+ this.parse = function(text, next) {
+ reset();
+ split(text).each(parse_line);
+ return next && next(specification.export()) || specification.export();
+ };
+
+ function reset() {
+ specification = new Specification();
+ comment = false;
+ }
+
+ function split(text) {
+ return $(text.split(/\r\n|\n/));
+ }
+
+ function parse_line(line, index) {
+ /* jslint boss: true */
+ var match;
+ try {
+ if (match = MULTI_LINE_COMMENT_REGEX.test(line)) return comment = !comment;
+ if (comment) return;
+ if (match = SINGLE_LINE_COMMENT_REGEX.test(line)) return;
+ if (match = SIMPLE_ANNOTATION_REGEX.exec(line)) return specification.handle('Annotation', { key: StringUtils.trim(match[1]), value: true });
+ if (match = NVP_ANNOTATION_REGEX.exec(line)) return specification.handle('Annotation', { key: StringUtils.trim(match[1]), value: StringUtils.trim(match[2]) });
+ if (match = FEATURE_REGEX.exec(line)) return specification.handle('Feature', match[1]);
+ if (match = SCENARIO_REGEX.exec(line)) return specification.handle('Scenario', match[1]);
+ if (match = BACKGROUND_REGEX.exec(line)) return specification.handle('Background', match[1]);
+ if (match = EXAMPLES_REGEX.exec(line)) return specification.handle('Examples');
+ if (match = BLANK_REGEX.test(line)) return specification.handle('Blank');
+ if (match = DASH_REGEX.exec(line)) return specification.handle('Dash', match[1]);
+ if (match = TEXT_REGEX.exec(line)) return specification.handle('Text', match[1]);
+ } catch (e) {
+ e.message = 'Error parsing line ' + (index + 1) + ', "' + line + '".\nOriginal error was: ' + e.message;
+ throw e;
+ }
+ }
+};
+
+var Handlers = function(handlers) {
+
+ /* jslint shadow: true */
+ var handlers = handlers || {};
+
+ this.register = function(event, handler) {
+ handlers[event] = handler;
+ };
+
+ this.unregister = function() {
+ $(Array.prototype.slice.call(arguments)).each(function(event) {
+ delete handlers[event];
+ });
+ };
+
+ this.find = function(event) {
+ if (!handlers[event.toLowerCase()]) throw new Error(event + ' is unexpected at this time');
+ return { handle: handlers[event.toLowerCase()] };
+ };
+};
+
+var Specification = function() {
+
+ var current_element = this;
+ var feature;
+ var annotations = new Annotations();
+ var handlers = new Handlers({
+ text: fn.noop,
+ blank: fn.noop,
+ annotation: stash_annotation,
+ feature: start_feature,
+ scenario: start_scenario,
+ background: start_background,
+ });
+
+ function stash_annotation(event, annotation) {
+ handlers.unregister('background');
+ annotations.stash(annotation.key, annotation.value);
+ }
+
+ function start_feature(event, title) {
+ /* jslint boss: true */
+ return feature = new Feature(title, annotations, new Annotations());
+ }
+
+ function start_scenario(event, title) {
+ feature = new Feature(title, new Annotations(), annotations);
+ return feature.on(event, title);
+ }
+
+ var start_background = start_scenario;
+
+ this.handle = function(event, data) {
+ current_element = current_element.on(event, data);
+ };
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ if (!feature) throw new Error('A feature must contain one or more scenarios');
+ return feature.export();
+ };
+};
+
+var Annotations = function() {
+
+ var annotations = {};
+
+ this.stash = function(key, value) {
+ if (/\s/.test(key)) throw new Error('Invalid annotation: ' + key);
+ annotations[key.toLowerCase()] = value;
+ };
+
+ this.export = function() {
+ return annotations;
+ };
+};
+
+var Feature = function(title, annotations, stashed_annotations) {
+
+ var description = [];
+ var scenarios = [];
+ var background = new NullBackground();
+ var handlers = new Handlers({
+ text: capture_description,
+ blank: end_description,
+ annotation: stash_annotation,
+ scenario: start_scenario,
+ background: start_background
+ });
+ var _this = this;
+
+ function start_background(event, title) {
+ background = new Background(title, _this);
+ stashed_annotations = new Annotations();
+ return background;
+ }
+
+ function stash_annotation(event, annotation) {
+ handlers.unregister('background');
+ stashed_annotations.stash(annotation.key, annotation.value);
+ }
+
+ function capture_description(event, text) {
+ description.push(StringUtils.trim(text));
+ }
+
+ function end_description() {
+ handlers.unregister('text');
+ handlers.register('blank', fn.noop);
+ }
+
+ function start_scenario(event, title) {
+ var scenario = new Scenario(title, background, stashed_annotations, _this);
+ scenarios.push(scenario);
+ stashed_annotations = new Annotations();
+ return scenario;
+ }
+
+ function validate() {
+ if (scenarios.length === 0) throw new Error('Feature requires one or more scenarios');
+ }
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ validate();
+ return {
+ title: title,
+ annotations: annotations.export(),
+ description: description,
+ scenarios: $(scenarios).collect(function(scenario) {
+ return scenario.export();
+ }).flatten().naked()
+ };
+ };
+};
+
+var Background = function(title, feature) {
+
+ var steps = [];
+ var handlers = new Handlers({
+ text: capture_step,
+ blank: fn.noop,
+ annotation: stash_annotation,
+ scenario: start_scenario
+ });
+ var _this = this;
+
+ function capture_step(event, text) {
+ steps.push(StringUtils.trim(text));
+ }
+
+ function stash_annotation(event, annotation) {
+ validate();
+ return feature.on(event, annotation);
+ }
+
+ function start_scenario(event, data) {
+ validate();
+ return feature.on(event, data);
+ }
+
+ function validate() {
+ if (steps.length === 0) throw new Error('Background requires one or more steps');
+ }
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ validate();
+ return {
+ steps: steps
+ };
+ };
+};
+
+var NullBackground = function() {
+ var handlers = new Handlers();
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ return {
+ steps: []
+ };
+ };
+};
+
+var Scenario = function(title, background, annotations, feature) {
+ var description = [];
+ var steps = [];
+ var examples;
+ var handlers = new Handlers({
+ text: capture_step,
+ blank: fn.noop,
+ annotation: start_scenario,
+ scenario: start_scenario,
+ examples: start_examples
+ });
+ var _this = this;
+
+ function capture_step(event, text) {
+ steps.push(StringUtils.trim(text));
+ }
+
+ function start_scenario(event, data) {
+ validate();
+ return feature.on(event, data);
+ }
+
+ function start_examples(event, data) {
+ validate();
+ /* jslint boss: true */
+ return examples = new Examples(_this);
+ }
+
+ function validate() {
+ if (steps.length === 0) throw new Error('Scenario requires one or more steps');
+ }
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.export = function() {
+ validate();
+ var result = {
+ title: title,
+ annotations: annotations.export(),
+ description: description,
+ steps: background.export().steps.concat(steps)
+ };
+ return examples ? examples.expand(result) : result;
+ };
+};
+
+var Examples = function(scenario) {
+
+ var headings = [];
+ var examples = $();
+ var annotations = new Annotations();
+ var handlers = new Handlers({
+ text: capture_headings,
+ blank: fn.noop,
+ scenario: start_scenario
+ });
+ var _this = this;
+
+ function capture_headings(event, data) {
+ handlers.register('annotation', stash_annotation);
+ handlers.register('text', capture_singleline_fields);
+ handlers.register('dash', enable_multiline_examples);
+ headings = split(data).collect(function(column) {
+ return { text: StringUtils.trim(column), indentation: StringUtils.indentation(column) };
+ }).naked();
+ }
+
+ function stash_annotation(event, annotation) {
+ handlers.unregister('blank', 'dash');
+ annotations.stash(annotation.key, annotation.value);
+ }
+
+ function capture_singleline_fields(event, data) {
+ handlers.unregister('dash');
+ handlers.register('blank', start_scenario);
+ examples.push({ annotations: annotations, fields: parse_fields(data, {}) });
+ annotations = new Annotations();
+ }
+
+ function enable_multiline_examples(event, data) {
+ handlers.register('text', start_capturing_multiline_fields);
+ handlers.register('dash', stop_capturing_multiline_fields);
+ }
+
+ function start_capturing_multiline_fields(event, data) {
+ handlers.register('text', continue_capturing_multiline_fields);
+ handlers.register('dash', stop_capturing_multiline_fields);
+ handlers.register('blank', start_scenario);
+ examples.push({ annotations: annotations, fields: parse_fields(data, {}) });
+ }
+
+ function continue_capturing_multiline_fields(event, data) {
+ parse_fields(data, examples.last().fields);
+ }
+
+ function stop_capturing_multiline_fields(event, data) {
+ handlers.register('text', start_capturing_multiline_fields);
+ annotations = new Annotations();
+ }
+
+ function parse_fields(row, fields) {
+ split(row, headings.length).each(function(field, index) {
+ var column = headings[index].text;
+ var indentation = headings[index].indentation;
+ var text = StringUtils.rtrim(field.substr(indentation));
+ if (StringUtils.isNotBlank(field) && StringUtils.indentation(field) < indentation) throw new Error('Indentation error');
+ fields[column] = (fields[column] || []).concat(text);
+ });
+ return fields;
+ }
+
+ function split(row, number_of_fields) {
+ var separator = row.indexOf('\u2506') >= 0 ? '\u2506' : '|';
+ var fields = $(row.split(separator));
+ if (number_of_fields !== undefined && number_of_fields != fields.length) {
+ throw new Error('Incorrect number of fields in example table. Expected ' + number_of_fields + ' but found ' + fields.length);
+ }
+ return fields;
+ }
+
+ function start_scenario(event, data) {
+ validate();
+ return scenario.on(event, data);
+ }
+
+ function validate() {
+ if (headings.length === 0) throw new Error('Examples table requires one or more headings');
+ if (examples.length === 0) throw new Error('Examples table requires one or more rows');
+ }
+
+ this.on = function(event, data) {
+ return handlers.find(event).handle(event, data) || this;
+ };
+
+ this.expand = function(scenario) {
+ validate();
+ return examples.collect(function(example) {
+ return {
+ title: substitute(example.fields, scenario.title),
+ annotations: shallow_merge(example.annotations.export(), scenario.annotations),
+ description: substitute_all(example, scenario.description),
+ steps: substitute_all(example.fields, scenario.steps)
+ };
+ }).naked();
+ };
+
+ function shallow_merge() {
+ var result = {};
+ $(Array.prototype.slice.call(arguments)).each(function(annotations) {
+ for (var key in annotations) {
+ result[key] = annotations[key];
+ }
+ });
+ return result;
+ }
+
+ function substitute_all(example, lines) {
+ return $(lines).collect(function(line) {
+ return substitute(example, line);
+ }).naked();
+ }
+
+ function substitute(example, line) {
+ for (var heading in example) {
+ line = line.replace(new RegExp('\\[\\s*' + heading + '\\s*\\]', 'g'), StringUtils.rtrim(example[heading].join('\n')));
+ }
+ return line;
+ }
+};
+
+module.exports = FeatureParser;
+
+},{"../Array":1,"../StringUtils":14,"../fn":16,"../localisation":27}],30:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('../Array');
+
+var StepParser = function() {
+
+ var NON_BLANK_REGEX = /[^\s]/;
+
+ this.parse = function(text, next) {
+ var steps = split(text).find_all(non_blanks);
+ return next && next(steps) || steps;
+ };
+
+ var split = function(text) {
+ return $(text.split(/\n/));
+ };
+
+ var non_blanks = function(text) {
+ return text && NON_BLANK_REGEX.test(text);
+ };
+};
+
+module.exports = StepParser;
+
+},{"../Array":1}],31:[function(require,module,exports){
+/* jslint node: true */
+"use strict";
+
+module.exports = {
+ StepParser: require('./StepParser'),
+ FeatureParser: require('./FeatureParser'),
+ FeatureFileParser: require('./FeatureFileParser')
+};
+
+},{"./FeatureFileParser":28,"./FeatureParser":29,"./StepParser":30}],32:[function(require,module,exports){
+(function (global){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+if (!module.client) {
+ var fs = require('fs');
+ global.process = global.process || {
+ cwd: function() {
+ return fs.workingDirectory;
+ }
+ };
+}
+
+
+module.exports = function(yadda, casper) {
+
+ var EventBus = require('yadda').EventBus;
+
+ yadda.interpreter.interpret_step = function(step, ctx, next) {
+
+ var _this = this;
+ casper.then(function() {
+ casper.test.info(step);
+ EventBus.instance().send(EventBus.ON_STEP, { step: step, ctx: ctx });
+ _this.rank_macros(step).clear_winner().interpret(step, ctx, next);
+ });
+ };
+
+ casper.yadda = function(script, ctx) {
+ if (script === undefined) return this;
+ yadda.run(script, ctx);
+ };
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"fs":41,"yadda":"yadda"}],33:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = {
+ casper: require('./CasperPlugin'),
+ mocha: {
+ ScenarioLevelPlugin: require('./mocha/ScenarioLevelPlugin'),
+ StepLevelPlugin: require('./mocha/StepLevelPlugin')
+ },
+ get jasmine() {
+ return this.mocha;
+ }
+};
+
+},{"./CasperPlugin":32,"./mocha/ScenarioLevelPlugin":35,"./mocha/StepLevelPlugin":36}],34:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Localisation = require('../../localisation');
+var Platform = require('../../Platform');
+var FeatureFileParser = require('../../parsers/FeatureFileParser');
+var $ = require('../../Array');
+
+module.exports.create = function(options) {
+
+ /* jslint shadow: true */
+ var platform = new Platform();
+ var language = options.language || Localisation.default;
+ var parser = options.parser || new FeatureFileParser(language);
+ var container = options.container || platform.get_container();
+
+ function featureFiles(files, iterator) {
+ $(files).each(function(file) {
+ features(parser.parse(file), iterator);
+ });
+ }
+
+ function features(features, iterator) {
+ $(features).each(function(feature) {
+ describe(feature.title, feature, iterator);
+ });
+ }
+
+ function describe(title, subject, iterator) {
+ var _describe = getDescribe(subject.annotations);
+ _describe(title, function() {
+ iterator(subject);
+ });
+ }
+
+ function it_async(title, subject, iterator) {
+ var _it = getIt(subject.annotations);
+ _it(title, function(done) {
+ iterator(this, subject, done);
+ });
+ }
+
+ function it_sync(title, subject, iterator) {
+ var _it = getIt(subject.annotations);
+ _it(title, function() {
+ iterator(this, subject);
+ });
+ }
+
+ function getIt(annotations, next) {
+ if (has_annotation(annotations, 'pending')) return container.xit;
+ if (has_annotation(annotations, 'only')) return container.it.only || container.fit || container.iit;
+ return container.it;
+ }
+
+ function getDescribe(annotations, next) {
+ if (has_annotation(annotations, 'pending')) return container.xdescribe;
+ if (has_annotation(annotations, 'only')) return container.describe.only || container.fdescribe || container.ddescribe;
+ return container.describe;
+ }
+
+ function has_annotation(annotations, name) {
+ var regexp = new RegExp('^' + language.localise(name) + '$', 'i');
+ for (var key in annotations) {
+ if (regexp.test(key)) return true;
+ }
+ }
+
+ return {
+ featureFiles: featureFiles,
+ features: features,
+ describe: describe,
+ it_async: it_async,
+ it_sync: it_sync
+ };
+};
+
+},{"../../Array":1,"../../Platform":12,"../../localisation":27,"../../parsers/FeatureFileParser":28}],35:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('../../Array');
+var Platform = require('../../Platform');
+var BasePlugin = require('./BasePlugin');
+
+module.exports.init = function(options) {
+
+ /* jslint shadow: true */
+ var options = options || {};
+ var platform = new Platform();
+ var container = options.container || platform.get_container();
+
+ var base_plugin = BasePlugin.create(options);
+
+ function scenarios(scenarios, iterator) {
+ $(scenarios).each(function(scenario) {
+ var itFn = iterator.length == 1 ? base_plugin.it_sync : base_plugin.it_async;
+ itFn(scenario.title, scenario, iterator);
+ });
+ }
+
+ container.featureFiles = container.featureFile = base_plugin.featureFiles;
+ container.features = container.feature = base_plugin.features;
+ container.scenarios = container.scenario = scenarios;
+};
+
+},{"../../Array":1,"../../Platform":12,"./BasePlugin":34}],36:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var $ = require('../../Array');
+var Platform = require('../../Platform');
+var BasePlugin = require('./BasePlugin');
+
+module.exports.init = function(options) {
+
+ /* jslint shadow: true */
+ var options = options || {};
+ var platform = new Platform();
+ var container = options.container || platform.get_container();
+
+ var base_plugin = BasePlugin.create(options);
+
+ function scenarios(scenarios, iterator) {
+ $(scenarios).each(function(scenario) {
+ base_plugin.describe(scenario.title, scenario, iterator);
+ });
+ }
+
+ function steps(steps, iterator) {
+
+ var abort = false;
+
+ $(steps).each(function(step) {
+ var stepFn = iterator.length == 1 ? step_sync : step_async;
+ stepFn(step, iterator);
+ });
+
+ function step_async(step, iterator) {
+ base_plugin.it_async(step, step, function(context, step, done) {
+ if (abort) {
+ context.skip && context.skip();
+ return done();
+ }
+ abort = true;
+ iterator(step, function(err) {
+ if (err) return done(err);
+ abort = false;
+ done();
+ });
+ });
+ }
+
+ function step_sync(step, iterator) {
+ base_plugin.it_sync(step, step, function(context, step) {
+ if (abort) return context.skip && context.skip();
+ abort = true;
+ iterator(step);
+ abort = false;
+ });
+ }
+ }
+
+ container.featureFiles = container.featureFile = base_plugin.featureFiles;
+ container.features = container.feature = base_plugin.features;
+ container.scenarios = container.scenario = scenarios;
+ container.steps = steps;
+};
+
+},{"../../Array":1,"../../Platform":12,"./BasePlugin":34}],37:[function(require,module,exports){
+(function (process){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var Platform = require('../Platform');
+
+module.exports = (function() {
+
+ var platform = new Platform();
+
+ var shims = {
+ node: function() {
+ return {
+ fs: require('fs'),
+ path: require('path'),
+ process: process
+ };
+ },
+ phantom: function() {
+ return {
+ fs: require('./phantom-fs'),
+ path: require('./phantom-path'),
+ process: require('./phantom-process')
+ };
+ },
+ };
+
+ function get_shim() {
+ if (platform.is_phantom()) return shims.phantom();
+ if (platform.is_node()) return shims.node();
+ return {};
+ }
+
+ return get_shim();
+})();
+
+}).call(this,require('_process'))
+},{"../Platform":12,"./phantom-fs":38,"./phantom-path":39,"./phantom-process":40,"_process":43,"fs":41,"path":42}],38:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = (function() {
+ if (module.client) return {}; // Running in browser, not via node
+
+ var fs = require('fs');
+
+ fs.existsSync = fs.existsSync || fs.exists;
+
+ fs.readdirSync = fs.readdirSync || function(path) {
+ return fs.list(path).filter(function(name) {
+ return name != '.' && name != '..';
+ });
+ };
+
+ fs.statSync = fs.statSync || function(path) {
+ return {
+ isDirectory: function() {
+ return fs.isDirectory(path);
+ }
+ };
+ };
+
+ return fs;
+})();
+
+},{"fs":41}],39:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+module.exports = (function() {
+ if (module.client) return {}; // Running in browser, not via node
+
+ var fs = require('fs');
+ var path = {};
+
+ try {
+ path = require('path');
+ } catch (e) {
+ // meh
+ }
+
+ path.join = path.join || function() {
+ return Array.prototype.join.call(arguments, fs.separator);
+ };
+
+ path.relative = path.relative || function(from, to) {
+ return from + fs.separator + to;
+ };
+
+ return path;
+
+})();
+
+},{"fs":41,"path":42}],40:[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/* jslint node: true */
+"use strict";
+
+module.exports = (function() {
+ if (module.client) return {}; // Running in browser, not via node
+
+ var fs = require('fs');
+ var process = typeof process != 'undefined' ? process : {};
+
+ process.cwd = function() {
+ return fs.workingDirectory;
+ };
+
+ return process;
+
+})();
+
+},{"fs":41}],41:[function(require,module,exports){
+
+},{}],42:[function(require,module,exports){
+(function (process){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// resolves . and .. elements in a path array with directory names there
+// must be no slashes, empty elements, or device names (c:\) in the array
+// (so also no leading and trailing slashes - it does not distinguish
+// relative and absolute paths)
+function normalizeArray(parts, allowAboveRoot) {
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = parts.length - 1; i >= 0; i--) {
+ var last = parts[i];
+ if (last === '.') {
+ parts.splice(i, 1);
+ } else if (last === '..') {
+ parts.splice(i, 1);
+ up++;
+ } else if (up) {
+ parts.splice(i, 1);
+ up--;
+ }
+ }
+
+ // if the path is allowed to go above the root, restore leading ..s
+ if (allowAboveRoot) {
+ for (; up--; up) {
+ parts.unshift('..');
+ }
+ }
+
+ return parts;
+}
+
+// Split a filename into [root, dir, basename, ext], unix version
+// 'root' is just a slash, or nothing.
+var splitPathRe =
+ /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
+var splitPath = function(filename) {
+ return splitPathRe.exec(filename).slice(1);
+};
+
+// path.resolve([from ...], to)
+// posix version
+exports.resolve = function() {
+ var resolvedPath = '',
+ resolvedAbsolute = false;
+
+ for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
+ var path = (i >= 0) ? arguments[i] : process.cwd();
+
+ // Skip empty and invalid entries
+ if (typeof path !== 'string') {
+ throw new TypeError('Arguments to path.resolve must be strings');
+ } else if (!path) {
+ continue;
+ }
+
+ resolvedPath = path + '/' + resolvedPath;
+ resolvedAbsolute = path.charAt(0) === '/';
+ }
+
+ // At this point the path should be resolved to a full absolute path, but
+ // handle relative paths to be safe (might happen when process.cwd() fails)
+
+ // Normalize the path
+ resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
+ return !!p;
+ }), !resolvedAbsolute).join('/');
+
+ return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
+};
+
+// path.normalize(path)
+// posix version
+exports.normalize = function(path) {
+ var isAbsolute = exports.isAbsolute(path),
+ trailingSlash = substr(path, -1) === '/';
+
+ // Normalize the path
+ path = normalizeArray(filter(path.split('/'), function(p) {
+ return !!p;
+ }), !isAbsolute).join('/');
+
+ if (!path && !isAbsolute) {
+ path = '.';
+ }
+ if (path && trailingSlash) {
+ path += '/';
+ }
+
+ return (isAbsolute ? '/' : '') + path;
+};
+
+// posix version
+exports.isAbsolute = function(path) {
+ return path.charAt(0) === '/';
+};
+
+// posix version
+exports.join = function() {
+ var paths = Array.prototype.slice.call(arguments, 0);
+ return exports.normalize(filter(paths, function(p, index) {
+ if (typeof p !== 'string') {
+ throw new TypeError('Arguments to path.join must be strings');
+ }
+ return p;
+ }).join('/'));
+};
+
+
+// path.relative(from, to)
+// posix version
+exports.relative = function(from, to) {
+ from = exports.resolve(from).substr(1);
+ to = exports.resolve(to).substr(1);
+
+ function trim(arr) {
+ var start = 0;
+ for (; start < arr.length; start++) {
+ if (arr[start] !== '') break;
+ }
+
+ var end = arr.length - 1;
+ for (; end >= 0; end--) {
+ if (arr[end] !== '') break;
+ }
+
+ if (start > end) return [];
+ return arr.slice(start, end - start + 1);
+ }
+
+ var fromParts = trim(from.split('/'));
+ var toParts = trim(to.split('/'));
+
+ var length = Math.min(fromParts.length, toParts.length);
+ var samePartsLength = length;
+ for (var i = 0; i < length; i++) {
+ if (fromParts[i] !== toParts[i]) {
+ samePartsLength = i;
+ break;
+ }
+ }
+
+ var outputParts = [];
+ for (var i = samePartsLength; i < fromParts.length; i++) {
+ outputParts.push('..');
+ }
+
+ outputParts = outputParts.concat(toParts.slice(samePartsLength));
+
+ return outputParts.join('/');
+};
+
+exports.sep = '/';
+exports.delimiter = ':';
+
+exports.dirname = function(path) {
+ var result = splitPath(path),
+ root = result[0],
+ dir = result[1];
+
+ if (!root && !dir) {
+ // No dirname whatsoever
+ return '.';
+ }
+
+ if (dir) {
+ // It has a dirname, strip trailing slash
+ dir = dir.substr(0, dir.length - 1);
+ }
+
+ return root + dir;
+};
+
+
+exports.basename = function(path, ext) {
+ var f = splitPath(path)[2];
+ // TODO: make this comparison case-insensitive on windows?
+ if (ext && f.substr(-1 * ext.length) === ext) {
+ f = f.substr(0, f.length - ext.length);
+ }
+ return f;
+};
+
+
+exports.extname = function(path) {
+ return splitPath(path)[3];
+};
+
+function filter (xs, f) {
+ if (xs.filter) return xs.filter(f);
+ var res = [];
+ for (var i = 0; i < xs.length; i++) {
+ if (f(xs[i], i, xs)) res.push(xs[i]);
+ }
+ return res;
+}
+
+// String.prototype.substr - negative index don't work in IE8
+var substr = 'ab'.substr(-1) === 'b'
+ ? function (str, start, len) { return str.substr(start, len) }
+ : function (str, start, len) {
+ if (start < 0) start = str.length + start;
+ return str.substr(start, len);
+ }
+;
+
+}).call(this,require('_process'))
+},{"_process":43}],43:[function(require,module,exports){
+// shim for using process in browser
+
+var process = module.exports = {};
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = setTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ currentQueue[queueIndex].run();
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ clearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ setTimeout(drainQueue, 0);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+// TODO(shtylman)
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}],"yadda":[function(require,module,exports){
+/*
+ * Copyright 2010 Acuminous Ltd / Energized Work Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* jslint node: true */
+"use strict";
+
+var api = {
+ Yadda: require('./Yadda'),
+ EventBus: require('./EventBus'),
+ Interpreter: require('./Interpreter'),
+ Context: require('./Context'),
+ Library: require('./Library'),
+ Dictionary: require('./Dictionary'),
+ FeatureFileSearch: require('./FeatureFileSearch'),
+ FileSearch: require('./FileSearch'),
+ Platform: require('./Platform'),
+ localisation: require('./localisation/index'),
+ parsers: require('./parsers/index'),
+ plugins: require('./plugins/index'),
+ shims: require('./shims/index'),
+ createInstance: function() {
+ // Not everyone shares my sense of humour re the recursive api :(
+ // See https://github.com/acuminous/yadda/issues/111
+ return api.Yadda.apply(null, Array.prototype.slice.call(arguments, 0));
+ }
+};
+
+module.exports = api;
+
+},{"./Context":3,"./Dictionary":4,"./EventBus":5,"./FeatureFileSearch":6,"./FileSearch":7,"./Interpreter":8,"./Library":10,"./Platform":12,"./Yadda":15,"./localisation/index":27,"./parsers/index":31,"./plugins/index":33,"./shims/index":37}]},{},["yadda"]);
diff --git a/examples/qunit/test.html b/examples/qunit/test.html
index fea2f459..3dd47187 100644
--- a/examples/qunit/test.html
+++ b/examples/qunit/test.html
@@ -4,7 +4,7 @@
QUnit Example
-
+