<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fahri Nurul Hidayat</title>
    <description>The latest articles on DEV Community by Fahri Nurul Hidayat (@fahrinh).</description>
    <link>https://dev.to/fahrinh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F196218%2F548537b7-095f-44c1-9e66-f095e386994d.png</url>
      <title>DEV Community: Fahri Nurul Hidayat</title>
      <link>https://dev.to/fahrinh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fahrinh"/>
    <language>en</language>
    <item>
      <title>File Downloading in Headless Chrome Using ChromeDriver and Hound</title>
      <dc:creator>Fahri Nurul Hidayat</dc:creator>
      <pubDate>Tue, 18 Feb 2020 13:39:03 +0000</pubDate>
      <link>https://dev.to/fahrinh/file-downloading-in-headless-chrome-using-chromedriver-and-hound-2goa</link>
      <guid>https://dev.to/fahrinh/file-downloading-in-headless-chrome-using-chromedriver-and-hound-2goa</guid>
      <description>&lt;p&gt;Recently, I am making a simple Elixir application performing some actions to a website in an automated way.&lt;/p&gt;

&lt;p&gt;The automated testing tool is a perfect candidate to be used to help to build application like that. I use &lt;a href="https://github.com/HashNuke/hound"&gt;Hound&lt;/a&gt; as browser automation library and Chrome as a controlled browser. For the browser driver, I use &lt;a href="https://chromedriver.chromium.org/"&gt;ChromeDriver&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Problem
&lt;/h1&gt;

&lt;p&gt;Back to building my application.&lt;/p&gt;

&lt;p&gt;One of the tasks my application doing is downloading a file on the website.&lt;br&gt;
That is not a problem in a normal setup. However, the file is not downloaded when the headless mode is enabled.&lt;/p&gt;



&lt;p&gt;After Google has enlightened me, in security perspective, that behaviour is needed to prevent malicious website quietly download unwanted files through the browser in headless mode.&lt;/p&gt;
&lt;h1&gt;
  
  
  Solution
&lt;/h1&gt;

&lt;p&gt;For the solution, we have to instruct ChromeDriver via REST API to allow file downloading&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST http://localhost:9515/session/&amp;lt;session_id&amp;gt;/chromium/send_command
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cmd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Page.setDownloadBehavior"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"behavior"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"downloadPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/path/download"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt;  &lt;code&gt;9515&lt;/code&gt; is ChromeDriver default port&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Hound
&lt;/h2&gt;

&lt;p&gt;Unfortunately, Hound does not provide &lt;code&gt;send_command&lt;/code&gt; as its API method. But, we can use &lt;code&gt;Hound.RequestUtils.make_req&lt;/code&gt; to send API request to ChromeDriver.&lt;/p&gt;

&lt;p&gt;For the complete demonstration, these are steps to build a sample application that download file (&lt;code&gt;Docs.zip&lt;/code&gt;) in &lt;a href="https://elixir-lang.org/docs.html"&gt;https://elixir-lang.org/docs.html&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Chrome &amp;amp; ChromeDriver Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Download and install Chrome&lt;/li&gt;
&lt;li&gt;Download and install
&lt;a href="https://chromedriver.chromium.org/downloads"&gt;ChromeDriver&lt;/a&gt;. Make sure Chrome and ChromeDriver have same major version.&lt;/li&gt;
&lt;li&gt;Start ChromeDriver and leave it running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nv"&gt;$ &lt;/span&gt;chromedriver &lt;span class="nt"&gt;--verbose&lt;/span&gt;
    Starting ChromeDriver 77.0.3865.40 &lt;span class="o"&gt;(&lt;/span&gt;f484704e052e0b556f8030b65b953dce96503217-refs/branch-heads/3865@&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="c"&gt;#442}) on port 9515&lt;/span&gt;
    Only &lt;span class="nb"&gt;local &lt;/span&gt;connections are allowed.
    Please protect ports used by ChromeDriver and related &lt;span class="nb"&gt;test &lt;/span&gt;frameworks to prevent access by malicious code.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Building Application
&lt;/h3&gt;

&lt;p&gt;Generate a new application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mix new file_downloader
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;file_downloader
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;hound&lt;/code&gt; as a dependency library&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file_downloader/mix.exs&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;FileDownloader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;MixProject&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Project&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="ss"&gt;app:&lt;/span&gt; &lt;span class="ss"&gt;:file_downloader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;version:&lt;/span&gt; &lt;span class="s2"&gt;"0.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;elixir:&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;start_permanent:&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:prod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;deps:&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="ss"&gt;extra_applications:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:logger&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:hound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.1.0"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;- add hound library&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Download the dependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mix deps.gets
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Config &lt;code&gt;hound&lt;/code&gt; to use ChromeDriver and Chrome in headless mode&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file_downloader/config/config.exs&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Mix&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Config&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:hound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;driver:&lt;/span&gt; &lt;span class="s2"&gt;"chrome_driver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;browser:&lt;/span&gt; &lt;span class="s2"&gt;"chrome_headless"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Code the logic of our application. Step 3 describes how to enable file downloading in headless mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# file_downloader/lib/file_downloader.ex&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;FileDownloader&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Hound&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Hound&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RequestUtils&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;download_elixir_docs&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# 1) Start hound session&lt;/span&gt;
    &lt;span class="no"&gt;Hound&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# 2) Visit the website&lt;/span&gt;
    &lt;span class="n"&gt;navigate_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://elixir-lang.org/docs.html"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3) By using 'Hound.RequestUtils.make_req', enable file downloading&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;download_path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hound&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_session_id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;make_req&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"session/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/chromium/send_command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;%{&lt;/span&gt;
        &lt;span class="ss"&gt;cmd:&lt;/span&gt; &lt;span class="s2"&gt;"Page.setDownloadBehavior"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;params:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;behavior:&lt;/span&gt; &lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;downloadPath:&lt;/span&gt; &lt;span class="n"&gt;download_path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 4) Find download link and click it to download file&lt;/span&gt;
    &lt;span class="n"&gt;download_link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:xpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"//*[@id='stable']/small/a"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;download_link&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# 5) Wait until download process is completed&lt;/span&gt;
    &lt;span class="n"&gt;wait_download_started&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;wait_download_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 6) Stop hound session&lt;/span&gt;
    &lt;span class="no"&gt;Hound&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;wait_download_started&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;wait_crdownload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;wait_download_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;wait_crdownload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;download_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;wait_crdownload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait_time&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;count_crdownload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;dir&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"*.crdownload"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wildcard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;unless&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;count_crdownload&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count_crdownload&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;!exist?&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wait_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;wait_crdownload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;Full source code:&lt;br&gt;
&lt;a href="https://github.com/fahrinh/blog-labs/tree/master/2019-09-16/file_downloader"&gt;https://github.com/fahrinh/blog-labs/tree/master/2019-09-16/file_downloader&lt;/a&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Running the Application
&lt;/h3&gt;

&lt;p&gt;Run the application and wait until it is finished.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mix run &lt;span class="nt"&gt;-e&lt;/span&gt; FileDownloader.download_elixir_docs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The downloaded file (&lt;code&gt;Docs.zip&lt;/code&gt;) will be available in the current directory (&lt;code&gt;file_downloader&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting
&lt;/h3&gt;

&lt;p&gt;If you got a runtime error (invalid session id) like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mix run &lt;span class="nt"&gt;-e&lt;/span&gt; FileDownloader.download_elixir_docs
Compiling 1 file &lt;span class="o"&gt;(&lt;/span&gt;.ex&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;RuntimeError&lt;span class="o"&gt;)&lt;/span&gt; invalid session &lt;span class="nb"&gt;id&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;hound&lt;span class="o"&gt;)&lt;/span&gt; lib/hound/request_utils.ex:52: Hound.RequestUtils.handle_response/3
    &lt;span class="o"&gt;(&lt;/span&gt;file_downloader&lt;span class="o"&gt;)&lt;/span&gt; lib/file_downloader.ex:13: FileDownloader.download_elixir_docs/0
    &lt;span class="o"&gt;(&lt;/span&gt;stdlib&lt;span class="o"&gt;)&lt;/span&gt; erl_eval.erl:680: :erl_eval.do_apply/6
    &lt;span class="o"&gt;(&lt;/span&gt;elixir&lt;span class="o"&gt;)&lt;/span&gt; lib/code.ex:240: Code.eval_string/3
    &lt;span class="o"&gt;(&lt;/span&gt;elixir&lt;span class="o"&gt;)&lt;/span&gt; lib/enum.ex:783: Enum.&lt;span class="s2"&gt;"-each/2-lists^foreach/1-0-"&lt;/span&gt;/2
    &lt;span class="o"&gt;(&lt;/span&gt;elixir&lt;span class="o"&gt;)&lt;/span&gt; lib/enum.ex:783: Enum.each/2
    &lt;span class="o"&gt;(&lt;/span&gt;mix&lt;span class="o"&gt;)&lt;/span&gt; lib/mix/tasks/run.ex:141: Mix.Tasks.Run.run/5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;it might be caused by different versions of Chrome and ChromeDriver.&lt;br&gt;
You can check the log of running ChromeDriver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;chromedriver &lt;span class="nt"&gt;--verbose&lt;/span&gt;
...

&lt;span class="o"&gt;[&lt;/span&gt;1578222297.224][INFO]: Failed to connect to Chrome. Attempting to &lt;span class="nb"&gt;kill &lt;/span&gt;it.
&lt;span class="o"&gt;[&lt;/span&gt;1578222297.244][INFO]: &lt;span class="o"&gt;[&lt;/span&gt;e5d2ce77a1b56643db3d11b6fad7d946] RESPONSE InitSession ERROR session not created: This version of ChromeDriver only supports Chrome version 77

...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>elixir</category>
      <category>headlesschrome</category>
      <category>chromedriver</category>
      <category>hound</category>
    </item>
    <item>
      <title>How to Update Pomera DM100 Firmware</title>
      <dc:creator>Fahri Nurul Hidayat</dc:creator>
      <pubDate>Tue, 21 Nov 2017 23:13:35 +0000</pubDate>
      <link>https://dev.to/fahrinh/how-to-update-pomera-dm100-firmware-72d</link>
      <guid>https://dev.to/fahrinh/how-to-update-pomera-dm100-firmware-72d</guid>
      <description>&lt;p&gt;Pomera DM100 is a distraction-free digital writing tool. Think mini laptop with only text editor application loaded.&lt;/p&gt;

&lt;p&gt;It comes from Japan. It means its manual is in all Japanese. It becomes hard when you want to do the crucial procedure (like &lt;strong&gt;upgrading firmware&lt;/strong&gt; ) &lt;strong&gt;AND&lt;/strong&gt; Google Translate doesn’t help you much.&lt;/p&gt;

&lt;p&gt;I can only follow some steps in &lt;a href="https://translate.google.co.id/translate?hl=en&amp;amp;sl=ja&amp;amp;u=http://www.kingjim.co.jp/support/pomera/software/dm100&amp;amp;prev=search"&gt;its updating guide web page&lt;/a&gt; and the rest of important steps in &lt;a href="http://www.kingjim.co.jp/resource/media/support/pomera/update_dm100.pdf"&gt;PDF&lt;/a&gt; are not translatable by Google Translate.&lt;/p&gt;

&lt;p&gt;In the end, I have successfully updated my Pomera DM100 to the latest firmware.&lt;/p&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;p&gt;Read more: &lt;a href="https://fahrinh.github.io/post/2017-11-22-how-to-update-pomera-dm100-firmware/"&gt;https://fahrinh.github.io/post/2017-11-22-how-to-update-pomera-dm100-firmware/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
