10 ways we improved our docs

July 5, 2017

By Jennifer Shehane

As part of our strategy towards going open source, we wanted to make several improvements to our documentation.

There were a few problems we wanted to address:

  1. There was no clear explanation of why you would want to use Cypress.
  2. There were no clear “Getting Started” guides that introduced you to Cypress.
  3. Most API commands were missing explanations of critical core concepts.
  4. We found many users were confused by the Cypress ✨ magic ✨.
  5. Our users asked the same questions over and over again.
  6. Searching our documentation was not great.
  7. Our deployment process was constantly breaking.
  8. Navigating between pages was slow.
  9. Contributing to the docs was confusing.
  10. Interlinking between all the docs was difficult to keep up with and often led to broken links.

We’ve spent the last few months addressing each of these issues and are proud to announce that our newly improved documentation is now live! 🎉

https://on.cypress.io

Why Cypress?

While our previous docs listed out a few of Cypress’ features - they were missing a comprehensive overview of our mission and more importantly why we built Cypress.

Many of our users have expressed concern or confusion over which parts of Cypress are open source and which parts are behind a paywall.

Our new: Why Cypress? doc is intended to clear up this confusion. It makes it clear that we fully embrace open source, defines the ecosystem we are working to build, and outlines our mission.

Getting started

Instead of dropping you into the ocean of testing and expecting you to swim, we’ve written two new guides to help ease you into Cypress.

These guides include not only step-by-step instructions, but lots of images and animated gifs that show you exactly how it’s supposed to work.

We’ve also done a much better job introducing you to solid testing best practices along with many of the debugging tools we’ve built.

API commands

We found that our previous API docs were missing many core concepts that were otherwise confusing.

After making an inventory of every single command doc, we rewrote the commands with each document covering essentials including:

  • Syntax: The method signature and any accepted arguments.
  • Usage: How to call the method including valid and invalid uses.
  • Arguments: Definition of arguments including types accepted.
  • Yields: What is yielded (and in rare cases, returned) from the method.
  • Examples: Real world examples of using the methods.
  • Notes: Any exceptions to take into account when using the method.
  • Requirements: What built in requirements the command has.
  • Assertions: How assertions are handled, and whether default ones are applied.
  • Timeout: If defined, the amount of time the method allows to execute before throwing.
  • Command Log: If and how the method displays in the Cypress Command Log and console.
  • See also: Other related methods, guides or code.

We’re really excited about these changes and think this will clear up a lot of confusing when writing tests, especially over how to chain command together.

We’ve also done a much better referencing and cross linking commands to core concepts, or even to some of our actionability algorithms. Check them out.

Cypress ✨magic✨ explained

We could tell that some of our users were having a difficult time getting a grasp of how to write their initial tests based off of the chatter we saw in our chatroom.

We’ve consolidated and rewritten from scratch many of the “Core Concepts” of how Cypress works.

  • Introduction to Cypress now explains how chaining works, commands are queued, assertions, actionability, and timeouts all work.
  • Interacting with Elements explains how Cypress defines waiting for an element to become actionable - and lists out our algorithm used when interacting with elements.

Frequently asked questions

Our chatroom also had some questions that were asked over and over again. We wanted a central, searchable place outside of the chatroom where these answers could go.

So, now we have an FAQ organized by the types of questions we regularly see.

Search Cypress high and low

With the nature of how we set up our docs in Readme.io (hackily splitting it up into version 0 and 1), it made searching the entire set of docs impossible. The search results would also only give the title of the document that had the results - not very helpful.

We researched some searching solutions and decided on Algolia. We setup our docs to use docsearch and their scraper to scrape our docs on deployment. Plus the results look much better 👍

Searching should now be 1000x better. We even indexed our Changelog so that every version released is searchable.

Deploying our docs

We used to host our docs on Readme.io. They were a great place to throw our ideas together when we were ready to move off GitHub Wikis a couple years ago.

On top of Readme.io, we used readmeio-sync to sync local Markdown files to Readme’s API directly. We didn’t want to log in to Readme.io’s site every time we wanted to make a change to our docs. This plugin allowed us to save our docs markdown files in our repo, run a few commands and have our public docs updated. Notice - we didn’t have a way to preview the docs in a staging environment before pushing 😓.

Unfortunately, as Readme.io updated their APIs, readmeio-sync often didn’t update their side to account for those changes. We began to encounter errors every few months when we tried to publish our docs and would have to jump into readmeio-sync’s code to fix them ourselves.

This process was unsustainable, so we researched some other solutions - mainly solutions where the codebase was in Node, since that’s what our entire codebase is written in. We finally decided to use Hexo. Hexo is advertised as:

A fast, simple & powerful blog framework

But, Hexo themselves uses Hexo for more than just blogging. They use it for their own documentation. We liked how their own docs were structured and pulled many ideas from them.

Now we have a lot more control over how our docs are created from the ground up and Hexo’s plugins, tags, helpers, and extensibility in general has been a great fit for us. It actually was such a good fit that we converted our public site to Hexo as well. This blog was written and created using Hexo!

Slow page navigation

Because we were using Readme.io, our content was dynamically served by their servers (and likely was stored in a database).

We’ve now moved all of our docs to static pages deployed behind s3 and cloudflare. Page navigation should now be orders of magnitude faster.

Only you can prevent bad docs

Our docs have grown quite a bit over the years. We have 140+ documents to keep track of and while we do our best to keep it up to date, sometimes things go stale or just have a typo. We wanted contributing to our docs to be easy for ourselves and outside contributors.

Therefore we moved our docs into the same repository as the rest of our codebase. So, as soon as we are open source, you’ll see the docs code and content in the same repo as Cypress code.

Our docs also support internationalization, so that our docs can be translated into other languages by anyone that wants to contribute.

Keeping up with doc content

As part of our move to hexo we’ve now written dozens of helpers to automatically keep our docs in sync with each other.

We wrote:

  • URL tags that automatically verify that the doc was linked correctly.
  • API snippet tags to ensure all commands have a standardized language.
  • Tags to quickly link and reference open GitHub issue numbers.
  • Standardized notes and icons.
const util = require('hexo-util')

function issue (hexo, args) {
  // In our docs, now we can write...
  // {% issue 74 'not currently supported' %}

  const num = args[0]

  const attrs = {
    href: `https://github.com/cypress-io/cypress/issues/${num}`,
    target: '_blank',
  }

  const text = args[1] || `issue #${num}`

  return util.htmlTag('a', attrs, text)
}

Wait, what about tests?

Oh, and last but not least, all of our docs are now tested using Cypress. 😄

Since a lot of our menus are generated using data files, we were able to do some cool stuff, like ensure that our English translations are working for our sidebar titles and links:

YAML = require("yamljs")
_ = require("lodash")

API_PATH = "/api/introduction/api"
API_HTML = API_PATH + ".html"

context "Sidebar", ->
  beforeEach ->
    cy.visit(API_PATH + ".html")

    cy.readFile("source/_data/sidebar.yml").then (yamlString) ->
      @sidebar = YAML.parse(yamlString)
      @sidebarTitles = _.keys(@sidebar.api)

      @sidebarLinkNames =  _.reduce @sidebar.api, (memo, nestedObj, key) ->
         memo.concat(_.keys(nestedObj))
      , []

      @sidebarLinks =  _.reduce @sidebar.api, (memo, nestedObj, key) ->
           memo.concat(_.values(nestedObj))
        , []

    cy.readFile("themes/cypress/languages/en.yml").then (yamlString) ->
      @english = YAML.parse(yamlString)

  it "displays English titles in sidebar", ->
    cy.get("#sidebar")
      .find(".sidebar-title").each (displayedTitle, i) ->
        englishTitle  = @english.sidebar.api[@sidebarTitles[i]]
        expect(displayedTitle.text()).to.eq(englishTitle)

  it "displays English link names in sidebar", ->
    cy.get("#sidebar")
      .find(".sidebar-link").first(5).each (displayedLink, i) ->
        englishLink  = @english.sidebar.api[@sidebarLinkNames[i]]
        expect(displayedLink.text().trim()).to.eq(englishLink)

So, go check them out and don’t be shy to let us know what you think in the comments below.