Before this commit, the result of our custom qunit assert 'doesNotHaveClass' was incorrect when it was called with several classnames. For instance assert.doesNotHaveClass(<span class="a"/>, 'a b'); passed, whereas it shouldn't. closes odoo/odoo#49892 X-original-commit: c40fc67a130ae8e0f061a89d3a21d2507a0f9ae3 Signed-off-by: Julien Mougenot (jum) <jum@odoo.com> Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
245 lines
8.5 KiB
JavaScript
245 lines
8.5 KiB
JavaScript
odoo.define('web.qunit_asserts', function (require) {
|
|
"use strict";
|
|
|
|
/**
|
|
* In this file, we extend QUnit by adding some specialized assertions. The goal
|
|
* of these new assertions is twofold:
|
|
* - ease of use: they should allow us to simplify some common complex assertions
|
|
* - safer: these assertions will fail when some preconditions are not met.
|
|
*
|
|
* For example, the assert.isVisible assertion will also check that the target
|
|
* matches exactly one element.
|
|
*/
|
|
|
|
const Widget = require('web.Widget');
|
|
|
|
/** @todo use testUtilsDom.getNode to extract the element from the 'w' argument */
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Private functions
|
|
//-------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Helper function, to check if a given element
|
|
* - is unique (if it is a jquery node set)
|
|
* - has (or has not) a css class
|
|
*
|
|
* @private
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} className
|
|
* @param {boolean} shouldHaveClass
|
|
* @param {string} [msg]
|
|
*/
|
|
function _checkClass(w, className, shouldHaveClass, msg) {
|
|
if (w instanceof jQuery && w.length !== 1) {
|
|
const assertion = shouldHaveClass ? 'hasClass' : 'doesNotHaveClass';
|
|
QUnit.assert.ok(false, `Assertion '${assertion} ${className}' targets ${w.length} elements instead of 1`);
|
|
}
|
|
|
|
const el = w instanceof Widget || w instanceof owl.Component ? w.el :
|
|
w instanceof jQuery ? w[0] : w;
|
|
|
|
msg = msg || `target should ${shouldHaveClass ? 'have' : 'not have'} class ${className}`;
|
|
const isFalse = className.split(" ").some(cls => {
|
|
const hasClass = el.classList.contains(cls);
|
|
return shouldHaveClass ? !hasClass : hasClass;
|
|
});
|
|
QUnit.assert.ok(!isFalse, msg);
|
|
}
|
|
|
|
/**
|
|
* Helper function, to check if a given element
|
|
* - is unique (if it is a jquery node set)
|
|
* - is (or not) visible
|
|
*
|
|
* @private
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {boolean} shouldBeVisible
|
|
* @param {string} [msg]
|
|
*/
|
|
function _checkVisible(w, shouldBeVisible, msg) {
|
|
if (w instanceof jQuery && w.length !== 1) {
|
|
const assertion = shouldBeVisible ? 'isVisible' : 'isNotVisible';
|
|
QUnit.assert.ok(false, `Assertion '${assertion}' targets ${w.length} elements instead of 1`);
|
|
}
|
|
|
|
const el = w instanceof Widget || w instanceof owl.Component ? w.el :
|
|
w instanceof jQuery ? w[0] : w;
|
|
|
|
msg = msg || `target should ${shouldBeVisible ? '' : 'not'} be visible`;
|
|
let isVisible = el &&
|
|
el.offsetWidth &&
|
|
el.offsetHeight;
|
|
if (isVisible) {
|
|
// This computation is a little more heavy and we only want to perform it
|
|
// if the above assertion has failed.
|
|
const rect = el.getBoundingClientRect();
|
|
isVisible = rect.width + rect.height;
|
|
}
|
|
const condition = shouldBeVisible ? isVisible : !isVisible;
|
|
QUnit.assert.ok(condition, msg);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Public functions
|
|
//-------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Checks that the target element (described by widget/jquery or html element)
|
|
* contains exactly n matches for the selector.
|
|
*
|
|
* Example: assert.containsN(document.body, '.modal', 0)
|
|
*
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} selector
|
|
* @param {number} n
|
|
* @param {string} [msg]
|
|
*/
|
|
function containsN(w, selector, n, msg) {
|
|
if (typeof n !== 'number') {
|
|
throw new Error("containsN assert should be called with a number as third argument");
|
|
}
|
|
let matches = [];
|
|
if (w instanceof owl.Component) {
|
|
if (!w.el) {
|
|
throw new Error(`containsN assert with selector '${selector}' called on an unmounted component`);
|
|
}
|
|
matches = w.el.querySelectorAll(selector);
|
|
} else {
|
|
const $el = w instanceof Widget ? w.$el :
|
|
w instanceof HTMLElement ? $(w) :
|
|
w; // jquery element
|
|
matches = $el.find(selector);
|
|
}
|
|
if (!msg) {
|
|
msg = `Selector '${selector}' should have exactly ${n} matches (inside the target)`;
|
|
}
|
|
QUnit.assert.strictEqual(matches.length, n, msg);
|
|
}
|
|
|
|
/**
|
|
* Checks that the target element (described by widget/jquery or html element)
|
|
* contains exactly 0 match for the selector.
|
|
*
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} selector
|
|
* @param {string} [msg]
|
|
*/
|
|
function containsNone(w, selector, msg) {
|
|
containsN(w, selector, 0, msg);
|
|
}
|
|
|
|
/**
|
|
* Checks that the target element (described by widget/jquery or html element)
|
|
* contains exactly 1 match for the selector.
|
|
*
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} selector
|
|
* @param {string} [msg]
|
|
*/
|
|
function containsOnce(w, selector, msg) {
|
|
containsN(w, selector, 1, msg);
|
|
}
|
|
|
|
/**
|
|
* Checks that the target element (described by widget/jquery or html element)
|
|
* - exists
|
|
* - is unique
|
|
* - has the given class (specified by className)
|
|
*
|
|
* Note that it uses the hasClass jQuery method, so it can be used to check the
|
|
* presence of more than one class ('some-class other-class'), but it is a
|
|
* little brittle, because it depends on the order of these classes:
|
|
*
|
|
* div.a.b.c: has class 'a b c', but does not have class 'a c b'
|
|
*
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} className
|
|
* @param {string} [msg]
|
|
*/
|
|
function hasClass(w, className, msg) {
|
|
_checkClass(w, className, true, msg);
|
|
}
|
|
|
|
/**
|
|
* Checks that the target element (described by widget/jquery or html element)
|
|
* - exists
|
|
* - is unique
|
|
* - does not have the given class (specified by className)
|
|
*
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} className
|
|
* @param {string} [msg]
|
|
*/
|
|
function doesNotHaveClass(w, className, msg) {
|
|
_checkClass(w, className, false, msg);
|
|
}
|
|
|
|
/**
|
|
* Checks that the target element (described by widget/jquery or html element)
|
|
* - exists
|
|
* - is unique
|
|
* - has the given attribute with the proper value
|
|
*
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} attr
|
|
* @param {string} value
|
|
* @param {string} [msg]
|
|
*/
|
|
function hasAttrValue(w, attr, value, msg) {
|
|
const $el = w instanceof Widget ? w.$el :
|
|
w instanceof HTMLElement ? $(w) :
|
|
w; // jquery element
|
|
|
|
if ($el.length !== 1) {
|
|
const descr = `hasAttrValue (${attr}: ${value})`;
|
|
QUnit.assert.ok(false,
|
|
`Assertion '${descr}' targets ${$el.length} elements instead of 1`
|
|
);
|
|
} else {
|
|
msg = msg || `attribute '${attr}' of target should be '${value}'`;
|
|
QUnit.assert.strictEqual($el.attr(attr), value, msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks that the target element (described by widget/jquery or html element)
|
|
* - exists
|
|
* - is visible (as far as jQuery can tell: not in display none, ...)
|
|
*
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} [msg]
|
|
*/
|
|
function isVisible(w, msg) {
|
|
_checkVisible(w, true, msg);
|
|
}
|
|
|
|
/**
|
|
* Checks that the target element (described by widget/jquery or html element)
|
|
* - exists
|
|
* - is not visible (as far as jQuery can tell: display none, ...)
|
|
*
|
|
* @param {Widget|jQuery|HTMLElement|owl.Component} w
|
|
* @param {string} [msg]
|
|
*/
|
|
function isNotVisible(w, msg) {
|
|
_checkVisible(w, false, msg);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Exposed API
|
|
//-------------------------------------------------------------------------
|
|
|
|
QUnit.assert.containsN = containsN;
|
|
QUnit.assert.containsNone = containsNone;
|
|
QUnit.assert.containsOnce = containsOnce;
|
|
|
|
QUnit.assert.hasClass = hasClass;
|
|
QUnit.assert.doesNotHaveClass = doesNotHaveClass;
|
|
|
|
QUnit.assert.hasAttrValue = hasAttrValue;
|
|
|
|
QUnit.assert.isVisible = isVisible;
|
|
QUnit.assert.isNotVisible = isNotVisible;
|
|
});
|