emacs-director
emacs-director copied to clipboard
Script Emacs sessions for automated screencasts and end-to-end tests
Director: Simulate Emacs user sessions
Director drives an Emacs session from the point of view of the user. It can be used for end-to-end testing, hands-free screencast recording, probably more.
Director is similar in spirit to web tools such as Selenium Webdriver. It is not a general purpose solution for Emacs automation; use Lisp for that.
Table of Contents
- Status
- Quickstart
- Usage
- Recording screencasts
- End-to-end testing
- Running in a controlled environment
- Debugging
- API reference
- Command:
director-resume - Function:
director-run
- Command:
- Limitations and further development
Status
Developer preview, low-level API only. See limitations. Currently used in run-command.
Quickstart
-
Clone this repository.
-
Review examples/demo/demo.el:
(director-bootstrap
:user-dir "/tmp/director-demo"
:packages '()
:load-path '("../.."))
(director-run
:version 1
:before-start (lambda ()
(switch-to-buffer (get-buffer-create "*example*"))
(menu-bar-mode -1)
(setq python-indent-guess-indent-offset nil)
(python-mode))
:steps '((:call run-python)
(:type "def greet():\r")
(:type "print(\"hello, world!\")")
(:type "\M-x")
(:type "python-shell-send-defun")
(:type [return])
(:type "\C-xo")
(:type "greet()\r"))
:typing-style 'human
:delay-between-steps 1
:after-end (lambda () (kill-emacs 0))
:on-error (lambda () (kill-emacs 1)))
- Launch it:
$ cd examples/demo
$ emacs -Q -nw -l ../../util/director-bootstrap.el -l demo.el
- Emacs starts and plays:

Usage
Recording screencasts
- Write a session script (see demo.el in this repository for a minimal example, or run-command's demo.el for a real-life example)
- Install asciicast and asciicast2gif
- Create a session script and save it as e.g.
my-session-script.el - Launch with:
$ asciinema rec demo.cast -c 'emacs -nw -Q -l director-bootstrap.el -l my-session-script.el'
- Review the recording with:
asciinema play demo.cast
- Convert to a gif with:
asciicast2gif demo.cast demo.gif
See below for information about director-bootstrap.el.
End-to-end testing
See run-command's test scenarios for an example.
run.shlaunches a headless (viascreen) Emacs instance for eachscenario-*.elfilescenario-*.elloadssetup.elto perform common setup and load fixturessetup.elusesdirector-bootstrapto create a controlled testing environment in/tmpand install dependencies
scenario-*.elinvokesdirector-runwith instructions to run the scenario
See below for information about director-bootstrap.el.
Running in a controlled environment
You can create and refine simulate sessions in your everyday interactive Emacs, but often you'll want to run them in a minimal, reproducible environment, either for correctness (tests) or clarity (demos).
Preparing such environment may consist of:
- setting a
user-directoryother than your.emacs.d - initializing the package system and downloading dependencies
- adding local paths to the
load-path - loading Director itself
As a convenience, you can copy
util/director-bootstrap.el to your project, and
invoke director-bootstrap before director-run:
(director-bootstrap
:user-dir "/tmp/my-package-test"
:packages '(some-package-we-depend-on some-other-package)
:load-path '("/path/to/director" ;; will move to :packages once director is on MELPA
"/path/to/my/package")))
Load it before everything else with:
$ emacs -Q -nw -l director-bootstrap.el -l my-session-script.el
It would be nice if Director itself were able to provide bootstrapping, but since making Director available is part of the bootstrapping, there's an obvious chicken-and-egg problem.
Debugging
Debugging strategies are rudimentary for now:
- if you're running a headless session under
screen -D -m, run a visible one instead - increase
:delay-between-stepsto see what's going on - set a
:log-targetfile andtail -fit - add
(:log FORM)steps - add a
(:suspend)step, inspect the session interactively, resume withM-x director-resume
API reference
Command: director-resume
Resume from a (:suspend) step.
Function: director-run
Simulate a user session as defined by CONFIG.
CONFIG is a property list containing the following properties and their values:
:version: required number indicating the config format version; must be1:steps: required list of steps (see below for the step format):before-start: optional function to run before the first step:after-endoptional function to run after the last step:after-stepoptional function to run after every step:on-failure: optional function to run when an:assertstep fails:on-error: optional function to run when a step triggers an error:log-target: optional cons cell of the format(file . "filename")specifying a file to save the log to:typing-style: optional symbol changing the way that:typesteps type characters; set tohumanto simulate a human typing:delay-between-steps: optional number specifying how many seconds to wait after a step; defaults to1; set lower for automated tests
A step can be one of:
:type: simulate typing text; can be a string or a vector of key events; if a string, it will be converted to key events usinglistify-key-sequenceand can contain special characters, e.g.(:type "\M-xsetenv\r"):call: shortcut to invoke an interactive command, e.g.(:call setenv):log: Lisp form; it will be evaluated and its result will be written to log; e.g.(:log (buffer-file-name (current-buffer))):wait: number; seconds to wait before next step; overrides config-wide:delay-between-steps:assert: Lisp form; if it evaluates tonil, execution is interrupted and function configured through:on-failureis called:suspend: suspend execution; useful for debugging; resume using thedirector-resumecommand
Limitations and further development
The currently entry point, director-run, is a low-level building block rather
than a proper user interface: it requires you to specify everything, every time,
and doesn't provide higher-level functionality such as interactive debugging,
parallel runs, and test resporting. The goal is to eventually have that, though
driven by real use cases rather than upfront design.