Jest Markdown Pipeline

"Nothing is better than documentation with examples. But nothing is worse than examples that don't work because the code has changed since the documentation was written." - rustbook

Example code blocks in our markdown or inline documentation comments can be useful to quickly demonstrate how one can use our software. As they are part of our living standards, each example code should be functional code that can be asserted and tested. Documentation example code should be automated, tested and linted as part of our workflow.

Initially, we were thinking of creating a md-jest for parsing code blocks in markdown and transforming them into jest tests. Similar to how ts-jest transform TS to CommonJS, but using contented-processor to transform MD files.

@eli-lim suggested we transform jest files *.test.ts into Markdown instead. We get the DX of writing test with Jest and IDE support (without having to support write our own runner). It is much easier to transform comments to markdown than transform markdown to code (although not much harder, the focus here is on the DX). Further, you get the natural flow of writing narrative and writing tests as a narrative.

This page, is written in a jest test file. JestMarkdown.spec.ts

Comments Parsing

All comments are automatically picked up as Markdown with indent stripped. Indent are stripped using strip-indent, * are also stripped. For bullet points, you should use - and avoid using * to prevent unintentional stripping.

  • // - Line Comment
  • /* - Block Comment
  • /** - Block Comment

This section is generated by the code snippet below.

This is a multi line with * stripped from each line.

This is a Line Comment

This is a Block Comment

This is a Multi Line Block Comment without *.

/**
 * ### This section is generated by the code snippet below.
 *
 * This is a multi line with `*` stripped from each line.
 */
// This is a `Line Comment`
/* This is a `Block Comment` */
/* This is a `Multi Line`
 `Block Comment` without `*`. */

Including Codeblocks

JestMarkdown has the ability to "pickup" codes and add them into a codeblock. Codeblock is parsed line by line, @contented codeblock:start indicate the starting line and @contented codeblock:end indicate the ending line. Everything between will be included into a codeblock. Although not recommended, you can nest multiple @contented codeblock between.

'@contented codeblock:start',
'@contented codeblock:end',
it('this it statement is in a codeblock with nested codeblock', () => {
  const symbols = [
    // @contented codeblock:start
    '@contented codeblock:start',
    // @contented codeblock:end
    'everything in between will be included as a TypeScript codeblock ```ts\n${codes}\n```',
    // @contented codeblock:start
    '@contented codeblock:end',
    // @contented codeblock:end
  ];
  expect(join(...symbols)).toBeDefined();
});

Beta Pipeline

This Pipeline is currently in Beta!

This pipeline is considered Beta, while the idea and the intent will stay intact. We want to optimize this pipeline for better integration with Jest. Better integration with Jest Semantic describe, it, before, and after; to achieve a more natural authoring process.

Imagine the below:

describe('MyAPI: Javascript Client', () => {
  const api = new MyApi('https://my-api.com');

  describe('GetRecords', () => {
    it('Example', function () {
      const records = api.GetRecords(10);
    });
  });

  describe('PutRecord', () => {
    it('Example', async function () {
      await api.PutRecord({
        text: 'MyText',
      });
    });
  });
});

Should generate this Markdown:

MyAPI: Javascript Client

const api = new MyApi('https://my-api.com');

GetRecords

Example
const records = api.GetRecords(10);

PutRecord

Example
await api.PutRecord({
  text: 'MyText',
});