integration bot from the future
.spec files and The Rule of 3
You tell Specford what to do via
.spec files. Those files are syntactically minimalistic, made up of only 2 things: Commands and Observations. Each adhering, in some way, to the rule of three. Before going deeper into the
.spec language, let's look at a small example:
visit http://google.com: query body: text 'About' exists text 'Foo bar baz' doesNotExist
Specford reads this as:
- Go to google.com
- Query the document for 'body', set that as the context
- Check if the text 'About' exists (in the current context).
- Check if the text 'Foo bar baz' does not exist (also in the context).
Statements in which Specford does stuff that has side effects.
Before Specford can operate on a page, you need to send him to one.
The syntax breakdown reveals 3 things:
<visit keyword> <some/url> <colon>
Notice the colon at the end.
visit, and the yet-to-be-mentioned
query, both terminate with a colon. What's
query is used to set the context for a series of Commands and Observations.
That syntax breakdown; still 3 parts:
<query keyword> <css-selector> <colon>
The css-selector portion of the command is any valid CSS. If it would work with
querySelector it will work here.
Indenting, Stacking, Scoping
Unique to the
query Command is the fact that its indent level is tracked. This allows queries to be stacked (and unstacked), combining to form the current context, via whitespace.
query .main-content: query h1:
This forms the context
main-content h1, scoping any Commands or Observations to those Elements that this would apply. Larger indents stack, equal levels replace, and smaller unstack:
query .container: query #foo: -- current context is ".container #foo" query span: -- current context is".container #foo span" query #bar: -- current context is ".container #bar" query footer: -- current context is "footer"
Instruct Specford to click something:
click selector 'input.btn-primary'
The three parts here can be defined as:
<click keyword> <selector keyword> <css-selector reference>
Instruct Specford to select an
radio via CSS selector:
select selector 'value[foo="bar"]'
The three parts here can be defined as:
<select keyword> <selector keyword> <css-selector reference>
Notice the CSS selector in the above click command is in single quotes. This is what we call a Reference. A Reference is any series of characters that may, or may not, at some point, contain whitespace.
- Values for the
- Text to be verified via the
- CSS selectors not in a Query Command
A Reference must be wrapped in single quotes, not double. You may use double quotes inside of a Reference however:
click selector 'a[href="foo/bar"]'
Remember that the Visit and Query Commands do not utilize References. They do not require the use of quotation marks
These are syntax components that are not References, but combine with them to make up the Commands and Observations
Insert values into an input field:
fill 'input.foo-bar' 'bazzy baz'
Notice the use of two References here:
<fill keyword> <css-selector reference> <value reference>
The first Reference is the field, the second is the value to be inserted.
capture Command to get a screenshot. It will save in the Specford root directory.
capture jpeg 'screenshot' // <capture keyword> <jpg | jpeg | png> <name reference>
The second piece of the command is obviously an option for image type. You can use
Notice that this part is not a Reference and does not need to be in quotations (as whitespace is never an option here).
Last is a Reference, so do use single quotes there.
Tests that Specford performs, scoped to the current context.
Is a given Reference on (or not on) the page? This "existential" Observation can be made on:
Take the textContent of the current context and see if it contains the stated Reference.
text 'foo bar' exists
Keep in mind that this is just a match operation with the given the paramaters (non-global). How exact the search is will be dependant on how much you reference.
Element via a CSS selector:
Does a querySelector operation on the current context with the given Reference return a truthy result?
selector '.popup-foo' doesNotExist
Counting and exist
Execute a querySelectorAll operation on the current context. Is the resulting NodeList:
// equal to the reference? 3 '.foo' exist // less-than the reference? <4 '.bar' exist // greater-than the ref? >5 '.baz' exist
Notice that the cases with quantifier (gt, lt) use a combined operand+number. Don't separate those
with any space. Rule of three remember? There is also a slight tweak to the language in that
exist is used here as
exists would just be bad grammar. Syntax breakdown:
<number> <reference> <exist keyword>
doesNotExist does not exist
In counting use cases there is no
doesNotExist. You do have the option of either
// explicitly state 0 0 '.foo' exist // or... selector '.foo' doesNotExist
Display vs Visibility
Yes, there is a big-assed difference between
visibility:hidden. Specford, like a user, does not give a damn. It's for this reason that the provided
isNotVisible observations bundle the two.
selector '.foo' isNotVisible
The above observation will be truthy if
.foo is either
visibility:hidden. Why? Because a user wouldn't see it either way.
Does a given Reference on the page have the
This Observation can be made on
selector '[value="foo"]' isSelected' selector '[value="bar"]' isNotSelected
There are two observations specific to the page URL,
// does any part of the current URL "contain" a reference url contains 'foo' // does the entire URL match the Reference exactly url matches 'https://www.my-site.com/foo'
Things on a page don't happen instantly. Sometimes Specford should wait. You can tell Specford to wait until some Observation is true before proceeding. You do this by simply adding the after keyword to any Observation.
after selector '.foo' exists after >2 '.bar' exist after text 'Delete Me!' doesNotExist
Stating the obvious, that syntax is:
<after keyword> <Observation>
That's not 3!
Correct, after is viewed as a modifier, if you will, to an Observation. The Observation still observes the rule, after is just a stipulation.
The mechanics of the process are such that, when encountering the after keyword, Specford knows to keep executing the Observation until either one of two conditions are met:
- the Observation returns a truthy result (pass)
- three seconds pass and the result of the Observation is still falsy (fail)
Notice the three seconds part. Specford executes the observation once every 250ms for those 3s, doing nothing else until, well, after. Remember that regardless of the outcome your .spec will then continue.
5 '.foo' exist click selector '.delete-a-foo' after 4 '.foo' exist fill 'input.finished' 'YAY!'
Use this command to set a variable that the
fill command can use as a "fixture".
require users/bob Bob
This command will look for a 'bob.json' (or 'bob.js') in a
/users directory in
the provided '/fixtures' directory. It will set what that file exports as "Bob".
The synax essentially then is:
<require keyword> <path/to/file/in/fixtures> <name>
name can then be used in conjunction with the
fill 'input.foo' 'Bob.firstName'
Obviously you clone this repo. Obviously you
cd into this repo, then you can
npm install, Next...
Specford's default runner is slimerjs. If you are of the Mac persuasion you can use homebrew
brew install slimerjs
Slimer, depending on release, will usually need a version of Firefox that is not
the latest to run. Our solution is to get an older Firefox and place that the
/runners directory located in the Specford root. Current Slimerjs on Homebrew seems to like
There is a handy
bash snippet provided in the addons,
whereIsRunners.sh, copy that into a dot file of
your choice (
~/.bash_profile for instance), and adjust the path for where you cloned the Specford project.
Is the authors entry.
Compiling and Running
spec files and place them in the
specs directory in the Specford root.
gulp compile --spec foo
Will look for a
foo.spec in your
specs directory and compile that into a
runnable script that Slimerjs (or other runner) can use.
You can then run your
If you are using the provided bash add-on, if not there is a gulp task
gulp run --spec foo
There is a provided example
goog.spec, you can compile and run
just to see the awesomeness.
gulp compile --spec goog specford goog
The above assumes you are using the bash snippet for running the specs if not
you can use the gulp task
gulp run --spec goog