require_relative '../unit_helper' describe Watir::Locators::Element::Matcher do include LocatorSpecHelper let(:query_scope) { @query_scope || browser } let(:values_to_match) { @values_to_match || {} } let(:matcher) { described_class.new(query_scope, values_to_match) } describe '#match' do context 'a label element' do it 'returns elements with for / id pairs' do input_wds = [wd_element(tag_name: 'input', attributes: {id: 'foob_id'}), wd_element(tag_name: 'input', attributes: {id: 'bfoo_id'}), wd_element(tag_name: 'input', attributes: {id: 'foo_id'})] label_wds = [wd_element(tag_name: 'label'), wd_element(tag_name: 'label'), wd_element(tag_name: 'label')] allow(browser).to receive(:execute_script).and_return('foob', 'Foo', 'foo') labels = [element(watir_element: Watir::Label, wd: label_wds[0], for: 'foob_id'), element(watir_element: Watir::Label, wd: label_wds[1], for: 'bfoo_id'), element(watir_element: Watir::Label, wd: label_wds[2], for: 'foo_id')] # Only the Watir::Label matching the text provided will have wd called expect(labels[0]).not_to receive(:for) expect(labels[1]).not_to receive(:for) allow(query_scope).to receive(:labels).and_return(labels) allow_any_instance_of(Watir::Input).to receive(:wd).and_return(input_wds[2], input_wds[2]) values_to_match = {label_element: 'foo'} expect(matcher.match(input_wds, values_to_match, :all)).to eq [input_wds[2]] end it 'returns elements without for / id pairs' do input_wds = [wd_element(tag_name: 'input'), wd_element(tag_name: 'input'), wd_element(tag_name: 'input')] inputs = [element(watir_element: Watir::Input, wd: input_wds[0]), element(watir_element: Watir::Input, wd: input_wds[1]), element(watir_element: Watir::Input, wd: input_wds[2])] label_wds = [wd_element(tag_name: 'label'), wd_element(tag_name: 'label'), wd_element(tag_name: 'label')] allow(browser).to receive(:execute_script).and_return('foob', 'Foo', 'foo') labels = [element(watir_element: Watir::Label, wd: label_wds[0], for: '', input: inputs[0]), element(watir_element: Watir::Label, wd: label_wds[1], for: '', input: inputs[1]), element(watir_element: Watir::Label, wd: label_wds[2], for: '', input: inputs[2])] # Only the Watir::Label matching the text provided will have wd called expect(labels[0]).not_to receive(:for) expect(labels[1]).not_to receive(:for) allow(query_scope).to receive(:labels).and_return(labels) values_to_match = {label_element: 'foo'} expect(matcher.match(input_wds, values_to_match, :all)).to eq [input_wds[2]] end it 'returns elements with multiple matching label text but first missing corresponding element' do input_wds = [wd_element(tag_name: 'input'), wd_element(tag_name: 'input')] inputs = [element(watir_element: Watir::Input, wd: wd_element(tag_name: 'input')), element(watir_element: Watir::Input, wd: input_wds[1])] label_wds = [wd_element(tag_name: 'label'), wd_element(tag_name: 'label'), wd_element(tag_name: 'label')] allow(browser).to receive(:execute_script).and_return('foo', 'foo') labels = [element(watir_element: Watir::Label, wd: label_wds[0], for: '', input: inputs[0]), element(watir_element: Watir::Label, wd: label_wds[1], for: '', input: inputs[1])] allow(query_scope).to receive(:labels).and_return(labels) values_to_match = {label_element: 'foo'} expect(matcher.match(input_wds, values_to_match, :all)).to eq [input_wds[1]] end it 'returns empty Array if no label element matches' do input_wds = [wd_element(tag_name: 'input', attributes: {id: 'foo'}), wd_element(tag_name: 'input', attributes: {id: 'bfoo'})] label_wds = [wd_element(tag_name: 'label'), wd_element(tag_name: 'label'), wd_element(tag_name: 'label')] allow(browser).to receive(:execute_script).and_return('Not this', 'or this') labels = [element(watir_element: Watir::Label, wd: label_wds[0], for: 'foo'), element(watir_element: Watir::Label, wd: label_wds[1], for: 'bfoo')] allow(query_scope).to receive(:labels).and_return(labels) values_to_match = {label_element: 'foo'} expect(matcher.match(input_wds, values_to_match, :all)).to eq [] end it 'returns empty Array if matching label elements do not have an corresponding input element' do input_wds = [wd_element(tag_name: 'input'), wd_element(tag_name: 'input')] inputs = [element(watir_element: Watir::Input, wd: wd_element(tag_name: 'input')), element(watir_element: Watir::Input, wd: wd_element(tag_name: 'input'))] label_wds = [wd_element(tag_name: 'label'), wd_element(tag_name: 'label')] allow(browser).to receive(:execute_script).and_return('foob', 'foo') labels = [element(watir_element: Watir::Label, wd: label_wds[0], for: '', input: inputs[0]), element(watir_element: Watir::Label, wd: label_wds[1], for: '', input: inputs[1])] allow(query_scope).to receive(:labels).and_return(labels) values_to_match = {label_element: 'foo'} expect(matcher.match(input_wds, values_to_match, :all)).to eq [] end end context 'when locating one element' do before { @filter = :first } it 'by tag name' do elements = [wd_element(tag_name: 'div'), wd_element(tag_name: 'span')] @values_to_match = {tag_name: 'span'} expect(matcher.match(elements, values_to_match, @filter)).to eq elements[1] end it 'by attribute' do elements = [wd_element(attributes: {id: 'foo'}), wd_element(attributes: {id: 'bar'}), wd_element(attributes: {id: 'foobar'})] @values_to_match = {id: 'foobar'} expect(matcher.match(elements, values_to_match, @filter)).to eq elements[2] end it 'by class array' do elements = [wd_element(attributes: {class: 'foob bar'}), wd_element(attributes: {class: 'bar'}), wd_element(attributes: {class: 'bar foo'})] @values_to_match = {class: %w[foo bar]} expect(matcher.match(elements, values_to_match, @filter)).to eq elements[2] end it 'by positive index' do elements = [wd_element(tag_name: 'div'), wd_element(tag_name: 'span'), wd_element(tag_name: 'div')] @values_to_match = {tag_name: 'div', index: 1} expect(matcher.match(elements, values_to_match, @filter)).to eq elements[2] end it 'by negative index' do elements = [wd_element(tag_name: 'div'), wd_element(tag_name: 'span'), wd_element(tag_name: 'div')] @values_to_match = {tag_name: 'div', index: -1} expect(matcher.match(elements.dup, values_to_match, @filter)).to eq elements[2] end it 'by visibility true' do elements = [wd_element(tag_name: 'div', "displayed?": false), wd_element(tag_name: 'span', "displayed?": true)] @values_to_match = {visible: true} expect(matcher.match(elements, values_to_match, @filter)).to eq elements[1] end it 'by visibility false' do elements = [wd_element(tag_name: 'div', "displayed?": true), wd_element(tag_name: 'span', "displayed?": false)] @values_to_match = {visible: false} expect(matcher.match(elements, values_to_match, @filter)).to eq elements[1] end it 'by text' do elements = [wd_element, wd_element] @values_to_match = {text: 'Foob'} allow(browser).to receive(:execute_script).and_return('foo', 'Foob') expect(matcher.match(elements, values_to_match, @filter)).to eq elements[1] end it 'by visible text' do elements = [wd_element(text: 'foo'), wd_element(text: 'Foob')] @values_to_match = {visible_text: /Foo|Bar/} allow(elements[0]).to receive(:text).and_return 'foo' allow(elements[1]).to receive(:text).and_return 'Foob' expect(matcher.match(elements, values_to_match, @filter)).to eq elements[1] end it 'by href' do elements = [wd_element(tag_name: 'div', attributes: {href: 'froo.com'}), wd_element(tag_name: 'span', attributes: {href: 'bar.com'})] @values_to_match = {href: /foo|bar/} expect(matcher.match(elements, values_to_match, @filter)).to eq elements[1] end it "returns nil if found element doesn't match the selector tag_name" do elements = [wd_element(tag_name: 'div')] @values_to_match = {tag_name: 'span'} expect(matcher.match(elements, values_to_match, @filter)).to eq nil end end context 'when locating collection' do before { @filter = :all } it 'by tag name' do elements = [wd_element(tag_name: 'div'), wd_element(tag_name: 'span'), wd_element(tag_name: 'div')] @values_to_match = {tag_name: 'div'} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]] end it 'by attribute' do elements = [wd_element(attributes: {foo: 'foo'}), wd_element(attributes: {foo: 'bar'}), wd_element(attributes: {foo: 'foo'})] @values_to_match = {foo: 'foo'} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]] end it 'by single class' do elements = [wd_element(attributes: {class: 'foo bar cool'}), wd_element(attributes: {class: 'foob bar'}), wd_element(attributes: {class: 'bar foo foobar'})] @values_to_match = {class: 'foo'} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]] end it 'by class array' do elements = [wd_element(attributes: {class: 'foo bar cool'}), wd_element(attributes: {class: 'foob bar'}), wd_element(attributes: {class: 'bar foo foobar'})] @values_to_match = {class: %w[foo bar]} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]] end it 'by visibility true' do elements = [wd_element(tag_name: 'div'), wd_element(tag_name: 'span'), wd_element(tag_name: 'div')] @values_to_match = {visible: true} allow(elements[0]).to receive(:displayed?).and_return false allow(elements[1]).to receive(:displayed?).and_return true allow(elements[2]).to receive(:displayed?).and_return true expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[1], elements[2]] end it 'by visibility false' do elements = [wd_element("displayed?": false), wd_element("displayed?": true), wd_element("displayed?": false)] @values_to_match = {visible: false} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]] end it 'by text' do elements = [wd_element, wd_element, wd_element] @values_to_match = {text: /Foo|Bar/} allow(browser).to receive(:execute_script).and_return('foo', 'Foob', 'bBarb') expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[1], elements[2]] end it 'by visible text' do elements = [wd_element(text: 'foo'), wd_element(text: 'Foob'), wd_element(text: 'bBarb')] @values_to_match = {visible_text: /Foo|Bar/} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[1], elements[2]] end it 'by href' do elements = [wd_element(attributes: {href: 'froo.com'}), wd_element(attributes: {href: 'bar.com'}), wd_element(attributes: {href: 'foobar.com'})] @values_to_match = {href: /foo|bar/} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[1], elements[2]] end it 'returns empty Array if found no element matches the selector tag_name' do elements = [wd_element(tag_name: 'div')] @values_to_match = {tag_name: 'span'} expect(matcher.match(elements, values_to_match, @filter)).to eq [] end end context 'when matching Regular Expressions' do it 'with white space' do allow(browser).to receive(:execute_script).and_return("\n match this \n", 'no', 'match this') elements = [wd_element, wd_element, wd_element] @values_to_match = {text: /^match this$/} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]] end it 'with tag_name' do elements = [wd_element(tag_name: 'div'), wd_element(tag_name: 'span'), wd_element(tag_name: 'div')] @values_to_match = {tag_name: /d|q/} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]] end it 'with single class' do elements = [wd_element(attributes: {class: 'foo bar cool'}), wd_element(attributes: {class: 'foob bar'}), wd_element(attributes: {class: 'bar foo foobar'})] @values_to_match = {class: /foob|q/} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[1], elements[2]] end it 'with multiple classes' do elements = [wd_element(attributes: {class: 'foo bar cool'}), wd_element(attributes: {class: 'foob bar'}), wd_element(attributes: {class: 'bar foo foobar'})] @values_to_match = {class: [/foob/, /bar/]} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[1], elements[2]] end it 'with attributes' do elements = [wd_element(attributes: {foo: 'foo'}), wd_element(attributes: {foo: 'bar'}), wd_element(attributes: {foo: 'foo'})] @values_to_match = {foo: /fo/} expect(matcher.match(elements, values_to_match, @filter)).to eq [elements[0], elements[2]] end end # TODO: These should have the same tests for label locators, but the mocking is more complex # See equivalent tests in checkbox_spec context 'text_regexp deprecations' do let(:filter) { :first } let(:elements) { [wd_element, wd_element] } it 'not thrown when still matched by text content' do @values_to_match = {text: /some visible/} allow(browser).to receive(:execute_script).and_return('all visible', 'some visible') expect(matcher.match(elements, values_to_match, filter)).to eq elements[1] end it 'not thrown with complex regexp matched by text content' do @values_to_match = {text: /some (in|)visible/} allow(browser).to receive(:execute_script).and_return('all visible', 'some visible') expect(matcher.match(elements, values_to_match, filter)).to eq elements[1] end it 'thrown when no longer matched by text content' do @values_to_match = {text: /some visible$/} allow(browser).to receive(:execute_script).and_return('all visible', 'some visible') allow(browser).to receive(:timer).and_return(Watir::Wait::Timer.new) allow(browser).to receive(:timer=).and_return(Watir::Wait::Timer.new) expect(matcher.match(elements, values_to_match, filter)).to eq elements[1] end it 'not thrown when element does not exist' do @values_to_match = {text: /definitely not there/} allow(browser).to receive(:execute_script).and_return('all visible', 'some visible') expect(matcher.match(elements, values_to_match, filter)).to eq nil end # Note: This will work after:text_regexp deprecation removed it 'keeps element from being located' do @values_to_match = {text: /some visible some hidden/} allow(browser).to receive(:execute_script).and_return('all visible', 'some visible') expect(matcher.match(elements, values_to_match, filter)).to be_nil end end end end