20. Developing the Javascript for Runestone Components¶
The following is what you need to do to work on the javascript for a component testing it against a local build of a book written in PreTeXt.
Make a branch in your clone of
https://github.com/RunestoneInteractive/rsWork on the javascript for the component in
bases/rsptx/interactives/runestone/...From
bases/rsptx/interactivesrunnpm installand thennpm run build -- --env builddir=PATH_TO_BOOK_OUTPUT/_staticwherePATH_TO_BOOKis a relative or absolute path to the book build folder (and, yes, the double--are necessary - one set for npm and the other for webpack).This should result in the Runestone assets being built in development mode (uncompressed) to
_static/in the book build folder. If you do not set--env builddir, the files will be built to`bases/rsptx/interactives/runestone/dist.To have webpack monitor for changes, and rebuild the Runestone assets on file change, use
npm run watch -- --env builddir=PATH_TO_BOOK_OUTPUT/_staticSet a
stringparamsin the PreteXt book fordebug.rs.devtotrue. This will cause the book to load the development version of the Runestone assets. Without this flag, PreTeXt will look to download production assets from the Runestone CDN. Note that it is not enough to just copy the new files to_static; the PreTeXt must be built with the flag set for the pages to know what to load.If using the PreTeXt CLI, version 2.0 or greater, you can add a string param to a build target in your project file (i.e. as a child of the appropriate
project/targets/target) by adding the following:<stringparams debug.rs.dev="yes"/>.If you don’t already have a stringparams element, you might have to change the target from a self-closing tag to an element that can have a child. Change
<target name="web" format="html"/>to<target name="web" format="html"><stringparams debug.rs.dev="yes"/></target>.Run
pretext buildin the root folder of the bookRun
pretext view
If you change your PreTeXt source, you just need to rerun 5 & 6. If you change your Runestone components code, rerun 3 (or use watch) and then hard refresh your browser (Ctrl-F5).
If you are still working with old RST-based books, you can simply use the runestone build command which automatically copies the files to the correct location.
20.1. Unit tests¶
The components have a vitest-based unit test suite
that runs in a jsdom environment — no book build, server, or browser needed.
Tests live next to each component in
runestone/<component>/test/*.test.js.
20.1.1. Running the tests¶
From bases/rsptx/interactives:
npm install # first time only
npm test # run the whole suite once
npm run test:watch # re-run on file change while developing
To run a single file or filter by test name:
npx vitest run runestone/activecode/test/activecode.test.js
npx vitest run -t "history scrubber"
20.1.2. How the harness works¶
The pieces, all under bases/rsptx/interactives:
vitest.config.js— configures the jsdom environment and aliasescommon/js/renderComponent.jsto a stub (the real module importswebpack.index.jsand with it every component in the repo).test-support/setup.js— runs before every test file. It defines a minimal logged-outeBookConfig, polyfillslocalStorage, patches jsdom’sRangefor CodeMirror, and importsbookfuncs.jsfirst to break thebookfuncs/runestonebasecircular import.test-support/jquery-globals.js— provides the page globals$andjQuery(plus tiny jQuery UI slider/resizable stubs). On a real book page these come from Sphinx/PreTeXt; in tests, a component that still uses jQuery must import this shim explicitly at the top of its test file. Do not import it in tests for a component that has been migrated off jQuery — a passing suite without the shim is the proof that the component no longer depends on it.
20.1.3. Writing tests¶
Tests build a DOM fixture the same way a book page does and construct the component directly, for example:
document.body.innerHTML = `
<div class="runestone">
<div data-component="activecode" id="test_ac_1" class="ac_section">
<textarea data-lang="python">print('hello')</textarea>
</div>
</div>`;
let ac = new ActiveCode({
orig: document.getElementById("test_ac_1"),
useRunestoneServices: false,
});
Skulpt really runs Python inside jsdom, so tests can click Run (or call
runProg()) and assert on the output. Note that stdout writes are
appended from short timeouts, so await a ~50ms delay after runProg()
before asserting on the output element.
If you add a feature or a new component, please include tests that verify it
works. runestone/activecode/test/ is a good model to copy.