_baseAssertion.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. /**
  2. * Abstract assertion class that will subclass all defined Chai assertions
  3. *
  4. * All assertions must implement the following api:
  5. *
  6. * - @type {function}
  7. * executeCommand
  8. * - @type {string}
  9. * elementFound
  10. * - @type {function}
  11. * elementNotFound
  12. * - @type {function}
  13. * retryCommand
  14. * - @type {string}
  15. * expected
  16. * - @type {string}
  17. * actual
  18. * - @type {string}
  19. * message
  20. * - @type {boolean}
  21. * passed
  22. *
  23. * @constructor
  24. */
  25. var util = require('util');
  26. var events = require('events');
  27. var Assertion = require('../../core/assertion.js');
  28. var chai = require('chai-nightwatch');
  29. var flag = chai.flag;
  30. var Utils = require('../../util/utils.js');
  31. function BaseAssertion() {
  32. }
  33. BaseAssertion.ASSERT_FLAGS = [
  34. 'be',
  35. 'that',
  36. 'have',
  37. 'which',
  38. 'equal',
  39. 'contains',
  40. 'matches',
  41. 'before',
  42. 'after',
  43. 'waitFor'
  44. ];
  45. /**
  46. * @override
  47. */
  48. BaseAssertion.prototype.executeCommand = function(callback) {};
  49. BaseAssertion.prototype.setAssertion = function(assertion) {
  50. this.assertion = assertion;
  51. return this;
  52. };
  53. BaseAssertion.prototype.flag = function(key, value) {
  54. if (typeof value == 'undefined') {
  55. return flag(this.assertion, key);
  56. }
  57. flag(this.assertion, key, value);
  58. return this;
  59. };
  60. BaseAssertion.prototype.setClient = function(client) {
  61. this.client = client;
  62. return this;
  63. };
  64. BaseAssertion.prototype.setProtocol = function(protocol) {
  65. this.protocol = protocol;
  66. return this;
  67. };
  68. BaseAssertion.prototype.init = function() {
  69. this.promise = this.flag('promise');
  70. this.element = this.flag('element');
  71. this.selector = this.flag('selector');
  72. this.setNegate();
  73. this.waitForMs = this.client.api.globals.waitForConditionTimeout || null;
  74. if (this.waitForMs) {
  75. this.flag('waitFor', this.waitForMs);
  76. }
  77. this.retryInterval = this.client.api.globals.waitForConditionPollInterval || 500;
  78. this.retries = 0;
  79. this.messageParts = [];
  80. this.abortOnFailure = typeof this.client.api.globals.abortOnAssertionFailure == 'undefined' || this.client.api.globals.abortOnAssertionFailure;
  81. this.passed = false;
  82. this.expected = null;
  83. this.elementResult = null;
  84. this.flags = [];
  85. };
  86. BaseAssertion.prototype.start = function() {
  87. this.promise.then(this.onPromiseResolved.bind(this), this.onPromiseRejected.bind(this));
  88. };
  89. BaseAssertion.prototype.onPromiseResolved = function(elementResult) {
  90. if (elementResult) {
  91. this.elementResult = elementResult;
  92. }
  93. this.executeCommand(function(result) {
  94. this.resultValue = result.value;
  95. this.resultStatus = result.status;
  96. this.resultErrorStatus = result.errorStatus;
  97. this.processFlags();
  98. this.elementFound();
  99. if (!this.passed && this.shouldRetry()) {
  100. this.scheduleRetry();
  101. } else {
  102. this.done();
  103. }
  104. }.bind(this));
  105. };
  106. BaseAssertion.prototype.onPromiseRejected = function(response) {
  107. this.processFlags();
  108. if (this.shouldRetry() && !this.negate) {
  109. this.scheduleRetry();
  110. return;
  111. }
  112. this.messageParts.push(' - element was not found');
  113. this.actual = 'not present';
  114. this.expected = 'present';
  115. this.elementNotFound();
  116. this.done();
  117. };
  118. BaseAssertion.prototype.processFlags = function() {
  119. BaseAssertion.ASSERT_FLAGS.forEach(function(entry) {
  120. var value = this.flag(entry);
  121. if ((typeof value != 'undefined') && (typeof this['@' + entry + 'Flag'] == 'function')) {
  122. this.flags.push([entry, value]);
  123. this['@' + entry + 'Flag'](value);
  124. }
  125. }.bind(this));
  126. };
  127. BaseAssertion.prototype.hasFlag = function(type) {
  128. return this.flags.some(function(flag) {
  129. return flag[0] === type;
  130. });
  131. };
  132. /**
  133. * @override
  134. */
  135. BaseAssertion.prototype.elementFound = function() {};
  136. /**
  137. * @override
  138. */
  139. BaseAssertion.prototype.elementNotFound = function() {};
  140. BaseAssertion.prototype.done = function() {
  141. this.formatMessage();
  142. var stackTrace = this.flag('element')._stackTrace;
  143. Assertion.assert(
  144. this.passed,
  145. this.actual,
  146. this.expected,
  147. this.message,
  148. this.abortOnFailure,
  149. stackTrace
  150. );
  151. this.element.emitter.emit('complete');
  152. };
  153. BaseAssertion.prototype.getResult = function(value, fn) {
  154. var result = fn.call(this);
  155. this.setNegate();
  156. return this.negate ? !result : result;
  157. };
  158. BaseAssertion.prototype.shouldRetryLocateElement = function() {
  159. return (!this.elementResult || this.resultStatus === 10 || this.resultErrorStatus === 10);
  160. };
  161. BaseAssertion.prototype.shouldRetry = function() {
  162. if (!this.waitForMs) {
  163. return false;
  164. }
  165. this.elapsedTime = this.getElapsedTime();
  166. return (this.elapsedTime < this.waitForMs);
  167. };
  168. BaseAssertion.prototype.getElapsedTime = function() {
  169. var timeNow = new Date().getTime();
  170. return timeNow - this.element.startTime;
  171. };
  172. BaseAssertion.prototype.scheduleRetry = function() {
  173. this.retries++;
  174. setTimeout(this.retryCommand.bind(this), this.retryInterval);
  175. };
  176. BaseAssertion.prototype.formatMessage = function() {
  177. this.message = Utils.format(this.message || this.message, this.selector);
  178. this.message += this.messageParts.join('');
  179. };
  180. BaseAssertion.prototype['@containsFlag'] = function(value) {
  181. var verb = (this.hasFlag('that') || this.hasFlag('which')) ? 'contains' : 'contain';
  182. this.conditionFlag(value, function() {
  183. return this.resultValue ? this.resultValue.indexOf(value) > -1 : false;
  184. }, [
  185. 'not ' + verb,
  186. verb
  187. ]);
  188. return this;
  189. };
  190. BaseAssertion.prototype['@equalFlag'] = function(value) {
  191. var verb;
  192. if (this.hasFlag('have')) {
  193. verb = (this.hasFlag('that') || this.hasFlag('which')) ? 'equals' : 'equal to';
  194. } else {
  195. verb = 'equal';
  196. }
  197. this.conditionFlag(value, function() {
  198. return this.resultValue == value;
  199. }, [
  200. 'not ' + verb,
  201. verb
  202. ]);
  203. return this;
  204. };
  205. BaseAssertion.prototype['@matchesFlag'] = function(re) {
  206. var adverb = this.hasFlag('that') || this.hasFlag('which');
  207. var verb = adverb ? 'matches' : 'match';
  208. this.conditionFlag(re, function() {
  209. return re.test(this.resultValue);
  210. }, [
  211. (adverb ? 'does ' : '') + 'not match',
  212. verb
  213. ]);
  214. return this;
  215. };
  216. BaseAssertion.prototype.conditionFlag = function(value, conditionFn, arrverb) {
  217. this.passed = this.getResult(value, conditionFn);
  218. var verb = this.negate ? arrverb[0]: arrverb[1];
  219. this.expected = verb + ' \'' + value + '\'';
  220. this.actual = this.resultValue;
  221. if (this.retries > 0) {
  222. return;
  223. }
  224. var needsSpace = this.messageParts.length === 0 ? true : this.messageParts[this.messageParts.length-1].slice(-1) != ' ';
  225. if (needsSpace) {
  226. verb = ' ' + verb;
  227. }
  228. if (!this.customMessage) {
  229. this.messageParts.push(
  230. verb,
  231. ': "', value, '"'
  232. );
  233. }
  234. };
  235. BaseAssertion.prototype.setNegate = function() {
  236. this.negate = this.flag('negate') || false;
  237. return this;
  238. };
  239. BaseAssertion.prototype['@beforeFlag'] = function(value) {};
  240. BaseAssertion.prototype['@afterFlag'] = function(value) {};
  241. BaseAssertion.prototype['@haveFlag'] = function(value) {};
  242. BaseAssertion.prototype['@waitForFlag'] = function(value) {
  243. if (this.waitForMs !== value) {
  244. this.waitForMs = value;
  245. if (!this.customMessage) {
  246. this.messageParts.push(this.checkWaitForMsg(this.waitForMs));
  247. }
  248. }
  249. };
  250. BaseAssertion.prototype['@thatFlag'] = function() {
  251. if (this.retries > 0) {
  252. return;
  253. }
  254. if (!this.customMessage) {
  255. this.messageParts.push(' that ');
  256. }
  257. return this;
  258. };
  259. BaseAssertion.prototype['@whichFlag'] = function() {
  260. if (this.retries > 0) {
  261. return;
  262. }
  263. if (!this.customMessage) {
  264. this.messageParts.push(' which ');
  265. }
  266. return this;
  267. };
  268. BaseAssertion.prototype['@beFlag'] = function() {};
  269. BaseAssertion.prototype.hasCondition = function() {
  270. return (this.hasFlag('contains') || this.hasFlag('equal') || this.hasFlag('matches'));
  271. };
  272. BaseAssertion.prototype.checkWaitForMsg = function(waitFor) {
  273. var preposition = this.flag('before') && 'in' ||
  274. this.flag('after') && 'after' ||
  275. 'in';
  276. return ' ' + preposition +' ' + waitFor + 'ms';
  277. };
  278. BaseAssertion.prototype.retryCommand = function() {
  279. if (this.shouldRetryLocateElement()) {
  280. this.promise = this.element.createPromise();
  281. this.promise.then(this.onPromiseResolved.bind(this), this.onPromiseRejected.bind(this));
  282. this.element.locate();
  283. } else {
  284. this.onPromiseResolved();
  285. }
  286. };
  287. module.exports = BaseAssertion;