nightwatch
Version:
Easy to use Node.js based end-to-end testing solution for web applications using the W3C WebDriver API.
202 lines (168 loc) • 4.88 kB
JavaScript
const Utils = require('../../utils');
const {Context} = require('../../testsuite');
const {Logger} = Utils;
class TagsMatcher {
static get SEPARATOR() {
return ',';
}
constructor(settings) {
this.settings = settings;
this.tagsToIncludeArrays = TagsMatcher.convertFilterTags(settings.tag_filter);
this.tagsToSkipArray = TagsMatcher.convertTags(settings.skiptags);
this.usingMocha = this.settings.test_runner && this.settings.test_runner.type === 'mocha';
}
/**
* @param {string} testFilePath - file path of a test
* @returns {boolean} true if specified test matches given tag
*/
async match(testFilePath) {
let context;
try {
context = await this.loadModule(testFilePath);
} catch (e) {
Logger.error(e);
return false;
}
if (!context) {
return false;
}
if (context.isDisabled()) {
return false;
}
return this.checkModuleTags(context);
}
async loadModule(modulePath) {
const {settings} = this;
const context = new Context({modulePath, settings});
context.setReloadModuleCache();
// Defining global browser object to make it available before nightwatch client created.
// To avoid errors like browser is not defined if testsuits has tags
Object.defineProperty(global, 'browser', {
configurable: true,
get: function() {
return {};
}
});
try {
if (this.usingMocha) {
context.loadTags({usingMocha: true});
} else {
await context.init();
}
} catch (e) {
Logger.error(e);
return false;
}
if (context.isDisabled()) {
return false;
}
return context;
}
/**
* Verify if the current module contains or does not contain specific tags
* @returns {boolean}
*/
checkModuleTags(context) {
const tags = context.getTags();
const moduleTags = TagsMatcher.convertTags(tags);
// if we passed the --tag argument, check if the module contains the tag (or tags)
if (this.hasIncludeTagFilter() && !this.matchesIncludeTagFilter(moduleTags)) {
return false;
}
// if we passed the --skiptags argument, check if the module doesn't contain any of the tags in the skiptags array
if (this.hasSkipTagFilter() && !excludesAllTags(moduleTags, this.tagsToSkipArray)) {
return false;
}
return true;
}
matchesIncludeTagFilter(moduleTags) {
return this.tagsToIncludeArrays.some(tagsArray => containsAllTags(moduleTags, tagsArray));
}
/**
* @returns {boolean}
*/
hasIncludeTagFilter() {
return this.tagsToIncludeArrays.length > 0;
}
/**
* @returns {boolean}
*/
hasSkipTagFilter() {
return this.tagsToSkipArray.length > 0;
}
/**
* @returns {boolean}
*/
anyTagsDefined() {
return this.hasIncludeTagFilter() || this.hasSkipTagFilter();
}
/**
* @param {String|Number|Array} tags
* @return {Array}
*/
static convertTags(tags) {
let tagsArray = Array.isArray(tags) ? tags : [];
if (Utils.isString(tags) && tags.length > 0) {
tagsArray = tags.split(TagsMatcher.SEPARATOR);
} else if (Utils.isNumber(tags)) {
tagsArray.push(tags);
}
// convert individual tags to strings
return tagsArray.map(tag => String(tag).toLowerCase());
}
/**
* @param {Array|String} tags
* @returns {*}
*/
static convertFilterTags(tags) {
if (!tags) {
return [];
}
// when multiple --tag arguments are passed (e.g.: --tag a --tag b)
// the resulting array is [a, b], otherwise it is [a]
const tagsArray = Array.isArray(tags) ? tags : [tags];
// now parse each --tag argument
return tagsArray.map(t => TagsMatcher.convertTags(t));
}
}
/**
* Whether a given list of tags contains one particular tag
*
* @param {Array} moduleTags
* @param {String} testTag
* @returns {boolean}
*/
const containsTag = function(moduleTags, testTag) {
return moduleTags.includes(testTag);
};
/**
* Whether a given list of tags does not contain one particular tag
*
* @param {Array} moduleTags
* @param {String} testTag
* @returns {boolean}
*/
const excludesSingleTag = function(moduleTags, testTag) {
return !containsTag(moduleTags, testTag);
};
/**
* Whether a given list of tags does not contain any tags in another list
*
* @param {Array} moduleTags
* @param {Array} tagsArray
* @returns {boolean}
*/
const excludesAllTags = function(moduleTags, tagsArray) {
return moduleTags.every(testTag => excludesSingleTag(tagsArray, testTag));
};
/**
* Whether a given list of tags contains aall tags in another list
*
* @param {Array} moduleTags
* @param {Array} tagsArray
* @returns {boolean}
*/
const containsAllTags = function(moduleTags, tagsArray) {
return tagsArray.every(testTag => containsTag(moduleTags, testTag));
};
module.exports = TagsMatcher;