Skip to main content

Data references & placeholders

Some values in the data files are not literal strings — they are placeholders that get replaced at runtime. This page explains what each placeholder does and when you would use one.

The {{…}} syntax

Any value wrapped in double curly braces is a placeholder. The data resolver expands it before handing the value to a test. There are four kinds:

PlaceholderExpands to
{{env:VARIABLE_NAME}}The value of an environment variable from .env
{{timestamp:short}}Milliseconds since the Unix epoch (unique per test run)
{{timestamp}}Full ISO 8601 timestamp (e.g. 2026-06-29T14:35:02.123Z)
{{date:today}}Today's date formatted as DD-Mon-YY (e.g. 29-Jun-26)
{{date:start_of_month}}The first day of the current month
{{date:start_of_month +6m}}The first day of a month six months from now (offsets: d, m, y)

{{env:…}} — reading from .env

The .env file holds your login credentials. It is never committed to the repo because it contains a real password. The data files reference credentials indirectly via this placeholder:

// tests/data/common.jsonc — exact file content
{
"$schema": "../schemas/generated/common.schema.json",

"common": {
"credentials": {
"standard_user": {
"username": "{{env:TEST_USERNAME}}",
"password": "{{env:TEST_PASSWORD}}",
},
},

"urls": {
"portfolio_dashboard": "/portfolio/dashboard",
"projects_list": "/projects/portfolio-projects",
},

"timeouts": {
"default_ms": 30000,
"short_ms": 10000,
"long_ms": 60000,
},
},
}

When a test loads common.credentials.standard_user, the resolver replaces {{env:TEST_USERNAME}} with whatever value you set in .env. The credentials file is safe to commit because no real password is ever written in it.

How to set up .env

Copy .env.sample to .env and fill in your credentials:

cp .env.sample .env

Then edit .env:

TEST_USERNAME=your.email@example.com
TEST_PASSWORD=yourpassword

See Environments & .env for the full list of .env variables.

When would you add a new {{env:…}} placeholder?

Only when the value must not be stored in the repo at all — typically a secret. For non-secret values that differ per environment, use a per-environment override instead.


{{timestamp:short}} — unique values per run

Some tests create something in PACE (a project, an estimate, a change order). If you hard-code a name like "Test Project", two tests running at the same time can collide, and re-running a test against a real environment can fail because the name already exists.

{{timestamp:short}} expands to the number of milliseconds since the Unix epoch at the moment the test starts — a number like 1751206502123. Because it changes every millisecond, every test run gets a name that has never been used before.

You can see it in tests/data/projects.jsonc:

"from_other_project": {
// …
"form": {
"project_long_name": "test-at-kk-{{timestamp:short}}",
// …
},
},

At runtime, project_long_name becomes something like "test-at-kk-1751206502123".

{{timestamp}} (without :short) gives the full ISO timestamp string instead. Use :short when the value appears in a UI field (shorter, no colons or periods that might confuse a form), and the full form when you need a sortable, readable date-time string in a log or report field.

When would you add {{timestamp:short}}?

Whenever a test creates a named record and you want to avoid conflicts between parallel runs or repeated executions. A good rule of thumb: if you are adding a project_long_name, an estimate name, or any other "new record" name to a data file, use this placeholder.


{{date:…}} — calendar-relative dates

Date fields in PACE forms often need realistic dates — not far in the future, and not in the past. The {{date:…}} family generates dates relative to when the test runs:

"sch_start_date": "{{date:start_of_month}}",
"sch_end_date": "{{date:start_of_month +24m}}",

This sets the start date to the first of the current month, and the end date to 24 months later. Offsets can be in days (d), months (m), or years (y).

When would you use {{date:…}}?

When a form field requires a date that must be valid relative to today. Avoid hard-coding a literal date like "01-Jan-25" — it will become stale as time passes and may cause validation errors.


How a spec connects to data

A spec file lists the data it needs in its frontmatter data: block:

data:
- common.credentials.standard_user
- team.account_manager_smith
- projects.default

Each entry is a dotted path: domain → entry (→ sub-field, if needed). The data resolver loads the matching file, applies any environment override, expands all placeholders, and hands the resolved object to the test.

Inside the step list, fields are referenced by name:

4. Fill the Role dropdown
- method: `grid.selectCellDropdownOption('role_id', rowIndex, member.role_id)`

member.role_id resolves to whatever "role_id" is in team.account_manager_smith — in this case "Account Manager". Change the data file and the spec automatically uses the new value without touching a single test step.


Quick reference: which placeholder to use

You need…Use
A secret (password, token) from .env{{env:VARIABLE_NAME}}
A unique name for a record this test will create{{timestamp:short}}
A readable date-time string{{timestamp}}
A form date relative to today{{date:today}}
A form date relative to the start of a month{{date:start_of_month}}
A future date N months/years from now{{date:start_of_month +Nm}}