jest-dom
jest-dom copied to clipboard
Add .toAppearBefore(...) assertion
Describe the feature you'd like:
API
expect(HTMLElement).toAppearBefore(HTMLElement)
This assertion would check that one DOM node appears before the other. This is important if say, you have two buttons on screen, and you want the primary / most important one to appear first in the DOM.
Drawbacks This assertion is very specific - if similar queries were to be added for say, .toAppearAfter, they'd have to be separate functions. However by keeping the assertion specific we make the test language more organic - i.e.
const purchaseButton = getByText("Buy Item")
const favouriteButton = getByText("Add to favourites")
expect(purchaseButton).toAppearBefore(favouriteButton)
is more readable than a more extensible API, e.g.
const purchaseButton = getByText("Buy Item")
const favouriteButton = getByText("Add to favourites")
expect(purchaseButton).toAppearInOrder(favouriteButton, ORDER_POSITION.BEFORE)
In the extendable example above you may think it's the other way round, i.e. favourite button should come before purchase button.
Another drawback is people could mistake this for a visual check, taking into account external styling rules from CSS etc, rather than on the DOM.
Suggested implementation:
There's the Node.compareDocumentPosition API - https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition. This takes another element and returns a bitmask value back to say whether it's before, right after or in an unrelated place in the DOM.
I haven't confirmed if JSDOM has a working implementation of this API, but I did find this bug report that suggests it will be supported - https://github.com/jsdom/jsdom/issues/777
I'm not familiar with how this library works under the hood, but if this is a feature that's approved I'd be happy to dive in and contribute a PR or implementation suggestion back to the community.
Describe alternatives you've considered:
The main alternative (that I can think of currently) would be using the Node.compareDocumentPosition API directly in a test, and do some bitmask operations to work out if the element passes or not. Bitmasks are rarely used in JS/TS codebases (in my experience at least) and the assertion message wouldn't be very useful (along the lines of expect 2 to be 4).
Teachability, Documentation, Adoption, Migration Strategy:
Given the API is limited in scope, I believe we shouldn't need a huge amount of additional docs to explain it. Hopefully the API and a brief description would suffice. Happy to extend this with proposed docs if there's interest in the feature.
Would be great to have this in the library. In the meantime, I extended Jest's matchers myself in my test setup file:
interface CustomMatchers<R = unknown> {
toAppearBefore: (argument: HTMLElement) => R;
}
declare global {
/* eslint-disable */
// https://jestjs.io/docs/26.x/expect#expectextendmatchers
namespace jest {
interface Expect extends CustomMatchers {}
interface Matchers<R> extends CustomMatchers<R> {}
interface InverseAsymmetricMatchers extends CustomMatchers {}
}
/* eslint-enable */
}
expect.extend({
toAppearBefore(received: HTMLElement, argument: HTMLElement) {
const pass =
received.compareDocumentPosition(argument) &
Node.DOCUMENT_POSITION_FOLLOWING;
return pass
? {
message: () => `expected ${received} not to be before ${argument}`,
pass: true,
}
: {
message: () => `expected ${received} to be before ${argument}`,
pass: false,
};
},
});
I have no big objections to this.
One potential problem with this is flex-direction: row-reverse, flex-direction: column-reverse and some configurations of display: grid where the visual order is not the same as the order of elements in the DOM or HTML source code.
But I agree that that should not be a reason not to have it. We only need to document these gotchas.
Anyone open to contribute this new feature?