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
Developer preview, low-level API only. See limitations. Currently used in run-command.
-
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:
- 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
.
See run-command's test scenarios for an example.
run.sh
launches a headless (viascreen
) Emacs instance for eachscenario-*.el
filescenario-*.el
loadssetup.el
to perform common setup and load fixturessetup.el
usesdirector-bootstrap
to create a controlled testing environment in/tmp
and install dependencies
scenario-*.el
invokesdirector-run
with instructions to run the scenario
See below for information about director-bootstrap.el
.
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-directory
other 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 strategies are rudimentary for now:
- if you're running a headless session under
screen -D -m
, run a visible one instead - increase
:delay-between-steps
to see what's going on - set a
:log-target
file andtail -f
it - add
(:log FORM)
steps - add a
(:suspend)
step, inspect the session interactively, resume withM-x director-resume
Resume from a (:suspend)
step.
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-end
optional function to run after the last step:after-step
optional function to run after every step:on-failure
: optional function to run when an:assert
step 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:type
steps type characters; set tohuman
to 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-sequence
and can contain special characters, e.g.(:type "\M-xsetenv\r")
:call
: shortcut to invoke an interactive command, e.g.(:call setenv)
:eval
: Lisp form; it will be evaluated: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-failure
is called:suspend
: suspend execution; useful for debugging; resume using thedirector-resume
command
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.