mclosson

Remote pairing and browser sharing with tmux

image

Lately I’ve been doing a lot of remote pairing with Jacob Swanner (@jswanner), another Envy Labs developer. The setup we’ve been using is typical for many Rails developers. We both SSH into a shared tmux session and use Vim to write code, then we use some shell and tmux aliases to execute the tests.

The great thing about this setup is that SSH, tmux and Vim are all low bandwidth applications. This makes it possible to use this pairing setup no matter the what the quality is of your internet connection at the time. Eventually, you will want to validate the results in a web browser, even if you’re writing your test code first. Now you both have to run a browser locally or use a higher bandwidth graphical tool to share their entire desktop, just so you can both simultaneously view the application you’re developing. Instead, we decided to try out another alternative.

When I was first starting out, I didn’t have the resources necessary to acquire a computer with a full graphical environment, so I chose to run FreeBSD with a text console only. This was a great opportunity to learn many of the awesome text-based tools that we now take for granted. One of these tools was Lynx, which you could use as a text console based web browser. We can now make use of that same family of tools by sharing a browser over SSH and tmux when pair programming remotely.

Lynx is a fairly basic browser that will render markup while completely ignoring CSS and JavaScript. Given that most modern applications will make use of the latter mentioned technologies, we decided to try a few alternatives to see which was best. We found that ELinks offered the most advanced text-based browser that featured multiple tabs, Lua scripting, and some rendering of CSS (it even integrates with the Mozilla SpiderMonkey JavaScript engine, provided you install the proper bindings for your operating system).

Installing elinks

To get started, you can easily install it using most package managers on OS X and Linux.

# OSX
$ brew install elinks
# (Debian based) Linux distributions
$ sudo apt-get install elinks
$ sudo apt-get install libmozjs-dev    

Navigation

To run ELinks, just issue the ELinks command with a URL or path to a file on your local filesystem.

$ elinks http://localhost:3000

image

Once your application is loaded up in ELinks, there are a few useful navigational commands that are of immediate use. Use the up and down arrow keys to move through the elements on a page. Use the right and left arrow keys like a browser’s back and forward buttons.

To search use the slash key, followed by the ‘n’ key to cycle through the results just like in Vim.

When an input element is highlighted, you can press enter to activate or deactivate typing in it.

Press the backslash key at any time to display the source of the page you’re currently viewing.

Press the pipe | key to view the current HTTP Header info.

image

To go directly to a new URL without exiting the browser, press the ‘gkey. This will bring up a dialog where you can enter and view the new URL or file path. You may wish to instead use the ‘G' key, which will bring up the same dialog but with the current URL populated already. You can also use up and down arrows in this dialog to navigate between recent URLs. To go to a visited page in your history, bring up the history list with ‘h’. You will also make great use of Ctrl+R to reload the current page or resubmit a form.

image

The escape button will bring up a top menu bar, which you can subsequently navigate through with your arrow and enter keys.

image

In the bottom right-hand corner is a small status bar that gives you information, which includes notice of any JavaScript errors. If you have an error in your site’s JavaScript then you will see an ‘E’ indicator like this: [—E—-]

Testing

Running ELinks against your live application is helpful, but you can also do a postmortem analysis on any failing tests in your test suite. We like to use Capybara request specs to test some high level scenarios in our Rails applications. Say you have the following scenario below, where your test is yielding unexpected results. You decide to insert a call to Capybara’s save_and_open_page method to get a view of exactly which page is being rendered:

scenario "An administrator can update a user's info" do
  user = build :user
  visit edit_user_path(user)
  fill_in 'user[phone]', with: Faker::PhoneNumber.cell_phone
  click_button 'Save User'

  save_and_open_page

  expect(page).to have_content('User Updated')
end

Underneath the hood, Capybara is saving a copy of the page markup, which is then written out to a timestamped file in the relative tmp/capybara directory. It then uses the launchy gem to try to identify a suitable launching program and browser for viewing the file.

lib/capybara/session.rb

##
#
# Save a snapshot of the page.
#
# @param  [String] path     The path to where it should be saved [optional]
#
def save_page(path=nil)
  path ||= "capybara-#{Time.new.strftime("%Y%m%d%H%M%S")}#{rand(10**10)}.html"
  path = File.expand_path(path, Capybara.save_and_open_page_path) if Capybara.save_and_open_page_path

  FileUtils.mkdir_p(File.dirname(path))

  File.open(path,'w') { |f| f.write(Capybara::Helpers.inject_asset_host(body)) }
  path
end

##
#
# Save a snapshot of the page and open it in a browser for inspection
#
# @param  [String] file_name  The path to where it should be saved [optional]
#
def save_and_open_page(file_name=nil)
  require "launchy"
  Launchy.open(save_page(file_name))
rescue LoadError
  warn "Please install the launchy gem to open page with save_and_open_page"
end

By default (unless you hack or fork launchy) it does not come out of the box preconfigured to launch command line browsers, so the result is that Capybara will save the temp HTML file but it won’t be opened in the tmux session. If you want to alter the launchers used then you could do that in the following file:

lib/launchy/application/browser.rb

def windows_app_list
  [ 'start /b' ]
end

def cygwin_app_list
  [ 'cmd /C start /b' ]
end

def darwin_app_list
  [ find_executable( "open" ) ]
end

def nix_app_list
  app_list = %w[ xdg-open ]
  if nix_de = Launchy::Detect::NixDesktopEnvironment.detect then
    app_list << nix_de.browser
    app_list << nix_de.fallback_browsers
  end
  app_list.flatten!
  app_list.delete_if { |b| b.nil? || (b.strip.size == 0) }
  app_list.collect { |bin| find_executable( bin ) }.find_all { |x| not x.nil? }
end

If you don’t want to modify and maintain a launchy fork, then the
easiest thing to do is to create a simple shell alias (which opens the latest file in the relative tmp/capybara directory in tmux).

~/.bash_profile

alias=elc="elinks \"tmp/capybara`ls -Ut tmp/capybara | head -n1`\""

This will open an ELinks browser with the saved snapshot of the page, and will allow us to collaboratively review the results.

There is a lot more that can be done, including adding support for alternative JavaScript engines to ELinks and additional launcher support to launchy on the Linux platform. However, this should get you started and help conserve your bandwidth when  remote pairing. Another benefit is that when you avoid switching contexts to a graphical browser while using testing to write an application, then it’s easier to keep focused in your code writing and testing flow.

For more information on ELinks, check out its manual page, here: elinks.or.cz/documentation/manual.html

If you have a Code School account and would like more information on remote pairing with tmux, check out this Code TV screencast: www.codeschool.com/code_tv/remote-pairing-with-tmux

Although modern browsers are amazing and required at times, I hope this gives you another tool to stay optimized in your work flow when remote pair programming with tmux.

Have you used ELinks or another text based browser in your remote pairing or testing flow? Leave a comment and let us know about your experiences.

-Matthew (@MatthewClosson)

09.30.13 ← See All Posts
blog comments powered by Disqus