diff --git a/packages/qunit-dom/lib/__tests__/does-not-exist.ts b/packages/qunit-dom/lib/__tests__/does-not-exist.ts
index 59fdedb97..378200814 100644
--- a/packages/qunit-dom/lib/__tests__/does-not-exist.ts
+++ b/packages/qunit-dom/lib/__tests__/does-not-exist.ts
@@ -54,6 +54,21 @@ describe('assert.dom(...).doesNotExist()', () => {
},
]);
});
+
+ test('fails if passed null', () => {
+ document.body.innerHTML = '
foo
bar';
+
+ assert.dom(null).doesNotExist();
+
+ expect(assert.results).toEqual([
+ {
+ actual: 'Element does not exist',
+ expected: 'Element does not exist',
+ message: 'Element does not exist',
+ result: true,
+ },
+ ]);
+ });
});
test('custom message', () => {
diff --git a/packages/qunit-dom/lib/__tests__/does-not-have-attribute.ts b/packages/qunit-dom/lib/__tests__/does-not-have-attribute.ts
index 40a1beff5..55ac346c2 100644
--- a/packages/qunit-dom/lib/__tests__/does-not-have-attribute.ts
+++ b/packages/qunit-dom/lib/__tests__/does-not-have-attribute.ts
@@ -83,6 +83,17 @@ describe('assert.dom(...).doesNotHaveAttribute()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).doesNotHaveAttribute('disabled');
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).doesNotHaveAttribute('disabled')).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/does-not-have-class.ts b/packages/qunit-dom/lib/__tests__/does-not-have-class.ts
index acaa0f6fb..028837cea 100644
--- a/packages/qunit-dom/lib/__tests__/does-not-have-class.ts
+++ b/packages/qunit-dom/lib/__tests__/does-not-have-class.ts
@@ -117,6 +117,17 @@ describe('assert.dom(...).doesNotHaveClass()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).doesNotHaveClass('foo');
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).doesNotHaveClass('foo')).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/does-not-have-style.ts b/packages/qunit-dom/lib/__tests__/does-not-have-style.ts
index 755dda489..fab9f06b3 100644
--- a/packages/qunit-dom/lib/__tests__/does-not-have-style.ts
+++ b/packages/qunit-dom/lib/__tests__/does-not-have-style.ts
@@ -90,6 +90,18 @@ describe('assert.dom(...).doesNotHaveStyle()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).doesNotHaveStyle({
+ opacity: 0,
+ });
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).doesNotHaveStyle({ opacity: 1 })).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/does-not-match-selector.ts b/packages/qunit-dom/lib/__tests__/does-not-match-selector.ts
index d07ae80be..2b753604b 100644
--- a/packages/qunit-dom/lib/__tests__/does-not-match-selector.ts
+++ b/packages/qunit-dom/lib/__tests__/does-not-match-selector.ts
@@ -82,6 +82,21 @@ describe('assert.dom(...).doesNotMatchSelector()', () => {
]);
});
+ test('null passed', () => {
+ assert.dom(null).doesNotMatchSelector('div>p:last-child');
+
+ expect(assert.results).toEqual([
+ {
+ actual: '0 elements, selected by null, did not also match the selector div>p:last-child.',
+ expected:
+ '0 elements, selected by null, did not also match the selector div>p:last-child.',
+ message:
+ '0 elements, selected by null, did not also match the selector div>p:last-child.',
+ result: true,
+ },
+ ]);
+ });
+
test('multiple elements, some matching compareSelector', () => {
assert.dom('p').doesNotMatchSelector('div>p');
diff --git a/packages/qunit-dom/lib/__tests__/has-any-text.ts b/packages/qunit-dom/lib/__tests__/has-any-text.ts
index c491db025..602e802fe 100644
--- a/packages/qunit-dom/lib/__tests__/has-any-text.ts
+++ b/packages/qunit-dom/lib/__tests__/has-any-text.ts
@@ -64,6 +64,17 @@ describe('assert.dom(...).hasAnyText()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasAnyText();
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasAnyText()).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/has-any-value.ts b/packages/qunit-dom/lib/__tests__/has-any-value.ts
index fa130c4ba..d9d84653c 100644
--- a/packages/qunit-dom/lib/__tests__/has-any-value.ts
+++ b/packages/qunit-dom/lib/__tests__/has-any-value.ts
@@ -65,6 +65,17 @@ describe('assert.dom(...).hasAnyValue()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasAnyValue();
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasAnyValue()).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/has-attribute.ts b/packages/qunit-dom/lib/__tests__/has-attribute.ts
index ca3cfd942..1b2eb9cd1 100644
--- a/packages/qunit-dom/lib/__tests__/has-attribute.ts
+++ b/packages/qunit-dom/lib/__tests__/has-attribute.ts
@@ -238,6 +238,17 @@ describe('assert.dom(...).hasAttribute()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasAttribute('foo');
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasAttribute('foo')).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/has-class.ts b/packages/qunit-dom/lib/__tests__/has-class.ts
index ac9ab744c..95db00759 100644
--- a/packages/qunit-dom/lib/__tests__/has-class.ts
+++ b/packages/qunit-dom/lib/__tests__/has-class.ts
@@ -112,6 +112,17 @@ describe('assert.dom(...).hasClass()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasClass('foo');
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasClass('foo')).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/has-no-text.ts b/packages/qunit-dom/lib/__tests__/has-no-text.ts
index 48e97383c..fe6f4e3ce 100644
--- a/packages/qunit-dom/lib/__tests__/has-no-text.ts
+++ b/packages/qunit-dom/lib/__tests__/has-no-text.ts
@@ -62,6 +62,17 @@ describe('assert.dom(...).hasNoText()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasNoText();
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasNoText()).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/has-no-value.ts b/packages/qunit-dom/lib/__tests__/has-no-value.ts
index 63de2fc45..cb3653e57 100644
--- a/packages/qunit-dom/lib/__tests__/has-no-value.ts
+++ b/packages/qunit-dom/lib/__tests__/has-no-value.ts
@@ -64,6 +64,17 @@ describe('assert.dom(...).hasNoValue()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasNoValue();
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasNoValue()).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/has-property.ts b/packages/qunit-dom/lib/__tests__/has-property.ts
index bce3a2d6b..101ea21c0 100644
--- a/packages/qunit-dom/lib/__tests__/has-property.ts
+++ b/packages/qunit-dom/lib/__tests__/has-property.ts
@@ -150,6 +150,17 @@ describe('assert.dom(...).hasProperty()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasProperty('foo', 'bar');
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasProperty('foo', 'bar')).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/has-style.ts b/packages/qunit-dom/lib/__tests__/has-style.ts
index d6f3af590..d6676c68c 100644
--- a/packages/qunit-dom/lib/__tests__/has-style.ts
+++ b/packages/qunit-dom/lib/__tests__/has-style.ts
@@ -108,6 +108,18 @@ describe('assert.dom(...).hasStyle()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasStyle({
+ opacity: 0,
+ });
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasStyle({ opacity: 1 })).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/has-value.ts b/packages/qunit-dom/lib/__tests__/has-value.ts
index 538405fda..2201d8448 100644
--- a/packages/qunit-dom/lib/__tests__/has-value.ts
+++ b/packages/qunit-dom/lib/__tests__/has-value.ts
@@ -221,6 +221,17 @@ describe('assert.dom(...).hasValue()', () => {
]);
});
+ test('fails for null', () => {
+ assert.dom(null).hasValue('foo');
+
+ expect(assert.results).toEqual([
+ {
+ message: 'Element should exist',
+ result: false,
+ },
+ ]);
+ });
+
test('throws for unexpected parameter types', () => {
//@ts-ignore -- These assertions are for JavaScript users who don't have type checking
expect(() => assert.dom(5).hasValue('foo')).toThrow('Unexpected Parameter: 5');
diff --git a/packages/qunit-dom/lib/__tests__/is-not-visible.ts b/packages/qunit-dom/lib/__tests__/is-not-visible.ts
index c9e6531cf..4276c2dc8 100644
--- a/packages/qunit-dom/lib/__tests__/is-not-visible.ts
+++ b/packages/qunit-dom/lib/__tests__/is-not-visible.ts
@@ -34,6 +34,23 @@ describe('assert.dom(...).isNotVisible()', () => {
});
});
+ describe('element only', () => {
+ test('succeeds if element is missing', () => {
+ document.body.innerHTML = 'foo
bar';
+
+ assert.dom(null).isNotVisible();
+
+ expect(assert.results).toEqual([
+ {
+ actual: 'Element is not visible',
+ expected: 'Element is not visible',
+ message: 'Element is not visible',
+ result: true,
+ },
+ ]);
+ });
+ });
+
describe('custom messages', () => {
test('shows custom messages', () => {
document.body.innerHTML = 'foo
bar';
diff --git a/packages/qunit-dom/lib/__tests__/matches-selector.ts b/packages/qunit-dom/lib/__tests__/matches-selector.ts
index 3be42b8ee..6075615dd 100644
--- a/packages/qunit-dom/lib/__tests__/matches-selector.ts
+++ b/packages/qunit-dom/lib/__tests__/matches-selector.ts
@@ -82,6 +82,19 @@ describe('assert.dom(...).matchesSelector()', () => {
]);
});
+ test('null passed', () => {
+ assert.dom(null).matchesSelector('div>p:last-child');
+
+ expect(assert.results).toEqual([
+ {
+ actual: '0 elements, selected by null, also match the selector div>p:last-child.',
+ expected: '0 elements, selected by null, also match the selector div>p:last-child.',
+ message: '0 elements, selected by null, also match the selector div>p:last-child.',
+ result: true,
+ },
+ ]);
+ });
+
test('multiple elements not all matching compareSelector', () => {
assert.dom('p').matchesSelector('p + p');
diff --git a/packages/qunit-dom/lib/assertions.ts b/packages/qunit-dom/lib/assertions.ts
index 68e646b93..c117fe0d7 100644
--- a/packages/qunit-dom/lib/assertions.ts
+++ b/packages/qunit-dom/lib/assertions.ts
@@ -74,8 +74,8 @@ export default class DOMAssertions {
*
* @see {@link #doesNotExist}
*/
- exists(options: ExistsOptions | string, message?: string): DOMAssertions {
- exists.call(this, options, message);
+ exists(...args: [options: ExistsOptions, message?: string] | [message?: string]): DOMAssertions {
+ exists.call(this, ...args);
return this;
}
@@ -298,8 +298,10 @@ export default class DOMAssertions {
*
* @see {@link #isNotVisible}
*/
- isVisible(options: ExistsOptions | string, message?: string): DOMAssertions {
- isVisible.call(this, options, message);
+ isVisible(
+ ...args: [options: ExistsOptions, message?: string] | [message?: string]
+ ): DOMAssertions {
+ isVisible.call(this, ...args);
return this;
}
@@ -388,7 +390,7 @@ export default class DOMAssertions {
let actualValue = element.getAttribute(name);
if (value instanceof RegExp) {
- let result = value.test(actualValue);
+ let result = typeof actualValue === 'string' && value.test(actualValue);
let expected = `Element ${this.targetDescription} has attribute "${name}" with value matching ${value}`;
let actual =
actualValue === null
@@ -451,7 +453,7 @@ export default class DOMAssertions {
*/
doesNotHaveAttribute(name: string, message?: string): DOMAssertions {
let element = this.findTargetElement();
- if (!element) return;
+ if (!element) return this;
let result = !element.hasAttribute(name);
let expected = `Element ${this.targetDescription} does not have attribute "${name}"`;
@@ -480,6 +482,35 @@ export default class DOMAssertions {
return this.doesNotHaveAttribute(name, message);
}
+ /**
+ * Assert that the {@link HTMLElement} has an ARIA attribute with the provided
+ * `name`.
+ *
+ * @param {string} name
+ *
+ * @example
+ * assert.dom('button').hasAria('pressed');
+ *
+ * @see {@link #doesNotHaveAria}
+ */
+ hasAria(name: string): DOMAssertions;
+
+ /**
+ * Assert that the {@link HTMLElement} has an ARIA attribute with the provided
+ * `name` and checks if the attribute `value` matches the provided text or
+ * regular expression.
+ *
+ * @param {string} name
+ * @param {string|RegExp|object} value
+ * @param {string?} message
+ *
+ * @example
+ * assert.dom('button').hasAria('pressed', 'true');
+ *
+ * @see {@link #doesNotHaveAttribute}
+ */
+ hasAria(name: string, value: string | RegExp | { any: true }, message?: string): DOMAssertions;
+
/**
* Assert that the {@link HTMLElement} has an ARIA attribute with the provided
* `name` and optionally checks if the attribute `value` matches the provided
@@ -494,8 +525,15 @@ export default class DOMAssertions {
*
* @see {@link #doesNotHaveAria}
*/
- hasAria(name: string, value?: string | RegExp | { any: true }, message?: string): DOMAssertions {
- return this.hasAttribute(`aria-${name}`, value, message);
+ hasAria(
+ name: string,
+ ...args: [value: string | RegExp | { any: true }, message?: string] | []
+ ): DOMAssertions {
+ if (args.length === 0) {
+ return this.hasAttribute(`aria-${name}`);
+ } else {
+ return this.hasAttribute(`aria-${name}`, ...args);
+ }
}
/**
@@ -740,7 +778,7 @@ export default class DOMAssertions {
return this.hasPseudoElementStyle(null, expected, message);
}
- hasPseudoElementStyle(selector: string, expected: object, message?: string): DOMAssertions;
+ hasPseudoElementStyle(selector: string | null, expected: object, message?: string): DOMAssertions;
/**
* Assert that the pseudo element for `selector` of the [HTMLElement][] has the `expected` style declarations using
@@ -816,7 +854,11 @@ export default class DOMAssertions {
return this.doesNotHavePseudoElementStyle(null, expected, message);
}
- doesNotHavePseudoElementStyle(selector: string, expected: object, message: string): DOMAssertions;
+ doesNotHavePseudoElementStyle(
+ selector: string | null,
+ expected: object,
+ message?: string
+ ): DOMAssertions;
/**
* Assert that the pseudo element for `selector` of the [HTMLElement][] does not have the `expected` style declarations using
@@ -836,7 +878,7 @@ export default class DOMAssertions {
doesNotHavePseudoElementStyle(
selector: string | null,
expected: Record,
- message: string
+ message?: string
): DOMAssertions {
let element = this.findTargetElement();
if (!element) return this;
@@ -902,7 +944,7 @@ export default class DOMAssertions {
if (!element) return this;
if (expected instanceof RegExp) {
- let result = expected.test(element.textContent);
+ let result = typeof element.textContent === 'string' && expected.test(element.textContent);
let actual = element.textContent;
if (!message) {
@@ -923,7 +965,7 @@ export default class DOMAssertions {
this.pushResult({ result, actual, expected, message });
} else if (typeof expected === 'string') {
expected = collapseWhitespace(expected);
- let actual = collapseWhitespace(element.textContent);
+ let actual = collapseWhitespace(element.textContent || '');
let result = actual === expected;
if (!message) {
@@ -997,7 +1039,7 @@ export default class DOMAssertions {
let element = this.findTargetElement();
if (!element) return this;
- let collapsedText = collapseWhitespace(element.textContent);
+ let collapsedText = collapseWhitespace(element.textContent || '');
let result = collapsedText.indexOf(text) !== -1;
let actual = collapsedText;
let expected = text;
@@ -1043,7 +1085,7 @@ export default class DOMAssertions {
let element = this.findTargetElement();
if (!element) return this;
- let collapsedText = collapseWhitespace(element.textContent);
+ let collapsedText = collapseWhitespace(element.textContent || '');
let result = collapsedText.indexOf(text) === -1;
let expected = `Element ${this.targetDescription} does not include text "${text}"`;
let actual = expected;
@@ -1386,6 +1428,9 @@ export default class DOMAssertions {
if (this.target === null) {
return null;
} else if (typeof this.target === 'string') {
+ if (!this.rootElement) {
+ throw new Error('Cannot do selector-based queries without a root element');
+ }
return this.rootElement.querySelector(this.target);
} else if (this.target instanceof Element) {
return this.target;
@@ -1404,6 +1449,9 @@ export default class DOMAssertions {
if (this.target === null) {
return [];
} else if (typeof this.target === 'string') {
+ if (!this.rootElement) {
+ throw new Error('Cannot do selector-based queries without a root element');
+ }
return toArray(this.rootElement.querySelectorAll(this.target));
} else if (this.target instanceof Element) {
return [this.target];
diff --git a/packages/qunit-dom/lib/install.ts b/packages/qunit-dom/lib/install.ts
index af8b877ba..5c9034be1 100644
--- a/packages/qunit-dom/lib/install.ts
+++ b/packages/qunit-dom/lib/install.ts
@@ -20,11 +20,11 @@ export default function (assert: Assert) {
rootElement = rootElement || this.dom.rootElement || getRootElement();
- if (arguments.length === 0) {
- target = rootElement instanceof Element ? rootElement : null;
- }
-
- return new DOMAssertions(target, rootElement, this);
+ return new DOMAssertions(
+ target !== undefined ? target : rootElement instanceof Element ? rootElement : null,
+ rootElement,
+ this
+ );
};
function isValidRootElement(element: any): element is Element {
diff --git a/packages/qunit-dom/lib/root-element.ts b/packages/qunit-dom/lib/root-element.ts
index c92eb5fcc..4c78318bd 100644
--- a/packages/qunit-dom/lib/root-element.ts
+++ b/packages/qunit-dom/lib/root-element.ts
@@ -1,6 +1,6 @@
let _getRootElement: () => Element | null = () => null;
-export function overrideRootElement(fn: () => Element) {
+export function overrideRootElement(fn: () => Element | null) {
_getRootElement = fn;
}
diff --git a/packages/qunit-dom/tsconfig.json b/packages/qunit-dom/tsconfig.json
index c5a2810cc..d2396043f 100644
--- a/packages/qunit-dom/tsconfig.json
+++ b/packages/qunit-dom/tsconfig.json
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"noImplicitAny": true,
+ "strictNullChecks": true,
"declaration": true,
// Forces import type when imports are only used as types.
"verbatimModuleSyntax": true,