Skip to content

feat(ByRole): Check only elements that could be a match#1046

Merged
eps1lon merged 3 commits into
testing-library:mainfrom
ph-fritsche:perf-byrole
Oct 13, 2021
Merged

feat(ByRole): Check only elements that could be a match#1046
eps1lon merged 3 commits into
testing-library:mainfrom
ph-fritsche:perf-byrole

Conversation

@ph-fritsche

@ph-fritsche ph-fritsche commented Oct 4, 2021

Copy link
Copy Markdown
Member

What:

Let querySelectorAll pre-filter elements if no normalizer is passed before running our own filters on them in queryAllByRole.

Why:

byRole is slow.

How:

If role parameter is a string, we can predetermine which elements might match our filter functions.
By dismissing other elements in querySelectorAll we reduce the time spent in our filter functions.

Checklist:

  • [N/A] Documentation added to the
    docs site
  • [N/A] Tests
  • [N/A] TypeScript definitions updated
  • Ready to be merged

Additional notes

I ran getByAllRoles(container, 'table') on the example document for role-helpers.
The avg time spent dropped from 9.5ms to 4.2ms.

@codesandbox-ci

codesandbox-ci Bot commented Oct 4, 2021

Copy link
Copy Markdown

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit a592dba:

Sandbox Source
react-testing-library-examples Configuration

@codecov

codecov Bot commented Oct 4, 2021

Copy link
Copy Markdown

Codecov Report

Merging #1046 (a592dba) into main (fb6f2d5) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##              main     #1046   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           25        25           
  Lines          919       927    +8     
  Branches       283       288    +5     
=========================================
+ Hits           919       927    +8     
Flag Coverage Δ
node-12 100.00% <100.00%> (ø)
node-14 100.00% <100.00%> (ø)
node-16.9.1 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/queries/role.js 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fb6f2d5...a592dba. Read the comment docs.

@MatanBobi

Copy link
Copy Markdown
Member

If I'm not mistaken here, the thing is that we have roles that are implicitly defined (by the elements) and we'll probably lose those if we filter using explicit role selector won't we?

@eps1lon

eps1lon commented Oct 4, 2021

Copy link
Copy Markdown
Member

How did you verify that this improves performance? Please share the results so that we can judge whether this optimization is worth the added code.

@ph-fritsche

Copy link
Copy Markdown
Member Author

If I'm not mistaken here, the thing is that we have roles that are implicitly defined (by the elements) and we'll probably lose those if we filter using explicit role selector won't we?

Handled here:

const roleRelations = roleElements.get(role)
const implicitRoleSelectors =
roleRelations && new Set(Array.from(roleRelations).map(({name}) => name))

The matching on attributes with implicit roles as performed by the filter could not be handled by querySelectorAll because the query runs on content attributes and we support querying roles per IDL attributes. Therefore I could not skip the following filter function completely.

But narrowing the selection according to the element types which might be matched is possible.

@ph-fritsche

Copy link
Copy Markdown
Member Author

How did you verify that this improves performance? Please share the results so that we can judge whether this optimization is worth the added code.

https://github.com/ph-fritsche/dom-testing-library/blob/a63a64f2bc73bfb89feba576eca7fe014ef9d2b8/src/__tests__/role-perf.js

@eps1lon

eps1lon commented Oct 4, 2021

Copy link
Copy Markdown
Member

So this was only tested in JSDOM? Would be nice if you could share a fixture that we can run with this PR in any environment and post the findings you have.

@ph-fritsche

Copy link
Copy Markdown
Member Author

In the browser just the queryAllByRole:

old avg: 3.1051999999284745 vs new avg: 0.20780000004172325

@ph-fritsche

Copy link
Copy Markdown
Member Author

old avg: 3.1051999999284745 vs new avg: 0.20780000004172325

@eps1lon Do you consider this enough evidence that adding code for creating a narrower selector is more efficient than running the following filter on all elements in the tree?

@ph-fritsche ph-fritsche requested a review from eps1lon October 13, 2021 05:56

@eps1lon eps1lon left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very helpful test fixture, thanks!

Something did set me off about this implementation. I think we tested this in the past and didn't see an significant gains.

Fiddled around with it a bit and it turns out that it can be slower for small DOM trees. Though for small DOM trees we're already fast enough that a slight slowdown (-~10%) is totally fine compared to having an order of magnitude faster queries for large DOM trees where perf really matters.

In the future, please include this work up front. If you already tested it locally, then sharing the work is in your interest as well for future reference. Perf improvements should just never go in untested.

@eps1lon eps1lon changed the title perf(byRole): check only elements that could be a match feat(byRole): Check only elements that could be a match Oct 13, 2021
@eps1lon eps1lon added the enhancement New feature or request label Oct 13, 2021
@eps1lon eps1lon changed the title feat(byRole): Check only elements that could be a match feat(ByRole): Check only elements that could be a match Oct 13, 2021
@eps1lon eps1lon merged commit 8edfad0 into testing-library:main Oct 13, 2021
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 8.9.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants