Scenarios

A project's own checks, Scenarios/<Name>.luau files of plain Luau that shot run plays top to bottom.

This is the System project form, which Generic projects share; Roblox projects have their own, the Roblox Scenarios page.

What a scenario is

  • It lives in the Scenarios folder, outside the program and the export.
  • Each plays on a fresh copy of the tree: what one changes is gone for the next.
  • It passes if it ends without an error.
shot write Scenarios/Rounding.luau --project Units.ssp    writes one, its text on stdin (shot edit, shot show: the same path)
shot run Units.ssp                                         a library: lists its scenarios
shot run Units.ssp Rounding                                plays one (Rounding,Freezing for several, all for every one)
shot run Forecast.ssp Used                                 a program has scenarios too; shot run Forecast.ssp runs the program

Writing checks only that it parses; a line that doesn't is reported, the file kept:

Scenarios/Broken.luau:3   Expected ')' (to close '(' at line 2), got 'print'

shot check never reads scenarios: with require(root.Unit) it printed 0 errors, and the run:

FAILED Units:Scenarios.Typo
  error: Scenarios/Typo.luau:2: Unit is not a valid member of Folder "Units"
    at Scenarios/Typo.luau:2
0 of 1 scenario(s) passed

Writing one

Units.ssp's Scenarios/Rounding.luau:

-- Rounding: CelsiusOf and Round give the Celsius readings Station keeps.
local Units = require(root.Units)

local boiling = Units.Round(Units.CelsiusOf(212), 1)
print("212 F is " .. boiling .. " C")
assert(boiling == 100, "212 F should be 100 C")

shot run Units.ssp Rounding prints:

ok     Units:Scenarios.Rounding
  212 F is 100 C
1 of 1 scenario(s) passed

A failed assert or an error ends it there, after earlier prints; the exit code is 1. Freezing.luau asserts 32 F is 1 C:

FAILED Units:Scenarios.Freezing
  32 F is 0 C
  error: Scenarios/Freezing.luau:6: 32 F should be 1 C
    at Scenarios/Freezing.luau:6
0 of 1 scenario(s) passed
  • Plain Luau: no types line, use or doc. A use line doesn't parse:
Scenarios/UseLine.luau:2   Incomplete statement: expected assignment or a function call
  • root is the root instance, script the scenario, and script.Parent the Scenarios folder, outside the project.

What a scenario reaches

require(root.Units)a module of the project by its place in the tree: here the export Units (require(root.Units.Units), its file, works too)
require("@Station")a project this one uses, by the alias its header records (Externals)
require("@shotsystem/FileService")a service (Services)

Forecast.ssp uses Station; its Scenarios/Used.luau:

-- Used: a project this one uses comes by the alias its header records.
local Station = require("@Station")

local station = Station.new("unused.json")
station:AddFahrenheit(212)
print("one reading, average " .. station:Average() .. " C")

shot run Forecast.ssp Used prints:

ok     Forecast:Scenarios.Used
  one reading, average 100 C
1 of 1 scenario(s) passed

An alias names only a used project, never the project's own export. require("@Units") in Units.ssp:

FAILED Units:Scenarios.Alias
  error: Scenarios/Alias.luau:2: require("@Units"): Units.ssp has no external of that name (a string require is @<Alias> from the project's header, or @shotsystem/<Service>)
    at Scenarios/Alias.luau:2
0 of 1 scenario(s) passed

Not the Roblox form

A Roblox scenario returns function(world). Here a returned function is called with no world, so Scenarios/World.luau

-- World: the Roblox form, which a System project can't run.
return function(world)
    local Units = require(root.Units)
    world.Logf("212 F is %s C", world.Fmt(Units.CelsiusOf(212)))
end

fails at its first use:

FAILED Units:Scenarios.World
  error: Scenarios/World.luau:4: attempt to index nil with 'Logf'
    at Scenarios/World.luau:4
0 of 1 scenario(s) passed

Write the steps at the top level instead, as Rounding does.