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

Support document fragments #41

Open
brettz9 opened this issue Nov 4, 2019 · 7 comments
Open

Support document fragments #41

brettz9 opened this issue Nov 4, 2019 · 7 comments

Comments

@brettz9
Copy link
Contributor

brettz9 commented Nov 4, 2019

Hi,

I'm wondering if you'd be open to supporting checks of the HTML (or text) on document fragments?

I'm currently using this awkward hack:

const container = (string) => {
  const dummyContainer = document.createElement('div');
  dummyContainer.append(string);
  return dummyContainer;
};
expect(container(frag)).to.have.html(...)

Thanks!

@nathanboktae
Copy link
Owner

Seems easy enough to do your own fragment chai assertion chain. Why would 70-90% of chai-dom users care about this use case?

@brettz9
Copy link
Contributor Author

brettz9 commented Nov 4, 2019

In every library, some people won't need everything. Some already might not need text() but need html(), or vice versa. And your library is named "chai-dom" so I thought it aimed itself as being generically useful for testing the DOM. I don't think a fragment is that obscure of a use case; it's not like asking to test attribute nodes or something. But hey, it's obviously your call...

@brettz9
Copy link
Contributor Author

brettz9 commented Nov 4, 2019

FWIW, I've adapted your own html method, and this seems to work all right:

chai.use(function (_chai, utils) {
  const {flag} = utils;

  const container = (contents) => {
    const dummyContainer = document.createElement('div');
    dummyContainer.append(contents);
    return dummyContainer;
  };

  _chai.Assertion.addMethod('fragmentHtml', function (html) {
    const frag = flag(this, 'object'),
      actual = container(flag(this, 'object')).innerHTML;

    if (flag(this, 'contains')) {
      this.assert(
        actual.includes(html),
        'expected #{act} to contain HTML #{exp}',
        'expected #{act} not to contain HTML #{exp}',
        html,
        actual
      );
    } else {
      this.assert(
        actual === html,
        'expected ' + String(frag) +
          ' to have HTML #{exp}, but the HTML was #{act}',
        'expected ' + String(frag) + ' not to have HTML #{exp}',
        html,
        actual
      );
    }
  });
});

@nathanboktae
Copy link
Owner

So to understand your use case, you have a string of HTML, and you want to check that it's parsed correctly?

@brettz9
Copy link
Contributor Author

brettz9 commented Mar 31, 2020

I have DocumentFragment nodes (as created by document.createDocumentFragment()) and want to check their contents, but there is no innerHTML for fragments, making it harder to check their contents.

I have an i18n (internationalization) library, intl-dom which returns a string by default if there is no injecting of DOM elements/nodes, while returning a fragment otherwise.

As an example, the i18n function my app produces can accept DOM objects for safe injection into locale strings (without allowing the locale to include arbitrary HTML) like this:

const result = _('translationWithLineBreak', {
    lb: $('<br>')[0]
});

with the locale file:

{
  "translationWithLineBreak": "here is a translation with a{lb}line break"
}

And the resulting DOM fragment (represented here as a string):

here is a translation with a<br/>line break

I don't need or want to return this encapsulated in a <div>, as the user might want to directly inject the resulting fragment into some other element (and I don't want to trouble my users to need to add innerHTML to get at the inner contents in addition to creating an unnecessary container element for them like div).

Sometimes, however, a project may use our i18n API but not have any DOM to inject. In such cases, we just return a Javascript string rather than a DOM fragment (unless the user opts to always get a fragment).

We return different types by default because:

  1. The new HTML methods, append and prepend (which are simplifications of appendChild and insertBefore) as well as before and after now accept strings (which are converted by these methods to text nodes before appending)--not only Nodes. This promotes a polymorphism between fragments and strings in the context of being able to add either or both to the DOM in the same method call (e.g., as part of a template).

E.g., in my templating tool, jamilih, one can embed fragments or strings:

jml('section', [
  'some text: ',
  _('translationWithLineBreak', {
      lb: $('<br>')[0]
  }),
  ['div', [
    'and some text in an element
  ]]
])

...giving this DOM:

<section>some text: here is a translation with a<br/>line break<div>and some text in an element</div></section>
  1. Strings can be easier to manipulate and introspect (when one knows one hasn't injected any DOM), but fragments are necessary when the string contains DOM elements.

So with the above, whether a string or fragment is returned, one can always do:

desiredParentContainer.append(result);

TL;DR: Even if your project doesn't allow checking of strings in the same manner as fragments, it would be nice to explicitly be able to check document fragment contents out of the box since fragments have long been a part of the DOM, and as per the above, they continue to have their uses.

@nathanboktae
Copy link
Owner

What might be nice is to have html() handle DocumentFragments so it stays clean and readable, and very little change to the existing assertion.

@brettz9
Copy link
Contributor Author

brettz9 commented Mar 31, 2020

That'd work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants