如何编写一个 RSpec 匹配器来尊重块内的水豚?

How to write an RSpec matcher that respect's Capybara's within block?

我正在尝试编写自定义 RSpec 匹配器以用于 Rails 系统测试,运行 在 Capybara 下 — 想法是在忽略某些 <span> 用它标记。

这是匹配器:

RSpec::Matchers.define :have_cleaned_text do |text|
  match do |content|
    content.body.include?(text) || content.body.gsub(%r{<span class=["']separator["'].*?>.*?</span>}, ' ').include?(text)
  end
end

和被测试页面的 HTML 正文:

<h1>Test Page</h1>
<div id='constraint'>
  <p>Clean text</p>
  <p>Broken<span class='separator'>|<span>text</p>
</div>

前两个测试通过:

within('#constraint') do
  expect(page).to have_cleaned_text('Clean text')
  expect(page).to have_cleaned_text('Broken text')
  expect(page).not_to have_cleaned_text('Test Page') # fails!
end

…但是第三个失败了,因为 have_cleaned_text 忽略了 within 块并针对整个页面进行了测试。

如何让我的匹配器遵守 within 块?我本以为它会被传递为 content,而不是整个页面……

在您的示例中,page 是一个 Capybara 会话(包含其当前范围)。当您在会话中调用 bodysourcehtml 是别名)时,它 returns 文档的 HTML 来源。由于您正在寻找元素的 HTML 来源,因此您需要

RSpec::Matchers.define :have_cleaned_text do |text|
  match do |session|
    session.current_scope[:innerHTML].include?(text) || session.current_scope[:innerHTML].gsub(%r{<span class=["']separator["'].*?>.*?</span>}, ' ').include?(text)
  end
end

请注意,这样编写的匹配器不会有任何 waiting/retrying 行为,就像 Capybara 提供的匹配器一样,因此您需要确保您的页面在使用前 loaded/stable。