@suffolklitlab/alkiln 5.13.1 ā 5.13.2-fix-da-dom-fieldset-1
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/github_server.yml +3 -3
- package/.github/workflows/playground.yml +3 -3
- package/CHANGELOG.md +13 -1
- package/README.md +26 -5
- package/docs/constrained_random_tests.md +27 -0
- package/docs/decisions/move_runtime_config_to_sources_folder.md +1 -0
- package/docs/prioritize_constrained_random_tests.md +101 -0
- package/docs/seprate_file_generation_GitHub_action.md +11 -0
- package/lib/scope.js +59 -13
- package/lib/steps.js +2 -1
- package/package.json +1 -1
|
@@ -52,8 +52,8 @@ jobs:
|
|
|
52
52
|
EMPTY_STRING: ""
|
|
53
53
|
NORMAL_USER_EMAIL: alkiln@example.com
|
|
54
54
|
NORMAL_USER_PASSWORD: User123^
|
|
55
|
-
WRONG_EMAIL
|
|
56
|
-
WRONG_PASSWORD
|
|
55
|
+
WRONG_EMAIL: wrong_email@example.com
|
|
56
|
+
WRONG_PASSWORD: wrong_password
|
|
57
57
|
|
|
58
58
|
steps:
|
|
59
59
|
# Place the root directory in this branch to access
|
|
@@ -160,7 +160,7 @@ jobs:
|
|
|
160
160
|
#### Developer note: You can probably leave the rest out
|
|
161
161
|
#### To learn more, see https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#optional-inputs
|
|
162
162
|
ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
|
|
163
|
-
ALKILN_VERSION:
|
|
163
|
+
ALKILN_VERSION: da_DOM_fieldset
|
|
164
164
|
|
|
165
165
|
#### Developer note: Example of making an issue when tests fail
|
|
166
166
|
#### that includes the text of the failure output file
|
|
@@ -47,8 +47,8 @@ jobs:
|
|
|
47
47
|
NORMAL_USER_PASSWORD: ${{ secrets.USER_NO_PERMISSIONS_PASSWORD }}
|
|
48
48
|
NORMAL_USER_API_KEY: ${{ secrets.USER_NO_PERMISSIONS_API_KEY }}
|
|
49
49
|
INVALID_API_KEY: invalidAPIkey
|
|
50
|
-
WRONG_EMAIL
|
|
51
|
-
WRONG_PASSWORD
|
|
50
|
+
WRONG_EMAIL: wrong_email@example.com
|
|
51
|
+
WRONG_PASSWORD: wrong_password
|
|
52
52
|
EMPTY_STRING: ""
|
|
53
53
|
SECRET_VAR1: secret-var1-value
|
|
54
54
|
SECRET_VAR2: secret-var2-value
|
|
@@ -81,4 +81,4 @@ jobs:
|
|
|
81
81
|
# want to check up on this.
|
|
82
82
|
ALKILN_TAG_EXPRESSION: "${{ env.ALKILN_TAG_EXPRESSION }}"
|
|
83
83
|
#### Developer note: You can probably leave this out
|
|
84
|
-
ALKILN_VERSION:
|
|
84
|
+
ALKILN_VERSION: da_DOM_fieldset
|
package/CHANGELOG.md
CHANGED
|
@@ -45,6 +45,18 @@ Format:
|
|
|
45
45
|
|
|
46
46
|
## [Unreleased]
|
|
47
47
|
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Updates to latest docassemble HTML. See [#960](https://github.com/SuffolkLITLab/ALKiln/issues/960). This handles more than what is necessary, but it there's little downside.
|
|
51
|
+
|
|
52
|
+
## [5.13.2] - 2024-11-23
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- Fixes download Step unable to use a partial filename match. The Step needed the whole name, including the extension. See [#725](https://github.com/SuffolkLITLab/ALKiln/issues/725).
|
|
57
|
+
|
|
58
|
+
## [5.13.1] - 2024-10-23
|
|
59
|
+
|
|
48
60
|
### Changed
|
|
49
61
|
|
|
50
62
|
- Moves "expected" status error to only be visible to internal test errors. Closes [#933](https://github.com/SuffolkLITLab/ALKiln/issues/933).
|
|
@@ -52,7 +64,6 @@ Format:
|
|
|
52
64
|
### Fixed
|
|
53
65
|
|
|
54
66
|
- Detects failed sign in. Closes [#918](https://github.com/SuffolkLITLab/ALKiln/issues/918).
|
|
55
|
-
- Updates pdfjs-dist for node v20 and v22. See [#952](https://github.com/SuffolkLITLab/ALKiln/issues/952).
|
|
56
67
|
|
|
57
68
|
### Internal
|
|
58
69
|
|
|
@@ -63,6 +74,7 @@ Format:
|
|
|
63
74
|
- Adds decision docs
|
|
64
75
|
- Updated CONTRIBUTING.md
|
|
65
76
|
- Added example.env, closes [#374](https://github.com/SuffolkLITLab/ALKiln/issues/374)
|
|
77
|
+
- Updates pdfjs-dist for node v20 and v22. See [#952](https://github.com/SuffolkLITLab/ALKiln/issues/952).
|
|
66
78
|
|
|
67
79
|
## [5.13.0] - 2024-07-11
|
|
68
80
|
|
package/README.md
CHANGED
|
@@ -41,25 +41,46 @@ Read about contributing in our [CONTRIBUTING.md document](CONTRIBUTING.md). Here
|
|
|
41
41
|
## Cheat sheet
|
|
42
42
|
|
|
43
43
|
*Once you've already read the contributing documentation, you can use these as quick reminders for running our internal tests.*
|
|
44
|
-
|
|
44
|
+
|
|
45
|
+
### `setup` before starting development of a feature/fix
|
|
46
|
+
|
|
47
|
+
Set up for the cucumber integration tests.
|
|
48
|
+
|
|
49
|
+
Add the feature branch name to your `.env` file:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
BRANCH=42_feat_life_the_univers_and_everything
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Then create the project on the server:
|
|
45
56
|
|
|
46
57
|
```bash
|
|
47
58
|
npm run setup
|
|
48
59
|
```
|
|
49
60
|
|
|
50
|
-
|
|
61
|
+
### Run tests repeatedly
|
|
62
|
+
|
|
63
|
+
Run all cucumber tests that are not currently blocked by upstream changes:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm run pass
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Trigger cucumber tests with specific tags:
|
|
51
70
|
|
|
52
71
|
```bash
|
|
53
|
-
npm run cucumber
|
|
72
|
+
npm run cucumber "@tagname"
|
|
54
73
|
```
|
|
55
74
|
|
|
56
|
-
|
|
75
|
+
Run the unit tests:
|
|
57
76
|
|
|
58
77
|
```bash
|
|
59
78
|
npm run unit
|
|
60
79
|
```
|
|
61
80
|
|
|
62
|
-
|
|
81
|
+
### Always run `takedown` before running `setup` again
|
|
82
|
+
|
|
83
|
+
If you or someone else changes the interview code in our `./docassemble` directory, you have to delete the code currently on the testing server before running `setup` again. Also do this when you're done with the feature/fix or starting a new feature/fix.
|
|
63
84
|
|
|
64
85
|
```bash
|
|
65
86
|
npm run takedown
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Constrained random input tests architecture and decisions
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## What should throw an error in the generator parsing stage?
|
|
5
|
+
|
|
6
|
+
Nothing? Should it all get reported in the ALKiln tests?
|
|
7
|
+
|
|
8
|
+
## Author wrote multiple generator Steps in one Scenario
|
|
9
|
+
|
|
10
|
+
Discuss: Warning or error
|
|
11
|
+
|
|
12
|
+
We think the easiest and clearest reaction is to refuse to generate any test and give a warning or error.
|
|
13
|
+
|
|
14
|
+
Other options:
|
|
15
|
+
|
|
16
|
+
- Use the last Step and delete the others. This may be hard to do. We'd need to mutate the Scenario's file text lines, maybe as we were looping through them. It would take some research
|
|
17
|
+
- Use the first Step and leave the others as they are. This would generate a broken test, but the test would at least work until it gets to the next generator Step line. We could add a Step in the core code for the generator Step and use it to give a meaningful error instead of just letting cucumber set a status of `undefined`. Maybe that would at least be a bit more clear, but I think not as clear as just refusing to generate and producing a warning or error.
|
|
18
|
+
|
|
19
|
+
Complications:
|
|
20
|
+
|
|
21
|
+
- What if there are other Steps between and after the generator Steps? Do we keep those and put them all at the bottom? Do we only keep the ones after the chosen generator Step?
|
|
22
|
+
|
|
23
|
+
If we do decide to pick a generator Step, maybe we can make sure to pick one that is otherwise valid. That is, it has a dataTable that has rows, is otherwise correctly formatted, etc.
|
|
24
|
+
|
|
25
|
+
## Grammar
|
|
26
|
+
|
|
27
|
+
Post MVP, discuss: should generator Steps without a number, but with the word "tests" generate 2 Scenarios?
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Moving the runtime config to the sources folder would make it very easy for each Project to have its own runtime config which would make ALKilnInThePlayground work more smoothly. On the other hand, it could be confusing to authors (though it's fine if they check it in now, it may not be fine later). On the other other hand, we could delete it after every test suite run and and "temp" and "alkiln" to the name. If there are multiple sources folders with config files then...? We'll just have to pick one?
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Constrained random input tests
|
|
2
|
+
|
|
3
|
+
I do 10 constrained random tests
|
|
4
|
+
|
|
5
|
+
## Why do people want purely random input tests?
|
|
6
|
+
|
|
7
|
+
1. They don't have time to write individual specific tests
|
|
8
|
+
2. They are worried they haven't covered all eventualities
|
|
9
|
+
|
|
10
|
+
## Problems for purely random tests
|
|
11
|
+
|
|
12
|
+
- Non-specific input. E.g. a name field might get a sentence. Can we do our best? That would be more work.
|
|
13
|
+
|
|
14
|
+
## Why would people prefer constrained random input tests?
|
|
15
|
+
|
|
16
|
+
- They want to make the tests more focused on certain scenarios and avoid wasting time on unimportant tests. For example, sometimes several options actually give the same answer. A purely random test would give more preference to the repeated answer.
|
|
17
|
+
- **Some fields might require very specific answers, like a real lawyer's bar number or a specific file name for uploading exhibits.**
|
|
18
|
+
- Narrow down the paths at some points, like always choosing that you have minor children
|
|
19
|
+
- **Even if the user doesn't know it, this is the most flexible option**
|
|
20
|
+
|
|
21
|
+
## Note
|
|
22
|
+
|
|
23
|
+
~~If in the future we want to add the option to not specify, then we can and it won't be a breaking change.~~
|
|
24
|
+
|
|
25
|
+
We have to decide what it means to allow a person to not specify. E.g if excluding a row means don't give an answer to that variable at all, then it can't in future mean "give a random answer to this field". Options:
|
|
26
|
+
|
|
27
|
+
- (bad) Excluding a row means don't give an answer. Including a row with a blank answer means give a random answer.
|
|
28
|
+
- (bad) Including a row with a blank answer means don't give an answer. Give unlisted variables random answers.
|
|
29
|
+
- Or should we require a string (e.g. ALK_NO_ANSWER) for fields that should not get random inputs?
|
|
30
|
+
- (most easy purely random input method to implement without changing current code?) Or specify randomness (e.g. ALK_RANDOM_ANSWER)?
|
|
31
|
+
- **(backwards compatible because we can become more flexible later) Require both of the above?**
|
|
32
|
+
|
|
33
|
+
Which of these is worse? Mind reading can be bad. On the other hand, specifying everything may be somewhat antithetical to the 2 goals of people who want purely random input tests. (added to the interview an unrequired value, but forgot to add it to the test, they won't test all eventualities).
|
|
34
|
+
|
|
35
|
+
Possible solutions for people who want purely random tests or bits of tests:
|
|
36
|
+
- Add a Step for answering randomly for a whole test.
|
|
37
|
+
- Add a Step for answering randomly up to various page ids (list is allowed).
|
|
38
|
+
- Add Step to answer randomly for one page.
|
|
39
|
+
|
|
40
|
+
## Flexible constraints?
|
|
41
|
+
|
|
42
|
+
Post MVP
|
|
43
|
+
|
|
44
|
+
- Allow number range "ALKiln: [1, 5)"? Or they have to do "1, 2, 3, 4"
|
|
45
|
+
- "ALKiln: year 1965 - 1999"
|
|
46
|
+
- "ALKiln: date [today, today - 10)"
|
|
47
|
+
- "ALKiln: max length 3" (of 5 checkbox answers)
|
|
48
|
+
- "ALKiln: name"
|
|
49
|
+
- "ALKiln: number" (and other types like "currency") (we can probably figure those out)
|
|
50
|
+
|
|
51
|
+
## Note
|
|
52
|
+
|
|
53
|
+
MVP of random input tests
|
|
54
|
+
|
|
55
|
+
## no seeds
|
|
56
|
+
|
|
57
|
+
seeds can break more easily. instead we save the spec of the test(s)
|
|
58
|
+
|
|
59
|
+
## MVP
|
|
60
|
+
|
|
61
|
+
save the spec of the test...somewhere. Probably using special name to say
|
|
62
|
+
"rerun" and then change the name at the end of the run.
|
|
63
|
+
retry the test somehow (if needed)...
|
|
64
|
+
present the test and results to the user
|
|
65
|
+
|
|
66
|
+
## Better
|
|
67
|
+
|
|
68
|
+
Avoid repeating previous random tests
|
|
69
|
+
Avoid repeating tests of users (which is much(?) harder)
|
|
70
|
+
|
|
71
|
+
## Specifics
|
|
72
|
+
|
|
73
|
+
Create a file with a certain # of Scenarios and run it
|
|
74
|
+
- Does cucumber decide the tests it'll run at runtime or before? I think the former
|
|
75
|
+
- Is it better to split tests up into separate files if there are a lot?
|
|
76
|
+
- Is it better or worse to help the developer feel the magnitude of the #
|
|
77
|
+
of tests they're running (using Example) [Answer: worse]
|
|
78
|
+
- How to handle separate constrained random tables?
|
|
79
|
+
|
|
80
|
+
## right now
|
|
81
|
+
[r1 opt 1, r1 opt 2, r1 opt 3, name input, date input, description input]
|
|
82
|
+
|
|
83
|
+
## need for random
|
|
84
|
+
|
|
85
|
+
detect r1 while looping through the fields
|
|
86
|
+
[r1 opt 1, r1 opt 2, r1 opt 3, name input, date input, description input]
|
|
87
|
+
Con: as we loop through, we don't know the number of options at each particular instance
|
|
88
|
+
|
|
89
|
+
or make grouped fields into groups. Basically group r1:
|
|
90
|
+
[[r1 opt 1, r1 opt 2, r1 opt 3], name input, date input, description input]
|
|
91
|
+
|
|
92
|
+
## why not generate a test that uses all the constraints and thus is fully random?
|
|
93
|
+
|
|
94
|
+
Because we'd have to parse docassembled *compiled* files, which is very hard
|
|
95
|
+
|
|
96
|
+
Alternative:
|
|
97
|
+
|
|
98
|
+
When the developer runs a test, they can use a Step to have it collect all possible fields that ALKiln sees during that test run.
|
|
99
|
+
Q: How effectively can we decode the fields? Or can we make a tool that will help a developer decode their fields? (Include an input that just decodes one encoded string (`atob()`))
|
|
100
|
+
Q: If the fields are multiple choice and the developer's test picks at least one choice from that group of fields, can we use that to properly decode all the names of the fields? Hesitant to do this because some values may be encoded differently from other values (I think).
|
|
101
|
+
Q: Is this more work than just refactoring how we group fields?
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Pros for separate action:
|
|
2
|
+
|
|
3
|
+
- Reflects nature of process - generating files is inherently separate.
|
|
4
|
+
- Potentially cleaner separation conceptually for us.
|
|
5
|
+
|
|
6
|
+
Cons:
|
|
7
|
+
|
|
8
|
+
- Main beef: Developing and maintaining GitHub actions is hard
|
|
9
|
+
- Authors have more in their workflow file
|
|
10
|
+
- Need to update existing workflows
|
|
11
|
+
- Need to update ALKilnSetup interview
|
package/lib/scope.js
CHANGED
|
@@ -514,13 +514,17 @@ module.exports = {
|
|
|
514
514
|
},
|
|
515
515
|
|
|
516
516
|
continue_button_selector: `fieldset.da-field-buttons button[type="submit"].btn-primary`,
|
|
517
|
+
continue_button_selector_backup: `.da-fieldset.da-field-buttons button[type="submit"].btn-primary`,
|
|
517
518
|
signature_selector: `fieldset .dasigsave`,
|
|
519
|
+
signature_selector_backup: `.da-fieldset .dasigsave`,
|
|
518
520
|
continue: async function ( scope ) {
|
|
519
521
|
/* Presses whatever button it finds that might lead to the next page. */
|
|
520
522
|
// Any selectors I find seem somewhat precarious.
|
|
521
523
|
let elem = await Promise.race([
|
|
522
524
|
scope.page.waitForSelector(scope.continue_button_selector), // other pages (this is the most consistent way)
|
|
525
|
+
scope.page.waitForSelector(scope.continue_button_selector_backup),
|
|
523
526
|
scope.page.waitForSelector(scope.signature_selector), // signature page
|
|
527
|
+
scope.page.waitForSelector(scope.signature_selector_backup),
|
|
524
528
|
]);
|
|
525
529
|
await elem.evaluate( el => { return el.className });
|
|
526
530
|
// Waits for navigation or user error
|
|
@@ -540,9 +544,11 @@ module.exports = {
|
|
|
540
544
|
// <button type="submit" class="btn btn-da btn-danger" name="X211bHRpcGxlX2Nob2ljZQ" value="0">exit</button>
|
|
541
545
|
// `buttons:` can be used in question blocks as choices
|
|
542
546
|
let regular = await scope.page.$(scope.continue_button_selector);
|
|
547
|
+
let regular_backup = await scope.page.$(scope.continue_button_selector_backup);
|
|
543
548
|
let signature = await scope.page.$(scope.signature_selector);
|
|
549
|
+
let signature_backup = await scope.page.$(scope.signature_selector_backup);
|
|
544
550
|
|
|
545
|
-
return regular !== null || signature !== null;
|
|
551
|
+
return regular !== null || regular_backup !== null || signature !== null || signature_backup !== null;
|
|
546
552
|
|
|
547
553
|
}, // Ends scope.continue_exists()
|
|
548
554
|
|
|
@@ -916,7 +922,7 @@ module.exports = {
|
|
|
916
922
|
// All the different types of fields
|
|
917
923
|
// buttons, canvases, inputs of all kinds, selects (dropdowns), textareas. Are there more?
|
|
918
924
|
// Will deal with `option` once inside `select`
|
|
919
|
-
let all_nodes = $( `#dasigpage, fieldset button, .daquestionactionbutton, fieldset input, .da-form-group input, .da-form-group select, form select, .da-form-group textarea` );
|
|
925
|
+
let all_nodes = $( `#dasigpage, fieldset button, .da-fieldset button, .daquestionactionbutton, fieldset input, da-fieldset input, .da-form-group input, .da-form-group select, form select, .da-form-group textarea` );
|
|
920
926
|
for ( let node of all_nodes ) {
|
|
921
927
|
// Decision: Do not abstract the below. There's too much data to pass around for it to make sense
|
|
922
928
|
let $node = $( node );
|
|
@@ -2635,6 +2641,7 @@ module.exports = {
|
|
|
2635
2641
|
|
|
2636
2642
|
let fullPage = true;
|
|
2637
2643
|
let signature_elem = await scope.page.$(scope.signature_selector);
|
|
2644
|
+
signature_elem = signature_elem || await scope.page.$(scope.signature_selector_backup);
|
|
2638
2645
|
if ( signature_elem !== null ) {
|
|
2639
2646
|
fullPage = false;
|
|
2640
2647
|
}
|
|
@@ -2781,7 +2788,7 @@ module.exports = {
|
|
|
2781
2788
|
await scope.afterStep(scope);
|
|
2782
2789
|
}, // Ends scope.steps.sign()
|
|
2783
2790
|
|
|
2784
|
-
download: async ( scope,
|
|
2791
|
+
download: async ( scope, full_or_partial_file_href ) => {
|
|
2785
2792
|
/* Taps the link that leads to the given filename to trigger downloading.
|
|
2786
2793
|
* and waits till the file has been downloaded before allowing the tests to continue.
|
|
2787
2794
|
* WARNING: Cannot download the same file twice in a single scenario.
|
|
@@ -2791,47 +2798,86 @@ module.exports = {
|
|
|
2791
2798
|
* TODO: Properly wait for download to complete. See notes in
|
|
2792
2799
|
* scope.js scope.detectDownloadComplete()
|
|
2793
2800
|
*/
|
|
2794
|
-
let [elem] = await scope.page.$$(`xpath/.//a[contains(@href, "${
|
|
2801
|
+
let [elem] = await scope.page.$$(`xpath/.//a[contains(@href, "${ full_or_partial_file_href }")]`);
|
|
2795
2802
|
|
|
2796
|
-
let msg = `"${
|
|
2803
|
+
let msg = `"${ full_or_partial_file_href }" seems to be missing on the page. Cannot find a link to that document.`;
|
|
2797
2804
|
if ( !elem ) { reports.addToReport(scope, { type: `error`, code: `ALK0152`, value: msg }); }
|
|
2798
2805
|
expect( elem, msg ).to.exist;
|
|
2799
2806
|
|
|
2800
2807
|
let failed_to_download = false;
|
|
2801
2808
|
let err_msg = "";
|
|
2802
2809
|
try {
|
|
2803
|
-
|
|
2810
|
+
|
|
2811
|
+
const { disposition, binaryStr } = await scope.page.evaluate(async function ( el ) {
|
|
2804
2812
|
const url = el.getAttribute("href");
|
|
2805
2813
|
return new Promise(async (resolve, reject) => {
|
|
2806
2814
|
const response = await fetch(url, {method: "GET"});
|
|
2807
2815
|
const reader = new FileReader();
|
|
2808
2816
|
reader.readAsBinaryString(await response.blob());
|
|
2809
|
-
|
|
2817
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
|
|
2818
|
+
reader.onload = () => resolve({
|
|
2819
|
+
disposition: response.headers.get(`Content-Disposition`),
|
|
2820
|
+
binaryStr: reader.result
|
|
2821
|
+
});
|
|
2810
2822
|
reader.onerror = () => reject(`š¤ ALK0153 ERROR: Error occurred on page when downloading ${ url }: ${ reader.error }`);
|
|
2811
2823
|
});
|
|
2812
2824
|
}, elem);
|
|
2825
|
+
|
|
2826
|
+
err_msg = `could not get the actual name of the file from the response headers.`;
|
|
2827
|
+
let actual_filename = await scope.steps.get_response_filename({
|
|
2828
|
+
disposition, default_name: full_or_partial_file_href
|
|
2829
|
+
});
|
|
2830
|
+
|
|
2813
2831
|
if (binaryStr !== '') {
|
|
2832
|
+
err_msg = `binary data for download was empty`;
|
|
2814
2833
|
const fileData = Buffer.from(binaryStr, 'binary');
|
|
2815
|
-
fs.writeFileSync(`${ scope.paths.scenario }/${
|
|
2816
|
-
reports.addToReport(scope, { type: `row info`, code: `ALK0154`, value: `Downloaded ${
|
|
2834
|
+
fs.writeFileSync(`${ scope.paths.scenario }/${ actual_filename }`, fileData);
|
|
2835
|
+
reports.addToReport(scope, { type: `row info`, code: `ALK0154`, value: `Downloaded "${ actual_filename }" (${ fileData.length } bytes) to ${ scope.paths.scenario }`});
|
|
2817
2836
|
} else {
|
|
2818
2837
|
failed_to_download = true;
|
|
2819
|
-
err_msg = `Couldn't download ${ filename }, binary data for download was empty`;
|
|
2820
2838
|
}
|
|
2839
|
+
|
|
2821
2840
|
} catch (error) {
|
|
2822
2841
|
failed_to_download = true;
|
|
2823
2842
|
err_msg = error;
|
|
2824
2843
|
}
|
|
2825
2844
|
|
|
2826
2845
|
if (failed_to_download) {
|
|
2827
|
-
reports.addToReport(scope, { type: `warning`, code: `ALK0155`, value: `Could not download file using fetch (${ err_msg }). ALKiln will now fallback to the click download method.` });
|
|
2828
|
-
scope.toDownload =
|
|
2846
|
+
reports.addToReport(scope, { type: `warning`, code: `ALK0155`, value: `Could not download a file matching the name "${ full_or_partial_file_href }" using fetch (${ err_msg }). ALKiln will now fallback to the click download method.` });
|
|
2847
|
+
scope.toDownload = full_or_partial_file_href;
|
|
2829
2848
|
// Should this be using `scope.tapElement`?
|
|
2830
2849
|
await elem.evaluate( elem => { return elem.click(); });
|
|
2831
2850
|
await scope.detectDownloadComplete( scope );
|
|
2832
2851
|
}
|
|
2833
2852
|
}, // Ends scope.steps.download()
|
|
2834
2853
|
|
|
2854
|
+
get_response_filename: async function({ disposition, default_name=`found_no_file_name.pdf` }) {
|
|
2855
|
+
/** Given a fetch response headers' Content-Disposition str, return the
|
|
2856
|
+
* filename of the response's file.
|
|
2857
|
+
*
|
|
2858
|
+
* @return { string | null } - Name of fetched file
|
|
2859
|
+
* */
|
|
2860
|
+
let filename = default_name;
|
|
2861
|
+
if ( disposition ) {
|
|
2862
|
+
filename = disposition.split(`;`)[1].split(`=`)[1];
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
// Handle potential UTF-8 encoded filenames
|
|
2866
|
+
if ( filename.toLowerCase().startsWith( `utf-8''` )) {
|
|
2867
|
+
filename = decodeURIComponent( filename.replace( /utf-8''/i, `` ));
|
|
2868
|
+
} else {
|
|
2869
|
+
// Replace starting and ending quotes if they exist
|
|
2870
|
+
filename = filename.replace( /^['"]/, `` ).replace( /['"]$/, `` );
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
// TODO: Add debug log here
|
|
2874
|
+
// console.log(`filename:`, filename);
|
|
2875
|
+
|
|
2876
|
+
// TODO: Add to the report if we had to use the default name
|
|
2877
|
+
|
|
2878
|
+
return filename;
|
|
2879
|
+
}, // Ends scope.steps.get_response_filename()
|
|
2880
|
+
|
|
2835
2881
|
compare_pdfs: async function (scope, {existing_pdf_path, new_pdf_path}) {
|
|
2836
2882
|
let existing_paths = await scope.findFiles(scope, {to_find_names: [existing_pdf_path]});
|
|
2837
2883
|
if (existing_paths.length == 0) {
|
|
@@ -2852,7 +2898,7 @@ module.exports = {
|
|
|
2852
2898
|
let removed_str = diffs.filter(part => part.removed).reduce((err_str, part) => {
|
|
2853
2899
|
return err_str + `- ${ part.value }\n`
|
|
2854
2900
|
}, 'The new PDF removed: \n');
|
|
2855
|
-
let msg = `The PDFs were
|
|
2901
|
+
let msg = `The PDFs were different.\nAdded:\n${ added_str }\nRemoved:\n${removed_str}\n\nThere might be more information if you actually look at the files. You can see the full PDFs at ${ full_existing_path } and ${ full_new_pdf_path}`;
|
|
2856
2902
|
reports.addToReport(scope, { type: `error`, code: `ALK0157`, value: msg });
|
|
2857
2903
|
scope.failed_pdf_compares.push(msg);
|
|
2858
2904
|
}
|
package/lib/steps.js
CHANGED
|
@@ -1285,10 +1285,11 @@ After(async function(scenario) {
|
|
|
1285
1285
|
if ( scope.failed_pdf_compares.length > 0) {
|
|
1286
1286
|
let msg = scope.failed_pdf_compares.reduce((str, new_msg) => `${ str }\nāāā\n${ new_msg }`)
|
|
1287
1287
|
changeable_test_status = `FAILED`;
|
|
1288
|
+
// TODO: This may be redundant and therefore confusing
|
|
1288
1289
|
reports.addToReport(scope, {
|
|
1289
1290
|
type: `error`,
|
|
1290
1291
|
code: `ALK0093`,
|
|
1291
|
-
value: `
|
|
1292
|
+
value: `ALKiln ran into an error when it tried to compare ${ scope.failed_pdf_compares.length } PDF(s)\nāāā\n${ msg }\nāāā\n`
|
|
1292
1293
|
});
|
|
1293
1294
|
}
|
|
1294
1295
|
|
package/package.json
CHANGED