Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add isVisible and isNotVisible assertion helpers #67

Merged
merged 6 commits into from
Mar 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,54 @@ Assert that the [HTMLElement][] or an [HTMLElement][] matching the
assert.dom('input[type="text"]').isNotRequired();
```

### isVisible

Assert that the [HTMLElement][] or an [HTMLElement][] matching the
`selector` is visible. Visibility is determined with the hueristic
used in [jQuery's :visible pseudo-selector](https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/src/css/hiddenVisibleSelectors.js#L12),
specifically:

- is the element's offsetWidth non-zero
- is the element's offsetHeight non-zero
- is the length of an element's DOMRect objects found via getClientRects() non-zero
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to clarify, this means it's visible on the page, but not necessarily that it's within the currently rendered viewport, correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's exactly what it means.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, can we add that to the docs so that they read a little easier? 😉

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Absolutely.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


Additionally, visibility in this case means that the element is visible on the page,
but not necessarily in the viewport.

**Parameters**

- `message` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?**

**Examples**

```javascript
assert.dom('.foo').isVisible();
```

### isNotVisible

Assert that the [HTMLElement][] or an [HTMLElement][] matching the
`selector` is not visible. Visibility is determined with the hueristic
used in [jQuery's :visible pseudo-selector](https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/src/css/hiddenVisibleSelectors.js#L12),
specifically:

- is the element's offsetWidth non-zero
- is the element's offsetHeight non-zero
- is the length of an element's DOMRect objects found via getClientRects() non-zero

Additionally, visibility in this case means that the element is visible on the page,
but not necessarily in the viewport.

**Parameters**

- `message` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?**

**Examples**

```javascript
assert.dom('.foo').isNotVisible();
```

### hasAttribute

- **See: [#doesNotHaveAttribute](#doesnothaveattribute)**
Expand Down
47 changes: 47 additions & 0 deletions lib/__tests__/is-not-visible.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-env jest */

import TestAssertions from '../helpers/test-assertions';

describe('assert.dom(...).isNotVisible()', () => {
let assert;

beforeEach(() => {
assert = new TestAssertions();
});

describe('selector only', () => {
test('fails if element is missing', () => {
document.body.innerHTML = '<h1 class="baz">foo</h1>bar';

assert.dom('h2').isNotVisible();

expect(assert.results).toEqual([{
message: 'Element h2 exists',
result: false,
}]);
});
});

describe('custom messages', () => {
test('shows custom messages', () => {
document.body.innerHTML = '<h1 class="baz">foo</h1>bar';

assert.dom('h1').isNotVisible('foo');

expect(assert.results).toEqual([{
actual: 'Element h1 is not visible',
expected: 'Element h1 is not visible',
message: 'foo',
result: true,
}]);
});
});

test('throws for unexpected parameter types', () => {
expect(() => assert.dom(5).isNotVisible()).toThrow('Unexpected Parameter: 5');
expect(() => assert.dom(true).isNotVisible()).toThrow('Unexpected Parameter: true');
expect(() => assert.dom(undefined).isNotVisible()).toThrow('Unexpected Parameter: undefined');
expect(() => assert.dom({}).isNotVisible()).toThrow('Unexpected Parameter: [object Object]');
expect(() => assert.dom(document).isNotVisible()).toThrow('Unexpected Parameter: [object HTMLDocument]');
});
});
47 changes: 47 additions & 0 deletions lib/__tests__/is-visible.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-env jest */

import TestAssertions from '../helpers/test-assertions';

describe('assert.dom(...).isVisible()', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a comment here explaining why this is difficult to test in the jsdom-based testing system and possibly a reference to the Ember-based tests?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do that for sure.

let assert;

beforeEach(() => {
assert = new TestAssertions();
});

describe('selector only', () => {
test('fails if element is missing', () => {
document.body.innerHTML = '<h1 class="baz">foo</h1>bar';

assert.dom('h2').isVisible();

expect(assert.results).toEqual([{
message: 'Element h2 exists',
result: false,
}]);
});
});

describe('custom messages', () => {
test('shows custom messages', () => {
document.body.innerHTML = '<h1 class="baz">foo</h1>bar';

assert.dom('h1').isVisible('foo');

expect(assert.results).toEqual([{
actual: 'Element h1 is not visible',
expected: 'Element h1 is visible',
message: 'foo',
result: false,
}]);
});
});

test('throws for unexpected parameter types', () => {
expect(() => assert.dom(5).isVisible()).toThrow('Unexpected Parameter: 5');
expect(() => assert.dom(true).isVisible()).toThrow('Unexpected Parameter: true');
expect(() => assert.dom(undefined).isVisible()).toThrow('Unexpected Parameter: undefined');
expect(() => assert.dom({}).isVisible()).toThrow('Unexpected Parameter: [object Object]');
expect(() => assert.dom(document).isVisible()).toThrow('Unexpected Parameter: [object HTMLDocument]');
});
});
48 changes: 48 additions & 0 deletions lib/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import isChecked from './assertions/is-checked';
import isNotChecked from './assertions/is-not-checked';
import isRequired from './assertions/is-required';
import isNotRequired from './assertions/is-not-required';
import isVisible from './assertions/is-visible';
import isNotVisible from './assertions/is-not-visible';

import elementToString from './helpers/element-to-string';
import collapseWhitespace from './helpers/collapse-whitespace';
Expand Down Expand Up @@ -136,6 +138,52 @@ export default class DOMAssertions {
isNotRequired.call(this, message);
}

/**
* Assert that the [HTMLElement][] or an [HTMLElement][] matching the
* `selector` is visible. Visibility is determined with the hueristic
* used in [jQuery's :visible pseudo-selector](https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/src/css/hiddenVisibleSelectors.js#L12),
* specifically:
*
* - is the element's offsetWidth non-zero
* - is the element's offsetHeight non-zero
* - is the length of an element's DOMRect objects found via getClientRects() non-zero
*
* Additionally, visibility in this case means that the element is visible on the page,
* but not necessarily in the viewport.
*
* @param {string?} message
*
* @example
* assert.dom('.foo').isVisible();
*
*/
isVisible(message) {
isVisible.call(this, message);
}

/**
* Assert that the [HTMLElement][] or an [HTMLElement][] matching the
* `selector` is not visible. Visibility is determined with the hueristic
* used in [jQuery's :visible pseudo-selector](https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/src/css/hiddenVisibleSelectors.js#L12),
* specifically:
*
* - is the element's offsetWidth non-zero
* - is the element's offsetHeight non-zero
* - is the length of an element's DOMRect objects found via getClientRects() non-zero
*
* Additionally, visibility in this case means that the element is visible on the page,
* but not necessarily in the viewport.
*
* @param {string?} message
*
* @example
* assert.dom('.foo').isNotVisible();
*
*/
isNotVisible(message) {
isNotVisible.call(this, message);
}

/**
* Assert that the [HTMLElement][] has an attribute with the provided `name`
* and optionally checks if the attribute `value` matches the provided text
Expand Down
18 changes: 18 additions & 0 deletions lib/assertions/is-not-visible.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import visible from '../helpers/visible';

export default function isNotVisible(message) {
let element = this.findTargetElement();
if (!element) return;

let result = !visible(element);
let actual = result
? `Element ${this.target} is not visible`
: `Element ${this.target} is visible`;
let expected = `Element ${this.target} is not visible`;

if (!message) {
message = expected;
}

this.pushResult({ result, actual, expected, message });
}
18 changes: 18 additions & 0 deletions lib/assertions/is-visible.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import visible from '../helpers/visible';

export default function isVisible(message) {
let element = this.findTargetElement();
if (!element) return;

let result = visible(element);
let actual = result
? `Element ${this.target} is visible`
: `Element ${this.target} is not visible`;
let expected = `Element ${this.target} is visible`;

if (!message) {
message = expected;
}

this.pushResult({ result, actual, expected, message });
}
6 changes: 6 additions & 0 deletions lib/helpers/visible.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Visible logic based on jQuery's
// https://github.com/jquery/jquery/blob/4a2bcc27f9c3ee24b3effac0fbe1285d1ee23cc5/src/css/hiddenVisibleSelectors.js#L11-L13

export default function visible(el) {
return el !== null && (el.offsetWidth !== 0 || el.offsetHeight !== 0 || el.getClientRects().length !== 0);
}
12 changes: 11 additions & 1 deletion tests/acceptance/qunit-dom-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import moduleForAcceptance from '../../tests/helpers/module-for-acceptance';
moduleForAcceptance('Acceptance | qunit-dom');

test('qunit-dom assertions are available', function(assert) {
assert.expect(7);
assert.expect(12);

assert.ok(assert.dom, 'assert.dom is available');
assert.ok(assert.dom('.foo').includesText, 'assert.dom(...).includesText is available');
Expand All @@ -18,5 +18,15 @@ test('qunit-dom assertions are available', function(assert) {
assert.dom('#title').exists();
assert.dom('#subtitle').doesNotExist();
assert.dom('#title').hasText('Welcome to Ember');

// JSDom based tests aren't able to discern visibility as we define it. Specifically,
// the JSDom tests don't do layouting, therefore calculating `offsetWdith` or `offsetHeight`
// won't work. As a result, we need to use Ember's test infrastructure to correctly assess
// visibility, as those tests run in a browser environment.
assert.dom('#title').isVisible();
assert.dom('#display-block').isVisible();
assert.dom('#display-none').isNotVisible();
assert.dom('#display-descendant').isNotVisible();
assert.dom('#hidden-input').isNotVisible();
});
});
6 changes: 6 additions & 0 deletions tests/dummy/app/templates/application.hbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<h2 id="title">
Welcome to <b>Ember</b>
</h2>
<p id="display-none" style="display: none;">You can't see me.</p>
<div style="display: none;">
<p id="display-descendant" style="display: block;">You can't see me.</p>
</div>
<p id="display-block" style="display: block;">You can see me.</p>
<input id="hidden-input" type="hidden">

{{outlet}}