# QKForce Framework CWQ The QKForce browser-automation template is the fixed `TypeScript + Cucumber + WebdriverIO + QKTestAnalytics` profile. ## Contract status CWQ consumes the central v1.1 run config and optional package-local version-locked CSV inputs before WDIO starts. It does not create packages or receive results; those are API/Client responsibilities. The remaining documentation/operational concern is a verifiable required CI check, tracked in [CWQ #6](https://github.com/QACGBDT/QKForce-Framework-CWQ/issues/6). ## Run ```bash npm ci npm run qkforce:test npm run qkforce:report ``` `qkforce:test` is the only execution entry point used by QKForce Client. Results belong under `qreport-results/`; raw test-data CSV files must not be copied into results archives. ## Immutable run configuration Every v1.1 workspace contains `qkforce.config.json` with numeric `schemaVersion: 1`, fixed profile id `qkforce-wdio-cucumber-qkta`, and `run.targetUrl`. The loader rejects unknown fields, non-fixed profiles, credentials/query/fragment URLs, non-HTTPS non-loopback targets, and target URLs over 2048 UTF-8 bytes. ```json { "schemaVersion": 1, "profile": { "id": "qkforce-wdio-cucumber-qkta", "language": "typescript", "bdd": "cucumber", "runner": "webdriverio", "adapter": "webdriverio-cucumber-node", "reporter": "qk-test-analytics" }, "run": { "targetUrl": "https://example.test" } } ``` ## Version-locked CSV test data When test data is bound, QKForce materializes only immutable selected CSV versions below `test-data/` and writes `qkforce.test-data.lock.json`. No lock is required for executions without bound test data. The framework preflights every present lock before WDIO starts and never fetches live data. Validation covers schema version, unknown/duplicate metadata, safe package-local `test-data/*.csv` paths, regular-file/symlink checks, SHA-256, UTF-8, RFC-4180 quoting, unique headers and declared row count. Use the typed step-definition helper: ```ts import { Given } from '@wdio/cucumber-framework'; import { loadTestData } from '../support/test-data.js'; const testData = loadTestData(); Given('I use customer {string}', async (id: string) => { const customer = testData.dataset('customers').first({ id }); await $('#username').setValue(customer.name); }); ``` Rows are immutable and scenario selection is never stored in global mutable state. Use `row(index)`, `where({...})` or `first({...})` for deterministic addressing. `typedRow(index, schema)` supports `string`, `integer`, `number`, `boolean` (`true`/`false`) and ISO `date` (`YYYY-MM-DD`); `{ type, nullable: true }` maps an empty cell to `null`. Diagnostics identify dataset/version, row and column without printing cell contents. `provenance()` returns only `testDataLockSha256` and `{datasetId, versionId, sha256}` entries, matching the QKForce results-manifest contract without raw CSV values. Starter positive/negative fixtures live under `test/fixtures/test-data/`. Execution-specific CSV files are generated by QKForce and should not be hand-edited in the template. Run non-browser validation with: ```bash npm run check ``` Active development is performed on `develop`.