# viddik

> Screenshot tests that render through a real Compose Desktop window instead of LayoutLib, so goldens hold across macOS, Linux and Windows.

Page: https://kotlin.website/viddik

## Why it exists

Android has Paparazzi, which renders through LayoutLib. Compose Multiplatform does not, because
LayoutLib is Android's renderer and the code under test is not Android code. viddik renders
through a real Compose Desktop window on Skiko, on a plain JVM — the same renderer that will
actually draw the component.

## One annotation, two outputs

```kotlin
@ViddikScreenshot
@Composable
fun EmptyBasket() = BasketScreen(state = Basket.empty)
```

A KSP processor collects every annotated composable into a registry, and that registry is used
twice: `ViddikEngine` captures each one to PNG and diffs it against the golden as a JUnit 5
test, and `ViddikShowroom` renders the **same** registry as a live component gallery. One set
of declarations, so the browser cannot drift from the tests.

## Fixtures can be declared with `@Preview`

Since 0.3.0 `@ViddikScreenshot` also works as a bare marker, with the details read off
`@Preview` — the same annotation the IDE preview pane and Android's own screenshot tooling
read, since Compose Multiplatform 1.12 ships it in `commonMain` under Android's
fully-qualified name.

```kotlin
@ViddikScreenshot
@PreviewWrapper(AppPreviewTheme::class)
@PreviewLightDark
@Composable
fun PrimaryButton() { ... }
```

`name`, `group`, `widthDp`, `heightDp`, `uiMode`, `fontScale`, the size in a `spec:` device,
repeated and multipreview annotations, and `@PreviewWrapper` are all read. Bare `@Preview` is
not scanned: `@ViddikScreenshot` stays the opt-in, because most previews in an application
exist for the IDE and cannot render headless.

## Install

```kotlin
// build.gradle.kts of the module holding the fixtures
plugins {
    id("com.google.devtools.ksp") version "<KSP_VERSION>"
    id("io.github.youndie.viddik") version "<VERSION>"
}
```

The plugin adds the artefacts, puts the processor on the right `ksp*` configuration for your
module's shape — `kspDesktopTest`, `kspJvmTest` or `kspTest`, and the per-target coordinates
where a non-KMP module needs them — and registers `viddikRecord`, `viddikVerify`,
`viddikDesignParity` and `viddikShowroom`. Wiring that by hand is still possible with
`viddik { addDependencies = false }`, but getting the configuration name wrong produces **no
error**, only zero generated tests, which is the reason the plugin exists.

From 0.4.0 viddik is on Maven Central as `io.github.youndie.viddik`, so no repository has to be
declared for it. Up to 0.3.3 it was `ru.workinprogress` on the snapshot server, and those versions
stay there.

0.2.x through 0.5.x require Compose Multiplatform 1.12.x; 0.1.x is the 1.11.x line.

## The browser runs on the phone too

Since 0.5.0 the same registry opens in an Android activity and an iOS view controller, not only in
the desktop window — a component library is judged on a phone, and a browser that opens on the
machine that ran the build and nowhere else is half a browser.

```kotlin
viddik { showroomTargets = true }
```

```kotlin
class ShowroomActivity : ViddikShowroomActivity() {
    override val components = GeneratedViddikRegistry.components
}
```

The flag is what moves the generated registry out of the test source set — which is never compiled
into an application — and into `commonMain`, so it exists for every target the module has. The
hosts come from `viddik-showroom`, the goldens keep working unchanged, and the list has a search
field over it. The registry is passed in rather than looked up, so a fixture that stopped compiling
is a build error instead of an empty gallery at launch.

## And a fixture can be measured against its design

Goldens answer "did the rendering change". `viddikDesignParity`, also 0.5.0, answers the question
that comes first while a screen is being built: how far it is from the design it was built to. Put
the exported PNG beside the golden under `design/`, and the task reports a percentage per fixture
— reports rather than fails, because a component library rarely has a reference for every state,
and a number to work from is worth more than a red build. `designStrict` turns it into a gate for
the module where matching the design is the acceptance criterion.

## Why goldens travel

Because capture goes through Compose Desktop rather than a platform toolkit, the same golden
holds on macOS, Linux and Windows — which is what makes goldens reviewable in a pull request
instead of noise every time CI changes machines.
