Como obter o nó pai em Capivara?

86

Estou trabalhando com muitos plug-ins jQuery, que geralmente criam elementos DOM sem id ou outras propriedades de identificação, e a única maneira de obtê-los na Capivara (por exemplo, clicando) - é obter seu vizinho (outro filho de seu ancestral) primeiro . Mas eu não encontrei em nenhum lugar, Capivara suporta tais coisas, por exemplo:

find('#some_button').parent.fill_in "Name:", :with => name

?

Sandrew
fonte
Também será muito útil para mim, se você disser, a Capivara gera cliques em elementos com {display: hidden}, e há uma maneira de encontrar elementos em algum escopo, onde display! = Hidden?
Sandrew
Esta é uma pergunta separada, mas depende do driver que você está usando. webrat encontrará alegremente coisas escondidas, mas selênio não fica tão feliz em clicar em itens que você não pode ver.
jamuraa 01 de

Respostas:

111

Eu realmente achei a resposta de Jamuraa útil, mas ir para o xpath completo me deu um pesadelo de uma string no meu caso, então eu felizmente usei a habilidade de concatenar find's em Capybara, me permitindo misturar seleção de css e xpath. Seu exemplo ficaria assim:

find('#some_button').find(:xpath,".//..").fill_in "Name:", :with => name

Atualização do Capybara 2.0 : find(:xpath,".//..")provavelmente resultará em um Ambiguous matcherro. Nesse caso, use first(:xpath,".//..").

Pascal Lindelauf
fonte
Obrigado por essa ideia - nunca teria pensado nisso! Eu estava fazendo el.parent para um elemento td que encontrei com find (: css), mas por algum motivo que não entendo, el.parent estava retornando # <Capybara :: Document>. el.find (: xpath, ".// ..") por outro lado, retorna # <Capybara :: Element tag = "tr">, que é o que eu precisava.
Tyler Rick
Outra maneira de encontrar recursivamente um certo nó pai é com o seletor ancestor-or-self do xpath. Confira stackoverflow.com/questions/1362466/…
vrish88
25
Você não precisa .//..encontrar o pai - ..basta, e isso também nunca é ambíguo.
Eamon Nerbonne
ty! Eu sei que este comentário está um pouco atrasado para a festa, mas condensado no seguinte depois de ler a edição, e o comentário de Eamon: el.first (: xpath, '..')
aschyiel
39

Não há como fazer isso com capivara e CSS. Eu usei XPath no passado para atingir esse objetivo, que tem uma maneira de obter o elemento pai e é compatível com Capivara:

find(:xpath, '//*[@id="some_button"]/..').fill_in "Name:", :with => name
Jamuraa
fonte
2
Quando faço um xpath find semelhante, recebo um erro de Nokogiri dizendo que a expressão é inválida. Eu adicionei um asterisco na frente do freio quadrado aberto na expressão para corrigir a expressão:find(:xpath, '//*[@id="some_button"]/..')
Andrew Ferk
35

Eu descobri o seguinte que funciona:

find(:xpath, '..')

Capivara foi atualizado para suportar isso.

https://github.com/jnicklas/capybara/pull/505

B Seven
fonte
1
Parece-me que você está respondendo / completando uma resposta anterior aqui ("Este" o que "não estava funcionando"?). Seria melhor se fosse uma resposta completa e independente. Então, eu recomendo que você edite. ;-)
helenov
Pode confirmar. Com o Capybara 2.14.4 mais recente, esta é a maneira correta de obter o nó pai.
fny
10

Se você se deparou com isso tentando descobrir como encontrar qualquer nó pai (como no ancestral ) (como sugerido no comentário de @ vrish88 na resposta de @Pascal Lindelauf):

find('#some_button').find(:xpath, 'ancestor::div[@id="some_div_id"]')
Ben Alavi
fonte
5

Esta resposta refere-se a como manipular um elemento irmão, ao qual acredito que a pergunta original está se referindo

Sua hipótese de pergunta funciona com um pequeno ajuste. Se o campo gerado dinamicamente se parecer com este e não tiver um id:

<div>
  <input></input>
  <button>Test</button>
</div>

Sua consulta seria:

find('button', text: 'Test').find(:xpath, "..").find('input').set('whatever')

Se a entrada gerada dinamicamente vier anexada com um elemento id (tenha cuidado com eles, embora como no angular, eles costumam mudar com base na adição e exclusão de elementos), seria algo assim:

find('button', text: 'Test').find(:xpath, "..").fill_in('#input_1', with: 'whatever')

Espero que ajude.

T. Slater
fonte
4

Estou usando uma abordagem diferente, encontrando primeiro o elemento pai usando o texto dentro deste elemento pai:

find("<parent element>", text: "text within your #some_button").fill_in "Name:", with: name

Talvez isso seja útil em uma situação semelhante.

Harm de Wit
fonte
1
Esta é uma ótima opção. Definir o escopo das ações da interface do usuário para uma parte da página que contém algum texto específico é um caso de uso comum para mim.
clemensp
2

Eu precisava encontrar um ancestral com uma classe css, embora fosse indeterminado se o ancestral de destino tinha uma ou mais classes css presentes, então não vi uma maneira de fazer uma consulta xpath determinística. Em vez disso, trabalhei nisso:

def find_ancestor_with_class(field, cssClass)
  ancestor = field
  loop do
    ancestor = ancestor.find(:xpath, '..')
    break if ancestor.nil?

    break if ancestor.has_css? cssClass
  end

  ancestor
end

Aviso: use com moderação, pois pode custar muito tempo nos testes, então certifique-se de que o ancestral está a apenas alguns saltos de distância.

Kross
fonte
Aceleração: substitua seu segundo intervalo por break if ancestor[:class].split(" ").include?(css_class).
hakunin
@hakunin Estou curioso para saber o quanto de aceleração você está vendo? Significativo?
Kross
Não é possível testar agora, mas has_css pareceu demorar um segundo, enquanto a correspondência com a classe parecia imediata.
hakunin de
Capivara agora tem um ancestor(selector)método integrado. Veja github.com/teamcapybara/capybara/commit/…
Tyler Rick
-5

Aqui está

http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Base:parent

Existe um método pai presente;)

Dmitriy Konovalov
fonte
5
Isso só retorna o documento de nível superior para mim. Não tenho certeza se é um problema de driver ou o quê.
Nerdmaster
Lendo a documentação, parece que isso pode significar o pai do "quadro", em vez do pai do elemento.
Nick,