Skip to content

Instantly share code, notes, and snippets.

@mattheworiordan
Created September 7, 2011 12:06
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save mattheworiordan/1200401 to your computer and use it in GitHub Desktop.
Save mattheworiordan/1200401 to your computer and use it in GitHub Desktop.
Test PDF within Cucumber and Capybara
# normal Gem dependancy declarations
# ...
group :test, :cucumber do
gem 'pdf-reader'
end
When /^I follow the PDF link "([^"]+)"$/ do |label|
click_link(label)
temp_pdf = Tempfile.new('pdf')
temp_pdf << page.source.force_encoding('UTF-8')
temp_pdf.close
pdf_text = PDF::PdfToText.new(temp_pdf.path)
page.driver.response.instance_variable_set('@body', pdf_text.get_text)
end
Scenario: Check printable PDF
When I follow the PDF link "Print"
Then I should see "My name"
And I should see "Something else"
module PDF
class PdfToText
def initialize(pdf_file)
@receiver = PDF::SimplePageTextReceiver.new
pdf = PDF::Reader.file(pdf_file, @receiver)
end
def get_text
@receiver.content.inspect
end
end
end
module PDF
class SimplePageTextReceiver
attr_accessor :content
def initialize
@content = []
end
# Called when page parsing starts
def begin_page(arg = nil)
@content << ""
end
# record text that is drawn on the page
def show_text(string, *params)
@content.last << string.strip
end
# there's a few text callbacks, so make sure we process them all
alias :super_show_text :show_text
alias :move_to_next_line_and_show_text :show_text
alias :set_spacing_next_line_show_text :show_text
# this final text callback takes slightly different arguments
def show_text_with_positioning(*params)
params = params.first
params.each { |str| show_text(str) if str.kind_of?(String)}
end
end
end
@chrisbloom7
Copy link

Thank you for the page.driver.response.instance_variable_set bit, I was trying to figure that out for the last 2 hours. I was able to avoid using any temp files or extra modules by using pdf-inspector instead of pdf-reader:

require "pdf/inspector"

Then /^I should be served the referral document as a PDF$/ do
  page.response_headers['Content-Type'].should == "application/pdf"
  pdf = PDF::Inspector::Text.analyze(page.source).strings.join(" ")
  page.driver.response.instance_variable_set('@body', pdf)
end

Then /^I should see the referral document details$/ do
  page.should have_content("#{@document.customer.name}")
  page.should have_content("#{@document.resources.first.resource.name}")
  page.should have_content("Document opened at #{@document.created_at.strftime("%e-%b-%4Y %r")}")
end

@chalmagean
Copy link

If you happen to get this error:

undefined method `join' for #<String:...> (NoMethodError)

you can fix it by converting the text to an array like this:

page.driver.response.instance_variable_set('@body', [pdf_text.get_text])

@theotherzach
Copy link

Thanks for the inspiration. I used StringIO to grab the contents of my pdf response and turn it into an io stream for PDF::Reader

 And the pdf file should contain:
          | Subtotal      | $10.00     |
          | Shipping      | $20.00     |
          | IN Sales Tax  | $2.00      |
          | Total         | $32.00     |
Then(/^the pdf file should contain:$/) do |table|
  pdf_io = StringIO.new(page.source.force_encoding('UTF-8'))
  reader = PDF::Reader.new(pdf_io)
  contents = reader.pages.map(&:to_s).join("\n")

  table.rows.each do |cells|
    cells.each do |cell|
      expect(contents).to include cell
    end
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment