# Configuration

Consumer applications preferably configure the package with `resources/4-config/bdd-ui.config.json`.
The loader also accepts a root-level `bdd-ui.config.json` or a `BDD_UI_CONFIG_PATH` override, but the
recommended project structure is:

You can scaffold that structure with:

```bash
npx bdd-ui-init-resources
```

or include starter sample files with:

```bash
npx bdd-ui-init-resources --include-samples
```

```text
resources/
  1-payload/
  2-actual/
  3-expected/
  4-config/
  5-input/
  features/
```

The scaffolded `resources/4-config/bdd-ui.config.json` created by `npx bdd-ui-init-resources` includes a starter
`jsonComparison` section:

```json
{
  "jsonComparison": {
    "engine": "jar",
    "output": "json"
  }
}
```

Keep the structure flat inside each feature-name folder. For example:

```text
resources/features/
  login.feature
  extract-visible-grid.feature
  qte-acceptance-criteria.feature

resources/5-input/
  qte-acceptance-criteria/
    qte-ac-core-valid.json
    qte-ac-premium-valid.json
  qte-global-field-synchronization/
    qte-global-sync.json

resources/3-expected/
  extract-visible-grid/
    qte-grid-list.json
  qte-acceptance-criteria/
    qte-created.json
    qte-success.json

resources/2-actual/
  extract-visible-grid/
    qte-grid-list.json
  qte-acceptance-criteria/
    qte-created.json
```

```json
{
  "baseUrl": "https://qa.example.com",
  "browser": "chromium",
  "headless": true,
  "timeout": 30000,
  "trace": true,
  "screenshotOnFailure": true,
  "viewport": {
    "width": 1440,
    "height": 900
  },
  "resourcePaths": {
    "payload": "resources/1-payload",
    "actual": "resources/2-actual",
    "expected": "resources/3-expected",
    "config": "resources/4-config",
    "input": "resources/5-input",
    "features": "resources/features"
  },
  "grids": {
    "trade": "[data-testid=\"trade-grid\"]"
  },
  "selectors": {
    "login-form": "[data-test-id=\"login-form\"]",
    "qte-form": "[data-test-id=\"qte-form\"]",
    "open-full-ticket-button": "[data-test-id=\"open-full-ticket-button\"]"
  },
  "selectorSources": [
    "resources/4-config/selectors/**/*.json"
  ],
  "uiScanner": {
    "language": "react-tsx",
    "include": [".tsx", ".ts"],
    "exclude": [".test.", ".spec.", ".stories.", "node_modules"],
    "selectorOutput": {
      "mode": "split",
      "strategy": "by-folder",
      "generatedDir": "resources/4-config/selectors",
      "stripPathPrefixes": ["src"]
    }
  },
  "screenshots": {
    "expectedDir": "resources/3-expected/screenshots",
    "actualDir": "resources/2-actual/screenshots",
    "diffDir": "resources/2-actual/screenshots-diff",
    "mode": "compare",
    "threshold": 0.1,
    "maxDiffPixels": 0
  },
  "fieldVerification": {
    "Notional": {
      "mode": "normalized"
    },
    "Counterparty": {
      "mode": "contains"
    },
    "Premium BPS": {
      "mode": "pattern",
      "expectedPattern": "^\\d+\\.\\d{2}$"
    },
    "Trade Date": {
      "mode": "formatted",
      "expectedPattern": "^\\d{2}/\\d{2}/\\d{4}$"
    },
    "Internal Notes": {
      "mode": "none"
    }
  },
  "auth": {
    "loginPath": "/login",
    "successUrlContains": "/home"
  },
  "credentials": {
    "standardUser": {
      "username": "qa-user",
      "password": "secret"
    }
  }
}
```

The optional `grids` section maps readable grid names from feature files to stable selectors in the consumer app.
For example, `"trade"` can point to a `data-testid`, id, or another selector that resolves to the grid or a visible
container that contains the grid.

The `screenshotOnFailure` flag controls failure evidence capture. When enabled, the framework attaches a full-page
screenshot only for steps that fail. It does not attach screenshots for passing steps.

The optional `selectors` section maps readable names to stable selectors for non-grid elements. These aliases can be
used with existing visibility, enabled-state, button-click, checkbox, and scoped-form steps.

```gherkin
Then "qte-form" should be visible
When I click the "open-full-ticket-button" button
When I fill the form "qte-form" from "qte-ac-core-valid.json"
```

They also work with the newer custom-control steps:

```gherkin
When I choose "Ireland" from "country-dropdown"
When I scroll to element "roll-field"
When I open the "main-tab" tab
When I open the "main-tab" tab in "workspace-panel"
Then "country-dropdown" should have value "Ireland"
Then "country-dropdown" should contain value "Ire"
Then the "main-tab" tab should be active
```

The dropdown content assertion steps reuse the same alias support. For example:

```gherkin
Then the dropdown "country-dropdown" should contain exactly:
  | Ireland |
  | France  |
```

A consumer app with stable custom-control hooks can configure them like this:

```json
{
  "selectors": {
    "country-dropdown": "[data-test-id=\"country-dropdown\"]",
    "main-tab": "[data-test-id=\"main-tab\"]",
    "roll-field": "[data-test-id=\"ROLL\"]",
    "workspace-panel": "[data-test-id=\"workspace-panel\"]"
  }
}
```

For long-term maintainability, prefer selector aliases over hardcoded raw selectors in feature files. A selector alias
can point to a stable `data-test-id`, `data-testid`, `id`, class selector, or another supported locator:

```json
{
  "selectors": {
    "save-button": "[data-test-id=\"save-button\"]",
    "primary-cta": ".primary-cta",
    "main-tab": "[data-test-id=\"main-tab\"]"
  }
}
```

Then the same aliases can be reused across generic steps:

```gherkin
When I click "save-button"
When I click "primary-cta"
When I open the "main-tab" tab
When I open the "main-tab" tab in "workspace-panel"
Then "save-button" should be visible
```

The same `selectors` map can also provide panel/container aliases for scoped tab lookup when duplicate tab names appear
in different parts of the page.

The optional `selectorSources` section lets consumer projects split selector aliases across multiple JSON files instead
of keeping everything inside one large `selectors` block:

```json
{
  "selectorSources": [
    "resources/4-config/selectors/**/*.json"
  ]
}
```

Each matched file should contain a simple string-to-selector map:

```json
{
  "rfq-ticket-form": "[data-test-id=\"rfq-ticket-form\"]",
  "submit-rfq-button": "[data-test-id=\"submit-rfq-button\"]"
}
```

Selector catalogs are merged in this order:
- selector files from `selectorSources`
- inline `selectors` from `bdd-ui.config.json`

That keeps the feature backward compatible. Existing projects can stay fully inline, while larger projects can move
aliases into multiple files over time. When the same alias exists in both places, the inline `selectors` value wins.

If the same target appears more than once, use the indexed variants:

```gherkin
When I click ".primary-cta" at index 1
Then "Country" at index 1 should have value "Ireland"
```

The optional `uiScanner` section configures the selector generator CLI. Today the generator supports
`"language": "react-tsx"` and scans `.tsx` / `.ts` files for stable `data-test-id` and `data-testid` values.
Future languages can be added under the same config shape without changing the command interface.

`uiScanner.selectorOutput` controls how generated selector aliases are written:

```json
{
  "uiScanner": {
    "selectorOutput": {
      "mode": "split",
      "strategy": "by-folder",
      "generatedDir": "resources/4-config/selectors",
      "stripPathPrefixes": ["src"]
    }
  }
}
```

Supported options:
- `mode`
  - `inline`: keep the previous behavior and merge generated selectors into `selectors` inside `bdd-ui.config.json`
  - `split`: write generated selector catalogs into multiple JSON files under `generatedDir`
- `strategy`
  - `single-file`: write one file named `generated.json`
  - `by-folder`: write one file per source folder
  - `by-file`: write one file per source file
- `generatedDir`
  - output folder for split selector catalogs
  - when the config lives under `resources/4-config/bdd-ui.config.json`, this path is resolved from the consumer
    project root, so `resources/4-config/selectors` lands beside the config as expected
- `stripPathPrefixes`
  - optional list of leading folder segments to remove before grouping output files

Examples for a UI source file `src/pages/rfq/Ticket.tsx`:
- `by-folder` with `stripPathPrefixes: ["src"]` writes `resources/4-config/selectors/pages/rfq.json`
- `by-file` with `stripPathPrefixes: ["src"]` writes `resources/4-config/selectors/pages/rfq/Ticket.json`
- `single-file` writes `resources/4-config/selectors/generated.json`

When split output is enabled, generated selector files currently contain selector aliases only. Grid aliases still stay
in the main `grids` section of `bdd-ui.config.json`.

The optional `resourcePaths` section controls where feature-scoped resources are resolved from:

```json
{
  "resourcePaths": {
    "payload": "resources/1-payload",
    "actual": "resources/2-actual",
    "expected": "resources/3-expected",
    "config": "resources/4-config",
    "input": "resources/5-input",
    "features": "resources/features"
  }
}
```

These roots are intended to mirror the API automation project structure:
- `1-payload` for request or event payloads
- `2-actual` for generated artifacts
- `3-expected` for baselines
- `4-config` for configuration
- `5-input` for UI test inputs such as form JSON
- `features` for feature files

File-based UI steps resolve relative to the appropriate root and the current feature name. For example:

```gherkin
When I fill the form from "qte-global-sync.json"
```

resolves under:

```text
resources/5-input/<feature-name>/qte-global-sync.json
```

The optional `viewport` section controls the browser context viewport. If the consumer project does not set a viewport,
the framework defaults to:

```json
{
  "viewport": {
    "width": 1440,
    "height": 900
  }
}
```

This keeps visual behavior stable across CI and local runs unless a consumer explicitly opts into another viewport.

The optional `screenshots` section controls screenshot baseline and artifact folders:

```json
{
  "screenshots": {
    "expectedDir": "resources/3-expected/screenshots",
    "actualDir": "resources/2-actual/screenshots",
    "diffDir": "resources/2-actual/screenshots-diff",
    "mode": "compare",
    "threshold": 0.1,
    "maxDiffPixels": 0
  }
}
```

These directories are intended for nested relative screenshot paths. For example, a logical screenshot id such as
`home/blotter/trade-grid` resolves to:

```text
resources/3-expected/screenshots/home/blotter/trade-grid.png
resources/2-actual/screenshots/home/blotter/trade-grid.png
resources/2-actual/screenshots-diff/home/blotter/trade-grid.png
```

The screenshot runtime rejects absolute paths and `..` traversal, creates nested folders automatically, and supports
alias-based masking through the `selectors` map. Screenshot-compare steps attach the actual screenshot to the
Cucumber/Allure report and attach the diff image when a mismatch generates one.

The optional `jsonComparison` section defines how the planned backend response comparison integration should resolve
the comparator runtime:

```json
{
  "jsonComparison": {
    "engine": "jar",
    "output": "json"
  }
}
```

The clean design assumes the comparator JAR is bundled inside the published `@genesislcap/bdd-ui-automation`
package. In that mode, consumer projects do not need Gradle or a populated `.m2` cache at runtime. The framework
should resolve the comparator in this order:
- `jsonComparison.jarPath` when a consumer explicitly overrides it
- the bundled package path for the comparator JAR
- fail clearly if neither exists

An explicit override can still be provided when needed:

```json
{
  "jsonComparison": {
    "engine": "jar",
    "jarPath": "tools/custom-bdd-comparator.jar",
    "output": "json"
  }
}
```

The bundled comparator CLI supports these options for JSON comparison:
- `--expected <path>`
- `--actual <path>`
- `--output <text|json>`
- `--parent-object <nameOrPath>`
- `--primary-key <field[,field2]>`
- `--ignore <field[,field2]>`
- `--fields <field[,field2]>`
- `--threshold-config <path>`
- `--health-check`
- `--fail-on-extra <true|false>`

When `--fields` is used, keep the selected fields within the same parent context. For nested JSON, the recommended
shape is to pair `for specific fields` with `with Parent Object "..."`, and optionally `with Primary Key "..."` when
the nested data is record-oriented.

The planned UI response-comparison steps use those options to compare:
- expected files from `resources/3-expected/<feature-name>/<file>`
- actual captured responses written to `resources/2-actual/<feature-name>/<file>`

The intended runtime behavior is:
- write the captured response JSON before invoking the comparator
- delete the actual file when comparison passes
- keep the actual file when comparison fails
- surface comparator exit codes clearly in the Cucumber failure

Comparator exit codes are:
- `0` pass
- `1` comparison failed
- `2` extra-only differences when `--fail-on-extra true`
- `3` invalid CLI usage
- `4` input missing or unreadable
- `5` internal comparator error

The optional `fieldVerification` section controls how the framework validates readback after `fill` steps and JSON
form fills:

```json
{
  "fieldVerification": {
    "Notional": {
      "mode": "normalized"
    },
    "Counterparty": {
      "mode": "contains"
    },
    "Premium BPS": {
      "mode": "pattern",
      "expectedPattern": "^\\d+\\.\\d{2}$"
    },
    "Trade Date": {
      "mode": "formatted",
      "expectedPattern": "^\\d{2}/\\d{2}/\\d{4}$"
    },
    "Internal Notes": {
      "mode": "none"
    }
  }
}
```

Supported modes are:
- default exact verification when the field is not listed
- `normalized` for values like `1000` vs `1,000`
- `contains` when the UI expands the value and the expected input should appear within it
- `pattern` for regex-based verification; requires `expectedPattern`
- `formatted` for transformed display values validated by regex; requires `expectedPattern`
- `none` to skip readback verification for a field entirely

Generate selectors with:

```text
npx bdd-ui-init-selectors --ui-path "<path-to-ui-project>" --config-path "<path-to-consumer>\\resources\\4-config\\bdd-ui.config.json" --write
```

For consumers using the recommended numbered `resources/...` layout, `--config-path` should usually point at:

```text
resources/4-config/bdd-ui.config.json
```

If `--config-path` is omitted, the command defaults to updating the current project's root-level
`bdd-ui.config.json`. When the target config already exists and `--write` is used, the command creates a timestamped
backup before writing the merged config.

With split selector output enabled, the command also writes generated selector JSON files under
`uiScanner.selectorOutput.generatedDir`. The main config keeps `grids` and scanner settings, while `selectorSources`
loads the generated selector catalogs back at runtime.

Environment variables override file values:

```text
BDD_UI_BASE_URL
BDD_UI_BROWSER
BDD_UI_HEADLESS
BDD_UI_TIMEOUT
BDD_UI_TRACE
BDD_UI_SCREENSHOT_ON_FAILURE
```

Credentials are referenced from feature files by profile and field:

```gherkin
When I fill "Username" with credential "standardUser.username"
When I fill "Password" with credential "standardUser.password"
Given I log in as "standardUser"
```

Do not commit real passwords or secrets in `bdd-ui.config.json`. Consumer applications should inject secrets through their pipeline or secret-management tooling.
