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.
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.
yonto-plugin lintChecks 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.
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.
doctor
Section titled “doctor”yonto-plugin doctorWalks 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".
Settings
Section titled “Settings”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.
Recordings
Section titled “Recordings”yonto-plugin doctor --recordyonto-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.
Writing tests
Section titled “Writing tests”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.
Trying it in the app
Section titled “Trying it in the app”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.