Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
@open-wc/semantic-dom-diff
Advanced tools
To compare dom and shadow dom trees. Part of open-wc recommendations
Part of Open Web Components
Open Web Components provides a set of defaults, recommendations and tools to help facilitate your web component project. Our recommendations include: developing, linting, testing, building, tooling, demoing, publishing and automating.
yarn add @open-wc/semantic-dom-diff --dev
semantic-dom-diff
allows diffing chunks of dom or HTML for semanticaly equality:
While semantic-dom-diff
can be used standalone (see below), it most commonly used as a Chai plugin.
If you are using
@open-wc/testing
this is already done for you.
import { chai } from '@bundled-es-modules/chai';
import { chaiDomDiff } from '@open-wc/semantic-dom-diff';
chai.use(chaiDomDiff);
The Chai plugin supports both the BDD (expect
) and TDD (assert
) APIs.
expect(el).dom.to.equal('<div></div>');
assert.dom.equal(el, '<div></div>');
expect(el).dom.to.equal('<div foo="bar"></div>', {ignoreAttributes: ['foo']});
assert.dom.equal(el, '<div foo="bar"></div>', {ignoreAttributes: ['foo']});
expect(el).lightDom.to.equal('<div></div>');
assert.lightDom.equal(el, '<div></div>');
expect(el).shadowDom.to.equal('<div></div>');
assert.shadowDom.equal(el, '<div></div>');
You can set up our chai plugin to diff different types of DOM:
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML = '<p> shadow content </p>';
}
}
customElements.define('my-element', MyElement);
it('my test', async () => {
const el = await fixture(`
<my-element>
<div> light dom content </div>
</my-element>
`);
expect(el).dom // dom is <my-element><div>light dom content</div></my-element>
expect(el).lightDom // dom is <div>light dom content</div>
expect(el).shadowDom // dom is <p>shadow content</p>
});
You can use the chai plugin to manually diff chunks of dom. The dom is diffed semantically: whitespace, newlines etc. are normalized.
class MyElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML = '<p> shadow content </p>';
}
}
customElements.define('my-element', MyElement);
it('my test', async () => {
const el = await fixture(`
<my-element>
<div> light dom content </div>
</my-element>
`);
expect(el).dom.to.equal('<my-element><div>light dom content</div></my-element>');
expect(el).lightDom.to.equal('<div>light dom content</div>');
expect(el).shadowDom.to.equal('<p>shadow content</p>');
});
The most powerful feature of semantic-dom-diff
is the ability to test and manage snapshots of your web components.
If you are not using
@open-wc/testing-karma
, you need to manually install karma-snapshot and karma-mocha-snapshot.
Snapshots are created by setting up your component in a specific state, and then calling .to.equalSnapshot()
. You can use .dom
, .lightDom
or .shadowDom
to set up the dom of your element:
import { fixture } from '@open-wc/testing';
describe('my-message', () => {
it('renders message foo correctly', () => {
const element = await fixture(`
<my-message message="Foo"></my-element>
`);
expect(element).shadowDom.to.equalSnapshot();
});
it('renders message bar correctly', () => {
const element = await fixture(`
<my-message message="Bar"></my-element>
`);
expect(element).shadowDom.to.equalSnapshot();
});
it('renders a capitalized message correctly', () => {
const element = await fixture(`
<my-message message="Bar" capitalized></my-element>
`);
expect(element).shadowDom.to.equalSnapshot();
});
it('allows rendering a message from a slot', () => {
const element = await fixture(`
<my-message capitalized>Bar</my-element>
`);
expect(element).lightDom.to.equalSnapshot();
});
});
Snapshots are stored in the __snapshots__
folder in your project, using the most top level describe
as the name for your snapshots file.
If you are not using the standard
@open-wc/testing-karma
configuration, see the documentation ofkarma-snapshot
how to pass the update/prune flags.
When your tests run for the first time the snapshot files are generated. On subsequent test runs your element is compared with the stored snapshots. If the element and the snapshots differ the test fails.
If the difference was an intended change, you can update the snapshots by passing the --update-snapshots
flag.
After refactoring there might be leftover snapshot files which are unused. You can run karma with the --prune-snapshots
flag to clean these up.
Ignoring tags and attributes
When working with libraries or custom elements there might be parts of the rendered dom which is random or otherwise outside of your control. In those cases, you might want to ignore certain attributes or tags entirely. This is possible by passing an options object.
it('renders correctly', async () => {
const el = await fixture(`
<div my-random-attribute="${Math.random()}">
Hey
</div>
`);
expect(el).dom.to.equal('<div>Hey</div>', {
ignoreAttributes: ['my-random-attribute']
});
expect(el).dom.to.equalSnapshot({
ignoreAttributes: ['my-random-attribute']
});
});
Ignoring an attribute only for certain tags
Randomly generated ids are often used, throwing off your diffs. You can ignore attributes on specific tags:
it('renders correctly', async () => {
const el = await fixture(`
<input id="customInput${Math.random()}">
`);
// ignore id attributes on input elements
expect(el).dom.to.equal('<div>Hey</div>', {
ignoreAttributes: [
{ tags: ['input'], attributes: ['id'] }
]
});
expect(el).dom.to.equalSnapshot({
ignoreAttributes: [
{ tags: ['input'], attributes: ['id'] }
]
});
});
Ignoring tags
You can tell the diff to ignore certain tags entirely:
it('renders correctly', async () => {
const el = await fixture(`
<div>
<my-custom-element></my-custom-element>
foo
</div>
`);
// ignore id attributes on input elements
expect(el).dom.to.equal('<div>Hey</div>', {
ignoreTags: ['my-custom-element']
});
expect(el).dom.to.equalSnapshot({
ignoreTags: ['my-custom-element']
});
});
Ignoring children
When working with web components you may find that they sometimes render to their light dom, for example to meet some accessibility requirements. We don't want to ignore the tag completely, as we would then not be able to test if we did render the tag.
We can ignore just it's light dom:
it('renders correctly', async () => {
const el = await fixture(`
<div>
<my-custom-input id="myInput">
<input id="inputRenderedInLightDom">
Some text rendered in the light dom
</my-custom-input>
foo
</div>
`);
// ignore id attributes on input elements
expect(el).dom.to.equal(`
<div>
<my-custom-input id="myInput"></my-custom-input>
foo
</div>
`, { ignoreChildren: ['my-custom-element'] });
expect(el).dom.to.equalSnapshot({
ignoreChildren: ['my-custom-element']
});
});
FAQs
To compare dom and shadow dom trees. Part of open-wc recommendations
The npm package @open-wc/semantic-dom-diff receives a total of 28,605 weekly downloads. As such, @open-wc/semantic-dom-diff popularity was classified as popular.
We found that @open-wc/semantic-dom-diff demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.