jest-dom icon indicating copy to clipboard operation
jest-dom copied to clipboard

Add .toAppearBefore(...) assertion

Open MattTennison opened this issue 3 years ago • 2 comments

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.

MattTennison avatar May 19 '22 16:05 MattTennison

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,
			  };
	},
});

narthur avatar Jun 27 '23 13:06 narthur

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?

gnapse avatar Jun 29 '23 18:06 gnapse