/power-doctest

JavaScript Doctest for JavaScript, Markdown and Asciidoc.

Primary LanguageTypeScript

power-doctest Actions Status: test

A monorepo for power-doctest.

power-doctest is a project that provide doctest system for JavaScript.

Features

  • Run Comments as Assertions
  • Support JavaScript, Markdown Code Block, Asciidoctor Code Block
  • Control doctest behavior from comments

Packages

power-doctest

power-doctest is consisted of two parts.

  • Comment Assertion Syntax
  • Doctest Control Annotation

Comment Assertion Syntax

You can write following comment in your JavaScript code. These comment will be canistered to assertion.

Syntax Transformed
1; // => 2 assert.strictEqual(1, 2)
console.log(1); // => 2 assert.strictEqual(1, 2)
a; // => b assert.deepStrictEqual(a, b)
[1, 2, 3]; // => [3, 4, 5] assert.deepStrictEqual([1, 2, 3], [3, 4, 5])
console.log({ a: 1 }): // => { b: 2 } assert.deepStrictEqual({ a: 1 }, { b: 2 })
throw new Error("message"); // => Error: "message" assert.throws(function() {throw new Error("message"); });"
Promise.resolve(1); // Resolve: 2 Promise.resolve(Promise.resolve(1)).then(v => { assert.strictEqual(1, 2) })
Promise.reject(1); // Reject: 2 assert.rejects(Promise.reject(1))

For more details, see comment-to-assert.

Doctest Control Annotation

Doctest Control Annotation is defined in Language specific plugin. For more details, see following package's README.

Recipes

Doctest JavaScript Code

Use @power-doctest/tester and @power-doctest/javascript in Mocha.

import { test } from "@power-doctest/tester";
import { parse } from "@power-doctest/javascript";
const globby = require("globby");
const fs = require("fs");
const path = require("path");
// doctest for source/**/*.js
describe("doctest:js", function() {
    const sourceDir = path.join(__dirname, "..", "source");
    const files = globby.sync([
        `${sourceDir}/**/*.js`,
        `!${sourceDir}/**/node_modules{,/**}`
    ]);
    files.forEach(filePath => {
        const normalizeFilePath = filePath.replace(sourceDir, "");
        it(`doctest:js ${normalizeFilePath}`, function() {
            const content = fs.readFileSync(filePath, "utf-8");
            const parsedResults = parse({
                content,
                filePath
            });
            const parsedCode = parsedResults[0];
            return test(parsedCode).catch(error => {
                // Stack Trace like
                console.error(`StrictEvalError: strict eval is failed
    at strictEval (${filePath}:1:1)`);
                return Promise.reject(error);
            });
        });
    });
});

Example

Doctest JavaScript Code in Markdown

Use @power-doctest/tester and @power-doctest/markdown in Mocha.

import { test } from "@power-doctest/tester";
import { parse } from "@power-doctest/markdown";
const globby = require("globby");
const fs = require("fs");
const path = require("path");
const transform = (code) => {
    return code; // you need pre transform for the code if needed.
};
// doctest for source/**/*.md
describe("doctest:md", function() {
    const sourceDir = path.join(__dirname, "..", "source");
    const files = globby.sync([
        `${sourceDir}/**/*.md`,
        `!${sourceDir}/**/node_modules{,/**}`,
    ]);
    files.forEach(filePath => {
        const normalizeFilePath = filePath.replace(sourceDir, "");
        describe(`${normalizeFilePath}`, function() {
            const content = fs.readFileSync(filePath, "utf-8");
            const parsedCodes = parse({
                filePath,
                content
            });
            // try to eval
            const dirName = path.dirname(filePath).split(path.sep).pop();
            parsedCodes.forEach((parsedCode, index) => {
                const codeValue = parsedCode.code;
                const testCaseName = codeValue.slice(0, 32).replace(/[\r\n]/g, "_");
                it(dirName + ": " + testCaseName, function() {
                    return test({
                        ...parsedCode,
                        code: transform(parsedCode.code)
                    }, {
                        defaultDoctestRunnerOptions: {
                            // Default timeout: 2sec
                            timeout: 1000 * 2
                        }
                    }).catch(error => {
                        const filePathLineColumn = `${error.fileName}:${error.lineNumber}:${error.columnNumber}`;
                        console.error(`Markdown Doctest is failed
  at ${filePathLineColumn}

----------
${codeValue}
----------
`);
                        return Promise.reject(error);
                    });
                });
            });
        });
    });
});

Example

Doctest JavaScript in Asciidoctor

Use @power-doctest/tester and @power-doctest/asciidoctor in Mocha.

const { test } = require("@power-doctest/tester");
const { parse } = require("@power-doctest/asciidoctor");
const globby = require("globby");
const fs = require("fs");
const path = require("path");
// Avoid "do not support nested sections" Error
// Replace Header with Dummy text
const replaceDummyHeader = (content) => {
    return content.split("\n").map(line => {
        return line.replace(/^(=+)/g, (all, match) => {
            return "♪".repeat(match.length);
        });
    }).join("\n");
};
// doctest for source/**/*.adoc
describe("doctest:adoc", function () {
    const sourceDir = path.join(__dirname, "..", "source");
    const files = globby.sync([
        `${sourceDir}/**/*.adoc`,
        `!**/node_modules{,/**}`,
    ]);
    files.forEach(filePath => {
        const normalizeFilePath = filePath.replace(sourceDir, "");
        describe(`${normalizeFilePath}`, function () {
            const content = fs.readFileSync(filePath, "utf-8");
            const parsedCodes = parse({
                filePath,
                content: replaceDummyHeader(content)
            });
            console.log("parsedCodes", parsedCodes);
            // try to eval
            const dirName = path.dirname(filePath).split(path.sep).pop();
            parsedCodes.forEach((parsedCode, index) => {
                const codeValue = parsedCode.code;
                const testCaseName = codeValue.slice(0, 32).replace(/[\r\n]/g, "_");
                it(dirName + ": " + testCaseName, function () {
                    return test(parsedCode).catch(error => {
                        const filePathLineColumn = `${error.fileName}:${error.lineNumber}:${error.columnNumber}`;
                        console.error(`Asciidoc Doctest is failed
  at ${filePathLineColumn}

----------
${codeValue}
----------
`);
                        return Promise.reject(error);
                    });
                });
            });
        });
    });
});

Example

Development

Require Yarn

Install project

yarn install
yarn boostrap

Build

yarn run build

Test

yarn test

Release: use npm

npm run versionup
# GH_TOKEN="${GITHUB_TOKEN}" yarn run versionup:patch --create-release=github
# prepare release note
npm relase