Skip to content

Test and debug

Everything here runs on your computer with yonto-plugin. It runs your plugin in the same QuickJS engine, with the same yonto global and the same limits as a television, so a plugin that passes here is not a plugin that only works on your machine.

Most commands take the plugin’s directory and default to the one you are in.

Terminal window
yonto-plugin init my-site --template blank --name "My site"

Writes a new plugin to ./my-site from a template, with its id and name filled in. The templates are blank and video-blog, and Write a plugin says what each is.

Terminal window
yonto-plugin lint

Checks the manifest against its schema, builds the file, and works out the contractVersion the code needs. It makes no request. It refuses a missing required function, an allowedHosts entry that cannot be a host, and a contractVersion below what the code uses. It warns about one above.

lint reads your code and does not run it. Calling a host function through a variable, or building the export object by spreading another, is read cautiously, so write the plain form where you can.

Terminal window
yonto-plugin run . search '"sourdough"'
yonto-plugin run . getMediaList '"latest"' '{"page":1}'

Calls one function and prints its JSON and the requests it made. Arguments are JSON, so a string needs two layers of quotes.

Terminal window
yonto-plugin doctor

Walks the plugin the way the app does: categories, then a list, then page 2, then a title from that list, then a search, then the optional functions it exports. Each step is fed by the one before. Its exit code is for continuous integration.

The rule that matters: a successful but empty answer is a failure. A site that changes its markup leaves a plugin that “works” and returns nothing, with no error anywhere. doctor calls that EMPTY_RESULT. It also warns about a value the app would quietly replace, such as type: "MOVIE".

A plugin whose configSchema has a required field cannot run without one. Put one in doctor.json next to the plugin, or set YONTO_PLUGIN_CONFIG='{"feedUrl":"…"}' for one run, which wins. Values are strings, and a number is refused here because it would work on this host and on no other.

doctor.json is committed, so keep it fixture-shaped and never put a real key in it.

Terminal window
yonto-plugin doctor --record
yonto-plugin doctor --replay

--record writes every response into fixtures/, with each cookie’s value replaced by redacted. --replay serves them with the network off. Record once against the real site, and from then on you can check a change in a second, offline, and so can anyone who clones your plugin.

A site that needs a session cookie cannot have recordings, because a cookie must never be committed. Test that one with ordinary unit tests over saved pages instead.

The CLI’s engine is a library as well, so your plugin’s own logic can have node --test tests. The video-blog template has the smallest one in tools/plugin-cli/test/templates.test.js in kangzj/yonto-plugins: it replays the recording, and answers with a scripted transport to test an error. Assert the rule and not the wording, so that a test about an error checks that the reason does not name the source and does not break when you rephrase it.

Serve the directory with python3 -m http.server and install its .js address under Settings, Plugins, Install from URL. From an Android emulator your computer is 10.0.2.2. From a real television it is your computer’s address on the local network. The installer reads the bytes and not the extension, so a bare .js works and nothing needs packaging.

If the install refuses, the dialog says why. A file over 1 MiB, or a download that takes over 30 seconds, is refused, and a plugin is usually tens of kilobytes.