Code Monkey home page Code Monkey logo

qunit-dom's Introduction

qunit-dom

CI Build Status Latest NPM release

High Level DOM Assertions for QUnit

assert.dom('h1').exists();
assert.dom('h1').hasClass('title');
assert.dom('h1').hasText('Welcome to Ember, John Doe!');

assert.dom('input').isFocused();
assert.dom('input').hasValue(/.+ Doe/);
assert.dom('input').hasAttribute('type', 'text');

Note

qunit-dom was written and is maintained by Mainmatter and contributors. We offer consulting, training, and team augmentation for web development teams – check out our website to learn more!

Install

npm

npm install --save-dev qunit-dom

or using yarn:

yarn add --dev qunit-dom

Ember projects using ember-qunit v6.x and above

Import and run the setup function in your test-helper.js file:

// tests/test-helper.js
import * as QUnit from 'qunit';
import { setup } from 'qunit-dom';

//...

setup(QUnit.assert);

setApplication(Application.create(config.APP));

start();

//...

This will attach the APIs to QUnit's assert object.

Ember projects using < v6.x of ember-qunit

Install qunit-dom v2.0.0

<script> Tag

Load qunit-dom.js after qunit.js:

<script src="https://unpkg.com/qunitjs/qunit/qunit.js"></script>
<script src="https://unpkg.com/qunit-dom/dist/qunit-dom.js"></script>

Usage

Once installed the DOM element assertions are available at assert.dom(...).*:

test('the title is welcoming', function(assert) {
  assert.dom('#title').hasText('Welcome to QUnit');
});

All available assertions are documented in API.md.

A basic codemod to automatically convert your assertions is available at https://github.com/simplabs/qunit-dom-codemod.

TypeScript

qunit-dom includes type definition files, but the way it extends QUnit means that you need import it somewhere so that TS and your editor can pick up the types. It is recommended to add the following line to your tests/test-helper.ts file:

import 'qunit-dom';

Rename your tests/test-helper.js to .ts if you do not have such a file yet.

Contributing

See CONTRIBUTING.md.

Related

  • chai-dom – DOM assertions for the Chai assertion library using vanilla JavaScript
  • chai-jquery – jQuery assertions for chai

License

qunit-dom is developed by and © Mainmatter GmbH and contributors. It is released under the MIT License.

qunit-dom's People

Contributors

adriancooney avatar alexzurek avatar bendemboski avatar bobrimperator avatar brookjordan avatar czikarito avatar ddoria921 avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar jayjayjpg avatar lindem avatar locks avatar lucashill avatar marcoow avatar mikoscz avatar nullvoxpopuli avatar oreoz avatar pangratz avatar renovate-bot avatar renovate[bot] avatar rtablada avatar rwjblue avatar scalvert avatar selvagsz avatar sergeastapov avatar spencer516 avatar steventsao avatar turbo87 avatar wuron avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qunit-dom's Issues

Make it possible to get the target element from the DOMAssertions instance

That way you would not have to .querySelector the element yourself when you need it for other test helpers (for example ember-test-helpers).

// ...
let buttonElement = this.element.querySelector('.some-button');
let domAssertion = assert.dom(buttonElement);
domAssertion.exists();

await click(buttonElement);
// ...

Could be changed to something like:

// ...
let domAssertion = assert.dom('.some-button');
domAssertion.exists();

await click(domAssertion.element);
// ...

It would improve the DX a tiny bit, in my opinion 😁

Thoughts?

string.replace is not a function

Following code:

  assert.dom('.some-existing-element').hasText(1234);

Produces following exception:

TypeError: string.replace is not a function
    at collapseWhitespace (http://localhost:7357/assets/test-support.js:7820:17)
    at DOMAssertions.hasText (http://localhost:7357/assets/test-support.js:8124:20)
    at Object._callee$ (http://localhost:7357/assets/tests.js:244:52)
    at tryCatch (http://localhost:7357/assets/vendor.js:381:40)
    at Generator.invoke [as _invoke] (http://localhost:7357/assets/vendor.js:655:22)
    at Generator.prototype.(anonymous function) [as next] (http://localhost:7357/assets/vendor.js:414:21)
    at step (http://localhost:7357/assets/tests.js:194:32)
    at http://localhost:7357/assets/tests.js:205:15
    at <anonymous>

I believe it goes down to this line. And the fact that I'm passing down number instead of string.

I know that hasText()'s API is defined as: expected (string | RegExp), but the library should at least warn about unsupported parameter and not blow up. Preferably though, I'd like to see automatic conversion from number to string #justWorks™

Add hasProperty() assertion

This assertion should work roughly similar to hasAttribute() and can then be used to check for properties that don't have dedicated assertions (like hasValue()).

.hasClass() error message is confusing when it fails

Say you have an element like <div id="fun" class="thing1 thing2 thing3">, and your testing code is written like assert.dom('#fun').hasClass('thing1').

IF it does not find a class, it fails with "Expected: thing1", "Result: thing8 thing2 thing3".

... I guess this is not a very important issue.

Add a SCOPE option into .dom()

I run tests by dropping an iFrame of the target into my testrunner.html file. Then I do

a = document.getElementById('idOfiFrame').contentWindow;

This allows me to view the visual state of the page as it's being tested. I test all target functions via a.targetFunctionName.

Your plugin looks great, but I can't figure out how to make the .dom() test things within (a), instead of the testrunner.html window.

Any tips?

How to do custom assertions

How would one go about adding their own, project-local assertions to qunit-dom?

With QUnit, we can say

QUnit.assert.myAssertion = function() {...}

However, to be able to do that with an object returned from QUnit.dom(...), one would have to extend a DOMAssertions object or prototype. I couldn't seem to find where that is accessible.

Make the assertions chainable

What do you think about making it possible to chain assertions? That way you don't have to call assert.dom() multiple times (or cache it).

assert.dom('.some-class')
  .exists()
  .hasText('Testing')
  .hasAttribute('disabled');

[Ember] different root element

I have quite a few failing tests after applying the codemod, for tests that were using the :first-child selector. This is due to the fact that in normal Ember tests the first child will be the component under test, while here it is another wrapper element inside #ember-testing. This is similar to the same issue in ember-native-dom-helpers: cibernox/ember-native-dom-helpers#63.

I used this to fix it for my use, the same selector could be used in your vendor script. Can submit a PR if you agree!?

add a matchesSelector() and doesNotMatchSelector() assertion

Would it be a good idea to have something like

assert.dom(selector).matchesSelector(anotherSelector, message)

to be able to check if a given CSS selector returns the exact same (set of?) element(s)?

I would probably save some boilerplate if we had that, and CSS selectors have come to express a lot more things than just CSS classes and attributes (sibling detection, last-child, ...)

Just checking the waters here.

Add isDisabled() assertion

Adding checked and disabled states to be able to assert them easily:

assert.ok(find('input[type="checkbox"]').checked);
assert.dom('input[type="checkbox"]').isChecked();

assert.ok(find('input[type="checkbox"]').disabled); 
assert.dom('input[type="checkbox"]').isDisabled();

Also what about readonly or required?

Typescript Type Definitions

Hey there!

Working with this addon is amazing, however, working with this and TypeScript is not great as it doesn't have type definitions.

Do you have intention on adding Type Definitions to easy integrate with Ember apps using TypeScript?

Release latest version

Hello 👋 Would it be possible to get the latest changes (like isVisible) released in a new version? Thanks 🙏

Allow asserting against the root element (e.g. without `target`).

The following should be possible:

test('element has class', function(assert) {
  assert.dom().hasClass('data-foo');
});

However, the following error is thrown:

TypeError: Unexpected Parameter: undefined

The current work around (in Ember's moduleForComponent parlance) is:

test('element has class', function(assert) {
  this.render(hbs`{{some-thing}}`);

  assert.dom(this._element).hasClass('foo');
});

add support for multiple elements

I extracted https://github.com/kellyselden/ember-text-test-helper from an app without knowing hasText does whitespace collapsing. I would like to ditch my addon, but to reach feature parity, this project would need to support selectors that match multiple elements and "combine", as well as assert.dom(document.querySelectorAll('div')).

I think @Turbo87 is open to it, but every assertion will need to be updated. For instance hasAttribute will probably turn into a "all have attribute".

Assertion to count number of items on the page

Currently, when you want to find a count of elements on the page you do:

assert.dom('[data-test-something]').exists({ count: 4 });

While this works, I find the API a bit inelegant. Any thoughts on adding a count alias (or something similarly named) which essentially is a wrapper around the above call?

assert.dom('[data-test-something]').count(4);

Feel free to close if it's not something you'd like to add.

Add `hasVisibleText()` assertion

would you be interested in having a new function that allows you to filter out all the visible-hidden elements and just assert against the visibleText??

assert.dom(..).hasNItems(5)

I do something like:

assert.equal(this.element.querySelectorAll('.column-map-row').length, 1, 'Has one row');

very often, would love a helper to do it quicker. Not really stuck on the name hasNItems.

New implementation of includesText fails when expected text contains line breaks

After the change made to satisfy issue #104, you can no longer pass in text containing line breaks (or probably multiple spacing) as the expected text in includesText.

This is not a clear limitation in the documentation, and the resulting message in Ember is confusing:

screen shot 2018-11-15 at 8 39 31 pm

As you can see, it appears as though the test is failing because of the diff at the beginning of the string. If you are not distracted by the red highlight in the diff, you might notice that there is actually a line break discrepancy, too.

findTargetElement does not accept SVGElement

If I pass an SVGElement into assert.dom(<svg element>) and try to run any assertions, it fails because it requires a HTMLElement explicitly. Maybe move up the inheritance chain in the instanceof check to Element rather than HTMLElement?

let svgElement = document.querySelector("svg"); // Element retrieved manually because too complicated for selector
assert.dom(svgElement).hasAttribute("width", "100");

Throws:

TypeError: Unexpected Parameter: [object SVGGElement]
--
  | at DOMAssertions.findTargetElement (http://localhost:7357/assets/test-support.js:22780:15)
  | at DOMAssertions.hasAttribute (http://localhost:7357/assets/test-support.js:22285:26)
  | at Object._callee21$ (http://localhost:7357/assets/tests.js:140150:76)
  | at tryCatch (http://localhost:7357/assets/vendor.js:10983:40)
  | at Generator.invoke [as _invoke] (http://localhost:7357/assets/vendor.js:11274:22)
  | at Generator.prototype.(anonymous function) [as next] (http://localhost:7357/assets/vendor.js:11035:21)
  | at step (http://localhost:7357/assets/tests.js:139513:32)
  | at http://localhost:7357/assets/tests.js:139524:15
  | at <anonymous>

Use ESLint

It seems like I have forgotten to implement linting on this project... 😱

We should make sure to implement it in a way so that it warns us about unsupported browser features like Array.from() (on IE11)

typescript import not working

Using qunit-dom in an Ember project with ember-cli-typescript

ember ts:precompile gives errors:
tests/filename...(line, col): error TS2339: Property 'dom' does not exist on type 'Assert'.

As per the readme, I've added the import

// test-helper.js
import Application from '../app';
...
import { start } from 'ember-qunit';
import 'qunit-dom';
...

Support passing in dom elements as well as strings

My use case: interop with ember-native-dom-helpers which returns dom elements.

That way I can write things like:

import { find } from 'ember-native-dom-helpers';

test('it exists', async function(assert) {
  assert.dom(find('h1')).hasClass('bar');
});

Add `withDescription` helper method

The "message" argument is not very consistent - in some assertion helpers, it's the first argument - but in others, it's the second argument.

This is confusing, especially for helpers with an optional first argument. For example:

assert.dom('.my-element').exists({}, 'My description') // why did I have to pass an empty object here?

I propose a chained method for descriptions to handle the inconsistency. Example:

assert.dom('.my-element').exists().withDescription('My description')

rootElement in assert.dom()

When I'm testing multiple elements in one particular area I need to write something like:

let rootElement = document.querySelector(".super .long .selector .that .i .dont .want .to .repeat");

assert.dom('.foo', rootElement).hasText('foo');
assert.dom('.bar', rootElement).hasText('bar');
assert.dom('.baz', rootElement).hasText('baz');
...
  • Because rootElement can be only HTMLElement, I can't use simple CSS selector string. Not really a problem, since that would come with performance penalty anyway.
  • I'm wondering whether there is different, more "standard" way to write this? Namely asking about document.querySelector which feels really "bare" to use.

White-space is unwieldy during includesText assertion

Issue

While building HTML we regularly leave line-breaks and various inline elements, and that shouldn't interfere with testing.

The current implementation of includesText includes all types of whitespace and line-breaks which can make assertions very cumbersome.

This would also have the side effect of bringing includesText in-line with hasText.

Possible solution

All white space inside the element is collapsed into a single space, similarly to how it happens in HTML.

Possible implementation

Update this:
https://github.com/simplabs/qunit-dom/blob/632205606020d9d7e6aa3c0a83bb34502a65f9e5/lib/assertions.ts#L559-L565

to:

let collapsedText = collapseWhitespace(element.textContent);
let result = collapsedText.indexOf(text) !== -1;
let actual = collapsedText;
let expected = text;

if (!message) {
  message = `Element ${this.targetDescription} has text containing "${text}"`;
}

And apply a similar change to: doesNotIncludeText

References

Edited upon finding the collapseWhitespace function

assert.dom('select.foo').selection.hasText()

It would be awesome if there was an easier way to run assertions on the selected <option> of a <select> element. Proposal:

assert.dom('select.foo').selection.hasText('Foo!');

instead of

assert.dom([...this.element.querySelector('select.foo').querySelectorAll('option')].find(it => it.selected)).hasText('Foo!');

Whitespace collapsing in `hasText` does not behave as expected

I have a translation string with HTML in it, and I wrote a test to verify that it renders, properly escaping the HTML.

My string looks like (simplified & redacted):
Something went wrong on our end. Please try again later or contact <a target="_blank" href="#support-team">our&nbsp;support&nbsp;team</a>.

My test looks like:
assert.dom('[data-test-not-found-subtitle]').hasText('Something went wrong on our end. Please try again later or contact our support team.');

In qunit I see this:
screen shot 2018-11-09 at 6 03 38 pm

It seems like it isn't doing the right thing with the &nbsp;. I've tried copying the qunit result line into my test string, and it still doesn't recognize a match.

Assertions do not have a return value

During a debug session of a failing test I was trying to see why the assertion failed (to see why the query selector was incorrect). But executing the same assertion methods in the devtools console window returned undefined instead of the actual result of the assertion. It would help during debugging what the result of the assertion is so that I could try the same assertion with a different selector.

So for example the current behaviour is as follows

assert.dom('div .my-selector').exists() // Returns undefined

I would expect something like

assert.dom('div .my-selector').exists() // Returns false/true

`Element <target> exists` error message when it does not.

So while using the isNotVisible, I tested on a selector that did not return any elements and got the following error:

assert.dom('.my_selector').isNotVisible();
// Throws: Element .my_selector exists

This message to begin with is a little cryptic because it doesn't mention visibility anywhere (which could be something we should look into). Secondly, elements matching the .my_selector don't exist! Exactly the opposite of what the error message suggests. This could be considered a bug, right? I think the offending code lies in findTargetElement with some interesting conditions:

  findTargetElement() {
    if (this.target === null) {
      let message = `Element <unknown> exists`;
      this.pushResult({ message, result: false });
      return null;
    }

    if (typeof this.target === 'string') {
      let el = this.rootElement.querySelector(this.target);

      if (el === null) {
        let message = `Element ${this.target || '<unknown>'} exists`;
        this.pushResult({ message, result: false });
      }

      return el;

    } else if (this.target instanceof Element) {
      return this.target;

    } else {
      throw new TypeError(`Unexpected Parameter: ${this.target}`)
    }
  }

Am I missing something? Or any ideas on a fix? I can submit a PR if necessary.

Add isVisible() assertion

Would there be any room for an isVisible/isNotVisible assertion? Obviously you don't want to add every possible assertion under the sun to the library but if you think it's something that would be beneficial, I'd be happy to work on it.

Add isRequired() and isNotRequired() assertions

This assertion should check if the required property of e.g. an <input> element is true or not.

Example:

assert.dom('.username-field').isRequired();
assert.dom('.coupon-code').isNotRequired();

Increase cohesion with ember-test-selectors

Would there be interest in either modifying how the hasValue method works or adding a new one that works well with data-test-* attribute selectors?

A common pattern we use is: assert.dom('[data-test-record-id="1"]').exists(). This fails with the same message if either the data-test-record-id attr is not present or it is present but has a different value. It would make our tests easier to work with if we could do something like:

assert.dom('[data-test-record-id]').hasValue("1")

And then this could fail with one useful message if data-test-record-id is missing, or a useful diff when it is present but has a value other than "1".

Overloading the hasValue method doesn't seem quite right, so perhaps this is an enhancement request, to add a new method. Maybe hasSelectorValue?
If there's interest in adding this I'd be happy to make a PR for it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.