If the available plugins aren't enough, you can write your own custom actions in the JavaScript dialect of your choice.
Let's start by building the simplest possible action first: A "hello-world" action that prints the text "hello world" in the test runner's console output when running. Inside a Markdown document we will trigger it like this:
<a type="hello-world"></a>
Create a file hello.md with this content to test it.
When TextRunner encounters this region of type hello-world
, it reads the file
text-run/hello-world.js and runs the function
exported by it. Let's create this file with the content:
export default action => {
action.log("Hello world!!")
}
Let's run Text-Runner:
$ text-run
The formatter displays test progress on the console as the test runs:
Hello world!! hello.md:1 -- Hello world
An action is a simple JavaScript function. It receives an object containing information and utility functions:
- location location of the currently executed region in the documentation (file and line)
- region: the document content inside the active region for this action
- document: the content of the entire document that contains this action
- configuration: TextRunner configuration data
- log: a function similar to
console.log
that outputs text to the person running the test - name: call this function to provide a human-readable name for this action
- SKIPPING: return this value if you have decided to skip the current action
TextRunner supports all forms of JavaScript functions as actions:
- synchronous functions
- functions receiving a callback
- functions returning a Promise
- async functions
Examples for custom actions written in ESM are here. You can write functions in TypeScript or in classic CommonJS. Throw an exception to fail a test.
The region
attribute contains the document
content inside the currently active region. It is a flat array of syntax tree
nodes that provides helper methods to extract document content:
- text(): returns the entire textual content in the current active region
- textInNodeOfType(type1, type2, ...): returns the text in the AST node of the given types. You can provide multiple alternative node types. Verifies that only one matching AST node exists.
- textInNodeOfTypes(type1, type2, ...): returns the text in the AST nodes of the given types. You can provide multiple alternative node types. Only one node is allowed to match.
- textInNodesOfType(type): provides the text in all nodes with the given type
- nodeOfTypes(type1, type2, ...): provides exactly one syntax node matching any of the given types
- nodesFor(node): provides a list of nodes from the given opening node to its closing counterpart
- nodesOfTypes(type1, type2, ...): provides the syntax nodes with any of the given types
- hasNodeOfType(type): indicates whether a syntax node with the given type exists
- nodeTypes(): provides the names of all node types in this document region
- textInNode(): providess the textual content for all nodes from the given opening node to its closing counterpart
To see the node types run text-run debug --ast <filename>
You can also iterate
region
manually. Each element has these
attributes:
- location: the file and line in the file at which this AST node begins
- type: the type of the AST node. Examples are
text
for normal text,code
for inline code blocks,fence
for multi-line code blocks,emphasized
for italic text,strong
for bold text, andlink_open
for links. - tag: corresponding HTML tag
- content: textual content of the AST node
- attributes: list of HTML attributes of the node
Here is an example for an action that runs a code block in the terminal. Create a file execute.md with the content:
<pre type="console-command">
echo "Hello world"
</pre>
Here is the corresponding action, implemented in text-run/console-command.ts (we are using TypeScript this time):
import * as child_process from "child_process"
import * as textRunner from "text-runner"
export function consoleCommand(action: textRunner.actions.Args) {
// determine the console command to run
const commandToRun = action.region.text()
// run the console command
const result = child_process.execSync(commandToRun, { encoding: "utf8" })
// print the output to the user
action.log(result)
}
You can access other attributes on the HTML nodes like so:
const attr = action.region[0].attributes
To see all custom activities that aren't currenly used, run:
text-run unused
Read more about:
- the built-in actions