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:
| Placeholder | Expands 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.
.envCopy .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}} |
Related pages
- Editing data files — how to safely change values in the base data files.
- Per-environment overrides — override a value for a specific environment using dotted-path keys.
- Test data — the conceptual overview of how data connects to specs.