Watir-Webdriver Wait for Download to Complete

2020-07-05 06:13发布

问题:

I am using Watir-Webdriver with Firefox and the method recommended on the watirwebdriver.com site to automate file downloads. This involves setting FireFox about:config parameters to disable the download dialog in FireFox for specific file types. This works well but now I am trying to figure out how best to determine when the file download has completed (some take a few seconds, some take minutes) so I can logout of the site and move on to the next test. It seems since there are no visual clues left in the browser I may have to monitor the file in the download directory. Any options would be appreciated.

回答1:

Chrome stores uncompleted downloads with an added .crdownload file extension. Check to see if the download directory has a file ending with .crdownload and that should tell you if a download is still ongoing



回答2:

Maybe you can track the file size to see when it stops changing for a few seconds.



回答3:

I didn't like just looking at filesize, it felt fragile, so I ended up using the lsof command to detect when there are no processes holding the file open, and then reading the file. It's better in that a pause in the download due to network hiccups won't cause intermittent errors, but worse(?) in that it is not portable and shells out to the lsof command.

The coded looked something like this:

# Watch the download dir for new files, and read the first new file that
# appears.
def read_newest_download
  existing_files = list_files_in_download_dir
  new_files = []

  Timeout::timeout(DOWNLOAD_TIMEOUT) do
    while (new_files = list_files_in_download_dir - existing_files).empty?
      sleep 0.25
    end
  end

  if 1 == new_files.size
    wait_for_file_to_be_closed(new_files.first)
    File.read(new_files.first)
  else
    fail "Found #{new_files.size} new files."
  end
end

# Ignore files ending in .part, which is common for temp files in Firefox.
def list_files_in_download_dir
  raise ArgumentError, "No download dir specified" unless @opts[:download_dir]
  @_download_glob ||= File.join(@opts[:download_dir], "*")

  # Ignore files ending in .part as they're temporary files from Firefox.
  Dir[@_download_glob].entries.reject {|e| /\.part$/ === e}
end

def wait_for_file_to_be_closed(filename)
  begin
    sleep 0.25
  end until `lsof #{filename}`.blank?
end


回答4:

I had a similar task where I wanted to extract the contents of a downloaded PDF file. I used to following solution:

t = ''
(0..19).each do
  sleep 5
  t = `pdftotext -raw some_directory/*.pdf -`
  break if $?.success?
end

It does 20 attempts at extracting the text using the shell command pdftotext and will break out of the block if the shell command was successful. The advantage of doing it this way is that if the file doesn't exist or if the file is only partially downloaded it will produce an error and then try again. If your file is not a PDF or you don't care about the contents then you use another shell command instead of pdftotext, so long as it produces an error if the file is incomplete.



回答5:

I have a bit different approach for file-downloads automation. I do it like this:

The requiures:

require 'rubygems'
require 'watir-webdriver'
require 'win32ole'

First create a method for the Filesize handling:

def fileinfo(name)
        if File.exists?(name)
                print "#{name} exists "
                bytes = File.size(name)
                print "and is #{bytes} in size;"
                whenm = File.mtime(name)
                print whenm,";"
                print whenm.to_i,";"
        else
                print "#{name} does NOT exist;"
        end
end

Secondly drive chrome with pre set-up download dir:

download_directory = "#{Dir.pwd}/downloads"
download_directory.gsub!("/", "\\") if  Selenium::WebDriver::Platform.windows?
profile = Selenium::WebDriver::Chrome::Profile.new
profile['download.prompt_for_download'] = false
profile['download.default_directory'] = download_directory

Next delete the file (from previous runs) for test case re-usability and validness (one of 3):

%x(DEL /Q C:\\automation\\qa\\downloads\\*.exe)
%x(DEL /Q downloads\\*.exe)
%x(DEL /Q downloads\\*.*)

Define the size variable of the downloaded component:

contains = Dir.new(download_directory).entries
dlc = contains[2]
dcinfo = fileinfo("downloads/"+dlc)
dlcsize = File.size("downloads/"+dlc)

And lastly you can include the validation point:

if dlcsize > 0
        puts "File found and is #{dlcsize} bytes."
                logfile = open("test_results.csv", "a")
                 begin
                    logRow = "#{__FILE__}"
                    logfile.puts logRow + "," + "Passed".to_s
                 end
    else
        puts "Test Failed! File not found either is zero."
            logfile = open("test_results.csv", "a")
                 begin
                    logRow = "#{__FILE__}"
                    logfile.puts logRow + "," + "Passed".to_s
                 end
end


回答6:

The way i handle this, is the following

Firefox download files end with .part

have all files' names being downloaded in a list after appending ".part" to them keep on looping on that list and check if any of those files still exist in the lsdir of the download folder

Python code:

import os
import time

def wait_till_download():
  if len(new_part_files) > 0:
      time.sleep(1)
      for part_file in new_part_files:
          if part_file in os.listdir("."):
              print "Downloading..."
              wait_till_download()

note: when there are no part files in os.listdir('.'), that means downloading is finished