@schwabyio/gta 0.9.0 → 0.10.0

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/README.md CHANGED
@@ -108,6 +108,8 @@ The steps within a collection always run in order.
108
108
 
109
109
  The exit code is `0` when everything passed, `1` when something failed, and `2` when
110
110
  `gta` could not run at all, such as with a bad setting or an unknown collection.
111
+ `gta get` exits `1` when a collection is broken, or has a `use:` or `extends:` a run
112
+ would stop at.
111
113
 
112
114
  ## Settings
113
115
 
package/dist/SPEC.md CHANGED
@@ -269,6 +269,10 @@ comma-separated: `--tags smoke,api`.
269
269
  | `gta all` | Every collection, except those with `exclude: true` (§2.4). |
270
270
  | `gta smoke,checkout/` | The collections and directories named, in that order. |
271
271
 
272
+ `gta get` also reports each `use:` and `extends:` that would stop a run, in every
273
+ collection, including those `gta all` leaves out (Appendix A). It exits `1` when it finds
274
+ one, or a broken collection, and `0` otherwise.
275
+
272
276
  A collection is named by its `id`, or by its place (`checkout/sessions`). A directory is
273
277
  named by its name, and `checkout/` names only the directory. `--flag name=value` sets a
274
278
  feature flag (§2.9), and `--json` prints the results as JSON. The exit code is `0` when
@@ -320,6 +324,8 @@ Any key not listed here is an error.
320
324
  | ---------- | ------------------- | -------- | ------- | ------------------------------------------------------------------------------ |
321
325
  | `id` | string | **yes** | | The file name without `.yml` (below). |
322
326
  | `steps` | list of steps | no | `[]` | The requests, in run order (§2.1). |
327
+ | `setup` | list of steps | no | | Run once before `steps`; what it sets lasts the run (§2.10). |
328
+ | `teardown` | list of steps | no | | Run once after the rest, even when a step failed (§2.10). |
323
329
  | `docs` | string | no | | Markdown. |
324
330
  | `tags` | list of tags | no | | Tags that select the whole collection (§2.4). |
325
331
  | `stepTags` | boolean | no | `false` | `true` lets steps carry their own tags (§2.4). |
@@ -376,6 +382,8 @@ Methods: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`.
376
382
  | `tests` | string | no | The checks, as JavaScript: calls on `gta` (§3) and any other code (§5). |
377
383
  | `tags` | list of tags | no | The step's own tags. Only with `stepTags: true` on the collection (§2.4). |
378
384
  | `flags` | map | no | Feature flags the step needs (§2.9). |
385
+ | `forEach` | string | no | Send the request once for each item of a list (below). |
386
+ | `useTests` | `true` | no | In a request set: the use step's `tests` check this response (§2.5). |
379
387
  | `base` | `false` | no | `false` leaves the step's endpoint base out (§2.6). |
380
388
  | `docs` | string | no | Markdown. |
381
389
 
@@ -387,6 +395,23 @@ Methods: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`.
387
395
  - **Any other key is an error**, so a misspelled key such as `heders:` fails at once
388
396
  rather than being ignored. A method key in lower case (`get:`) is reported as such.
389
397
 
398
+ **`forEach`** sends the request once for each item of a list, each reported as its own
399
+ result, `remove grant (item 2 of 3)`:
400
+
401
+ ```yaml
402
+ - name: remove grant
403
+ DELETE: '{{baseUrl}}/grants/{{item}}'
404
+ forEach: '{{grantedRoots}}' # a variable holding ["r1", "r2"], or a list written in place
405
+ ```
406
+
407
+ - Its `{{variables}}` resolve when the step runs, so an earlier step can make the list,
408
+ as `gta.set('grantedRoots', ids)` does. The result must be a JSON array.
409
+ - Each item is `{{item}}` in the request and `item` in its scripts. An item that is an
410
+ object or a list is its JSON in the request.
411
+ - An empty list skips the step. Anything that is not a list fails it before anything
412
+ is sent.
413
+ - A use step cannot have `forEach`.
414
+
390
415
  ### 2.2 `body`
391
416
 
392
417
  A body declares **exactly one** of these keys. Omit `body` for no body.
@@ -450,6 +475,9 @@ body:
450
475
  | `{ file, contentType?, filename? }` | A file part. |
451
476
  | a list of the above | One part for each item, all with the field name. |
452
477
 
478
+ `filename: ''` sends an empty name, as a browser does when no file is chosen. Pair it
479
+ with an empty file for the whole of that request.
480
+
453
481
  The `Content-Type` is `multipart/form-data` with a boundary chosen for the request. A
454
482
  declared multipart type without a boundary, such as `multipart/mixed`, gets one added.
455
483
  A declared boundary is used as written.
@@ -581,6 +609,10 @@ steps:
581
609
  `{{params.name}}`, and in code as `params.name`, a read-only object holding the real
582
610
  values. No variable can take the place of a `params.` name.
583
611
 
612
+ A default may name variables and other params, as in
613
+ `email: '{{params.accountId}}@example.com'`. Like a `with:` value, it is resolved once,
614
+ when the set starts, so `accountId: '{{$uuid}}'` is one id wherever the set reads it.
615
+
584
616
  **A use step** holds only `use`, `with`, `name`, `tags`, `flags`, `docs` and `tests`. A
585
617
  method key, `headers`, `body`, `settings` or `before` on it is an error, and `with`
586
618
  without `use` is an error too.
@@ -589,18 +621,54 @@ without `use` is an error too.
589
621
  global project. `use: auth/login` is one directory down. `use: global:login` looks
590
622
  only in the global project.
591
623
  - **`with:`** gives plain values; a param left out takes its default. A string may hold
592
- `{{variables}}`, resolved when the set starts. A missing required value, or a name the
593
- set does not take, stops the step before anything is sent.
624
+ `{{variables}}`, resolved when the set starts. A missing required value, a name the
625
+ set does not take, or a value or default that cannot be resolved stops the set's
626
+ steps before anything is sent.
594
627
  - **Running.** A use step runs each of the set's steps in turn, in the collection's
595
628
  variable scope, so what one sets the next can read, and so can the steps after the use
596
- step. Each request is reported as its own result.
629
+ step. Each request is reported as its own result. When the use step has a `name`,
630
+ reports use it: `sign in` for a set of one step, `sign in › get profile` for a longer
631
+ one.
597
632
  - **Layers.** Headers and settings: the collection's, under the set's, under each
598
633
  step's own. Scripts run collection, then set, then step: `before.script` before the
599
- request and `tests` after. The use step's own `tests` run last, after the set's last
600
- step.
634
+ request and `tests` after. The use step's own `tests` run last, on the response of
635
+ the set's step marked `useTests: true`, or else its last step.
601
636
  - **One level.** A request set must not `use:` another, and has `params`, not `vars`. A
602
637
  file in `requests/` without `params` is not a request set.
603
638
 
639
+ **Saving under the caller's name.** A set can take the name to save a value under as a
640
+ param, save it with `gta.set(params.saveAs, …)`, and read it back in its later steps
641
+ with `{{@params.saveAs}}` (§4). The caller then reads it by the name it chose:
642
+
643
+ ```yaml
644
+ # requests/create-user.yml
645
+ params:
646
+ saveTokenAs: { required: true }
647
+ steps:
648
+ - name: create token
649
+ POST: '{{authUrl}}/token'
650
+ tests: gta.set(params.saveTokenAs, res.body.accessToken)
651
+ - name: get profile
652
+ GET: '{{baseUrl}}/profile'
653
+ headers:
654
+ Authorization: Bearer {{@params.saveTokenAs}}
655
+ useTests: true # the use step's tests check this response
656
+ - name: wait for events
657
+ GET: '{{baseUrl}}/wait'
658
+ ```
659
+
660
+ ```yaml
661
+ # a collection
662
+ - use: create-user
663
+ name: user 1
664
+ with: { saveTokenAs: token1 }
665
+ tests: gta.expectResponseBodyToHaveProperty('email') # on get profile's response
666
+ - GET: '{{baseUrl}}/orders'
667
+ headers: { Authorization: 'Bearer {{token1}}' }
668
+ ```
669
+
670
+ Only one step of a set may have `useTests`, and only a set's steps.
671
+
604
672
  ### 2.6 Endpoint bases
605
673
 
606
674
  What every request to one endpoint gets, wherever the request is written. A file in
@@ -699,7 +767,8 @@ userId,expectedStatus,iterationLabel
699
767
 
700
768
  - **One run per row.** Every step runs for row 1, then every step again for row 2, and
701
769
  so on. Each run starts from a fresh variable scope, so nothing one row sets leaks into
702
- the next. Three steps and two rows report six results.
770
+ the next, except a value set to last the run (§2.10). Three steps and two rows report
771
+ six results.
703
772
  - **Precedence.** A row's values sit over the environment and under what code sets
704
773
  (§4).
705
774
  - **`iterationLabel`**, an optional column, names the row in reports, as in
@@ -793,6 +862,41 @@ own, what the `command` prints, then overrides.
793
862
 
794
863
  Every report records the flag values a run used and where each came from.
795
864
 
865
+ ### 2.10 Setup, teardown and values that last the run
866
+
867
+ `setup` and `teardown` are lists of steps, like `steps`, run once around it:
868
+
869
+ ```yaml
870
+ id: approved-domains
871
+ setup: # once, before the first row
872
+ - use: seed-admin # log in, grant an admin role, wait for it to land
873
+ teardown: # once, after the last row, even when a row failed
874
+ - name: remove the grant
875
+ DELETE: '{{baseUrl}}/admins/{{adminId}}'
876
+ steps: # once per row of approved-domains.csv, as before
877
+ - name: approve the domains
878
+ PUT: '{{baseUrl}}/orgs/{{orgId}}/domains'
879
+ ```
880
+
881
+ - **Order.** Setup, then the steps once per data row (or once, with no data file), then
882
+ teardown. They work the same with or without a data file.
883
+ - **What setup sets lasts the run.** Every value setup sets or captures is there for
884
+ every row and for teardown. It sits over the environment and under a row's values
885
+ (§4).
886
+ - **A row keeps a value for the rows after it** with
887
+ `gta.set(name, value, { scope: 'run' })`. Anything else a row sets lasts only that
888
+ row, as §2.8 says.
889
+ - **Setup failing stops the rows.** They are not run and count as skipped. Teardown
890
+ still runs.
891
+ - **Teardown always runs**, every step of it, after failures and after `bail`. A run that
892
+ is cancelled, or stopped by `timeoutCollection`, stops where it is.
893
+ - **The collection applies to them**: its headers, settings, `before`, `tests` and
894
+ flags, as to any step. Their steps may be use steps and may use `forEach`, but carry
895
+ no `tags`: they run whenever the collection does.
896
+ - A request set, a base collection and an endpoints file have no setup or teardown.
897
+ - Reports name their results `setup › log in` and `teardown › remove the grant`. Running
898
+ a single step in the desktop app does not run them.
899
+
796
900
  ---
797
901
 
798
902
  ## 3. Checking a response
@@ -817,6 +921,19 @@ tests: |
817
921
  A path addresses the body in dot or bracket notation: `user.name`; `groups.0.name` or
818
922
  `groups[0].name` for an index; and `sessions[].id` for a property of every item.
819
923
 
924
+ A key that holds a `.`, `[` or `]`, or is empty, goes in brackets as a JSON string:
925
+ `jwt.payload["https://example.com/id"]`, `modules[""].edition`. Reports show such keys
926
+ the same way.
927
+
928
+ As in xtest, a path can also be a list of keys: `['jwt', 'payload', 'https://example.com/id']`.
929
+ Each item is one key, whatever it holds, and a number is its digits, so
930
+ `['groups', 0, 'name']` reads an index. Every function that takes a path takes a list
931
+ too, and so does `pathToProperty`.
932
+
933
+ A path that runs into a `null` before its end, such as `phoneNumber.number` when
934
+ `phoneNumber` is `null`, reads as that `null` for a check that the value is `null`, as
935
+ xtest read it. For any other check the property is not present.
936
+
820
937
  ### Body conversion
821
938
 
822
939
  A JSON body is used as it is. An XML body is converted: the root element is the single
@@ -868,7 +985,15 @@ gta.expectResponseBodyToHaveUnorderedArray('users', [
868
985
  ```
869
986
 
870
987
  `gta.expectResponseBodyToHaveUnorderedArrayNotThisItem(path, list)` passes when no item
871
- matches.
988
+ matches. A `compareValue` may be a `RegExp`.
989
+
990
+ - **Each call prefers items an earlier call did not match.** Two calls with the same
991
+ description find two items when there are two, so each capture and strict validation
992
+ see a different one. A sort starts this over, since its indexes name other items.
993
+ - **A list of one `notThisExpectedValue` entry reads as xtest read it.** Without strict
994
+ validation it means no item has that value, so an empty array passes. With it, it
995
+ means one item whose value is something else, as any list does. The step's last word
996
+ on strict validation decides.
872
997
 
873
998
  ### Strict validation
874
999
 
@@ -881,6 +1006,8 @@ checked, ignored or captured.
881
1006
  vouch for what is inside.
882
1007
  - It is judged after the collection's and the step's `tests` have both run, over what
883
1008
  either checked.
1009
+ - A binary or HTML body has no properties to leave unchecked, so strict validation
1010
+ passes. A check on one still fails.
884
1011
 
885
1012
  ### Sorting
886
1013
 
@@ -922,10 +1049,15 @@ to. Whitespace inside the braces is ignored. In code, read a variable with
922
1049
 
923
1050
  - **Values keep their type.** A string that is exactly one reference returns the
924
1051
  variable's own value, so a variable written as `true` arrives as a boolean. Anything
925
- else is text, and `null` becomes the empty string inside it.
1052
+ else is text, and `null` becomes the empty string inside it, except in a `json` body,
1053
+ where it is written `null`, so `"website": {{site}}` stays valid JSON.
926
1054
  - **An unknown variable is an error**, never literal text. The step fails in its own
927
1055
  `interpolate` phase, naming the variable, and nothing is sent. A reference loop, or a
928
1056
  chain more than 16 deep, fails the same way.
1057
+ - **`{{@name}}` reads the variable `name` names.** With `saveAs: token1`,
1058
+ `{{@saveAs}}` is the value of `token1`. The name may itself be built from variables.
1059
+ A name that is not text, or names no variable, fails like an unknown variable. A
1060
+ request set uses it to read what it saved under its caller's name (§2.5).
929
1061
  - **In YAML, quote a value that starts with `{{`.** Unquoted, YAML reads `{` as a map.
930
1062
 
931
1063
  ### Built-in variables
@@ -945,7 +1077,8 @@ Lowest precedence first:
945
1077
 
946
1078
  ```
947
1079
  global project vars → project vars → base collection vars → collection vars
948
- → environment → data file row → gta.set and captures → process environment
1080
+ → environment → what lasts the run → data file row → gta.set and captures
1081
+ → process environment
949
1082
  ```
950
1083
 
951
1084
  The environment is the global project's file of that name, if there is one, with the
@@ -1006,13 +1139,19 @@ There is nothing to load, and no `startXTest` or `endXTest`.
1006
1139
  | `gta.test(name, fn)`: a named check that passes unless `fn` throws or rejects; `fn` may be async | |
1007
1140
  | `gta.get(name)`: a variable's current value | ✓ |
1008
1141
  | `gta.set(name, value)`: set a variable for the rest of the run | ✓ |
1142
+ | `gta.set(name, value, { scope: 'run' })`: the same, lasting past this data row too (§2.10) | ✓ |
1143
+ | `gta.skip(reason?)`: send nothing for this step, and report it skipped with the reason | ✓ |
1144
+ | `gta.skipRest(reason?)`: skip the rest of this row's steps; in `before.script`, this one too | ✓ |
1009
1145
  | `gta.flag(name)`: a feature flag's value; an unknown flag is an error | ✓ |
1010
1146
  | `gta.uuid()`: a random (version 4) UUID | ✓ |
1011
1147
  | `gta.uuidv7()`: a time-ordered (version 7) UUID | ✓ |
1012
1148
  | `gta.randomInt(min, max)`: a whole number, both ends included | ✓ |
1013
1149
  | `gta.date(format, secondsOffset = 0, timeZone = 'local')` | ✓ |
1014
1150
 
1015
- Calling a response check in `before.script` is an error.
1151
+ Calling a response check in `before.script` is an error, and so is `gta.skip` in
1152
+ `tests`, where the request has been sent. After `gta.skip` the script runs to its end,
1153
+ and no later `before.script` runs. `gta.skipRest` skips steps of this row only: the next
1154
+ row, and teardown, still run.
1016
1155
 
1017
1156
  `gta.date` formats with strftime specifiers: `%Y %y %m %d %e %H %I %M %S %L %p %b %B %a
1018
1157
  %A %j %Z %z %s %F %T %%`. An unrecognized specifier is left in the output, so a typo is
@@ -1024,7 +1163,7 @@ xtest's military zone letters (`U` is -08:00, not UTC).
1024
1163
  | Global | What it is |
1025
1164
  | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1026
1165
  | `res` | `tests` only: `status`, `statusText`, `headers` (lower-cased names), `header(name)`, `body` (parsed JSON, converted XML, or text), `text`, `time` (ms), `size` (bytes). |
1027
- | `req` | `method`, `url`, `headers`, `body`: as sent in `tests`, as written in `before.script`. |
1166
+ | `req` | `method`, `url`, `headers`, `body`: as sent in `tests`, as written in `before.script`, where a script may change `headers` and `body` (below). |
1028
1167
  | `assert` | Node's strict `assert`, for use inside `gta.test`. |
1029
1168
  | `console` | Captured into the step's result. |
1030
1169
  | `params` | A request set's params (§2.5), in its own scripts and in the tests of the use step running it. |
@@ -1042,6 +1181,24 @@ run the same on any machine, and a request belongs in a step, where it is record
1042
1181
  marked as errored. Checks made before it are kept.
1043
1182
  - A script that runs for more than 10 seconds is stopped.
1044
1183
 
1184
+ **Changing the request in `before.script`.** `req` there is the request as written,
1185
+ `{{variables}}` still in it. A script may change it before it is sent, and a later
1186
+ `before.script` sees the change:
1187
+
1188
+ ```js
1189
+ const claims = JSON.parse(req.body)
1190
+ if (!params.crmContactId) delete claims.crm_contact_id // leave the member out
1191
+ req.body = JSON.stringify(claims)
1192
+ req.headers['X-Trace'] = '{{traceId}}'
1193
+ delete req.headers['X-Debug']
1194
+ ```
1195
+
1196
+ - `req.body` is text, and can be changed for a `json`, `xml`, `text` or `graphql` body.
1197
+ A form, multipart or file body is built from its parts, so it cannot.
1198
+ - `req.headers` is a map of names to values: add, change or delete them.
1199
+ - Variables in what the script writes resolve afterwards, as in the file.
1200
+ - Anything else is a pre-request error, and nothing is sent.
1201
+
1045
1202
  ### Check files
1046
1203
 
1047
1204
  `checks/*.js` in a project, and in its global project, hold functions every script can
@@ -1154,7 +1311,8 @@ it. Rules checked at run time fail the step, or the run, before anything is sent
1154
1311
  ignoring case.
1155
1312
  - There is no `name` key (use `id`).
1156
1313
  - A step has `tags` only if the collection has `stepTags: true`.
1157
- - With `params`: no step is a use step, and there is no `vars`.
1314
+ - With `params`: no step is a use step, and there is no `vars`, `setup` or `teardown`.
1315
+ - A `setup` or `teardown` step has no `tags`.
1158
1316
  - A data file, when present, reads (§2.8).
1159
1317
 
1160
1318
  **Steps**
@@ -1167,6 +1325,8 @@ it. Rules checked at run time fail the step, or the run, before anything is sent
1167
1325
  - `before` holds only `script`. `before.set` is not supported.
1168
1326
  - There is no `expect:` key; checks go in `tests`.
1169
1327
  - `base` is only ever `false`.
1328
+ - `forEach` is a string, and a use step has none.
1329
+ - `useTests` is only ever `true`, only on a request set's step, and on one step at most.
1170
1330
 
1171
1331
  **Values**
1172
1332
 
@@ -1189,9 +1349,10 @@ it. Rules checked at run time fail the step, or the run, before anything is sent
1189
1349
  **Library files**
1190
1350
 
1191
1351
  - A request set (`requests/`) has `params`, uses no other set, and has no `vars`.
1192
- - A base collection (`bases/`) has no `steps` or `params`, and no `extends`.
1352
+ - A base collection (`bases/`) has no `steps`, `setup`, `teardown` or `params`, and no
1353
+ `extends`.
1193
1354
  - An endpoint (`endpoints/`) has a URL that is a path starting with `/`, and no use
1194
- steps.
1355
+ steps. An endpoints file has no `setup` or `teardown`.
1195
1356
  - A check file's name is a JavaScript identifier; if it isn't, the file is not loaded.
1196
1357
  - `collections/`, `requests/`, `endpoints/` and `bases/` hold files at most one directory
1197
1358
  down.
@@ -1222,7 +1383,13 @@ it. Rules checked at run time fail the step, or the run, before anything is sent
1222
1383
  - The flag `command`, if any, has every quote closed, starts, and exits `0` within 60
1223
1384
  seconds, printing a JSON object.
1224
1385
  - Every file a body names can be read from the project folder.
1225
- - Every `use:` and `extends:` names a usable file.
1386
+ - Every `use:` and `extends:` names a usable file, and every `with:` suits its set.
1387
+ `gta get` checks these without running anything (§1.3).
1388
+ - A step's `forEach` resolves to a JSON array.
1389
+ - In `{{@name}}`, `name` holds text naming a variable that exists.
1390
+ - A `before.script` sets `req.body` to text, and only for a `json`, `xml`, `text` or
1391
+ `graphql` body; `req.headers` stays a map.
1392
+ - `gta.set`'s `scope`, when given, is `'run'`.
1226
1393
 
1227
1394
  ---
1228
1395