Skip to content

Values

video.values(...) injects localized strings into the page through the values fixture, for content the app does not localize itself. The values can be owned by code or handed to Editor (the web app where non-developers edit them); see the two ways to declare values below. Declare a values map per language, then read values.<field> in the body:

video.values({
en: { heading: 'Dashboard', cta: 'Get started' },
fi: { heading: 'Hallinta', cta: 'Aloita' },
})('Landing', async ({ page, values }) => {
await page.goto('/')
await page.getByTestId('heading').fill(values.heading)
await page.getByTestId('cta').fill(values.cta)
})

values and narration can be combined by chaining the two feature methods: video.narration({...}).values({...})('Landing', async ({ page, narration, values }) => {...}). The language set is the union of both. Unlike voice (which only re-renders), changing injected values changes what is captured, so it re-records.

Two ways to declare values

There are two ways to declare values, and both are editable in the web app. The same two forms apply to narration, overlays, and audio. See the Editor guide for how the web editing works.

1. Code values. You write the strings; they are used at record time. Changing them re-records. They stay editable in Editor, and an Editor edit wins over the code value from then on.

video.values({ en: { cta: 'Get started' }, fi: { cta: 'Aloita' } })

2. Editor-owned (blank). Pass a bare array of field names: the names exist in code (so the body can read values.cta), but Editor owns the strings. An unset field is the empty string until set in Editor.

import { video } from 'screenci'
video.values(['heading', 'cta'])

Either way the field stays editable in Editor: a code value is used at record time until it is edited in the web app, and from then on the Editor value wins.

Editor-managed values

A field can instead start blank and be filled in from ScreenCI Editor: pass a bare array of field names (no code value) and set its per-language value from the web. This suits copy that non-developers maintain without editing the test:

import { video } from 'screenci'
video.values(['heading', 'cta'])('Landing', async ({ page, values }) => {
await page.getByTestId('heading').fill(values.heading) // '' until set in Editor
await page.getByTestId('cta').fill(values.cta) // '' until set in Editor
})

To start the web app from code copy instead of blank fields, pass a plain object (content-major like { cta: 'Get started' }, or language-major like { en: { cta: 'Get started' }, fi: { cta: 'Aloita' } }): the code values are used until the field is edited in Editor, and from then on the Editor value wins.

The first recording reports the declared fields so Editor learns them. Because a blank field has no value yet and on-screen values are captured into the frames, the video’s first render is held until you set the values, just like blank narration, overlays, and audio. The CLI prints the hold with a direct link to Editor. Open the video’s Values section in Editor, set each language’s value, then re-record: screenci record fetches the current values and injects them before the run. Because on-screen values are captured into the recording (not re-rendered), Editor values always take effect on the next record, never on a one-off re-render.

A field declared with a code value (the object form below) carries its text, so it is not held: the recording renders straight away from the code value while staying editable in Editor.

Values in screenshots

screenshot.values supports the same localized values form. Each language produces its own localized still:

import { screenshot } from 'screenci'
screenshot.values({
en: { heading: 'Dashboard' },
fi: { heading: 'Hallinta' },
})('Dashboard hero', async ({ page, language, values, crop }) => {
await page.goto('/' + language + '/dashboard')
await page.getByTestId('heading').fill(values.heading)
await crop(page.getByTestId('revenue-card'), { padding: 0.06 })
})

For how values interact with multi-language recording passes, see Languages.