Testing
Unit tests cover the pure logic and run on Node 18+ with no dependencies to install.
npm test # or: node --test
What is covered
Unit conversions in both directions and on both scales, heading and climb rate readouts, the low altitude warning, the artificial horizon's angles and ladder, the minimap's projection, its behaviour at the world's edge, and the course it draws on the chart, the keybinding map, the on-screen pads a machine with no keys is flown from and the question that decides whether it wants them, the device tilt those same machines open in and the sensor that reports it, the setting that says which machines are flown that way at all and the one that says how far the device is turned before it asks for anything, control rates at every sensitivity, the configured start state and the attitude that holds its climb, a start edited to any other condition and the attitude that holds that one, throttle and lift math, the engine and wind mix and its mute, the crash threshold and countdown, the touchdown rules that tell a landing from a crash, the geometry of a runway and the search that sites one, the takeoff a flight is held at, the crossing that carries a bounded world round at its edges, the grid the endless ground is laid on and the promise it makes that no position can see the end of it, the record a terrain keeps of the world it built and the copy that keeps a caller's own edits visible to it, photo mode's state and the names it writes, camera damping, pause toggling, the start screen's rules, start-up progress, menu selection by key and by pointer and the one cursor they share, a list drawn again from a menu whose rows have come and gone, the keys a menu takes back off the flight, the settings panel with its options, its start state fields, its radio group and its held box, and their stored choices, the game modes with their stages, their runs, their courses, the gate test and the gate gone past, the shape a gate is laid as and the angle it has to be flown at, the circuit a pattern is laid off a strip as and the mark each of its legs is held to, the corridor a canyon run is cut into and the crossing that says whether a cut was flown inside the walls and under the lid, the landmarks a survey lists and the height, range and heading window each shot is caught through, the guidance an approach is given and the stages it is withdrawn over, the reading taken of a landing and the marks it comes to, the rollout it is held for, the stage clock and the board it is recorded on, and the pointer that finds a gate off the screen, the element editor's rows, its steps, and the ground the placements it hands out generate, the build session and the three ways into it and the one predicate the entry and the key both read, the picker's six canvases read off the environment registry and the blank one that sits beside it, where the camera stands over a canvas and how far it has to see to read the corner of one, the one tile a canvas is drawn as and the flight's own reach left untouched beside it, the region a placement can carry - the seven kinds a pointer draws, two of them combined into one, how much of one covers a point, the band its weight falls across and the square that contains it - the lasso's ten tools and the regions they draw, the selection they leave and the key held at the press that says how a shape meets it, the cover tool's brush, its stroke laid into one batch at the press's scale and the frame the batch is moved, turned and stretched by, the clumps a cover's density breaks it into and the band a stroke's ground settles, the water line kept a vertex at a time beside the sea's level, drawn water resting level across its stroke at the lowest bank and following the ground down along it, a strip lifted clear of it, and drawn snow on any ground, the coarse step a press with shift makes, a strip placed where it was asked for and levelled to its ground, the runway templates placed as groups and expanded, turned and kept on the canvas, Insert Runway's cards and the template a click places, the placed strip a runway start opens on, paint, which lays a colour and no height, and the tint that keeps the ground's lightness under it, Free Paint's swatches, picker, hex figure and eye dropper, its brush laid in world units at the press's scale, a stroke shown while it is drawn as exactly the region it will cover, an erase batch cut out of what was laid before it at its strength and put back by the batch after it, the sea erased to dry ground and a strip taken whole and only by name, Erase's panel and its alt stroke, the DESELECT every panel carries and the popup a right click opens, the shape vocabulary the lasso and Extrude both draw from, an extrusion levelled to its ground's mean and raised with its top and its wall, Extrude's stages and the one button they share, shapes summed into one region and a selection lifted as it stands, the blue arrow pulling an extrusion up, a surface clicked pointing the panel at its colour and outlined at the weights it is read at, BACK dropping a change where APPLY keeps it and the eye dropper filling a surface's row, both surfaces tinted to their ground's lightness and the wall faded at its foot, a pan held from inside a tool that suspends it and puts it back, every row a tool's placement shows changing the ground it draws, the builder's own settings - their rows under their headings, their storage under a key of their own, ALL holding the moments on, the reset, the row on the game's settings that opens them and the screen BACK returns to, and the same ground built under any of them - auto accept keeping a draft at the next press that draws, at another tool and at the way out and never on a release or a pan, the builder's settings over a canvas suspending the tool and keeping the draft, a mark drawn in the repository kept off the copy's list, two strokes auto accept keeps in a row joined into the one placement a batch accepted by hand would be and a changed one kept apart, an aircraft put back over ground that changed under a paused flight - on it if it was on the ground, and in the air at the height it had over the old ground - the builder's control list carried into the docs and both cheat sheets line by line, the right button turning the view from any tool and a right press let go of before it travelled opening the popup on the selection, CANCEL on both confirms settling nothing, the ground's colour held to 0 and 1 in its shader so the fog reaches a saturated paint seen from far off, every world a flight or a canvas is drawn on coloured within 0 and 1, a fraction moved a tenth at a press and still written to the hundredth, a figure's field carrying the panel's own up and down and its keys in place of the browser's, a switch's or a choice's box stepping it round, what each insert, load or save section does written and read back as a mode type, the palette's later three declared with their art and drawn once each works, the bar across its foot, a built world written as an environment file and read back whole, refused whole with its first problem named and held to its ranges, and the file's schema held to the module that reads it, the last world a file brought kept as the file's text and read back through the format, the settings' two file rows under the worlds and the loaded world listed after them, chosen and let go of, a preset chosen over it a new world, the rows gone while a game mode runs, SAVE / LOAD's panel and what a save writes, a load over a build asking REPLACE THE BUILT WORLD and a load opening the file's world as the canvas, and the airports laid out from their strips, the keypad both number-holding panels read a typed figure through and each panel's reading of one - which rows take a figure, the bounds it is held to, and the row that says so when it is refused - terrain noise and the mountain formula, the element registry and every generator it holds, the assembled environments, a world laid as one tile of an assembly and the pass that settles what its elements drew at the joins, the day cycle and the light and the sun's place at every hour of it, the wave and the sheen the water is drawn with and the surface they are applied to, the API's option defaults and contract checks, the API document against the surface it describes and that surface against every export the document claims, the example host page against both halves it is meant to prove, page metadata, this documentation site against the pages it claims to have and against the code its pages list - the start fields, the worlds, the game modes, and the keys the game reads for itself, each page holding every key and the four that quote a refused row holding the notice it shows - the API reference page against the document it is built from, the addresses that page hands its headings and the sample markup it prints rather than carries, the one id per address every page is held to, every anchor read against the addresses a page offers rather than against an id it only prints and against the page it points at rather than the page that links it, the converter that builds it and the line it has no rule for, the link check that tells a page the browser opens from a file it downloads, and the static server's path and content type rules.
The suite reads the API reference, the README, and the pages of this site, and fails when one of them has drifted from the code or from the others. A document that has gone stale is a bug the tests catch rather than one a reader finds. The reference page is built from docs/api.md by npm run docs:api rather than written beside it, and the suite renders it again and compares, so what is published is always what the document now says.