---
title: "Self-testing JAM pages"
url: "https://www.cypress.io/blog/self-testing-jam-pages"
description: "JAM Stack [https://jamstack.org/] has become a popular way to build websites - you can write your pages using Markdown (the M in JAM), build using JavaScript…"
---

# Self-testing JAM pages

November 13, 2019

By Gleb Bahmutov

[JAM Stack](https://jamstack.org/?ref=cypress-io.ghost.io) has become a popular way to build websites - you can write your pages using Markdown (the `M` in `JAM`), build using JavaScript tools that fetch data from APIs and get mostly static sites that are very fast. In this blog post I will show how each Markdown page can also include its own end-to-end tests using the Cypress Test Runner.

**Code:** The source repo for this blog post is at [bahmutov/self-testing-jam](https://github.com/bahmutov/self-testing-jam?ref=cypress-io.ghost.io) and the deployed site is available at [](https://self-testing-jam.netlify.com/?ref=cypress-io.ghost.io)[https://self-testing-jam.netlify.com/](https://self-testing-jam.netlify.com/?ref=cypress-io.ghost.io). It is a JAM static site that uses a [Vuepress](https://vuepress.vuejs.org/?ref=cypress-io.ghost.io) site deployed on [Netlify](http://netlify.com/?ref=cypress-io.ghost.io).

Recently I have talked about end-to-end testing at [ReactiveConf 2019](https://reactiveconf.com/?ref=cypress-io.ghost.io) in Prague, Czech Republic. It was an excellent conference, and the 25 minute video of my talk was posted online almost immediately. We wanted to distribute the talk widely because it covered several topics very relevant to the testing community: code coverage, linting source code, and writing faster tests. You can watch the video down below and find the slides at [](https://slides.com/bahmutov/state-of-the-art/?ref=cypress-io.ghost.io)[https://slides.com/bahmutov/state-of-the-art/](https://slides.com/bahmutov/state-of-the-art/?ref=cypress-io.ghost.io)

Watching an entire 25 minute video and flipping through the 100 slide deck seems like a big commitment. I have created aliases that immediately go to the specific slide of the presentation for each topic, and found the video timestamp when I begin talking about it.

| Topic | slide alias | Video at |
| --- | --- | --- |
| Types of linters | #lint-pyramid | 3m46s |
| Tests and plugins | #only-tests | 7m9s |
| Cover coverage | #code-coverage | 11m39s |
| Splitting long tests | #test-length | 20m5s |

Whenever I need to send someone additional information about code coverage for an example, I can send urls to the slides [https://slides.com/bahmutov/state-of-the-art/#/code-coverage](https://slides.com/bahmutov/state-of-the-art/?ref=cypress-io.ghost.io#/code-coverage) and the video [https://youtu.be/JL3QKQO80fs?t=699](https://youtu.be/JL3QKQO80fs?t=699&ref=cypress-io.ghost.io) which is great.

## Self-testing the Markdown file

Publishing a separate documentation page with each talk thus should embed the talk video and a table of shortcuts, plus possible additional information. Here is the Markdown of the page.

ReactiveConf page with talk and individual links

The page might get complex, thus it is a good idea to write at least a simple Cypress end-to-end test. For me, the best test is the one that sits closely to the code it is testing. Placing a spec file next to the code file is great, I could place tests next to Markdown files like this.

```text
self-testing-jam/
  README.md
  reactiveconf.md
  reactive-conf-spec.js
  cypress.json
```

By default Cypress expects spec files to be placed in the `cypress/integration` folder, so we need to set the integration folder to be `.` using `cypress.json`

```json
{
  "integrationFolder": ".",
  "fixturesFolder": false,
  "supportFile": false
}
```

This is good, but we can go one step further. Cypress transpiles each spec file using [](https://github.com/cypress-io/cypress-browserify-preprocessor?ref=cypress-io.ghost.io)[cypress-browserify-preprocessor](https://github.com/cypress-io/cypress-browserify-preprocessor?ref=cypress-io.ghost.io), which you can change to tweak options and even transpile other file types. Recently I have written a Cypress Markdown transpiler and placed it in the [](https://github.com/cypress-io/cypress-fiddle?ref=cypress-io.ghost.io)[cypress-io/cypress-fiddle](https://github.com/cypress-io/cypress-fiddle?ref=cypress-io.ghost.io) repository. To use this preprocessor, install it and add the following code to your plugins file:

```js
// cypress/plugins/index.js
const mdPreprocessor = require('@cypress/fiddle/src/markdown-preprocessor')
module.exports = (on, config) => {
  on('file:preprocessor', mdPreprocessor)
}
```

The new preprocessor allows you to write end-to-end tests inside Markdown text files - just surround the test's JavaScript test block with special HTML comments like this:

Special "fiddle" comments tell Cypress Markdown preprocessor that there is a test inside

Great, we can write a test right inside the ReactiveConf video talk `reactiveconf.md` file. For example, we can confirm the video player is loading and the individual section links are present.

```js
cy.visit('/reactiveconf.html')
// YouTube player is embedded
cy.contains('Cypress.io - the State of the Art End-to-end Testing Tool')
cy.get('[data-cy=talk]')
  .then($iframe => {
    // this ensures the frame loaded
    cy.wrap($iframe.contents()).should('have.length', 1)
    return cy.wrap($iframe.contents().find("body"))
  })
  .find('.html5-video-player').should('be.visible')
// main sections links
;['Lint pyramid', 'Tests and plugins', 'Code coverage'].forEach(section => {
  cy.contains('li', section).should('be.visible')
})
```

**Tip:** because the video player comes from 3rd party domain, you need to disable the browser security check using `cypress.json` like this:

```json
{
  "baseUrl": "http://localhost:8080",
  "chromeWebSecurity": false,
  "viewportHeight": 1000
}
```

## Hiding the test itself

To prevent the test itself from showing up and taking up the majority of the rendered page, we can place it inside `<details>` tag right inside Markdown. Here is a screenshot of the page that shows the page and the embedded test fiddle.

Markdown page includes end-to-end test inside "details" tag

Here is the final result running a local Cypress test against the built page.

Self-testing Markdown page

**Tip:** you can completely hide the test by setting the test's tag to `display:none` like this

```html
<details style="display:none">
<summary>Cypress test for the current page</summary>
<!-- fiddle Talk and contents list -->
    ...
</details>
```

## Links

-   [](https://github.com/bahmutov/self-testing-jam?ref=cypress-io.ghost.io)[https://github.com/bahmutov/self-testing-jam](https://github.com/bahmutov/self-testing-jam?ref=cypress-io.ghost.io) has the source for this blog post, and you can find the deployed page at [](https://self-testing-jam.netlify.com/reactiveconf.html?ref=cypress-io.ghost.io)[https://self-testing-jam.netlify.com/reactiveconf.html](https://self-testing-jam.netlify.com/reactiveconf.html?ref=cypress-io.ghost.io)
-   The markdown preprocessor with test extraction lives in [](https://github.com/cypress-io/cypress-fiddle?ref=cypress-io.ghost.io)[https://github.com/cypress-io/cypress-fiddle](https://github.com/cypress-io/cypress-fiddle?ref=cypress-io.ghost.io), also read [Run End-to-end Tests from Markdown Files](https://glebbahmutov.com/blog/cypress-fiddle/?ref=cypress-io.ghost.io)
-   It is a good idea to test static sites as you build them and after deploy, read these blog posts: [Gatsby Netlify Circle and Cypress](https://glebbahmutov.com/blog/gatsby-netlify-circle-and-cypress/?ref=cypress-io.ghost.io) and [VuePress and some Cypress end-to-end testing tips](https://glebbahmutov.com/blog/vuepress-conditional-testing/?ref=cypress-io.ghost.io).