Add the demo logic
Add the demo logic
Store listings need screenshots, at exact pixel sizes, per device, for every store. Peko generates them by running your real app and scripting it, so they never drift from the product.
This step needs a paid Peko account.
pekoshotsis a gated package and will not resolve without one. If you do not have a paid account, skip this page and carry on. Everything that follows works without it; you will supply the store screenshots yourself.
Two things are needed from you: a state worth photographing, and a script.
How it is kept out of the shipped app
Demo code lives behind demo { ... } blocks, a conditional compilation form.
peko build --demo compiles them; a normal or release build strips them
entirely. The automation package is only ever imported from inside such a block,
so the binary that goes to the stores contains none of it.
Add the dependency:
[dependencies]
pekoui = "0.1.6"
pekoshots = "*"Then wire it into the entry. Note that the import is itself inside the block:
import pekoui as ui;
demo {
import demo_shots;
}
fn on_start() {
let app: ui::app::App = ui::app::from_bundle();
// window setup as before
demo {
demo_shots::install(app);
}
app.run();
}The demo module
src/demo_shots.peko declares the devices to produce and the shots to run:
import pekoui as ui;
import pekoshots as shots;
fn t(selector: string) => shots::builder::Target {
return shots::builder::sel(selector);
}
fn register(d: shots::demo::Demo) {
d.device(shots::catalog::mac_1280x800());
d.device(shots::catalog::iphone_69());
d.device(shots::catalog::play_phone());
d.quiet();
d.shots(
closure(set: shots::builder::ShotSet) => void {
build_shots(set);
},
);
}
fn build_shots(s: shots::builder::ShotSet) {
s.screenshot(
"counter",
closure(shot: shots::builder::Shot) => void {
shot.wait_for(t("#count")).click(t("#increment")).click(t("#increment")).click(
t("#increment"),
).wait_for_idle().caption("Your count, kept between launches").capture();
},
);
}
[public] fn install(app: ui::app::App) {
shots::driver::attach(
app,
closure(d: shots::demo::Demo) => void {
register(d);
},
);
}Each device is a store target with the pixel dimensions that store requires, so
one shot yields a correctly sized asset per device. quiet() suppresses
notifications and badges so nothing lands over the window mid-capture.
A screenshot of a counter reading zero says nothing about the app, so the script drives the real buttons first. This is why the elements have ids.
Three rules that will save you an afternoon
Wait for conditions, not time. wait_for and wait_for_idle gate on the DOM.
A fixed delay is for presentation pacing only, and a capture that races the
bridge read will occasionally photograph an empty screen.
Pass explicit closures. attach and shots take a closure. A bare function
reference is coerced into a broken thunk and crashes when called.
No comments inside a chain. A comment between two chained calls terminates the chain. Put the explanation above the statement.
Check it before you rely on it
peko demo
peko demo counter
That builds in demo mode, launches the app, and runs the steps in order, printing each result. No images are produced. It is verifying that the scripts navigate, find the elements, and interact as written, which is the part that breaks when you rename a selector.