Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

One suite, three apps

The kettle demo is the same app built three times, in GPUI, SwiftUI and React Native, and held to one suite. Each app embeds the Expectation driver for its stack (see Embedded drivers), and the same runner drives all three.

Browse the result: the demo manual. Its Platform menu switches between the three apps’ manuals: the same tutorials, with each app’s own screenshots and the instructions worded for it.

One suite

The behaviour is written once. The suite declares the platforms, and says where the wording differs:

Vocabulary:
  Platform: GPUI | SwiftUI | React Native

  State: the kettle is off | starting | heating | dry
    Shows: heating: the display reads HEATING and the light turns red
  State: the dial is 0..100 %
    Set: drag the DIAL slider to {value}

  Event: the user presses start
    Say: click START
    Say[SwiftUI]: tap START
    Say[React Native]: tap START

Everything else (the features, the exception for the child lock, the three stories) is plain, so it’s true on every platform. expectation check checks the suite as each platform reads it.

Three apps

Each app implements the kettle in its own language and binds the vocabulary to it:

GPUISwiftUIReact Native
Kettlekettle.rsKettle.swiftkettle.ts
Driverexpectation_remote::clientExpectationDriver (Swift package)expectation-driver (npm)
Commands run onthe GPUI foreground, every tickthe main actorthe JS thread
Screenshotsthe app, with screencapturethe app, from its key windowthe runner, from the simulator

The SwiftUI binding, for example:

let driver = ExpectationDriver(platform: "SwiftUI", app: "Kettle")
    .observe("the kettle") { .name(k.phase.name) }
    .observe("the dial") { .number(k.dial) }
    .set("the dial") { k.dial = $0.number ?? 0 }
    .on("the user presses start") { k.start() }
    .on("the water runs out") { k.runOut() }
    .onReset { k.restore() }
driver.start()

Running it

cd demos/kettle
./run.sh gpui
./run.sh swiftui
./run.sh react-native

Each run builds the app, starts expectation-run --platform <name>, launches the app, which connects, and saves the results and screenshots. A runner for one platform turns away apps for the others, so leftover apps from an earlier run can’t interfere. The site build then writes the manual for all three from what the runs saved:

expectation docs demos/kettle/expectations demos/kettle/docs

What it shows

The three apps are independent implementations, written in three languages. Nothing but the suite keeps them the same. The test reports show the same tests passing on each, and a behaviour change starts in the suite, where all three apps will be held to it.