@solidxai/core 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/run-tests.command.d.ts +37 -0
- package/dist/commands/run-tests.command.d.ts.map +1 -0
- package/dist/commands/run-tests.command.js +345 -0
- package/dist/commands/run-tests.command.js.map +1 -0
- package/dist/commands/test-data.command.d.ts +6 -6
- package/dist/commands/test-data.command.d.ts.map +1 -1
- package/dist/commands/test-data.command.js +25 -25
- package/dist/commands/test-data.command.js.map +1 -1
- package/dist/commands/test.command.d.ts +5 -0
- package/dist/commands/test.command.d.ts.map +1 -0
- package/dist/commands/test.command.js +26 -0
- package/dist/commands/test.command.js.map +1 -0
- package/dist/controllers/service.controller.d.ts +0 -9
- package/dist/controllers/service.controller.d.ts.map +1 -1
- package/dist/controllers/service.controller.js +0 -45
- package/dist/controllers/service.controller.js.map +1 -1
- package/dist/dtos/basic-filters.dto.d.ts.map +1 -1
- package/dist/dtos/basic-filters.dto.js.map +1 -1
- package/dist/dtos/create-user.dto.d.ts +1 -0
- package/dist/dtos/create-user.dto.d.ts.map +1 -1
- package/dist/dtos/create-user.dto.js +2 -1
- package/dist/dtos/create-user.dto.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +1 -20
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/seeders/module-test-data.service.d.ts.map +1 -1
- package/dist/seeders/module-test-data.service.js +3 -3
- package/dist/seeders/module-test-data.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts +2 -0
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js +18 -2
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/crud.service.d.ts.map +1 -1
- package/dist/services/crud.service.js.map +1 -1
- package/dist/services/queues/common.d.ts +3 -0
- package/dist/services/queues/common.d.ts.map +1 -0
- package/dist/services/queues/common.js +39 -0
- package/dist/services/queues/common.js.map +1 -0
- package/dist/services/queues/database-publisher.service.d.ts.map +1 -1
- package/dist/services/queues/database-publisher.service.js +3 -1
- package/dist/services/queues/database-publisher.service.js.map +1 -1
- package/dist/services/queues/database-subscriber.service.d.ts.map +1 -1
- package/dist/services/queues/database-subscriber.service.js +5 -2
- package/dist/services/queues/database-subscriber.service.js.map +1 -1
- package/dist/services/queues/rabbitmq-publisher.service.d.ts.map +1 -1
- package/dist/services/queues/rabbitmq-publisher.service.js +13 -6
- package/dist/services/queues/rabbitmq-publisher.service.js.map +1 -1
- package/dist/services/queues/rabbitmq-subscriber.service.d.ts.map +1 -1
- package/dist/services/queues/rabbitmq-subscriber.service.js +9 -5
- package/dist/services/queues/rabbitmq-subscriber.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +4 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/testing/__examples__/register-example-specs.d.ts +3 -0
- package/dist/testing/__examples__/register-example-specs.d.ts.map +1 -0
- package/dist/testing/__examples__/register-example-specs.js +8 -0
- package/dist/testing/__examples__/register-example-specs.js.map +1 -0
- package/dist/testing/__examples__/specs/custom-health.spec.d.ts +17 -0
- package/dist/testing/__examples__/specs/custom-health.spec.d.ts.map +1 -0
- package/dist/testing/__examples__/specs/custom-health.spec.js +30 -0
- package/dist/testing/__examples__/specs/custom-health.spec.js.map +1 -0
- package/dist/testing/adapters/api/api-adapter.d.ts +9 -0
- package/dist/testing/adapters/api/api-adapter.d.ts.map +1 -0
- package/dist/testing/adapters/api/api-adapter.js +76 -0
- package/dist/testing/adapters/api/api-adapter.js.map +1 -0
- package/dist/testing/adapters/api/api.types.d.ts +14 -0
- package/dist/testing/adapters/api/api.types.d.ts.map +1 -0
- package/dist/testing/adapters/api/api.types.js +3 -0
- package/dist/testing/adapters/api/api.types.js.map +1 -0
- package/dist/testing/adapters/ui/playwright-adapter.d.ts +14 -0
- package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -0
- package/dist/testing/adapters/ui/playwright-adapter.js +47 -0
- package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -0
- package/dist/testing/adapters/ui/ui.types.d.ts +5 -0
- package/dist/testing/adapters/ui/ui.types.d.ts.map +1 -0
- package/dist/testing/adapters/ui/ui.types.js +3 -0
- package/dist/testing/adapters/ui/ui.types.js.map +1 -0
- package/dist/testing/contracts/runtime-context.types.d.ts +35 -0
- package/dist/testing/contracts/runtime-context.types.d.ts.map +1 -0
- package/dist/testing/contracts/runtime-context.types.js +3 -0
- package/dist/testing/contracts/runtime-context.types.js.map +1 -0
- package/dist/testing/contracts/test-spec.types.d.ts +21 -0
- package/dist/testing/contracts/test-spec.types.d.ts.map +1 -0
- package/dist/testing/contracts/test-spec.types.js +3 -0
- package/dist/testing/contracts/test-spec.types.js.map +1 -0
- package/dist/testing/contracts/testing-metadata.types.d.ts +41 -0
- package/dist/testing/contracts/testing-metadata.types.d.ts.map +1 -0
- package/dist/testing/contracts/testing-metadata.types.js +3 -0
- package/dist/testing/contracts/testing-metadata.types.js.map +1 -0
- package/dist/testing/core/interpolation.d.ts +4 -0
- package/dist/testing/core/interpolation.d.ts.map +1 -0
- package/dist/testing/core/interpolation.js +180 -0
- package/dist/testing/core/interpolation.js.map +1 -0
- package/dist/testing/core/normalize-steps.d.ts +7 -0
- package/dist/testing/core/normalize-steps.d.ts.map +1 -0
- package/dist/testing/core/normalize-steps.js +20 -0
- package/dist/testing/core/normalize-steps.js.map +1 -0
- package/dist/testing/core/resource-store.d.ts +8 -0
- package/dist/testing/core/resource-store.d.ts.map +1 -0
- package/dist/testing/core/resource-store.js +41 -0
- package/dist/testing/core/resource-store.js.map +1 -0
- package/dist/testing/core/spec-registry.d.ts +10 -0
- package/dist/testing/core/spec-registry.d.ts.map +1 -0
- package/dist/testing/core/spec-registry.js +32 -0
- package/dist/testing/core/spec-registry.js.map +1 -0
- package/dist/testing/core/step-registry.d.ts +10 -0
- package/dist/testing/core/step-registry.d.ts.map +1 -0
- package/dist/testing/core/step-registry.js +26 -0
- package/dist/testing/core/step-registry.js.map +1 -0
- package/dist/testing/core/testing-engine.d.ts +14 -0
- package/dist/testing/core/testing-engine.d.ts.map +1 -0
- package/dist/testing/core/testing-engine.js +97 -0
- package/dist/testing/core/testing-engine.js.map +1 -0
- package/dist/testing/core/timeout.d.ts +2 -0
- package/dist/testing/core/timeout.d.ts.map +1 -0
- package/dist/testing/core/timeout.js +18 -0
- package/dist/testing/core/timeout.js.map +1 -0
- package/dist/testing/reporter/attachments.d.ts +4 -0
- package/dist/testing/reporter/attachments.d.ts.map +1 -0
- package/dist/testing/reporter/attachments.js +25 -0
- package/dist/testing/reporter/attachments.js.map +1 -0
- package/dist/testing/reporter/console-reporter.d.ts +45 -0
- package/dist/testing/reporter/console-reporter.d.ts.map +1 -0
- package/dist/testing/reporter/console-reporter.js +189 -0
- package/dist/testing/reporter/console-reporter.js.map +1 -0
- package/dist/testing/reporter/reporter.types.d.ts +37 -0
- package/dist/testing/reporter/reporter.types.d.ts.map +1 -0
- package/dist/testing/reporter/reporter.types.js +3 -0
- package/dist/testing/reporter/reporter.types.js.map +1 -0
- package/dist/testing/runner/lifecycle.d.ts +9 -0
- package/dist/testing/runner/lifecycle.d.ts.map +1 -0
- package/dist/testing/runner/lifecycle.js +33 -0
- package/dist/testing/runner/lifecycle.js.map +1 -0
- package/dist/testing/runner/run-from-metadata.d.ts +24 -0
- package/dist/testing/runner/run-from-metadata.d.ts.map +1 -0
- package/dist/testing/runner/run-from-metadata.js +70 -0
- package/dist/testing/runner/run-from-metadata.js.map +1 -0
- package/dist/testing/runner/scenario-filter.d.ts +9 -0
- package/dist/testing/runner/scenario-filter.d.ts.map +1 -0
- package/dist/testing/runner/scenario-filter.js +22 -0
- package/dist/testing/runner/scenario-filter.js.map +1 -0
- package/dist/testing/steps/api/auth.step.d.ts +3 -0
- package/dist/testing/steps/api/auth.step.d.ts.map +1 -0
- package/dist/testing/steps/api/auth.step.js +38 -0
- package/dist/testing/steps/api/auth.step.js.map +1 -0
- package/dist/testing/steps/api/index.d.ts +3 -0
- package/dist/testing/steps/api/index.d.ts.map +1 -0
- package/dist/testing/steps/api/index.js +10 -0
- package/dist/testing/steps/api/index.js.map +1 -0
- package/dist/testing/steps/api/request.step.d.ts +3 -0
- package/dist/testing/steps/api/request.step.d.ts.map +1 -0
- package/dist/testing/steps/api/request.step.js +281 -0
- package/dist/testing/steps/api/request.step.js.map +1 -0
- package/dist/testing/steps/assert/http.step.d.ts +3 -0
- package/dist/testing/steps/assert/http.step.d.ts.map +1 -0
- package/dist/testing/steps/assert/http.step.js +27 -0
- package/dist/testing/steps/assert/http.step.js.map +1 -0
- package/dist/testing/steps/assert/index.d.ts +3 -0
- package/dist/testing/steps/assert/index.d.ts.map +1 -0
- package/dist/testing/steps/assert/index.js +12 -0
- package/dist/testing/steps/assert/index.js.map +1 -0
- package/dist/testing/steps/assert/jsonpath.step.d.ts +3 -0
- package/dist/testing/steps/assert/jsonpath.step.d.ts.map +1 -0
- package/dist/testing/steps/assert/jsonpath.step.js +40 -0
- package/dist/testing/steps/assert/jsonpath.step.js.map +1 -0
- package/dist/testing/steps/assert/primitives.step.d.ts +3 -0
- package/dist/testing/steps/assert/primitives.step.d.ts.map +1 -0
- package/dist/testing/steps/assert/primitives.step.js +43 -0
- package/dist/testing/steps/assert/primitives.step.js.map +1 -0
- package/dist/testing/steps/test/index.d.ts +3 -0
- package/dist/testing/steps/test/index.d.ts.map +1 -0
- package/dist/testing/steps/test/index.js +8 -0
- package/dist/testing/steps/test/index.js.map +1 -0
- package/dist/testing/steps/test/test-spec.step.d.ts +3 -0
- package/dist/testing/steps/test/test-spec.step.d.ts.map +1 -0
- package/dist/testing/steps/test/test-spec.step.js +41 -0
- package/dist/testing/steps/test/test-spec.step.js.map +1 -0
- package/dist/testing/steps/ui/actions.step.d.ts +3 -0
- package/dist/testing/steps/ui/actions.step.d.ts.map +1 -0
- package/dist/testing/steps/ui/actions.step.js +31 -0
- package/dist/testing/steps/ui/actions.step.js.map +1 -0
- package/dist/testing/steps/ui/assertions.step.d.ts +3 -0
- package/dist/testing/steps/ui/assertions.step.d.ts.map +1 -0
- package/dist/testing/steps/ui/assertions.step.js +41 -0
- package/dist/testing/steps/ui/assertions.step.js.map +1 -0
- package/dist/testing/steps/ui/form.step.d.ts +3 -0
- package/dist/testing/steps/ui/form.step.d.ts.map +1 -0
- package/dist/testing/steps/ui/form.step.js +34 -0
- package/dist/testing/steps/ui/form.step.js.map +1 -0
- package/dist/testing/steps/ui/index.d.ts +3 -0
- package/dist/testing/steps/ui/index.d.ts.map +1 -0
- package/dist/testing/steps/ui/index.js +14 -0
- package/dist/testing/steps/ui/index.js.map +1 -0
- package/dist/testing/steps/ui/navigation.step.d.ts +3 -0
- package/dist/testing/steps/ui/navigation.step.d.ts.map +1 -0
- package/dist/testing/steps/ui/navigation.step.js +39 -0
- package/dist/testing/steps/ui/navigation.step.js.map +1 -0
- package/dist/testing/steps/util/index.d.ts +3 -0
- package/dist/testing/steps/util/index.d.ts.map +1 -0
- package/dist/testing/steps/util/index.js +12 -0
- package/dist/testing/steps/util/index.js.map +1 -0
- package/dist/testing/steps/util/log.step.d.ts +3 -0
- package/dist/testing/steps/util/log.step.d.ts.map +1 -0
- package/dist/testing/steps/util/log.step.js +18 -0
- package/dist/testing/steps/util/log.step.js.map +1 -0
- package/dist/testing/steps/util/require.step.d.ts +3 -0
- package/dist/testing/steps/util/require.step.d.ts.map +1 -0
- package/dist/testing/steps/util/require.step.js +16 -0
- package/dist/testing/steps/util/require.step.js.map +1 -0
- package/dist/testing/steps/util/sleep.step.d.ts +3 -0
- package/dist/testing/steps/util/sleep.step.d.ts.map +1 -0
- package/dist/testing/steps/util/sleep.step.js +13 -0
- package/dist/testing/steps/util/sleep.step.js.map +1 -0
- package/docs/test-data-workflow.md +51 -11
- package/package.json +4 -2
- package/src/commands/run-tests.command.ts +278 -0
- package/src/commands/test-data.command.ts +26 -26
- package/src/commands/test.command.ts +14 -0
- package/src/controllers/service.controller.ts +58 -59
- package/src/dtos/basic-filters.dto.ts +0 -2
- package/src/dtos/create-user.dto.ts +1 -0
- package/src/index.ts +3 -0
- package/src/seeders/module-metadata-seeder.service.ts +3 -24
- package/src/seeders/module-test-data.service.ts +5 -3
- package/src/services/chatter-message.service.ts +18 -1
- package/src/services/crud.service.ts +1 -0
- package/src/services/queues/common.ts +75 -0
- package/src/services/queues/database-publisher.service.ts +4 -1
- package/src/services/queues/database-subscriber.service.ts +5 -3
- package/src/services/queues/rabbitmq-publisher.service.ts +17 -7
- package/src/services/queues/rabbitmq-subscriber.service.ts +9 -5
- package/src/solid-core.module.ts +4 -0
- package/src/testing/README.md +364 -0
- package/src/testing/__examples__/register-example-specs.ts +6 -0
- package/src/testing/__examples__/specs/custom-health.spec.ts +29 -0
- package/src/testing/__examples__/testing.sample.json +82 -0
- package/src/testing/adapters/api/api-adapter.ts +85 -0
- package/src/testing/adapters/api/api.types.ts +15 -0
- package/src/testing/adapters/ui/playwright-adapter.ts +54 -0
- package/src/testing/adapters/ui/ui.types.ts +4 -0
- package/src/testing/contracts/runtime-context.types.ts +36 -0
- package/src/testing/contracts/test-spec.types.ts +24 -0
- package/src/testing/contracts/testing-metadata.types.ts +46 -0
- package/src/testing/core/interpolation.ts +189 -0
- package/src/testing/core/normalize-steps.ts +21 -0
- package/src/testing/core/resource-store.ts +38 -0
- package/src/testing/core/spec-registry.ts +33 -0
- package/src/testing/core/step-registry.ts +27 -0
- package/src/testing/core/testing-engine.ts +127 -0
- package/src/testing/core/timeout.ts +19 -0
- package/src/testing/reporter/attachments.ts +25 -0
- package/src/testing/reporter/console-reporter.ts +229 -0
- package/src/testing/reporter/reporter.types.ts +36 -0
- package/src/testing/runner/lifecycle.ts +31 -0
- package/src/testing/runner/run-from-metadata.ts +87 -0
- package/src/testing/runner/scenario-filter.ts +33 -0
- package/src/testing/steps/api/auth.step.ts +66 -0
- package/src/testing/steps/api/index.ts +10 -0
- package/src/testing/steps/api/request.step.ts +358 -0
- package/src/testing/steps/assert/http.step.ts +33 -0
- package/src/testing/steps/assert/index.ts +12 -0
- package/src/testing/steps/assert/jsonpath.step.ts +50 -0
- package/src/testing/steps/assert/primitives.step.ts +69 -0
- package/src/testing/steps/test/index.ts +8 -0
- package/src/testing/steps/test/test-spec.step.ts +52 -0
- package/src/testing/steps/ui/actions.step.ts +36 -0
- package/src/testing/steps/ui/assertions.step.ts +54 -0
- package/src/testing/steps/ui/form.step.ts +39 -0
- package/src/testing/steps/ui/index.ts +12 -0
- package/src/testing/steps/ui/navigation.step.ts +53 -0
- package/src/testing/steps/util/index.ts +10 -0
- package/src/testing/steps/util/log.step.ts +19 -0
- package/src/testing/steps/util/require.step.ts +16 -0
- package/src/testing/steps/util/sleep.step.ts +15 -0
- package/tsconfig.json +35 -25
- package/tsconfig.tests.json +14 -0
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# Testing Module
|
|
2
|
+
|
|
3
|
+
## Folder Structure
|
|
4
|
+
- `contracts/`: metadata + runtime context types
|
|
5
|
+
- `core/`: engine, registry, interpolation, utilities
|
|
6
|
+
- `adapters/`: API (axios) and UI (Playwright) adapters
|
|
7
|
+
- `steps/`: step implementations grouped by domain
|
|
8
|
+
- `reporter/`: reporting interfaces + console reporter
|
|
9
|
+
- `runner/`: lifecycle helpers + metadata runner
|
|
10
|
+
|
|
11
|
+
## Metadata Shape
|
|
12
|
+
`TestingMetadata` lives under `testing` in module metadata JSON files.
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"testing": {
|
|
17
|
+
"specs": ["path/to/register-test-specs.js"],
|
|
18
|
+
"data": [
|
|
19
|
+
{
|
|
20
|
+
"modelUserKey": "stateMaster",
|
|
21
|
+
"recUserKeyValue": "Maharashtra",
|
|
22
|
+
"data": {
|
|
23
|
+
"name": "Maharashtra",
|
|
24
|
+
"description": "State of Maharashtra"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"scenarios": [
|
|
29
|
+
{
|
|
30
|
+
"id": "api-authenticate-success",
|
|
31
|
+
"name": "Authenticate succeeds",
|
|
32
|
+
"type": "api",
|
|
33
|
+
"params": {
|
|
34
|
+
"username": "alice"
|
|
35
|
+
},
|
|
36
|
+
"tags": ["smoke"],
|
|
37
|
+
"timeoutMs": 30000,
|
|
38
|
+
"retries": 1,
|
|
39
|
+
"steps": [
|
|
40
|
+
{ "given": { "op": "api.request", "with": { "method": "POST", "url": "..." } } }
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Step Shape
|
|
49
|
+
Steps can be written as Given/When/Then blocks or as flat ops.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
{
|
|
53
|
+
id: "login-happy-path",
|
|
54
|
+
type: "ui",
|
|
55
|
+
steps: [
|
|
56
|
+
{ given: { op: "ui.goto", with: { url: "/login" } } },
|
|
57
|
+
{ when: { op: "ui.fill", with: { selector: "#user", value: "alice" } } },
|
|
58
|
+
{ and: { op: "ui.fill", with: { selector: "#pass", value: "secret" } } },
|
|
59
|
+
{ then: { op: "ui.click", with: { selector: "button[type=submit]" } } }
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
General step fields:
|
|
65
|
+
- `op` (required): operation name
|
|
66
|
+
- `with` (optional): op-specific options
|
|
67
|
+
- `saveAs` (optional): store the return value in the resource store (read via `${res:...}`)
|
|
68
|
+
- `name` (optional): label for reporting
|
|
69
|
+
- `timeoutMs` (optional): per-step timeout override
|
|
70
|
+
- `spec` (optional): used by `test.spec` to identify the spec
|
|
71
|
+
|
|
72
|
+
## Interpolation
|
|
73
|
+
Available tokens:
|
|
74
|
+
- `${env:NAME}` (environment variables)
|
|
75
|
+
- `${params.foo}` (scenario params)
|
|
76
|
+
- `${res:saveAs.path}` (saved step results)
|
|
77
|
+
- `${data:modelUserKey["recUserKeyValue"].field}` (test data from `testing.data`)
|
|
78
|
+
- `${data:modelUserKey["recUserKeyValue"]._rec}` (raw record object when used alone)
|
|
79
|
+
|
|
80
|
+
Test data lookup details:
|
|
81
|
+
- Data is indexed as `data:<modelUserKey>["<recUserKeyValue>"]`.
|
|
82
|
+
- You can access fields with `.fieldName`.
|
|
83
|
+
- Bracket syntax is recommended for keys with spaces or punctuation.
|
|
84
|
+
- Use `._rec` to return the full object when the token is the entire value.
|
|
85
|
+
- `data.` is still supported for backward compatibility, but `data:` is preferred.
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"params": {
|
|
91
|
+
"state": "${data:stateMaster["Maharashtra"].name}"
|
|
92
|
+
},
|
|
93
|
+
"steps": [
|
|
94
|
+
{
|
|
95
|
+
"given": {
|
|
96
|
+
"op": "api.request",
|
|
97
|
+
"with": {
|
|
98
|
+
"method": "POST",
|
|
99
|
+
"url": "${env:API_BASE_URL}/api/example",
|
|
100
|
+
"json": {
|
|
101
|
+
"stateName": "${params.state}",
|
|
102
|
+
"city": "${data:cityMaster["New Delhi"].name}",
|
|
103
|
+
"cityRecord": "${data:cityMaster["New Delhi"]._rec}"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Ops Reference
|
|
113
|
+
|
|
114
|
+
### **Op: `api.request`**
|
|
115
|
+
Description: Executes an HTTP request using the API adapter.
|
|
116
|
+
|
|
117
|
+
Options in `with`:
|
|
118
|
+
- `method` (required)
|
|
119
|
+
- `url` (required)
|
|
120
|
+
- `headers` (optional)
|
|
121
|
+
- `json` (optional)
|
|
122
|
+
- `bodyText` (optional)
|
|
123
|
+
- `query` (optional, object or querystring)
|
|
124
|
+
- `formData` (optional, array or object)
|
|
125
|
+
- `body` (optional alias for `formData`)
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
- `status`
|
|
129
|
+
- `headers`
|
|
130
|
+
- `bodyText`
|
|
131
|
+
- `bodyJson` (when JSON is detected)
|
|
132
|
+
- `body` (alias for `bodyJson ?? bodyText`)
|
|
133
|
+
|
|
134
|
+
Notes:
|
|
135
|
+
- Sets `ctx.last.apiResponse` for `assert.httpStatus`.
|
|
136
|
+
- `formData` can be an array of items or a plain object.
|
|
137
|
+
- If `formData` is an object, keys become field names and values become field values.
|
|
138
|
+
- File values can be `file:/absolute/path` or `url:https://...` and will be uploaded as files.
|
|
139
|
+
- Non-string object values are JSON-stringified before sending.
|
|
140
|
+
|
|
141
|
+
Form data item notes:
|
|
142
|
+
- Array form items: `{ name, value, type?: "text" | "file", filename?, contentType? }`.
|
|
143
|
+
- File values can be `file:/absolute/path` or `url:https://...`.
|
|
144
|
+
- If `formData` is an object, keys become field names and values become field values.
|
|
145
|
+
- Use `formData`/`body` instead of `json` or `bodyText`.
|
|
146
|
+
|
|
147
|
+
Example (array form):
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"when": {
|
|
151
|
+
"op": "api.request",
|
|
152
|
+
"with": {
|
|
153
|
+
"method": "POST",
|
|
154
|
+
"url": "${env:API_BASE_URL}/api/lead",
|
|
155
|
+
"formData": [
|
|
156
|
+
{ "name": "venueUserKey", "value": "Kolhapur-Karvir" },
|
|
157
|
+
{ "name": "coupon", "type": "file", "value": "file:/abs/path/image.jpg" },
|
|
158
|
+
{ "name": "campaignUserKey", "value": "Campaign: Drawing Competition" }
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Example (object form from `testing.data`):
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"when": {
|
|
169
|
+
"op": "api.request",
|
|
170
|
+
"with": {
|
|
171
|
+
"method": "POST",
|
|
172
|
+
"url": "${env:API_BASE_URL}/api/lead",
|
|
173
|
+
"formData": "${data:lead[\"LeadWithFile\"]._rec}"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### **Op: `api.auth.bearerFromLogin`**
|
|
180
|
+
Description: Logs in and returns a bearer access token from the response.
|
|
181
|
+
|
|
182
|
+
Options in `with`:
|
|
183
|
+
- `url` (required)
|
|
184
|
+
- `username` (required)
|
|
185
|
+
- `password` (required)
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
- access token string
|
|
189
|
+
|
|
190
|
+
### **Op: `ui.goto`**
|
|
191
|
+
Description: Navigates the browser to a URL.
|
|
192
|
+
|
|
193
|
+
Options in `with`:
|
|
194
|
+
- `url` (required)
|
|
195
|
+
|
|
196
|
+
### **Op: `ui.expectUrl`**
|
|
197
|
+
Description: Asserts the current page URL.
|
|
198
|
+
|
|
199
|
+
Options in `with`:
|
|
200
|
+
- `equals` (optional)
|
|
201
|
+
- `contains` (optional)
|
|
202
|
+
|
|
203
|
+
### **Op: `ui.fill`**
|
|
204
|
+
Description: Fills an input or editable element.
|
|
205
|
+
|
|
206
|
+
Options in `with`:
|
|
207
|
+
- `selector` (required)
|
|
208
|
+
- `value` (required)
|
|
209
|
+
|
|
210
|
+
### **Op: `ui.select`**
|
|
211
|
+
Description: Selects an option in a select element.
|
|
212
|
+
|
|
213
|
+
Options in `with`:
|
|
214
|
+
- `selector` (required)
|
|
215
|
+
- `value` (required)
|
|
216
|
+
|
|
217
|
+
### **Op: `ui.click`**
|
|
218
|
+
Description: Clicks an element located by selector.
|
|
219
|
+
|
|
220
|
+
Options in `with`:
|
|
221
|
+
- `selector` (required)
|
|
222
|
+
|
|
223
|
+
### **Op: `ui.press`**
|
|
224
|
+
Description: Presses a keyboard key on a focused element.
|
|
225
|
+
|
|
226
|
+
Options in `with`:
|
|
227
|
+
- `selector` (required)
|
|
228
|
+
- `key` (required)
|
|
229
|
+
|
|
230
|
+
### **Op: `ui.expectVisible`**
|
|
231
|
+
Description: Waits for an element to be visible.
|
|
232
|
+
|
|
233
|
+
Options in `with`:
|
|
234
|
+
- `selector` (required)
|
|
235
|
+
|
|
236
|
+
### **Op: `ui.expectText`**
|
|
237
|
+
Description: Asserts the text content of an element.
|
|
238
|
+
|
|
239
|
+
Options in `with`:
|
|
240
|
+
- `selector` (required)
|
|
241
|
+
- `equals` (optional)
|
|
242
|
+
- `contains` (optional)
|
|
243
|
+
|
|
244
|
+
### **Op: `assert.equals`**
|
|
245
|
+
Description: Asserts strict equality between two values.
|
|
246
|
+
|
|
247
|
+
Options in `with`:
|
|
248
|
+
- `actual` (required)
|
|
249
|
+
- `expected` (required)
|
|
250
|
+
|
|
251
|
+
### **Op: `assert.contains`**
|
|
252
|
+
Description: Asserts a string contains a substring.
|
|
253
|
+
|
|
254
|
+
Options in `with`:
|
|
255
|
+
- `actual` (required)
|
|
256
|
+
- `expected` (required)
|
|
257
|
+
|
|
258
|
+
### **Op: `assert.matches`**
|
|
259
|
+
Description: Asserts a string matches a regex pattern.
|
|
260
|
+
|
|
261
|
+
Options in `with`:
|
|
262
|
+
- `actual` (required)
|
|
263
|
+
- `pattern` (required)
|
|
264
|
+
|
|
265
|
+
### **Op: `assert.httpStatus`**
|
|
266
|
+
Description: Asserts the HTTP status of the last API response (or a provided response).
|
|
267
|
+
|
|
268
|
+
Options in `with`:
|
|
269
|
+
- `is` (required)
|
|
270
|
+
- `from` (optional, defaults to `ctx.last.apiResponse`)
|
|
271
|
+
|
|
272
|
+
### **Op: `assert.jsonPath`**
|
|
273
|
+
Description: Asserts a JSONPath-resolved value equals an expected value.
|
|
274
|
+
|
|
275
|
+
Options in `with`:
|
|
276
|
+
- `from` (required)
|
|
277
|
+
- `path` (required)
|
|
278
|
+
- `equals` (required)
|
|
279
|
+
|
|
280
|
+
### **Op: `util.log`**
|
|
281
|
+
Description: Logs a message (and optional data) to the console.
|
|
282
|
+
|
|
283
|
+
Options in `with`:
|
|
284
|
+
- `message` (required)
|
|
285
|
+
- `data` (optional)
|
|
286
|
+
|
|
287
|
+
### **Op: `util.sleep`**
|
|
288
|
+
Description: Pauses execution for a fixed duration.
|
|
289
|
+
|
|
290
|
+
Options in `with`:
|
|
291
|
+
- `ms` (required)
|
|
292
|
+
|
|
293
|
+
### **Op: `util.require`**
|
|
294
|
+
Description: Fails the step if a required resource is missing.
|
|
295
|
+
|
|
296
|
+
Options in `with`:
|
|
297
|
+
- `resource` (required)
|
|
298
|
+
- `message` (optional)
|
|
299
|
+
|
|
300
|
+
### **Op: `test.spec`**
|
|
301
|
+
Description: Runs a custom test spec registered via testing.specs.
|
|
302
|
+
|
|
303
|
+
Options in `with`:
|
|
304
|
+
- `input` (optional, free-form object passed to the spec)
|
|
305
|
+
- `specId` (optional alternative to `step.spec`)
|
|
306
|
+
|
|
307
|
+
Step fields:
|
|
308
|
+
- `spec` (required unless `with.specId` is provided)
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
- `SolidTestSpecResult` (saved if `saveAs` is provided)
|
|
312
|
+
|
|
313
|
+
## Custom Specs (test.spec)
|
|
314
|
+
Purpose: escape hatch for fully custom test logic.
|
|
315
|
+
|
|
316
|
+
Step shape example:
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"when": {
|
|
320
|
+
"op": "test.spec",
|
|
321
|
+
"spec": "example.customHealth",
|
|
322
|
+
"with": { "input": { "url": "${env:API_BASE_URL}/health" } },
|
|
323
|
+
"saveAs": "custom.health"
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Register specs in the consuming project:
|
|
329
|
+
```ts
|
|
330
|
+
import { runFromMetadata } from "./runner/run-from-metadata";
|
|
331
|
+
|
|
332
|
+
await runFromMetadata({
|
|
333
|
+
// ...
|
|
334
|
+
specs: (specRegistry) => {
|
|
335
|
+
specRegistry.register("example.customHealth", () => new CustomHealthSpec());
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
When using `solidctl test run`, specs are loaded from `testing.specs` in module metadata.
|
|
341
|
+
Use `--skip-scenario-ids` to exclude scenarios by id (comma-separated).
|
|
342
|
+
Use `--print-api-logs` to print full API request/response details for `api.request` steps.
|
|
343
|
+
Use `--print-api-logs` to print full API request/response details for `api.request` steps.
|
|
344
|
+
|
|
345
|
+
## Run From Metadata
|
|
346
|
+
```ts
|
|
347
|
+
import { runFromMetadata } from "./runner/run-from-metadata";
|
|
348
|
+
import metadata from "./testing-metadata.json";
|
|
349
|
+
|
|
350
|
+
await runFromMetadata({
|
|
351
|
+
metadata,
|
|
352
|
+
includeTags: ["smoke"],
|
|
353
|
+
defaults: { timeoutMs: 30_000, retries: 1 },
|
|
354
|
+
api: { baseUrl: "https://api.example.com" },
|
|
355
|
+
ui: { baseUrl: "https://app.example.com", headless: true }
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Add A New Step (SOP)
|
|
360
|
+
1. Create a new `*.step.ts` in the right domain folder.
|
|
361
|
+
2. Implement a `registerXSteps(registry)` function and `registry.register("op.name", handler)`.
|
|
362
|
+
3. Validate required `step.with` fields and throw clear errors.
|
|
363
|
+
4. Use adapters via `ctx.api` / `ctx.ui` and update `ctx.last` when helpful.
|
|
364
|
+
5. Export and register it from the domain `index.ts`.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SpecRegistry } from "../core/spec-registry";
|
|
2
|
+
import { CustomHealthSpec } from "./specs/custom-health.spec";
|
|
3
|
+
|
|
4
|
+
export function registerExampleSpecs(specRegistry: SpecRegistry): void {
|
|
5
|
+
specRegistry.register("example.customHealth", () => new CustomHealthSpec());
|
|
6
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ISolidTestSpec, SolidTestSpecArgs } from "../../contracts/test-spec.types";
|
|
2
|
+
|
|
3
|
+
export class CustomHealthSpec implements ISolidTestSpec {
|
|
4
|
+
async run({ ctx, input }: SolidTestSpecArgs) {
|
|
5
|
+
if (!ctx.api) {
|
|
6
|
+
throw new Error("Missing API adapter on context for CustomHealthSpec");
|
|
7
|
+
}
|
|
8
|
+
const url = input?.url as string | undefined;
|
|
9
|
+
if (!url) {
|
|
10
|
+
throw new Error('Missing "url" in input for CustomHealthSpec');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const response = await ctx.api.http({ method: "GET", url });
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
ok: response.status === 200,
|
|
17
|
+
name: "Custom API health spec",
|
|
18
|
+
details: { status: response.status },
|
|
19
|
+
attachments: [
|
|
20
|
+
{
|
|
21
|
+
name: "health-response",
|
|
22
|
+
contentType: "application/json",
|
|
23
|
+
data: JSON.stringify(response, null, 2),
|
|
24
|
+
encoding: "utf8" as const,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"testing": {
|
|
3
|
+
"scenarios": [
|
|
4
|
+
{
|
|
5
|
+
"id": "api-health",
|
|
6
|
+
"name": "API health check",
|
|
7
|
+
"type": "api",
|
|
8
|
+
"steps": [
|
|
9
|
+
{
|
|
10
|
+
"given": {
|
|
11
|
+
"op": "api.request",
|
|
12
|
+
"with": {
|
|
13
|
+
"method": "GET",
|
|
14
|
+
"url": "${env:API_BASE_URL}/health"
|
|
15
|
+
},
|
|
16
|
+
"saveAs": "lastHealth"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"then": {
|
|
21
|
+
"op": "assert.httpStatus",
|
|
22
|
+
"with": {
|
|
23
|
+
"is": 200
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "ui-home",
|
|
31
|
+
"name": "UI home page",
|
|
32
|
+
"type": "ui",
|
|
33
|
+
"steps": [
|
|
34
|
+
{
|
|
35
|
+
"given": {
|
|
36
|
+
"op": "ui.goto",
|
|
37
|
+
"with": {
|
|
38
|
+
"url": "${env:UI_BASE_URL}/"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"then": {
|
|
44
|
+
"op": "ui.expectUrl",
|
|
45
|
+
"with": {
|
|
46
|
+
"contains": "/"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "custom-spec-health",
|
|
54
|
+
"name": "Custom spec health check",
|
|
55
|
+
"type": "api",
|
|
56
|
+
"steps": [
|
|
57
|
+
{
|
|
58
|
+
"when": {
|
|
59
|
+
"op": "test.spec",
|
|
60
|
+
"spec": "example.customHealth",
|
|
61
|
+
"with": {
|
|
62
|
+
"input": {
|
|
63
|
+
"url": "${env:API_BASE_URL}/health"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"saveAs": "customHealthResult"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"then": {
|
|
71
|
+
"op": "assert.equals",
|
|
72
|
+
"with": {
|
|
73
|
+
"actual": "${res:customHealthResult.ok}",
|
|
74
|
+
"expected": "true"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
import type { ApiResponse } from "../../contracts/runtime-context.types";
|
|
4
|
+
import type { ApiAdapterOptions, ApiRequestOptions } from "./api.types";
|
|
5
|
+
|
|
6
|
+
function hasHeader(headers: Record<string, string>, name: string): boolean {
|
|
7
|
+
return Object.prototype.hasOwnProperty.call(headers, name.toLowerCase());
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class ApiAdapter {
|
|
11
|
+
private readonly baseUrl?: string;
|
|
12
|
+
private readonly defaultHeaders?: Record<string, string>;
|
|
13
|
+
|
|
14
|
+
constructor(opts?: ApiAdapterOptions) {
|
|
15
|
+
this.baseUrl = opts?.baseUrl;
|
|
16
|
+
this.defaultHeaders = opts?.defaultHeaders;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async http(req: ApiRequestOptions): Promise<ApiResponse> {
|
|
20
|
+
const headers: Record<string, string> = {
|
|
21
|
+
...(this.defaultHeaders ?? {}),
|
|
22
|
+
...(req.headers ?? {}),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
if (req.formData) {
|
|
26
|
+
const formHeaders = req.formData.getHeaders();
|
|
27
|
+
for (const [key, value] of Object.entries(formHeaders)) {
|
|
28
|
+
headers[key.toLowerCase()] = String(value);
|
|
29
|
+
}
|
|
30
|
+
} else if (req.json !== undefined && !hasHeader(headers, "content-type")) {
|
|
31
|
+
headers["content-type"] = "application/json";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const response = await axios.request({
|
|
35
|
+
baseURL: this.baseUrl,
|
|
36
|
+
url: req.url,
|
|
37
|
+
method: req.method as any,
|
|
38
|
+
headers,
|
|
39
|
+
data: req.formData ?? (req.json !== undefined ? req.json : req.bodyText),
|
|
40
|
+
responseType: "text",
|
|
41
|
+
transformResponse: (data) => data,
|
|
42
|
+
validateStatus: () => true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const bodyText = typeof response.data === "string"
|
|
46
|
+
? response.data
|
|
47
|
+
: String(response.data);
|
|
48
|
+
const responseHeaders = Object.fromEntries(
|
|
49
|
+
Object.entries(response.headers ?? {}).map(([key, value]) => [
|
|
50
|
+
key.toLowerCase(),
|
|
51
|
+
Array.isArray(value) ? value.join(", ") : String(value),
|
|
52
|
+
]),
|
|
53
|
+
) as Record<string, string>;
|
|
54
|
+
|
|
55
|
+
let bodyJson: unknown;
|
|
56
|
+
const contentType = responseHeaders["content-type"] ?? "";
|
|
57
|
+
if (contentType.toLowerCase().includes("application/json")) {
|
|
58
|
+
try {
|
|
59
|
+
bodyJson = JSON.parse(bodyText);
|
|
60
|
+
} catch {
|
|
61
|
+
// ignore parse errors
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (bodyJson === undefined) {
|
|
66
|
+
const trimmed = bodyText.trim();
|
|
67
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
68
|
+
try {
|
|
69
|
+
bodyJson = JSON.parse(trimmed);
|
|
70
|
+
} catch {
|
|
71
|
+
// ignore parse errors
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const apiResponse: ApiResponse = {
|
|
77
|
+
status: response.status,
|
|
78
|
+
headers: responseHeaders,
|
|
79
|
+
bodyText,
|
|
80
|
+
...(bodyJson !== undefined ? { bodyJson } : {}),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return apiResponse;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type FormData from "form-data";
|
|
2
|
+
|
|
3
|
+
export interface ApiAdapterOptions {
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
defaultHeaders?: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ApiRequestOptions {
|
|
9
|
+
method: string;
|
|
10
|
+
url: string;
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
json?: unknown;
|
|
13
|
+
bodyText?: string;
|
|
14
|
+
formData?: FormData;
|
|
15
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { chromium } from "playwright";
|
|
2
|
+
import type { Browser, BrowserContext, Page } from "playwright";
|
|
3
|
+
|
|
4
|
+
import type { PlaywrightAdapterOptions } from "./ui.types";
|
|
5
|
+
|
|
6
|
+
function isAbsoluteUrl(url: string): boolean {
|
|
7
|
+
return /^https?:\/\//i.test(url);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class PlaywrightAdapter {
|
|
11
|
+
private readonly baseUrl?: string;
|
|
12
|
+
private readonly headless: boolean;
|
|
13
|
+
private browser?: Browser;
|
|
14
|
+
private context?: BrowserContext;
|
|
15
|
+
public page?: Page;
|
|
16
|
+
|
|
17
|
+
constructor(opts?: PlaywrightAdapterOptions) {
|
|
18
|
+
this.baseUrl = opts?.baseUrl;
|
|
19
|
+
this.headless = opts?.headless ?? true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async start(): Promise<void> {
|
|
23
|
+
this.browser = await chromium.launch({ headless: this.headless });
|
|
24
|
+
this.context = await this.browser.newContext();
|
|
25
|
+
this.page = await this.context.newPage();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async stop(): Promise<void> {
|
|
29
|
+
try {
|
|
30
|
+
if (this.context) {
|
|
31
|
+
await this.context.close();
|
|
32
|
+
}
|
|
33
|
+
} finally {
|
|
34
|
+
this.context = undefined;
|
|
35
|
+
this.page = undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
if (this.browser) {
|
|
40
|
+
await this.browser.close();
|
|
41
|
+
}
|
|
42
|
+
} finally {
|
|
43
|
+
this.browser = undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
resolveUrl(url: string): string {
|
|
48
|
+
if (isAbsoluteUrl(url)) return url;
|
|
49
|
+
if (this.baseUrl) {
|
|
50
|
+
return new URL(url, this.baseUrl).toString();
|
|
51
|
+
}
|
|
52
|
+
return url;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ApiAdapter } from "../adapters/api/api-adapter";
|
|
2
|
+
import type { PlaywrightAdapter } from "../adapters/ui/playwright-adapter";
|
|
3
|
+
import type { Reporter } from "../reporter/reporter.types";
|
|
4
|
+
import type { SpecRegistry } from "../core/spec-registry";
|
|
5
|
+
import type { ScenarioType } from "./testing-metadata.types";
|
|
6
|
+
|
|
7
|
+
export type StepPhase = "given" | "when" | "then" | "and" | "step";
|
|
8
|
+
|
|
9
|
+
export interface ResourceStore {
|
|
10
|
+
get(path: string): unknown;
|
|
11
|
+
set(path: string, value: unknown): void;
|
|
12
|
+
has(path: string): boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ApiResponse {
|
|
16
|
+
status: number;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
bodyText: string;
|
|
19
|
+
bodyJson?: unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TestContext {
|
|
23
|
+
scenarioId: string;
|
|
24
|
+
scenarioType: ScenarioType;
|
|
25
|
+
params: Record<string, any>;
|
|
26
|
+
resources: ResourceStore;
|
|
27
|
+
api?: ApiAdapter;
|
|
28
|
+
ui?: PlaywrightAdapter;
|
|
29
|
+
last?: { apiResponse?: ApiResponse };
|
|
30
|
+
reporter: Reporter;
|
|
31
|
+
specRegistry?: SpecRegistry;
|
|
32
|
+
testData?: Record<string, Record<string, any>>;
|
|
33
|
+
options?: {
|
|
34
|
+
printApiLogs?: boolean;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { TestContext } from "../contracts/runtime-context.types";
|
|
2
|
+
|
|
3
|
+
export type SolidTestSpecArgs = {
|
|
4
|
+
ctx: TestContext;
|
|
5
|
+
input: Record<string, any>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type SolidTestSpecAttachment = {
|
|
9
|
+
name: string;
|
|
10
|
+
contentType: string;
|
|
11
|
+
data: string;
|
|
12
|
+
encoding?: "utf8" | "base64";
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type SolidTestSpecResult = {
|
|
16
|
+
ok: boolean;
|
|
17
|
+
name?: string;
|
|
18
|
+
details?: Record<string, any>;
|
|
19
|
+
attachments?: SolidTestSpecAttachment[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface ISolidTestSpec {
|
|
23
|
+
run(args: SolidTestSpecArgs): Promise<SolidTestSpecResult>;
|
|
24
|
+
}
|