Credentials in UI Tests
UI tests log into the application under test using the {{username}}/{{password}}
placeholders shown in the YAML format page. This guide explains
where those values come from and how to control them when running a test.
Resolution chain
When you trigger a test (POST /api/ui-tests/{id}/run), the backend resolves credentials
in this order — first match wins:
| Priority | Source | When used |
|---|---|---|
| 1 | inlineCredentials in the run request | A wizard or one-off run passing a literal {username, password} map |
| 2 | credentialTag matching auth_config.credentials[].tag on the environment | Pinned to a specific saved credential |
| 3 | Environment-level auth_config.defaultCredentialTag | A team default set on the environment |
| 4 | Any credential with isDefault: true in auth_config.credentials[] | Sane fallback for self-test / regression projects |
If none of those resolve, no credential is injected. There's intentionally no "first-active-credential" last-resort fallback — silently picking an arbitrary credential is the kind of "convenient" behaviour that lets a misconfigured test pass against the wrong user.
If none of those resolve and the test YAML contains {{username}}, {{password}},
{{token}}, or {{apiKey}}, the request is rejected with HTTP 400 before any
job is dispatched — no doomed run, no misleading green steps that "passed" because
they typed {{username}} literally into the login form.
Configuring credentials on an environment
Credentials live in the environment's auth_config.credentials[] array. Each entry has:
{
"id": "9a20e1b5-...",
"tag": "admin",
"username": "admin",
"password": "...",
"token": null,
"apiKeyValue": null,
"isDefault": true,
"isActive": true,
"description": "Platform admin credential"
}
Add or edit entries under Settings → Environments → <env> → Auth in the UI, or via
POST /api/environments/{environmentId}/auth-config/credentials.
Mark exactly one entry as isDefault: true — that's the credential the API and any
in-app wizard will pick when no explicit tag is supplied.
Triggering a test
From the wizard (default flow)
The UI wizard automatically attaches the environment's default credential. You don't have to think about it — pick the environment, pick the test, hit Run.
From the REST API
TOKEN=... # JWT from /api/auth/login
# 1. Use the env's default credential (whatever isDefault=true points to)
curl -X POST https://app.proofarc.ai/api/ui-tests/70/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target": "https://app.proofarc.ai",
"environmentId": 7,
"browsers": ["chrome"]
}'
# 2. Pin to a specific saved credential
curl -X POST https://app.proofarc.ai/api/ui-tests/70/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target": "https://app.proofarc.ai",
"environmentId": 7,
"credentialTag": "regression-test",
"browsers": ["chrome"]
}'
# 3. One-off inline credentials (not stored)
curl -X POST https://app.proofarc.ai/api/ui-tests/70/run \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"target": "https://app.proofarc.ai",
"environmentId": 7,
"inlineCredentials": {"username": "qa-user", "password": "..."},
"browsers": ["chrome"]
}'
What happens on the wire
The resolved credential becomes the credentials map on the dispatched job:
{
"yamlContent": "steps: …",
"testId": 70,
"browser": "chrome",
"credentials": {
"username": "admin",
"password": "admin123"
}
}
The UI Test Agent then substitutes {{username}} / {{password}} (and the alternative
${username} / ${password} syntax) using Apache Commons Text's StringSubstitutor
before each SEND_KEYS action.
Troubleshooting
Symptom: Login screen shows the literal string {{username}} in the username field
and a "Login failed" banner; subsequent steps cascade-skip.
Cause: No credentials were resolvable at dispatch time and the validation guard
was bypassed (older builds), or the test YAML uses a placeholder name not in
{username, password, token, apiKey} (e.g. {{adminUser}}) — the guard won't catch
custom names.
Fix: Either (a) mark a credential isDefault: true on the environment, (b) pass
credentialTag explicitly, or (c) use placeholder names from the supported set.