-1

With Capybara I am used to searching first('div', text: 'asdf'), but is there a way to search for elements that do not have the text?

I tried first('div', text: /^(?!asdf)/) and it still matches on div with 'asdf' text.

I checked to make sure my regex is right though: https://rubular.com/r/CN6MYKJNahiohD

Any ideas?

chasethesunnn
  • 2,149
  • 5
  • 25
  • 42
  • @ahmedAbdelhameed thanks, just tried and doesn't change anything – chasethesunnn Jul 24 '19 at 02:23
  • I tried `^(?!asdf).*$` – chasethesunnn Jul 24 '19 at 02:23
  • Try `^(?:(?!sdfg).)*$`. See it here: https://rubular.com/r/owVTZzw6y4Fws0. Or `^(?!.*sdfg).*$`. Please refer to [this answer](https://stackoverflow.com/a/37988661/4934172) for more. Always check other answers; don't just look at the accepted/most-upvoted one. – 41686d6564 stands w. Palestine Jul 24 '19 at 02:30
  • @ahmedabdelhameed thanks again, it still doesn't work. I don't think this is a regex issue. I didn't only look at the most upvoted. If you check my link, my regex works too, but it doesn't work in Capybara. So I am thinking this is a Capybara issue not specific to regex – chasethesunnn Jul 24 '19 at 02:34
  • No, your regex does match "asdf". At any case, I don't really have experience in Capybara so hopefully, someone else can help you. Good luck :) – 41686d6564 stands w. Palestine Jul 24 '19 at 02:36
  • https://rubular.com/r/iKZiHXDy7buiPP thanks – chasethesunnn Jul 24 '19 at 02:37
  • Note: https://gist.github.com/twalpole/8c791de49dcb0c6c95e011707c3e8090 shows Capybara working with the regex you're showing for text content containing `asdf` so to figure out exactly why it's not working for you it would be good to have the actual text content of the element it's matching. – Thomas Walpole Jul 24 '19 at 03:20
  • @thomaswalpole the actual text content is: `
    ` I tried `^(?!.*\bContributing Editor\b).*$` as the regex and it doesn't work. There are multiple `div` without `Contributing Editor` too
    – chasethesunnn Jul 24 '19 at 03:22
  • @Nils_e the content got escaped away so it's unreadable – Thomas Walpole Jul 24 '19 at 03:24
  • @thomasWalpole try now – chasethesunnn Jul 24 '19 at 03:29
  • 1
    @Nils_e I updated the linked gist to use that HTML and regex - and it works fine too - so not really sure what you're doing differently. – Thomas Walpole Jul 24 '19 at 03:33
  • @thomaswalpole you are right! I think it had to do with the fact that i assumed it was working similarly to `text` which matches the displayed case sensitive text. Even though the HTML is `CONTRIBUTING EDITOR` the css/js manipulates it to `Contributing Editor` and that is what `text` matches. I thought regex would be the same... so I wrote out Contributing Editor when in fact I should have wrote CONTRIBUTING EDITOR. I got it to match once I made that regex adjustment `^(?!.*\bCONTRIBUTING EDITOR\b).*$` – chasethesunnn Jul 24 '19 at 03:43
  • Thats what i get for trying to simplify a problem for explanation purposes – chasethesunnn Jul 24 '19 at 03:47
  • 1
    @Nils_e It should actually be comparing against the same text - however I'm guessing optimization is getting in the way. In order to speed up element finding on the first attempt Capybara will disassemble a regex and add the text fragments into the generated xpath - this would get applied against the unmodified text and wouldn't generally be an issue since if it didn't match it would just retry without the optimization. However with a negative lookup and CSS case adjustment this may not work as expected. I'll have to look into this later this week. – Thomas Walpole Jul 24 '19 at 03:57
  • @Nils_e unless you were running with the rack test driver which doesn’t process CSS? – Thomas Walpole Jul 24 '19 at 05:31

3 Answers3

1

Without knowing what the text content is of the actual element being returned it's tough to say what's wrong, but negative regexes get complicated (especially if the element has multiple lines of content) so it's pretty safe to assume the issue is your regex. In cases like this sometimes it's easier to just use a filter block

first('div') do |element|
  !element.text.include?('asdf')
end

Note: Capybara can't optimize this so it may not be as efficient, but for occasional use that's not always important.

Thomas Walpole
  • 48,548
  • 5
  • 64
  • 78
  • I had no idea you could pass a block into finders. This is good to know – chasethesunnn Jul 24 '19 at 02:57
  • @Nils_e Finders and matchers both support a filter block. It's generally better from a performance (and readability) perspective to use the builtin options when they will do what you need, but the flexibility is there when necessary - https://www.rubydoc.info/gems/capybara/Capybara/Node/Finders#find-instance_method – Thomas Walpole Jul 24 '19 at 03:01
  • Marking as the answer since it finds the element without causing too much overhead or further issues like race conditions – chasethesunnn Jul 24 '19 at 03:21
  • @thomswalpole this is a great solution and good knowledge to share. I moved the marked correct answer to the Regex solution by Emma – chasethesunnn Jul 24 '19 at 03:46
0

Not sure about Capybara, just guessing that you might be trying to design an expression that would exclude asdf with word boundaries in a string, maybe close to:

^(?!.*\basdf\b).*$

The expression is explained on the top right panel of regex101.com, if you wish to explore/simplify/modify it, and in this link, you can watch how it would match against some sample inputs, if you like.

Test

re = /^(?!.*\basdf\b).*$/s
str = 'some content before, then asdf as a word, some after
some content before, then noasdf as a word, some after'

str.scan(re) do |match|
    puts match.to_s
end
Community
  • 1
  • 1
Emma
  • 27,428
  • 11
  • 44
  • 69
  • 1
    Let me give this a try now – chasethesunnn Jul 24 '19 at 02:52
  • 1
    Thank you. This is really useful and I think it would help in finding the text, but for some reason it still doesn't work in capybara. Thank you though – chasethesunnn Jul 24 '19 at 03:00
  • 1
    this turned out to be the right answer actually. Capybara is working as expected and I need to adjust my regex. I was simplifying the regex for purposes of this example, but that create a layer of confusion. The actual expression was `^(?!.*\bCONTRIBUTING EDITOR\b).*$` and it worked great. This string doesn't match with my original `^(?!CONTRIBUTING EDITOR)`. Thank you! – chasethesunnn Jul 24 '19 at 03:45
0

My current workaround for this:

selected_element = 
   (all('div').select do |d|
      d.has_no_text?('asdf', wait: false)
   end).first

It will return nil if none are found.

chasethesunnn
  • 2,149
  • 5
  • 25
  • 42
  • 1
    Use a filter block passed to the finder rather than select because it gets evaluated inside the retry loop. Doing it like this answer shows is open to multiple race conditions – Thomas Walpole Jul 24 '19 at 03:04