nightwatch
Version:
Easy to use Node.js based end-to-end testing solution for web applications using the W3C WebDriver API.
297 lines (239 loc) • 7.25 kB
JavaScript
const Utils = require('../../utils');
const Common = require('./common.js');
/*
describe('test suite', function() {
this.timeout(1000);
});
*/
class DescribeInstance {
constructor({describeTitle, instance, client}) {
this['[instance]'] = instance;
this['[attributes]'] = {};
this['[client]'] = client;
this.define('@name', describeTitle);
}
/////////////////////////////////////////////////
// Attributes
/////////////////////////////////////////////////
get name() {
return this['[attributes]']['@name'];
}
set tags(value) {
this.define('@tags', value);
}
get tags() {
return this['[attributes]']['@tags'];
}
set unitTest(value) {
this.define('@unitTest', value);
}
get unitTest() {
return this['[attributes]']['@unitTest'];
}
set endSessionOnFail(value) {
this.define('@endSessionOnFail', value);
}
get endSessionOnFail() {
return this['[attributes]']['@endSessionOnFail'];
}
set skipTestcasesOnFail(value) {
this.define('@skipTestcasesOnFail', value);
}
get skipTestcasesOnFail() {
return this['[attributes]']['@skipTestcasesOnFail'];
}
set disabled(value) {
this.define('@disabled', value);
}
get disabled() {
return this['[attributes]']['@disabled'];
}
set desiredCapabilities(value) {
if (Utils.isObject(value) || Utils.isFunction(value)) {
this.define('@desiredCapabilities', value);
}
}
get desiredCapabilities() {
return this['[client]'].initialCapabilities;
}
/////////////////////////////////////////////////
// Getters
/////////////////////////////////////////////////
get page() {
if (this['[client]'] && this['[client]'].api) {
return this['[client]'].api.page;
}
return null;
}
get globals() {
return this.settings.globals;
}
get settings() {
if (this['[client]']) {
return this['[client]'].settings;
}
return this['[instance]'].settings;
}
get argv() {
return this['[instance]'].argv;
}
timeout(value) {
this.globals.waitForConditionTimeout = value;
this.globals.retryAssertionTimeout = value;
this.globals.unitTestsTimeout = value;
}
waitForTimeout(value) {
if (typeof value == 'undefined') {
return this.globals.waitForConditionTimeout;
}
return this.timeout(value);
}
waitForRetryInterval(value) {
if (typeof value == 'undefined') {
return this.globals.waitForConditionPollInterval;
}
return this.retryInterval(value);
}
retryInterval(value) {
this.globals.waitForConditionPollInterval = value;
}
retries(n) {
this['[instance]'].setTestcaseRetries(n);
}
suiteRetries(n) {
this['[instance]'].setSuiteRetries(n);
}
define(name, value) {
this['[attributes]'][name] = value;
const isAttributeValid = Object.keys(Common.DEFAULT_ATTRIBUTES).includes(name) ||
Object.keys(Common.DEFAULT_ATTRIBUTES).includes(`@${name}`);
if (isAttributeValid) {
if (!name.startsWith('@')) {
name = `@${name}`;
}
return this['[instance]'].setAttribute(name, value);
}
// eslint-disable-next-line no-console
console.warn(`Attribute "${name}" is not a valid attribute. Valid attributes are: ${Object.keys(Common.DEFAULT_ATTRIBUTES).join(', ')}.`);
}
}
class Describe extends Common {
constructor(instance, client = null) {
super(instance);
this.describeFn = null;
this.client = client;
this.describeInstance = null;
this.describeTitle = null;
this.instance.on('pre-require', (context) => this.createInterface(context));
}
createInstance(runOnly) {
if (!Utils.isFunction(this.describeFn)) {
throw new Error(`The describe/context must be a function. ${typeof this.describeFn} given.`);
}
const {describeTitle, instance, describeFn, client} = this;
this.describeInstance = new DescribeInstance({
describeTitle, instance, client
});
const {describeInstance} = this;
describeFn.call(describeInstance);
this.instance.setDescribeContext({describeTitle, describeInstance, runOnly});
}
/**
* Adds before, after, beforeEach, afterEach hooks to test suite
*
* @param context
*/
addHooks(context) {
const hooksContext = Common.TestHooks.reduce((prev, hookName) => {
prev[hookName] = hookFn => {
this.instance.addTestHook(hookName, hookFn, this.describeInstance);
};
return prev;
}, {});
Object.assign(context, hooksContext);
}
addRun(context) {
context.run = function() {
// TODO: implement
};
}
createTestsuite({title, describeFn, context, runOnly = false}) {
this.describeFn = describeFn;
this.describeTitle = title;
if (this.describeFn) {
this.createInstance(runOnly);
const testsuite = require.cache[this.instance.modulePath];
if (testsuite && testsuite.exports) {
testsuite.exports['[@nightwatchDescribe]'] = true;
}
}
}
addDescribe(context) {
context.xmodule = {};
context.describe =
context.context = (title, describeFn) => {
this.createTestsuite({
context,
title,
describeFn
});
};
context.xdescribe =
context.xcontext =
context.describe.skip = (title, describeFn) => {
this.instance.once('module-loaded', () => {
// in case tests have been declared using other interfaces (e.g. exports)
if (this.instance.tests.length === 0) {
this.instance.setAttribute('@disabled', true);
}
});
};
context.describe.only = (title, describeFn) => {
this.createTestsuite({
title, describeFn, context, runOnly: true
});
};
}
addTest(context) {
context.it =
context.specify =
context.test = (testName, testFn) => {
this.instance.addTestCase({testName, testFn, describeInstance: this.describeInstance});
};
context.xit =
context.xspecify =
context.xtest =
context.it.skip =
context.test.skip = (testName) => {
this.instance.addTestCase({testName, testFn: function() {}, describeInstance: this.describeInstance});
};
context.it.only =
context.specify.only =
context.test.only = (testName, testFn) => {
this.instance.addTestCase({testName, testFn, describeInstance: this.describeInstance, runOnly: true});
};
}
shouldReloadModuleCache() {
if (this.instance.shouldReloadModuleCache()) {
return true;
}
if (require.cache && require.cache[this.instance.modulePath]) {
const testsuiteModule = require.cache[this.instance.modulePath];
if (testsuiteModule.exports && testsuiteModule.exports['[@nightwatchDescribe]']) {
return true;
}
}
return false;
}
createInterface(context) {
// for suiteRetries to work with describe interface we need to re-require the file and clear the require cache
if (this.shouldReloadModuleCache()) {
delete require.cache[this.instance.modulePath];
}
this.addHooks(context);
this.addRun(context);
this.addDescribe(context);
this.addTest(context);
}
}
module.exports = Describe;