@suffolklitlab/alkiln 5.13.3-feat-random-3 → 5.13.3-feat-random-5
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/.github/workflows/playground.yml +1 -1
- package/CHANGELOG.md +14 -3
- package/action.yml +4 -4
- package/docs/4_environments.md +2 -2
- package/docs/artifacts_folder_name.md +121 -71
- package/docs/constrained_random_tests.md +24 -0
- package/lib/docassemble/server_install.js +0 -2
- package/lib/docassemble/setup.js +1 -3
- package/lib/globals.js +2 -2
- package/lib/scope.js +12 -1
- package/lib/setup/TestGenerator.js +32 -30
- package/lib/steps.js +50 -25
- package/lib/utils/Log.js +15 -16
- package/lib/utils/reports.js +13 -10
- package/lib/utils/session_vars.js +2 -2
- package/package.json +1 -1
- package/tests/unit_tests/constrained_random_answers/invalid.fixtures.js +2 -1
- package/tests/unit_tests/constrained_random_answers/valid.fixtures.js +49 -28
- package/tests/unit_tests/constrained_random_answers/valid.test.js +23 -1
|
@@ -76,7 +76,7 @@ jobs:
|
|
|
76
76
|
DOCASSEMBLE_DEVELOPER_API_KEY: "${{ env.DOCASSEMBLE_DEVELOPER_API_KEY }}"
|
|
77
77
|
#### Developer note: See https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#optional-inputs
|
|
78
78
|
#### You can probably leave this out
|
|
79
|
-
|
|
79
|
+
ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO: 10
|
|
80
80
|
# Internal: docassemble changes sometimes break @a11y tests. That is
|
|
81
81
|
# a da problem, not ours. We will trigger these tests manually when we
|
|
82
82
|
# want to check up on this.
|
package/CHANGELOG.md
CHANGED
|
@@ -47,9 +47,9 @@ Format:
|
|
|
47
47
|
|
|
48
48
|
### Added
|
|
49
49
|
|
|
50
|
-
- Add the `constrained random answers` Step. You must put the Step in a "constrained random test
|
|
51
|
-
- Add `
|
|
52
|
-
- Regular tests that have incorrect syntax will fail more quickly now - very near the start. So will invalid "constrained random test
|
|
50
|
+
- Add the `constrained random answers` Step. You must put the Step in a "constrained random test template" Scenario which goes in a "constrained random test template" file - a `.feature` file that starts with "alkiln_random". The file **MUST have ONLY 1 Scenario per template file** at the moment. The Step is pretty strict. If there are irregularities, it will probably fail, but it will fail quickly. It is flexible about number of tests, though, and will default to 1. Closes [#962](https://github.com/SuffolkLITLab/ALKiln/issues/962).
|
|
51
|
+
- Add `ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO` optional environment variable for the above `constrained random answers` Step. Default value is `40` until we hear otherwise.
|
|
52
|
+
- Regular tests that have incorrect syntax will fail more quickly now - very near the start. So will invalid "constrained random test template" files. This will be a little slower for GitHub Sandbox tests right now, but still faster than it has been in the past. Closes [#972](https://github.com/SuffolkLITLab/ALKiln/issues/972).
|
|
53
53
|
|
|
54
54
|
### Changed
|
|
55
55
|
|
|
@@ -72,6 +72,17 @@ Format:
|
|
|
72
72
|
- Fixed: log codes checker obob.
|
|
73
73
|
- Fixed: updated `actions/upload-artifact@v3` to `actions/upload-artifact@v4`
|
|
74
74
|
|
|
75
|
+
## [5.14.0] - 2025-05-15
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- Adds a linear Step to properly set multi-line values for `textarea`s with a doc string (closes [#1003](https://github.com/SuffolkLITLab/ALKiln/issues/1003)). We are unable to fix the current "set variable" linear Step for this. See [this comment on #655](https://github.com/SuffolkLITLab/ALKiln/issues/655#issuecomment-2876491571).
|
|
80
|
+
|
|
81
|
+
### Internal
|
|
82
|
+
|
|
83
|
+
- Changes how Log joins logs.
|
|
84
|
+
- Allows reports to accept log messages as an argument without duplicating metadata text. Debug logs will still save duplicate messages.
|
|
85
|
+
|
|
75
86
|
## [5.13.4] - 2025-01-11
|
|
76
87
|
|
|
77
88
|
### Changed
|
package/action.yml
CHANGED
|
@@ -33,7 +33,7 @@ inputs:
|
|
|
33
33
|
description: "The version of docassemblecli to install."
|
|
34
34
|
required: false
|
|
35
35
|
default: "0.0.17"
|
|
36
|
-
|
|
36
|
+
ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO:
|
|
37
37
|
description: "The maximum number of tests ALKiln will generate for every Scenario that can generate tests."
|
|
38
38
|
required: false
|
|
39
39
|
# Default will be internal to avoid duplication of data
|
|
@@ -72,7 +72,7 @@ runs:
|
|
|
72
72
|
MAX_SECONDS_FOR_SETUP: ${{ inputs.MAX_SECONDS_FOR_SETUP }}
|
|
73
73
|
SERVER_RELOAD_TIMEOUT_SECONDS: ${{ inputs.SERVER_RELOAD_TIMEOUT_SECONDS }}
|
|
74
74
|
DOCASSEMBLECLI_VERSION: ${{ inputs.DOCASSEMBLECLI_VERSION }}
|
|
75
|
-
|
|
75
|
+
ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO: ${{ inputs.ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO }}
|
|
76
76
|
shell: bash
|
|
77
77
|
run: |
|
|
78
78
|
log="💡 ALK0023 INFO: Set environment variables and create artifacts folder"
|
|
@@ -96,7 +96,7 @@ runs:
|
|
|
96
96
|
echo "DOCASSEMBLE_DEVELOPER_API_KEY=$DOCASSEMBLE_DEVELOPER_API_KEY" >> $GITHUB_ENV
|
|
97
97
|
echo "ALKILN_VERSION=$ALKILN_VERSION" >> $GITHUB_ENV
|
|
98
98
|
echo "MAX_SECONDS_FOR_SETUP=$MAX_SECONDS_FOR_SETUP" >> $GITHUB_ENV
|
|
99
|
-
echo "
|
|
99
|
+
echo "ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO=$ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO" >> $GITHUB_ENV
|
|
100
100
|
echo "SERVER_RELOAD_TIMEOUT_SECONDS=$SERVER_RELOAD_TIMEOUT_SECONDS" >> $GITHUB_ENV
|
|
101
101
|
echo "DOCASSEMBLECLI_VERSION=$DOCASSEMBLECLI_VERSION" >> $GITHUB_ENV
|
|
102
102
|
# Hard-coded internal ALKiln vars
|
|
@@ -106,7 +106,7 @@ runs:
|
|
|
106
106
|
run: |
|
|
107
107
|
log="💡 ALK0024 INFO: Confirm environment variables"
|
|
108
108
|
echo -e "\n$log" >> "${{ env.DEBUG_PATH }}"
|
|
109
|
-
vars_log='\nALKiln version is ${{ env.ALKILN_VERSION }}\nRepo is "${{ env.REPO_URL }}"\nBranch ref is "${{ env.BRANCH_PATH }}"\nCustom MAX_SECONDS_FOR_SETUP is "${{ env.MAX_SECONDS_FOR_SETUP }}"\nCustom SERVER_RELOAD_TIMEOUT_SECONDS is "${{ env.SERVER_RELOAD_TIMEOUT_SECONDS }}"\nCustom
|
|
109
|
+
vars_log='\nALKiln version is ${{ env.ALKILN_VERSION }}\nRepo is "${{ env.REPO_URL }}"\nBranch ref is "${{ env.BRANCH_PATH }}"\nCustom MAX_SECONDS_FOR_SETUP is "${{ env.MAX_SECONDS_FOR_SETUP }}"\nCustom SERVER_RELOAD_TIMEOUT_SECONDS is "${{ env.SERVER_RELOAD_TIMEOUT_SECONDS }}"\nCustom ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO is "${{ env.ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO }}"\n'
|
|
110
110
|
echo -e "$vars_log" >> "${{ env.DEBUG_PATH }}" && echo -e "$vars_log"
|
|
111
111
|
|
|
112
112
|
# Install ALKiln during GitHub workflow
|
package/docs/4_environments.md
CHANGED
|
@@ -223,7 +223,7 @@ This design is working within systems that limit how we can do what we need to d
|
|
|
223
223
|
|
|
224
224
|
Alternative: Get rid of our support for some of these environments. The temporary GitHub docassemble server environment hasn't been used by a lot of authors. Other than that, the other environments have gotten a lot of use. Even though authors have not yet used the temporary GitHub environment much, they say they want that functionality. The trouble there seems to be complexity with initial set up.
|
|
225
225
|
|
|
226
|
-
Alternative: Instead of dealing with separate runtimes, have one command and script that wraps all these currently separate processes in one runtime and handles which ones to trigger under what circumstances. If we included `./action_for_github/action.yml` in that command, we would prevent authors from customizing the temporary GitHub docassemble server. That would not be acceptable. To avoid closing off access to server customization, we can only partially integrate these different processes. Also, GitHub actions in general will still have to deal with some core business
|
|
226
|
+
Alternative: Instead of dealing with separate runtimes, have one command and script that wraps all these currently separate processes in one runtime and handles which ones to trigger under what circumstances. If we included `./action_for_github/action.yml` in that command, we would prevent authors from customizing the temporary GitHub docassemble server. That would not be acceptable. To avoid closing off access to server customization, we can only partially integrate these different processes. Also, GitHub actions in general will still sometimes have to deal with some core business. For example, they have to create the artifacts folder name so they can use it later to upload the artifacts. This combined runtime also feels more coupled/entangled and therefore harder to maintain. The need for more command line arguments could make local development more complicated. It is still worth considering.
|
|
227
227
|
|
|
228
228
|
<!-- Discuss: How to articulate the entanglement with more specifics and examples. -->
|
|
229
229
|
|
|
@@ -234,7 +234,7 @@ Alternative script names: `install_package`, `save_server`, `delete_package`.
|
|
|
234
234
|
_Security, privacy, observability, etc._
|
|
235
235
|
|
|
236
236
|
- Some tests need to use sensitive information. For example, some tests need a logged-in user and thus ALKiln needs the login info for a user. Different environments store this information differently and there is documentation about how to use sensitive information securely and privately.
|
|
237
|
-
- Core ALKiln code[^2] ends up needing to handle some of the complexity of the 4 environments. Example - the `Log` class `path` takes an optional initial argument to set the artifacts path
|
|
237
|
+
- Core ALKiln code[^2] ends up needing to handle some of the complexity of the 4 environments. Example - the `Log` class `path` takes an optional initial argument to set the artifacts path before the core code runs. The process in the core code (and elsewhere) have to assume that this path already exists and use config values. We have tried to abstract environment complexity out to `run_cucumber.js`, the script that wraps the core code, but there is still logic that the core code handles.
|
|
238
238
|
|
|
239
239
|
---
|
|
240
240
|
|
|
@@ -21,115 +21,165 @@ Our 4 environments can be condensed into 3 environments for the purpose of manag
|
|
|
21
21
|
- ALKilnInThePlayground
|
|
22
22
|
- The command line for internal development
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Every environment runs npm commands. Those commands are sometimes run separately and thus trigger separate processes. We must find a way to persist information, like the name of the artifacts folder, across these processes.
|
|
25
25
|
|
|
26
|
-
The artifact folder name
|
|
26
|
+
The GitHub action creates an artifact folder name and sends it to a script that use an instance of the `Log` class to create the folder and save the name. In other environments, an instance of the `Log` class is what creates the name.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
The scripts that GitHub action uses need to store its output in the same artifacts folder. The GitHub action therefore builds the name of the artifact folder and passes that around to the different scripts. The scripts pass the folder name to the `Log` class every time, which saves the folder name in `runtime_config.json`. After that, the core code uses that `json` value to put other artifacts in the same folder. We also have folder-naming code in the core code which we use for other purposes. The GitHub action duplicates that folder-naming code, which is unfortunate.
|
|
28
|
+
The instance of the `Log` saves the name in `runtime_config.json`. That instance of `Log` and other ALKiln code uses that name to create and store files in that artifacts folder.
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
The artifact folder name generation code is in 2 places - the GitHub action and ALKiln's core code[^1]. In future, we may be able to reduce that to just one location - the core code.
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
Below are more detailed descriptions of how each environment interacts with the folder name. Where the folder name is created, gets passed, gets saved, and gets used. These descriptions still gloss over many details.
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
-->
|
|
34
|
+
The diagrams highlight `Log` to help visualize a common point in all the flows.
|
|
37
35
|
|
|
38
|
-
A future goal is to remove the need to store the name in the runtime configuration file to reduce that complexity a bit. These flows are up for discussion.
|
|
39
36
|
|
|
40
|
-
|
|
37
|
+
### Shared behavior
|
|
38
|
+
|
|
39
|
+
For each environment, the start and end of how they manage the artifacts folder name is different. In the middle, though - once the artifacts folder name is already stored in the `runtime_config` file - the flow is the same.
|
|
40
|
+
|
|
41
|
+
Npm commands that come in the middle have this flow to save logs:
|
|
42
|
+
|
|
43
|
+
```mermaid
|
|
44
|
+
flowchart LR
|
|
45
|
+
|
|
46
|
+
environment --triggers--> commands["middle</br>commands"]
|
|
47
|
+
commands --trigger--> scripts["package</br>code"]
|
|
48
|
+
scripts --make--> log2["#96;Log#96;"]:::log
|
|
49
|
+
log2 --"gets name</br>from"--> runtime2["runtime config</br>file"]
|
|
50
|
+
log2 --"adds to"--> folder2[folder</br>& files]
|
|
51
|
+
|
|
52
|
+
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The core code also needs to use the artifacts folder name in order to save both logs and other artifacts:
|
|
56
|
+
|
|
57
|
+
```mermaid
|
|
58
|
+
flowchart LR
|
|
59
|
+
|
|
60
|
+
core["core</br>code"] --"makes"--> log4["#96;Log#96;"]:::log
|
|
61
|
+
core --"adds to"--> folder3[folder</br>& files]
|
|
62
|
+
core --"gets name</br>from"--> runtime3["runtime config</br>file"]
|
|
63
|
+
log4 --"gets name</br>from"--> runtime3
|
|
64
|
+
log4 --"adds to"--> folder3
|
|
65
|
+
|
|
66
|
+
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Those are the shared flows. Each environment, though, has its own way to store the name at the start and each environment has its own way to give the author the final artifacts.
|
|
41
70
|
|
|
42
|
-
The diagrams highlight `Log` to help visualize a common point in all the flows.
|
|
43
71
|
|
|
44
72
|
### GitHub action
|
|
45
73
|
|
|
46
|
-
|
|
47
|
-
2. GitHub action uses the same folder name, passes that same name to `run_cucumber`, which passes that same name to `Log`. The `runtime_cucumber` process uses that `Log`. The `run_cucumber` process also triggers the core code, which uses the folder name in `runtime_config.json` to create its own `Log`. The core code also uses that name to store other artifact files in the folder.
|
|
48
|
-
3. GitHub action creates the folder name, passes that same name to `takedown`, which passes that same name when instantiating its `Log`. The takedown `Log` uses the name to store logs in the folder.
|
|
74
|
+
The GitHub action starts off with an empty `runtime_config` file. The action creates a new folder name and passes that name to the `artifacts` script, which passes that name to a `Log`. The `Log` creates the artifacts folder and saves the name in `runtime_config.json`.
|
|
49
75
|
|
|
50
76
|
```mermaid
|
|
51
|
-
|
|
77
|
+
flowchart LR
|
|
78
|
+
|
|
79
|
+
action["GitHub action"] --creates--> name["artifacts</br>folder name"]
|
|
80
|
+
action --"passes</br>name "--> command["'artifacts'</br>command"]
|
|
81
|
+
command --"passes</br>name"--> artifacts["artifacts.js"]
|
|
82
|
+
artifacts --"passes</br>name"--> log1["#96;Log#96;"]:::log
|
|
83
|
+
log1 --"stores name in</br>& uses"--> runtime1["runtime config</br>file"]
|
|
84
|
+
log1 --"creates</br>with name"--> folder1["folder</br>& files"]
|
|
85
|
+
log1 --"adds to"--> folder1
|
|
52
86
|
|
|
87
|
+
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then other processes use the name from the config to store logs and other files in the artifacts folder (as we've already shown).
|
|
91
|
+
|
|
92
|
+
Finally, the action uses the name in the action context to download the artifacts folder so the user can have access to it after the action is done.
|
|
93
|
+
|
|
94
|
+
```mermaid
|
|
53
95
|
flowchart LR
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
log1 --create with name--> folder1[folder]
|
|
58
|
-
|
|
59
|
-
action2[action part 2] --pass name too--> run["run"]
|
|
60
|
-
run --pass name too--> log2["#96;Log#96;"]:::log
|
|
61
|
-
log2 --store name in/ignore--> runtime2["runtime config file"]
|
|
62
|
-
log2 --use with name--> folder2[folder]
|
|
63
|
-
run --> core["core code"]
|
|
64
|
-
core --pass name too--> log3["#96;Log#96;"]:::log
|
|
65
|
-
core --use with name--> folder2
|
|
66
|
-
core --get name from--> runtime2
|
|
67
|
-
log3 --use with name--> folder2
|
|
68
|
-
log3 --store name in/ignore--> runtime2
|
|
69
|
-
|
|
70
|
-
action3[action part 3] --pass name too--> takedown["takedown"]
|
|
71
|
-
takedown --pass name too--> log4["#96;Log#96;"]:::log
|
|
72
|
-
log4 --store name in/ignore--> runtime3["runtime config file"]
|
|
73
|
-
log4 --create with name--> folder5[folder]
|
|
96
|
+
|
|
97
|
+
action["GitHub action"] --previously created--> name["artifacts folder name"]
|
|
98
|
+
action --uses name to get--> folder["folder & files"]
|
|
74
99
|
|
|
75
100
|
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
76
101
|
```
|
|
77
102
|
|
|
103
|
+
|
|
78
104
|
### ALKilnInThePlayground
|
|
79
105
|
|
|
80
|
-
|
|
106
|
+
_Note: This should be implemented by the time anyone actually reads this. Here's hoping._
|
|
107
|
+
|
|
108
|
+
ALKilnInThePlayground starts off with an empty `runtime_config` file. ALKilnInThePlayground triggers the `artifacts` command, but without a folder name. The code still creates a `Log` (also without a folder name). The `Log` itself creates the name.
|
|
81
109
|
|
|
82
110
|
```mermaid
|
|
83
111
|
flowchart LR
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
log3 --use with name--> folder2
|
|
93
|
-
log3 --store name in/ignore--> runtime2
|
|
112
|
+
|
|
113
|
+
ALKilnInThePlayground --> command["'artifacts'</br>command"]
|
|
114
|
+
command --> artifacts["artifacts.js"]
|
|
115
|
+
artifacts --> log1["#96;Log#96;"]:::log
|
|
116
|
+
log1 --creates--> name["artifacts</br>folder name"]
|
|
117
|
+
log1 --stores name--> runtime1["runtime config file"]
|
|
118
|
+
log1 --"gets name</br>from"--> runtime1["runtime config</br>file"]
|
|
119
|
+
log1 --creates</br>with name--> folder1["folder</br>& files"]
|
|
94
120
|
|
|
95
121
|
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
96
122
|
```
|
|
97
123
|
|
|
124
|
+
Then other processes use the name from the config to store logs and other files in the artifacts folder (as we've already shown).
|
|
125
|
+
|
|
126
|
+
Finally, ALKilnInThePlayground itself uses the runtime config file to get the name so it can give the artifacts to the user.
|
|
127
|
+
|
|
128
|
+
```mermaid
|
|
129
|
+
flowchart LR
|
|
130
|
+
|
|
131
|
+
ALKilnInThePlayground --"gets name from"--> runtime1["runtime config file"]
|
|
132
|
+
ALKilnInThePlayground --"uses name to get"--> folder["folder & files"]
|
|
133
|
+
|
|
134
|
+
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
|
|
98
138
|
### Command line
|
|
99
139
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
140
|
+
The command line flow is similar to ALKilnInThePlayground, but has to deal with a pre-existing runtime config file[^2]. The command line flow overwrites the artifacts folder name on each run.
|
|
141
|
+
|
|
142
|
+
An internal developer uses the command line to trigger the `artifacts` command, but without a folder name. The code still creates a `Log` (also without a folder name). The `Log` itself creates a name and overwrites the current name in the runtime config file.
|
|
103
143
|
|
|
104
144
|
```mermaid
|
|
105
145
|
flowchart LR
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
log2 --create with name--> folder2[folder]
|
|
115
|
-
run --> core["core code"]
|
|
116
|
-
core --> log3["#96;Log#96;"]:::log
|
|
117
|
-
core --use with name--> folder2
|
|
118
|
-
core --get name from--> runtime2
|
|
119
|
-
log3 --use with name--> folder2
|
|
120
|
-
log3 --store name in/ignore--> runtime2
|
|
121
|
-
|
|
122
|
-
cmd3[command line] --> takedown["takedown"]
|
|
123
|
-
takedown --> log4["#96;Log#96;"]:::log
|
|
124
|
-
log4 --store name in/ignore--> runtime3["runtime config file"]
|
|
125
|
-
log4 --create with name--> folder5[folder]
|
|
146
|
+
|
|
147
|
+
dev --> command["'artifacts'</br>command"]
|
|
148
|
+
command --> artifacts["artifacts.js"]
|
|
149
|
+
artifacts --> log1["#96;Log#96;"]:::log
|
|
150
|
+
log1 --creates--> name["artifacts</br>folder name"]
|
|
151
|
+
log1 --"overwrites</br>previous name"--> runtime1["runtime config file"]
|
|
152
|
+
log1 --"gets name</br>from"--> runtime1["runtime config</br>file"]
|
|
153
|
+
log1 --creates</br>with name--> folder1["folder</br>& files"]
|
|
126
154
|
|
|
127
155
|
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
128
156
|
```
|
|
129
157
|
|
|
158
|
+
Then other processes use the name from the config to store logs and other files in the artifacts folder (as we've already shown).
|
|
159
|
+
|
|
160
|
+
ALKiln stores the artifacts folder in the root of the local repository folder. The developer has direct access to the file system and can see every new artifacts folder. In fact, they can also see any previous artifacts folders. We avoid adding those artifacts folders to the repo by including the folder name pattern in the `.gitignore` and `.npmignore` files.
|
|
161
|
+
|
|
162
|
+
```mermaid
|
|
163
|
+
flowchart LR
|
|
164
|
+
|
|
165
|
+
git --🙈--> folder["folders & files"]
|
|
166
|
+
dev --👁️--> folder
|
|
167
|
+
npm --🙉--> folder
|
|
168
|
+
|
|
169
|
+
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
|
|
130
173
|
## Alternatives considered
|
|
131
174
|
|
|
132
|
-
-
|
|
133
|
-
-
|
|
175
|
+
- Each originator (GitHub, ALKilnInThePlayground, the dev) could always create the name from the outside. This would duplicate work, but would give more fine-grained control.
|
|
176
|
+
- Have `Log` make a different folder name each time and, at the very end, combine the contents of those folders into one folder. Hey, all ideas are welcome here.
|
|
177
|
+
|
|
178
|
+
For name creation:
|
|
179
|
+
|
|
180
|
+
- Avoid GitHub actions creating names. That is, create names only in `artifacts.js` and pass that out again. Needs research.
|
|
181
|
+
|
|
134
182
|
|
|
135
183
|
[^1]: **Core framework code/core code:** the code that implements the Steps in the `.feature` test files.
|
|
184
|
+
|
|
185
|
+
[^2] Keeping the `runtime_config` file around helps greatly improves the local development experience. You can read more in the document that talks about the 4 environments ALKiln supports.
|
|
@@ -252,3 +252,27 @@ Scenario: I visit the zoo
|
|
|
252
252
|
|
|
253
253
|
The AST doesn't have a list of lines. For examples, Scenarios and comments are in separate lists. The AST also stores line numbers, so we'd have to keep updating line numbers for every object, which adds complexity. We've currently decided that the more structured model isn't worth that complexity.
|
|
254
254
|
|
|
255
|
+
|
|
256
|
+
## Ranges may not be that useful
|
|
257
|
+
|
|
258
|
+
Focused tests with actual thought out options are so useful. Take these examples:
|
|
259
|
+
|
|
260
|
+
```gherkin
|
|
261
|
+
# ❌ Too broad: May miss important scenarios
|
|
262
|
+
Then ALKiln will stop at "end" with any combination of:
|
|
263
|
+
| var | possible values |
|
|
264
|
+
| income | 0..1000000 |
|
|
265
|
+
| assets | 0..1000000 |
|
|
266
|
+
|
|
267
|
+
# ✅ Better: Focused ranges with critical points
|
|
268
|
+
Then ALKiln will stop at "end" with any combination of:
|
|
269
|
+
| var | possible values |
|
|
270
|
+
| income | 0;; 30000;; 50000;; 75000;; 100000 |
|
|
271
|
+
| assets | 0;; 50000;; 100000;; 250000 |
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
The first one will make a lot of useless tests. Is there a way to have a cake and eat it too? I'm not sure. Example:
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
| income | 0..100;; 900..1000 |
|
|
278
|
+
```
|
|
@@ -56,8 +56,6 @@ const server_install = async () => {
|
|
|
56
56
|
session_vars.validateEnvironment();
|
|
57
57
|
|
|
58
58
|
// 2. Prepare logs
|
|
59
|
-
// Local devs: This will be separate from your test paths
|
|
60
|
-
// unless you pass a `path` argument into the cucumber run command
|
|
61
59
|
const argv = require(`minimist`)(process.argv.slice(2));
|
|
62
60
|
const log = new Log({ path: argv.path, context, });
|
|
63
61
|
|
package/lib/docassemble/setup.js
CHANGED
|
@@ -54,7 +54,7 @@ const setup = async () => {
|
|
|
54
54
|
+ `dainstall this package on your server. See above for more details.`;
|
|
55
55
|
log.throw({ code: `ALK0047`, context,
|
|
56
56
|
before: `☝️ ☝️ ☝️ Error data ☝️ ☝️ ☝️\n`,
|
|
57
|
-
error: new Error(`Error while installing project on your docassemble server`) },
|
|
57
|
+
error: new Error(`Error while installing project on your docassemble server during GitHub+You™ tests`) },
|
|
58
58
|
install_err_msg
|
|
59
59
|
);
|
|
60
60
|
}
|
|
@@ -68,8 +68,6 @@ const setup = async () => {
|
|
|
68
68
|
session_vars.validateEnvironment();
|
|
69
69
|
|
|
70
70
|
// 2. Prepare logs
|
|
71
|
-
// Local devs: This will be separate from your test paths unless you pass a
|
|
72
|
-
// `path` argument into the cucumber run command
|
|
73
71
|
const argv = require(`minimist`)(process.argv.slice(2));
|
|
74
72
|
const log = new Log({ path: argv.path, context, });
|
|
75
73
|
|
package/lib/globals.js
CHANGED
package/lib/scope.js
CHANGED
|
@@ -888,7 +888,7 @@ module.exports = {
|
|
|
888
888
|
}, // Ends scope.normalizeTable()
|
|
889
889
|
|
|
890
890
|
getAllFields: async function ( scope, { html }) {
|
|
891
|
-
|
|
891
|
+
/** Given HTML string of a page, returns a list of all the fields in it
|
|
892
892
|
* as some kind of sophisticated data? With their selectors, for example.
|
|
893
893
|
* We will choose selectors that will continue to identify the node, eve
|
|
894
894
|
* after a hidden field is revealed, etc.
|
|
@@ -1019,12 +1019,15 @@ module.exports = {
|
|
|
1019
1019
|
// button, radio, checkbox yesno, and maybe others need the value of the `value` attribute
|
|
1020
1020
|
let values = [];
|
|
1021
1021
|
if ( node.name === `select` ) {
|
|
1022
|
+
// Get possible values
|
|
1022
1023
|
values = await scope.getOptionValues( scope, { $select: $node });
|
|
1023
1024
|
}
|
|
1024
1025
|
if ( values.length === 0 && node.attribs.value ) {
|
|
1025
1026
|
// TODO: Add links to docs about da YAML values that devs never fill in themselves and
|
|
1026
1027
|
// so don't know what they are. E.g. yesnomabye -> True/False/None.
|
|
1027
1028
|
// TODO: Text inputs and textareas must ignore the value, etc. How?
|
|
1029
|
+
|
|
1030
|
+
// Get the current value for not `select` fields
|
|
1028
1031
|
values.push( node.attribs.value );
|
|
1029
1032
|
}
|
|
1030
1033
|
|
|
@@ -1387,6 +1390,14 @@ module.exports = {
|
|
|
1387
1390
|
* @param <field[n].guesses[n].trigger> {str} - var name that triggered this page
|
|
1388
1391
|
*
|
|
1389
1392
|
* @returns [ var_data[n] ]
|
|
1393
|
+
* @returns [{
|
|
1394
|
+
* table_row: var_data[n],
|
|
1395
|
+
* field: {
|
|
1396
|
+
* selector: str,
|
|
1397
|
+
* guesses: [{ values: [str] }]
|
|
1398
|
+
* }
|
|
1399
|
+
* }]
|
|
1400
|
+
* What does it return when there's no match?
|
|
1390
1401
|
*/
|
|
1391
1402
|
|
|
1392
1403
|
let rows_that_match_field_names = [];
|
|
@@ -7,19 +7,18 @@ const get_Gherkin_AST = require(`./get_Gherkin_AST`);
|
|
|
7
7
|
const get_error_stacks = require(`../utils/get_error_stacks`);
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* TODO:
|
|
11
|
-
*
|
|
12
|
-
* (like "missing number in constraints random test")
|
|
10
|
+
* TODO: Update ALKilnInThePlayground to let author download the generated
|
|
11
|
+
* tests. Also, store them in a unique location.
|
|
13
12
|
* TODO: Post-MVP, don't repeat tests that the author has already created
|
|
14
13
|
* TODO: Post-MVP, write Steps that warn about fore-Steps and constraints Steps
|
|
15
14
|
* when they appear in a regular test. It probably means someone named the
|
|
16
15
|
* file incorrectly.
|
|
17
|
-
* Discuss: Require author to use "
|
|
18
|
-
* "
|
|
19
|
-
* scan) for 2 reasons. 1. Gets the vocab of "
|
|
16
|
+
* Discuss: Require author to use "Template Scenario:" as they keyword (Also
|
|
17
|
+
* "Template Scenario Outline:") (or "Scenario Template", maybe harder to
|
|
18
|
+
* scan) for 2 reasons. 1. Gets the vocab of "template" in early (though
|
|
20
19
|
* that doesn't match "constraints" Step, which is a whole other language
|
|
21
|
-
* problem we haven't solved yet.) 2. Saying "
|
|
22
|
-
* is confusing when "Scenario" means different things - a
|
|
20
|
+
* problem we haven't solved yet.) 2. Saying "ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO"
|
|
21
|
+
* is confusing when "Scenario" means different things - a template
|
|
23
22
|
* Scenario and a regular Scenario.
|
|
24
23
|
* Discuss: Now that Log is less fragile and duplicated, shall we create a new
|
|
25
24
|
* Log instance in here?
|
|
@@ -30,6 +29,9 @@ const get_error_stacks = require(`../utils/get_error_stacks`);
|
|
|
30
29
|
* do that now, but without the icon.
|
|
31
30
|
* Discuss: If/when we allow multiple Scenarios, research how we find the end
|
|
32
31
|
* of a Scenario that ends in an `Example`.
|
|
32
|
+
* Discuss: We might give various levels of categories for warnings. For example,
|
|
33
|
+
* severe warnings (like "will skip the random test") vs. slight warnings
|
|
34
|
+
* (like "missing number in constraints random test")
|
|
33
35
|
* */
|
|
34
36
|
|
|
35
37
|
let log = null;
|
|
@@ -177,7 +179,7 @@ module.exports = class TestGenerator {
|
|
|
177
179
|
let scenario_objs = doc_AST.feature.children;
|
|
178
180
|
if ( !scenario_objs[0] ) {
|
|
179
181
|
let syntax_error = log.error({ code: `ALK0235`, context: `generating files`, before: `\n\n`, },
|
|
180
|
-
`
|
|
182
|
+
`It looks like the author's template .feature file at "${ generator_path }" has 0 Scenarios.`
|
|
181
183
|
);
|
|
182
184
|
return { new_contents: null, errors: [new Error( syntax_error )]};
|
|
183
185
|
}
|
|
@@ -185,7 +187,7 @@ module.exports = class TestGenerator {
|
|
|
185
187
|
|
|
186
188
|
if ( scenario_objs.length > 1 ) {
|
|
187
189
|
let too_many_scenarios = log.error({ code: `ALK0236`, context: `generating files`, },
|
|
188
|
-
`A
|
|
190
|
+
`A template file can only have 1 template Scenario. This one has ${ scenario_objs.length }.`
|
|
189
191
|
);
|
|
190
192
|
return { new_contents: ``, errors: [new Error( too_many_scenarios )] };
|
|
191
193
|
}
|
|
@@ -250,7 +252,7 @@ module.exports = class TestGenerator {
|
|
|
250
252
|
let errs_n_warns = new Misbehaviors();
|
|
251
253
|
|
|
252
254
|
let strs_for_all_new_Scenarios = [];
|
|
253
|
-
// Soon we want to be able to handle multiple
|
|
255
|
+
// Soon we want to be able to handle multiple template Scenarios per file
|
|
254
256
|
for ( let scenario_obj_i = 0; scenario_obj_i < scenario_objs.length; scenario_obj_i++ ) {
|
|
255
257
|
|
|
256
258
|
errs_n_warns.reset_warnings();
|
|
@@ -370,11 +372,11 @@ module.exports = class TestGenerator {
|
|
|
370
372
|
|
|
371
373
|
let got_fewer_tests_than_desired = new_test_bodies.length < num_to_make;
|
|
372
374
|
if ( got_fewer_tests_than_desired ) {
|
|
373
|
-
let warn_too_few = log.warn({ code: `ALK0260`, context: `generating files
|
|
374
|
-
`ALKiln could only make ${ new_test_bodies.length } *unique* tests out of the ${ num_to_make } the author wanted for this Scenario. Note that ALKiln compares all generated Scenarios with each other to make sure all are unique. This Scenario may be a duplicate of a Scenario generated
|
|
375
|
+
let warn_too_few = log.warn({ code: `ALK0260`, context: `generating files template-scenario-level`, },
|
|
376
|
+
`ALKiln could only make ${ new_test_bodies.length } *unique* tests out of the ${ num_to_make } the author wanted for this Scenario. Note that ALKiln compares all generated Scenarios with each other to make sure all are unique. This Scenario may be a duplicate of a Scenario generated from a previous template. The Scenario:\n ${ scenario_description_for_logs({ scenario }) }`,
|
|
375
377
|
)
|
|
376
378
|
// Discuss: Should we add more structure to Misbehaviors to give them
|
|
377
|
-
// context levels (Feature,
|
|
379
|
+
// context levels (Feature, template Scenario, generated Scenario)
|
|
378
380
|
misfits.add({ warnings: [ warn_too_few ] });
|
|
379
381
|
}
|
|
380
382
|
|
|
@@ -493,7 +495,7 @@ function get_generator_AST({ file_text, generator_path }) {
|
|
|
493
495
|
|
|
494
496
|
if ( generator_errors.length > 0 ) {
|
|
495
497
|
let syntax_error = log.error({ code: `ALK0232`, context: `generating files`, before: `\n\n`, },
|
|
496
|
-
`The author's
|
|
498
|
+
`The author's template .feature file at "${ generator_path }" has Gherkin syntax error(s). You might see more warnings above or below this one.\n`,
|
|
497
499
|
...get_error_stacks({ errors: generator_errors })
|
|
498
500
|
);
|
|
499
501
|
return { doc_AST: null, errors: [new Error( syntax_error )] };
|
|
@@ -503,14 +505,14 @@ function get_generator_AST({ file_text, generator_path }) {
|
|
|
503
505
|
|
|
504
506
|
if ( !doc_AST.feature ) {
|
|
505
507
|
let feature_error = log.error({ code: `ALK0233`, context: `generating files`, before: `\n\n`, },
|
|
506
|
-
`The author's
|
|
508
|
+
`The author's template .feature file at "${ generator_path }" needs to include the \`Feature\` keyword.`
|
|
507
509
|
);
|
|
508
510
|
return { doc_AST: null, errors: [new Error( feature_error )] };
|
|
509
511
|
}
|
|
510
512
|
|
|
511
513
|
// Should not get here
|
|
512
514
|
let unknown_error = log.error({ code: `ALK0234`, context: `generating files`, before: `\n\n`, },
|
|
513
|
-
`The author's
|
|
515
|
+
`The author's template .feature file at "${ generator_path }" ran into an unanticipated error when ALKiln tried to parse it. If you have a moment, we would love for you to send us your debug.txt.`
|
|
514
516
|
);
|
|
515
517
|
return { doc_AST, errors: [new Error( unknown_error )] };
|
|
516
518
|
}
|
|
@@ -657,7 +659,7 @@ function get_fore_Steps({ scenario }) {
|
|
|
657
659
|
if ( there_were_no_fore_Steps ) {
|
|
658
660
|
misfits.add({ errors: [
|
|
659
661
|
new Error( log.error({ code: `ALK0242`, context: `generating files`, },
|
|
660
|
-
`A
|
|
662
|
+
`A template Scenario's first Step must say how many constrained random answers tests to make. The template Scenario on line ${ scenario.location.line } is missing that Step. The Scenario's description is:\n${ description }`
|
|
661
663
|
) )
|
|
662
664
|
] });
|
|
663
665
|
} else {
|
|
@@ -724,7 +726,7 @@ function get_number_of_Scenarios_to_generate2({ step, prev_num_to_make }) {
|
|
|
724
726
|
// a way that is also maintainable?
|
|
725
727
|
warnings.push(
|
|
726
728
|
log.warn({ code: `ALK0238`, context: `generating files`, },
|
|
727
|
-
`A
|
|
729
|
+
`A template Scenario has too many Steps that set the number of tests to make. ALKiln will use the final number Step it finds or calculates. The current Step:\n ${ text }`
|
|
728
730
|
)
|
|
729
731
|
);
|
|
730
732
|
}
|
|
@@ -735,7 +737,7 @@ function get_number_of_Scenarios_to_generate2({ step, prev_num_to_make }) {
|
|
|
735
737
|
if ( number_matcher === null ) {
|
|
736
738
|
warnings.push(
|
|
737
739
|
log.warn({ code: `ALK0239`, context: `generating files`, },
|
|
738
|
-
`A
|
|
740
|
+
`A template Scenario is missing the number of tests to make. ALKiln will now make 1 constrained random answers test. The current Step:\n ${ text }`
|
|
739
741
|
)
|
|
740
742
|
);
|
|
741
743
|
return { num_to_make: 1, warnings, errors };
|
|
@@ -744,7 +746,7 @@ function get_number_of_Scenarios_to_generate2({ step, prev_num_to_make }) {
|
|
|
744
746
|
/** Too many numbers */
|
|
745
747
|
if ( number_matcher && number_matcher.length > 1 ) {
|
|
746
748
|
// Some feedback indicated using the last number makes more sense to people
|
|
747
|
-
// than the first
|
|
749
|
+
// than the first. Also, matches Story Table behavior.
|
|
748
750
|
user_num = parseInt( number_matcher[ number_matcher.length - 1 ]);
|
|
749
751
|
warnings.push(
|
|
750
752
|
log.warn({ code: `ALK0240`, context: `generating files`, },
|
|
@@ -759,14 +761,14 @@ function get_number_of_Scenarios_to_generate2({ step, prev_num_to_make }) {
|
|
|
759
761
|
}
|
|
760
762
|
|
|
761
763
|
let num_to_make = Math.min(
|
|
762
|
-
session_vars.
|
|
764
|
+
session_vars.get_max_tests_per_template_scenario(),
|
|
763
765
|
user_num
|
|
764
766
|
);
|
|
765
767
|
|
|
766
768
|
if ( num_to_make !== user_num ) {
|
|
767
769
|
warnings.push(
|
|
768
770
|
log.warn({ code: `ALK0241`, context: `generating files`, },
|
|
769
|
-
`The author asked to make ${ user_num } tests, but maximum allowed is ${ num_to_make }. ALKiln will now make ${ num_to_make } tests. If you want to increase the maximum, use the
|
|
771
|
+
`The author asked to make ${ user_num } tests, but maximum allowed is ${ num_to_make }. ALKiln will now make ${ num_to_make } tests. If you want to increase the maximum, use the ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO input. Read more about inputs at https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#workflow-inputs. The Step:\n ${ text }`
|
|
770
772
|
)
|
|
771
773
|
);
|
|
772
774
|
}
|
|
@@ -1099,7 +1101,7 @@ function get_final_ids({ step_text }) {
|
|
|
1099
1101
|
let ids = step_text.match( CAPTURE_QUESTION_IDS_REGEX );
|
|
1100
1102
|
|
|
1101
1103
|
if ( ids === null ) {
|
|
1102
|
-
let missing_ids_msg = `The constrained random answers Step is missing its question ids. ALKiln is unable to create random tests for this
|
|
1104
|
+
let missing_ids_msg = `The constrained random answers Step is missing its question ids. ALKiln is unable to create random tests for this template Scenario. The Step:\n${ step_text }`;
|
|
1103
1105
|
let error = log.error({ code: `ALK0250`, context: `generating files`, }, missing_ids_msg );
|
|
1104
1106
|
errors.push(new Error( error ));
|
|
1105
1107
|
}
|
|
@@ -1348,7 +1350,7 @@ function remove_warning_tag_placeholder({ new_text }) {
|
|
|
1348
1350
|
|
|
1349
1351
|
|
|
1350
1352
|
function add_total_tests({ scenario_group_str, num_tests }) {
|
|
1351
|
-
/** Paste in the total number of tests generated for this
|
|
1353
|
+
/** Paste in the total number of tests generated for this template Scenario.
|
|
1352
1354
|
*
|
|
1353
1355
|
* @param {object} obj - Named arguments
|
|
1354
1356
|
* @param {str} obj.scenario_group_str - The combined string of all the
|
|
@@ -1369,10 +1371,10 @@ function add_warnings_Scenario({ scenarios_str, warnings }) {
|
|
|
1369
1371
|
* Scenarios. Basically, a whole-generator-scope warning.
|
|
1370
1372
|
*
|
|
1371
1373
|
* Discuss: Add a pre-group Scenario for every group of generated Scenarios
|
|
1372
|
-
* identifying the
|
|
1374
|
+
* identifying the template Scenario of the subsequent new Scenarios.
|
|
1373
1375
|
* That is, even when there is no warning to give.
|
|
1374
1376
|
*
|
|
1375
|
-
* TODO: If/when we allow multiple
|
|
1377
|
+
* TODO: If/when we allow multiple template Scenarios, use the ID here too
|
|
1376
1378
|
*
|
|
1377
1379
|
* @param {object} obj - Named arguments
|
|
1378
1380
|
* @param {str} obj.scenarios_str - The str of the already existing Scenarios
|
|
@@ -1381,7 +1383,7 @@ function add_warnings_Scenario({ scenarios_str, warnings }) {
|
|
|
1381
1383
|
* @returns {str} - The text of the combined Scenarios.
|
|
1382
1384
|
* */
|
|
1383
1385
|
let tags = `@alkiln_extra_intro ${ ALKILN_WARNING_TAG }`;
|
|
1384
|
-
let keyword_line = `Scenario: ALKiln
|
|
1386
|
+
let keyword_line = `Scenario: ALKiln begs the author's forgiveness. The next tests might be flawed.`;
|
|
1385
1387
|
let step_text = get_goofy_warnings_Step_text();
|
|
1386
1388
|
|
|
1387
1389
|
let warnings_lines = warnings_as_list_of_lines({ warnings });
|
|
@@ -1435,7 +1437,7 @@ function get_warnings_docstring({ warnings_lines, indent }) {
|
|
|
1435
1437
|
* */
|
|
1436
1438
|
let doc_str_lines = [
|
|
1437
1439
|
`"""`,
|
|
1438
|
-
`―――――
|
|
1440
|
+
`――――― Template file warnings ―――――`,
|
|
1439
1441
|
...warnings_lines,
|
|
1440
1442
|
`―――――`,
|
|
1441
1443
|
`"""`,
|
|
@@ -1506,7 +1508,7 @@ function save_files({ tests_path, artifacts_path, file_strings }) {
|
|
|
1506
1508
|
|
|
1507
1509
|
let count = 1;
|
|
1508
1510
|
for ( let file_str of file_strings ) {
|
|
1509
|
-
let filename =
|
|
1511
|
+
let filename = `_alkiln_generated_${ count }_${ Date.now() }.feature`;
|
|
1510
1512
|
count++;
|
|
1511
1513
|
// Implementation note: If the files don't exist, this creates them
|
|
1512
1514
|
fs.appendFileSync( `${ tests_path }/${ filename }`, file_str );
|
package/lib/steps.js
CHANGED
|
@@ -420,11 +420,11 @@ When(/I? ?gets? to ?.*?(".*").*?with.*?(?:this data:)?/i, {timeout: -1}, async (
|
|
|
420
420
|
// Some errors happen in there to be closer to the relevant code. We'd have to try/catch anyway.
|
|
421
421
|
let error = await scope.setFields(scope, { var_data: ensured_var_data, id, ensure_navigation: true });
|
|
422
422
|
|
|
423
|
-
//
|
|
423
|
+
// Discuss: use Log class to throw this error?
|
|
424
424
|
if ( error.was_found ) { await scope.throwPageError( scope, { id: id }); }
|
|
425
425
|
resolve();
|
|
426
426
|
} catch ( err ) {
|
|
427
|
-
let err_msg = `ALKiln ran into an error it tried to answer fields on the page with the question id "${ id }". The text above might have more information.`
|
|
427
|
+
let err_msg = `ALKiln ran into an error when it tried to answer fields on the page with the question id "${ id }". The text above might have more information.`
|
|
428
428
|
reports.addToReport( scope, { type: `error`, code: `ALK0062`, value: err_msg });
|
|
429
429
|
// Throw error instead?
|
|
430
430
|
reject( err );
|
|
@@ -721,39 +721,48 @@ Then(/the "([^"]+)" link opens in (a new window|the same window)/, async (linkTe
|
|
|
721
721
|
await scope.afterStep(scope);
|
|
722
722
|
});
|
|
723
723
|
|
|
724
|
-
Then(/(?:the )?text(?: in)?(?: the)?(?: JSON)?(?: var)?(?:iable)?(?: )?"([^"]+)" should be/i, async (var_name, expected_value) => {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
724
|
+
Then(/(?:the )?text(?: in)?(?: for)?(?: the)?(?: JSON)?(?: var)?(?:iable)?(?: )?"([^"]+)" should be/i, async (var_name, expected_value) => {
|
|
725
|
+
/** Check that the text string of the page's JSON var value matches
|
|
726
|
+
* the given text string. Does not currently accept nested values
|
|
727
|
+
* like "foo.bar". The word "text" is required.
|
|
728
|
+
*
|
|
729
|
+
* The format for this step is something like this:
|
|
730
|
+
* ```
|
|
731
|
+
* Then the text in the JSON variable "var_name" should be
|
|
732
|
+
* """
|
|
733
|
+
* A single or
|
|
734
|
+
* multiline string
|
|
735
|
+
* """
|
|
736
|
+
* ```
|
|
737
|
+
*
|
|
738
|
+
* Note: ALKiln removes `\r` from both the expected and actual values.
|
|
739
|
+
*
|
|
740
|
+
* Sentence combinations that will work:
|
|
741
|
+
* the text in the JSON variable "variable_with_text_value" should be
|
|
742
|
+
* the text in the JSON var "variable_with_text_value" should be
|
|
743
|
+
* the text for "variable_with_text_value" should be
|
|
744
|
+
* text "variable_with_text_value" should be
|
|
745
|
+
* */
|
|
744
746
|
|
|
745
747
|
// Get JSON variable value
|
|
746
748
|
let { json_path, json } = await scope.savePageJSONData( scope );
|
|
749
|
+
|
|
750
|
+
// docassemble (at least sometimes) makes new lines with `\r\n`
|
|
747
751
|
let actual_value = json.variables[ var_name ];
|
|
752
|
+
actual_value = actual_value.replace(/\r/, ``);
|
|
753
|
+
// Normalize `expected_value` as well for backwards compatibility in case any
|
|
754
|
+
// authors are already compensating for docassemble
|
|
755
|
+
expected_value = expected_value.replace(/\r/, ``);
|
|
748
756
|
|
|
749
757
|
// Add warning to report if value is not as expected
|
|
750
758
|
if ( actual_value !== expected_value ) {
|
|
751
759
|
reports.addToReport( scope, {
|
|
752
760
|
type: `error`,
|
|
753
761
|
code: `ALK0083`,
|
|
754
|
-
value: `The variable "${ var_name }" was
|
|
755
|
-
+ `value on the "${ scope.page_id }" screen
|
|
756
|
-
+
|
|
762
|
+
value: `The variable "${ var_name }" was different from the expected `
|
|
763
|
+
+ `value on the "${ scope.page_id }" screen.`
|
|
764
|
+
+ `The expected values was\n` + expected_value
|
|
765
|
+
+ `\nwhile the actual value was\n` + actual_value
|
|
757
766
|
});
|
|
758
767
|
}
|
|
759
768
|
|
|
@@ -1058,6 +1067,22 @@ When(/I set the var(?:iable)? "([^"]+)" to "([^"]+)"/, { timeout: -1 }, async (
|
|
|
1058
1067
|
|
|
1059
1068
|
});
|
|
1060
1069
|
|
|
1070
|
+
When(/I set the var(?:iable)? "([^"]+)" to\s*$/, { timeout: -1 }, async ( var_name, answer ) => {
|
|
1071
|
+
/** Set a non-story table variable (with or without a choice associated with
|
|
1072
|
+
* it) to a value that can have new lines in it. Most useful for
|
|
1073
|
+
* `textarea`s.
|
|
1074
|
+
*
|
|
1075
|
+
* Example:
|
|
1076
|
+
* I set the var "benefits['SSI']" to
|
|
1077
|
+
* """
|
|
1078
|
+
* Some multline
|
|
1079
|
+
* value
|
|
1080
|
+
* """
|
|
1081
|
+
* */
|
|
1082
|
+
// `page` timeout will deal with custom timeout
|
|
1083
|
+
await scope.steps.set_regular_var( scope, var_name, answer )
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1061
1086
|
When(/I set the var(?:iable)? "([^"]+)" to ?(?:the)? ?(?:GitHub)? secret "([^"]+)"/i, { timeout: -1 }, async ( var_name, set_to_env_name ) => {
|
|
1062
1087
|
/* Set a non-story table variable to a secrete value. Same as the above, but reads the value from `process.env` */
|
|
1063
1088
|
// This could theoretically tap an element, so
|
package/lib/utils/Log.js
CHANGED
|
@@ -624,31 +624,28 @@ class Log {
|
|
|
624
624
|
} // Ends Log._stringify_logs()
|
|
625
625
|
|
|
626
626
|
_stringify_inline({ prev_line_type, inline_log }) {
|
|
627
|
-
/** Formats the
|
|
628
|
-
*
|
|
627
|
+
/** Formats the log to add a space or a new line separator. May end up with
|
|
628
|
+
* a leading space for now.
|
|
629
629
|
*
|
|
630
630
|
* @param { "inline"|"block"|null } prev_line_type - Required. The type of
|
|
631
631
|
* the previous log.
|
|
632
632
|
* @param { null|non-obj } inline_log - Required. The log.
|
|
633
633
|
*
|
|
634
|
-
* @returns { string } The formatted
|
|
634
|
+
* @returns { string } The formatted line.
|
|
635
635
|
*
|
|
636
636
|
* @example
|
|
637
637
|
* // Backticks are to make the surrounding white space more clear
|
|
638
638
|
* log._stringify_inline({ prev_line_type: null, inline_log: "apple" });
|
|
639
|
-
* //
|
|
639
|
+
* // "apple "
|
|
640
640
|
* log._stringify_inline({ prev_line_type: 'inline', inline_log: "apple" });
|
|
641
|
-
* //
|
|
641
|
+
* // "apple "
|
|
642
642
|
* log._stringify_inline({ prev_line_type: 'block', inline_log: "apple" });
|
|
643
|
-
* //
|
|
644
|
-
* // apple
|
|
643
|
+
* // "
|
|
644
|
+
* // apple"
|
|
645
645
|
* */
|
|
646
|
-
// An inline log's string starts with nothing if it's the first string...
|
|
647
646
|
let start = ``;
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
// ...or starts on a new line if it's after a block of text, like an error
|
|
651
|
-
// or object.
|
|
647
|
+
if ( prev_line_type === `inline` && !inline_log.startsWith(`\n`) ) { start = ` `; }
|
|
648
|
+
// This is actually the end of the block-type log
|
|
652
649
|
else if ( prev_line_type === `block` ) { start = `\n`; }
|
|
653
650
|
return `${ start }${ inline_log }`;
|
|
654
651
|
}
|
|
@@ -658,6 +655,8 @@ class Log {
|
|
|
658
655
|
* a "block"-type separator at the start. Can handle circular references.
|
|
659
656
|
* Catch ANY error.
|
|
660
657
|
*
|
|
658
|
+
* @param { "inline"|"block"|null } prev_line_type - Required. The type of
|
|
659
|
+
* the previous log.
|
|
661
660
|
* @param { Object } obj_log - Required. The object to format.
|
|
662
661
|
*
|
|
663
662
|
* @returns { string } The obj as a string plus a separator
|
|
@@ -669,15 +668,15 @@ class Log {
|
|
|
669
668
|
* // { fruit: 'bananas' }
|
|
670
669
|
* */
|
|
671
670
|
let start = ``;
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
671
|
+
// Avoid adding a new line to the start of the first line
|
|
672
|
+
let is_first_line = prev_line_type === null;
|
|
673
|
+
if ( !is_first_line ) { start = `\n`; }
|
|
675
674
|
|
|
676
675
|
try {
|
|
677
676
|
// Try to stringify the object.
|
|
678
677
|
return `${ start }${ this._inspect({ value: obj_log }) }`;
|
|
679
678
|
|
|
680
|
-
// Discuss: record
|
|
679
|
+
// Discuss: record the outer error?
|
|
681
680
|
} catch ( outer_error ) {
|
|
682
681
|
// If that fails, see if it has its own way of stringifying
|
|
683
682
|
try {
|
package/lib/utils/reports.js
CHANGED
|
@@ -263,25 +263,28 @@ reports.getPrintableScenario = function( scenario ) {
|
|
|
263
263
|
return report;
|
|
264
264
|
}; // Ends reports.getPrintableScenario()
|
|
265
265
|
|
|
266
|
-
let getLineText = function ({ type=`no type`, code=`ALK00rl`, value=``, indent=`` }) {
|
|
266
|
+
let getLineText = function ({ from_log, type=`no type`, code=`ALK00rl`, value=``, indent=`` }) {
|
|
267
267
|
|
|
268
|
-
value = `${ value.replace( /\n/g,
|
|
268
|
+
value = `${ value.replace( /\n/g, `\n` + indent ) }`;
|
|
269
269
|
|
|
270
270
|
if ( type === `as_is` ) { return `${ indent }${ value }`; }
|
|
271
271
|
|
|
272
272
|
let start = indent;
|
|
273
|
-
if ( type.includes(`outcome`) ) { start = `\n`; }
|
|
273
|
+
if ( type.includes(`outcome`) ) { start = `\n`; } // extra new line
|
|
274
274
|
|
|
275
275
|
if ( type.includes(`page_id`) ) { start += `screen id: `; }
|
|
276
|
-
else if ( type.includes(`warning`) ) { start += `🔎 ${ code } WARNING: `; }
|
|
277
|
-
else if ( type.includes(`error`) ) { start +=
|
|
278
|
-
|
|
279
|
-
return
|
|
280
|
-
|
|
276
|
+
else if ( type.includes(`warning`) && !from_log ) { start += `🔎 ${ code } WARNING: `; }
|
|
277
|
+
else if ( type.includes(`error`) && !from_log ) { start += `🤕 ${ code } ERROR: `; } // extra new line
|
|
278
|
+
|
|
279
|
+
return `${ start }${ value }`;
|
|
280
|
+
|
|
281
|
+
}; // Ends getLine()
|
|
281
282
|
|
|
282
|
-
let getDebugLine = function ({ type=``, code=`
|
|
283
|
+
let getDebugLine = function ({ from_log, type=``, code=`ALK00rd`, value=``, indent=`` }) {
|
|
283
284
|
/** In debug log lines, include all the information possible. */
|
|
284
285
|
|
|
286
|
+
// Discuss: How to prevent saving in the debug log twice
|
|
287
|
+
if ( from_log ) { return value; }
|
|
285
288
|
// `plain` is usually used for visual clarity and shouldn't have
|
|
286
289
|
// codes and such associated with it. Return right away.
|
|
287
290
|
if ( type.includes(`plain`) ) { return `${ indent }${ value }`; }
|
|
@@ -310,7 +313,7 @@ let getDebugLine = function ({ type=``, code=`ALK0278`, value=``, indent=`` }) {
|
|
|
310
313
|
if ( type.includes(`warning`) ) { description_parts.push( `WARNING` ); }
|
|
311
314
|
if ( type.includes(`error`) ) { description_parts.push( `ERROR` ); }
|
|
312
315
|
// Default
|
|
313
|
-
if ( description_parts.length <= 0 ) { description_parts.push( `UNKNOWN` ); }
|
|
316
|
+
if ( description_parts.length <= 0 ) { description_parts.push( `UNKNOWN LOG TYPE` ); }
|
|
314
317
|
|
|
315
318
|
start = [ start, ...description_parts ].join(` `);
|
|
316
319
|
|
|
@@ -73,7 +73,7 @@ session_vars.get_setup_timeout_ms = function () {
|
|
|
73
73
|
return timeout;
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
session_vars.
|
|
76
|
+
session_vars.get_max_tests_per_template_scenario = function() {
|
|
77
77
|
/** Returns the maximum number of tests to generate for any one generator
|
|
78
78
|
* Scenario.
|
|
79
79
|
*
|
|
@@ -82,7 +82,7 @@ session_vars.get_max_tests_per_generator_scenario = function() {
|
|
|
82
82
|
*
|
|
83
83
|
* @returns {int} - Max number of tests for one generator Scenario
|
|
84
84
|
* */
|
|
85
|
-
return process.env.
|
|
85
|
+
return process.env.ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO || 40;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
session_vars.get_da_server_url = function () {
|
package/package.json
CHANGED
|
@@ -9,7 +9,8 @@ module.exports = fixts = {};
|
|
|
9
9
|
*
|
|
10
10
|
* TODO
|
|
11
11
|
* Research: Can we get back null or undefined without syntax errors? (unlikely)
|
|
12
|
-
* Research: Can feature.children
|
|
12
|
+
* Research: Can a feature.children item exist without having a `.scenario`
|
|
13
|
+
* prop? (unlikely)
|
|
13
14
|
* */
|
|
14
15
|
|
|
15
16
|
fixts.generator_missing_Feature = {};
|
|
@@ -51,22 +51,6 @@ simple.exact_log_codes = [
|
|
|
51
51
|
simple.num_Scenarios = 1;
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
/** TODO: 2 generator Steps in one Scenario */
|
|
55
|
-
|
|
56
|
-
`Feature: I generate constrained random tests
|
|
57
|
-
|
|
58
|
-
Scenario: I fail with 2 generator Steps in 1 Scenario
|
|
59
|
-
Given ALKiln makes 2 constrained answers tests
|
|
60
|
-
Given I start the interview at "form_entrypoint_file_name"
|
|
61
|
-
And I generate 2 constrained random tests that get to "end" when I pick from:
|
|
62
|
-
| var | possible_values |
|
|
63
|
-
| other_var | val1;; val2;; val3 |
|
|
64
|
-
And I generate 1 constrained random test that gets to "end" when I pick from:
|
|
65
|
-
| var | possible_values |
|
|
66
|
-
| user_choice | correct;; wrong |
|
|
67
|
-
`;
|
|
68
|
-
|
|
69
|
-
|
|
70
54
|
/**
|
|
71
55
|
* ===========
|
|
72
56
|
* Minimal required Step text:
|
|
@@ -677,10 +661,10 @@ fixts.warnings.no_number.expected =
|
|
|
677
661
|
Feature: I generate constrained random tests (generated by ALKiln)
|
|
678
662
|
|
|
679
663
|
@alkiln_extra_intro @alkiln_generator_warning
|
|
680
|
-
Scenario: ALKiln
|
|
664
|
+
Scenario: ALKiln begs the author's forgiveness. The next tests might be flawed.
|
|
681
665
|
Then ALKiln warns the author about a generator ${ fixts.FLUB_PLACEHOLDER }:
|
|
682
666
|
"""
|
|
683
|
-
―――――
|
|
667
|
+
――――― Template file warnings ―――――
|
|
684
668
|
🔎 ALK0239 ${ fixts.WARNING_PLACEHOLDER }
|
|
685
669
|
―――――
|
|
686
670
|
"""
|
|
@@ -713,10 +697,10 @@ fixts.warnings.multiple_numbers.expected =
|
|
|
713
697
|
Feature: I generate constrained random tests (generated by ALKiln)
|
|
714
698
|
|
|
715
699
|
@alkiln_extra_intro @alkiln_generator_warning
|
|
716
|
-
Scenario: ALKiln
|
|
700
|
+
Scenario: ALKiln begs the author's forgiveness. The next tests might be flawed.
|
|
717
701
|
Then ALKiln warns the author about a generator ${ fixts.FLUB_PLACEHOLDER }:
|
|
718
702
|
"""
|
|
719
|
-
―――――
|
|
703
|
+
――――― Template file warnings ―――――
|
|
720
704
|
🔎 ALK0240 ${ fixts.WARNING_PLACEHOLDER }
|
|
721
705
|
―――――
|
|
722
706
|
"""
|
|
@@ -750,10 +734,10 @@ fixts.warnings.multiple_number_Steps.expected =
|
|
|
750
734
|
Feature: I generate constrained random tests (generated by ALKiln)
|
|
751
735
|
|
|
752
736
|
@alkiln_extra_intro @alkiln_generator_warning
|
|
753
|
-
Scenario: ALKiln
|
|
737
|
+
Scenario: ALKiln begs the author's forgiveness. The next tests might be flawed.
|
|
754
738
|
Then ALKiln warns the author about a generator ${ fixts.FLUB_PLACEHOLDER }:
|
|
755
739
|
"""
|
|
756
|
-
―――――
|
|
740
|
+
――――― Template file warnings ―――――
|
|
757
741
|
🔎 ALK0238 ${ fixts.WARNING_PLACEHOLDER }
|
|
758
742
|
―――――
|
|
759
743
|
"""
|
|
@@ -787,10 +771,10 @@ fixts.warnings.multiple_number_Steps_and_numbers.expected =
|
|
|
787
771
|
Feature: I generate constrained random tests (generated by ALKiln)
|
|
788
772
|
|
|
789
773
|
@alkiln_extra_intro @alkiln_generator_warning
|
|
790
|
-
Scenario: ALKiln
|
|
774
|
+
Scenario: ALKiln begs the author's forgiveness. The next tests might be flawed.
|
|
791
775
|
Then ALKiln warns the author about a generator ${ fixts.FLUB_PLACEHOLDER }:
|
|
792
776
|
"""
|
|
793
|
-
―――――
|
|
777
|
+
――――― Template file warnings ―――――
|
|
794
778
|
🔎 ALK0240 ${ fixts.WARNING_PLACEHOLDER }
|
|
795
779
|
🔎 ALK0238 ${ fixts.WARNING_PLACEHOLDER }
|
|
796
780
|
🔎 ALK0240 ${ fixts.WARNING_PLACEHOLDER }
|
|
@@ -840,10 +824,10 @@ Feature: I generate valid constrained random answers tests 1 (generated by ALKil
|
|
|
840
824
|
# The 1st of 2 related tests. This 1st one tests uniqueness within one Scenario.
|
|
841
825
|
|
|
842
826
|
@alkiln_extra_intro @alkiln_generator_warning
|
|
843
|
-
Scenario: ALKiln
|
|
827
|
+
Scenario: ALKiln begs the author's forgiveness. The next tests might be flawed.
|
|
844
828
|
Then ALKiln warns the author about a generator ${ fixts.FLUB_PLACEHOLDER }:
|
|
845
829
|
"""
|
|
846
|
-
―――――
|
|
830
|
+
――――― Template file warnings ―――――
|
|
847
831
|
🔎 ALK0260 ${ fixts.WARNING_PLACEHOLDER }
|
|
848
832
|
―――――
|
|
849
833
|
"""
|
|
@@ -868,10 +852,10 @@ Feature: I generate valid constrained random answers tests 2 (generated by ALKil
|
|
|
868
852
|
# The 2nd of 2 related tests. Because ALKiln will try to only generate unique tests between all the generators, this 2nd one tests uniqueness of tests between generators. Only the body of the Scenario should matter for uniqueness.
|
|
869
853
|
|
|
870
854
|
@alkiln_extra_intro @alkiln_generator_warning
|
|
871
|
-
Scenario: ALKiln
|
|
855
|
+
Scenario: ALKiln begs the author's forgiveness. The next tests might be flawed.
|
|
872
856
|
Then ALKiln warns the author about a generator ${ fixts.FLUB_PLACEHOLDER }:
|
|
873
857
|
"""
|
|
874
|
-
―――――
|
|
858
|
+
――――― Template file warnings ―――――
|
|
875
859
|
🔎 ALK0260 ${ fixts.WARNING_PLACEHOLDER }
|
|
876
860
|
―――――
|
|
877
861
|
"""
|
|
@@ -888,6 +872,43 @@ fixts.warnings.fewer_unique_than_requested.included_log_codes = [
|
|
|
888
872
|
];
|
|
889
873
|
|
|
890
874
|
|
|
875
|
+
fixts.warnings.over_max = {};
|
|
876
|
+
fixts.warnings.over_max.max_num_allowed = 1;
|
|
877
|
+
fixts.warnings.over_max.arg =
|
|
878
|
+
`Feature: I generate constrained random tests
|
|
879
|
+
|
|
880
|
+
Scenario: I'm warned of asking for more than the max num tests
|
|
881
|
+
Given ALKiln makes 2 constrained random tests
|
|
882
|
+
Given I start the interview at "test_kickout"
|
|
883
|
+
And ALKiln gets to "end" when I pick from:
|
|
884
|
+
| var | possible_values |
|
|
885
|
+
| user_choice | correct;; wrong |`;
|
|
886
|
+
fixts.warnings.over_max.expected =
|
|
887
|
+
`@alkiln @generated @randomized @constraints
|
|
888
|
+
Feature: I generate constrained random tests (generated by ALKiln)
|
|
889
|
+
|
|
890
|
+
@alkiln_extra_intro @alkiln_generator_warning
|
|
891
|
+
Scenario: ALKiln begs the author's forgiveness. The next tests might be flawed.
|
|
892
|
+
Then ALKiln warns the author about a generator ${ fixts.FLUB_PLACEHOLDER }:
|
|
893
|
+
"""
|
|
894
|
+
――――― Template file warnings ―――――
|
|
895
|
+
🔎 ALK0241 ${ fixts.WARNING_PLACEHOLDER }
|
|
896
|
+
―――――
|
|
897
|
+
"""
|
|
898
|
+
|
|
899
|
+
@scenario1_of_1
|
|
900
|
+
Scenario: I'm warned of asking for more than the max num tests (1 of 1)
|
|
901
|
+
Given I start the interview at "test_kickout"
|
|
902
|
+
And I get to any of the question ids ["end"] with this data:
|
|
903
|
+
| var | value |
|
|
904
|
+
${ fixts.RANDOM_TABLE_PLACEHOLDER }`;
|
|
905
|
+
fixts.warnings.over_max.exact_log_codes = [
|
|
906
|
+
`ALK0237`, `ALK0241`, `ALK0243`, `ALK0244`,
|
|
907
|
+
`ALK0245`, `ALK0248`, `ALK0249`, `ALK0246`, `ALK0246`, `ALK0246`, `ALK0255`, `ALK0257`, `ALK0259`, `ALK0246`,
|
|
908
|
+
`ALK0261`, `ALK0262`, `ALK0264`,
|
|
909
|
+
];
|
|
910
|
+
|
|
911
|
+
|
|
891
912
|
/**
|
|
892
913
|
* ===========
|
|
893
914
|
* Silent warnings
|
|
@@ -20,7 +20,7 @@ const TABLE_ROW_REGEX = /^(?!(?:.*\| var \| value \|))(.*\| .* \|)/gm;
|
|
|
20
20
|
const TABLE_PLACEHOLDER_REGEX = new RegExp(`(${ RANDOM_TABLE_PLACEHOLDER }(\n)?)+`, `g`);
|
|
21
21
|
|
|
22
22
|
const WARNING_PLACEHOLDER = fixtures.WARNING_PLACEHOLDER;
|
|
23
|
-
const WARNING_MSG_REGEX = /(ALK\d{4} )(.*\d{4}-\d\d-\d\d \d\d:\d\d:\d\dUTC.*(?:\n
|
|
23
|
+
const WARNING_MSG_REGEX = /(ALK\d{4} )(.*\d{4}-\d\d-\d\d \d\d:\d\d:\d\dUTC.*(?!\n ―)(?:\n)?.+)/g;
|
|
24
24
|
|
|
25
25
|
const FLUB_PLACEHOLDER = fixtures.FLUB_PLACEHOLDER;
|
|
26
26
|
const FLUB_REGEX = fixtures.FLUB_OPTIONS_REGEX;
|
|
@@ -96,6 +96,11 @@ function mutate_globals_with({ testing_vals, do_compare=false }) {
|
|
|
96
96
|
// Reset log codes
|
|
97
97
|
logger.clear_internal_log_records();
|
|
98
98
|
|
|
99
|
+
process.env.ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO = 40;
|
|
100
|
+
if ( testing_vals.max_num_allowed ) {
|
|
101
|
+
process.env.ALKILN_MAX_RANDOM_TESTS_PER_SCENARIO = testing_vals.max_num_allowed;
|
|
102
|
+
}
|
|
103
|
+
|
|
99
104
|
// Fresh namespace
|
|
100
105
|
let generator = new TestGenerator();
|
|
101
106
|
// Mutates `fixture`
|
|
@@ -505,6 +510,23 @@ describe(`Constrained random answers parser, when given a valid generator Scenar
|
|
|
505
510
|
});
|
|
506
511
|
});
|
|
507
512
|
|
|
513
|
+
describe(`that asks for over max # of tests allowed - 1 -`, function () {
|
|
514
|
+
|
|
515
|
+
before(function () { mutate_globals_with({ testing_vals: fixtures.warnings.over_max }); });
|
|
516
|
+
|
|
517
|
+
let outer = fixtures.warnings.over_max;
|
|
518
|
+
it(`has these exact log codes with warnings: ${ JSON.stringify( outer.exact_log_codes )}`, function () {
|
|
519
|
+
expect( get_log_codes({ logger }) ).to.have.all.members( fixture.exact_log_codes );
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it(`returns no errors`, function () { expect( errors ).to.have.lengthOf( 0 ); });
|
|
523
|
+
|
|
524
|
+
it(`has max # of tests - 1`, function () {
|
|
525
|
+
expect( comparable_text( new_contents )).to.equal( fixture.expected );
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
});
|
|
529
|
+
|
|
508
530
|
describe(`that has a blank var value in a generator column`, function () {
|
|
509
531
|
|
|
510
532
|
before(function () { mutate_globals_with({ testing_vals: fixtures.empty_string_value }); });
|