antonmedv/finder

Supporting attributes

transitive-bullshit opened this issue · 5 comments

I've been doing quite a bit of background research on this space, and finder seems to be smarter than the competition by quite a bit. I also like the testing on real-world fixtures.

One feature that I'd love to see added is the ability to support HTML attributes. Some attributes like type=password are very strong signals and oftentimes unique single-level selectors.

Cheers 👍

Can you make an pr? With turned off filter for attributes?

What should the API for this be? Since attributes are ignored currently, it should be something to include attribute(s) (and optionally which value(s) thereof) rather than reject them (as with ids and classes currently). Penalized the same as classes (2)?

My current draft has this signature:


type Name = string
export type Options = {
  ...
  attr?: [[Name], (values: {[name: string]: any}) => Name | null]
  ...
}

such that you configure a tuple with the list of attributes you care about and a callback decide which attribute to use if any based on their values. But this leads to inelegant predicates:

test('config:attr', t => {
  const html = `
  <div data-test="1">
    <div data-qa="2"></div>
    <div data-qa="3"></div>
  </div>
  `
  check(t, html, {attr: [['data-qa', 'data-test'], (values) => {
    if(values.hasOwnProperty('data-test')) return null
    if(values.hasOwnProperty('data-qa')) return values['data-qa'] % 2 == 1 ? null : 'data-qa'
    return null
  }]})
})

but it at least shows the flexible behavior (skip data-test, use data-qa unless not even):

css html
css head
css body
css body > div
css [data-qa="2"]
css div:nth-child(2)

Thoughts?

What about adding something like this?

export type Options = {
  root: Element
  idName: (name: string) => boolean
  className: (name: string) => boolean
  tagName: (name: string) => boolean
+ attr: (name: string, value: string) => bool
  seedMinLength: number
  optimizedMinLength: number
  threshold: number
}

With usage:

  check(t, html, {attr: (name, value) => {
    if(name == 'data-test') {
       return false
    } else if(name == 'data-qa') { 
       return values % 2 == 1
    }
    return false
  }]})

I didn't want to iterate across all attributes of an element, but the simplicity is very appealing.

How would you feel about a penalty of 1.5?

Correction: I mean 0.5, so that it has priority between classes and ids.