@suffolklitlab/alkiln 4.9.3 → 4.10.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.
Files changed (123) hide show
  1. package/.github/workflows/test_tests.yml +8 -6
  2. package/CHANGELOG.md +21 -0
  3. package/MANIFEST.in +1 -0
  4. package/_alkiln-misc_artifacts/unit_tests_logs-2022-12-29 at 02h22m01s457ms UTC.txt +20 -0
  5. package/action.yml +2 -2
  6. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Base64 encoded corner cases are decoded correctly-2022-12-29 at 02h12m20s302ms UTC/report.txt +23 -0
  7. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail to find var while keeping value secret-2022-12-29 at 02h14m21s556ms UTC/report.txt +13 -0
  8. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with 2 wrong signin secret names-2022-12-29 at 02h14m19s733ms UTC/report.txt +12 -0
  9. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with a missing phrase-2022-12-29 at 02h13m42s632ms UTC/report.txt +10 -0
  10. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with cannot find missing document-2022-12-29 at 02h14m13s528ms UTC/report.txt +10 -0
  11. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with could not find files-2022-12-29 at 02h14m20s192ms UTC/report.txt +13 -0
  12. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with error page from random input-2022-12-29 at 02h13m27s716ms UTC/random_answers_screenshots-2022-12-29 at 02h13m28s915ms UTC/random-1672280009244.jpg +0 -0
  13. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with error page from random input-2022-12-29 at 02h13m27s716ms UTC/report.txt +12 -0
  14. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with incorrectly present phrase-2022-12-29 at 02h13m43s918ms UTC/report.txt +10 -0
  15. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with link text not visible-2022-12-29 at 02h13m52s649ms UTC/report.txt +10 -0
  16. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with link unexpectedly opens in a new window-2022-12-29 at 02h14m06s008ms UTC/report.txt +56 -0
  17. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with link unexpectedly opens in same window-2022-12-29 at 02h14m01s128ms UTC/report.txt +56 -0
  18. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with link with given text does not lead to correct url-2022-12-29 at 02h13m55s993ms UTC/report.txt +56 -0
  19. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with missing element id-2022-12-29 at 02h13m45s207ms UTC/report.txt +10 -0
  20. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with missing error message-2022-12-29 at 02h13m48s710ms UTC/report.txt +10 -0
  21. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with missing link with url-2022-12-29 at 02h13m54s329ms UTC/report.txt +10 -0
  22. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with missing term with the given text-2022-12-29 at 02h14m12s071ms UTC/report.txt +10 -0
  23. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with no interview at arbitrary url-2022-12-29 at 02h11m01s999ms UTC/report.txt +10 -0
  24. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with system error after Step using wrong file name as trigger-2022-12-29 at 02h14m15s021ms UTC/report.txt +10 -0
  25. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with unexpectedly able to continue-2022-12-29 at 02h13m46s819ms UTC/report.txt +11 -0
  26. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with var not on page-2022-12-29 at 02h14m10s627ms UTC/report.txt +12 -0
  27. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with was unexpectedly not able to continue for invalid field input message-2022-12-29 at 02h13m50s143ms UTC/report.txt +13 -0
  28. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with wrong email secret name-2022-12-29 at 02h14m18s763ms UTC/report.txt +10 -0
  29. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fail with wrong password secret name-2022-12-29 at 02h14m19s291ms UTC/report.txt +10 -0
  30. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fails as it doesn't try to 'continue' with a restart button-2022-12-29 at 02h12m22s559ms UTC/report.txt +19 -0
  31. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fails when table has no header row and rows have only one column-2022-12-29 at 02h14m50s261ms UTC/report.txt +10 -0
  32. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Fields are listed out of order-2022-12-29 at 02h14m55s580ms UTC/report.txt +103 -0
  33. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I am able to set a custom wait time before an interview has been loaded-2022-12-29 at 02h10m12s300ms UTC/report.txt +6 -0
  34. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I am able to set a custom wait time that allows steps to run longer than the half minute default-2022-12-29 at 02h09m35s267ms UTC/report.txt +6 -0
  35. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m20s448ms UTC/random-1672280001009.jpg +0 -0
  36. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m20s448ms UTC/random-1672280001551.jpg +0 -0
  37. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m20s448ms UTC/random-1672280002244.jpg +0 -0
  38. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m20s448ms UTC/random-1672280003073.jpg +0 -0
  39. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m20s448ms UTC/random-1672280003428.jpg +0 -0
  40. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m24s217ms UTC/random-1672280004868.jpg +0 -0
  41. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m24s217ms UTC/random-1672280005425.jpg +0 -0
  42. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m24s217ms UTC/random-1672280006159.jpg +0 -0
  43. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m24s217ms UTC/random-1672280007028.jpg +0 -0
  44. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/random_answers_screenshots-2022-12-29 at 02h13m24s217ms UTC/random-1672280007371.jpg +0 -0
  45. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I answer randomly till the end twice-2022-12-29 at 02h13m19s494ms UTC/report.txt +21 -0
  46. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I can include .yml in the filename-2022-12-29 at 02h12m39s613ms UTC/report.txt +6 -0
  47. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I can match JSON page var to str-2022-12-29 at 02h12m57s106ms UTC/json_for-showifs-2022-12-29 at 02h12m59s731ms UTC.json +154 -0
  48. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I can match JSON page var to str-2022-12-29 at 02h12m57s106ms UTC/report.txt +33 -0
  49. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I can wait as a first step in a test-2022-12-29 at 02h10m50s651ms UTC/report.txt +5 -0
  50. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I can't match JSON page var to str-2022-12-29 at 02h14m16s014ms UTC/json_for-showifs-2022-12-29 at 02h14m18s373ms UTC.json +154 -0
  51. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I can't match JSON page var to str-2022-12-29 at 02h14m16s014ms UTC/report.txt +37 -0
  52. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I check navigation-2022-12-29 at 02h12m41s028ms UTC/report.txt +54 -0
  53. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I check the pages for accessibility-2022-12-29 at 02h13m02s923ms UTC/report.txt +58 -0
  54. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I fill in random input for 1 page-2022-12-29 at 02h13m17s219ms UTC/random_answers_screenshots-2022-12-29 at 02h13m18s174ms UTC/random-1672279998764.jpg +0 -0
  55. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I fill in random input for 1 page-2022-12-29 at 02h13m17s219ms UTC/report.txt +7 -0
  56. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I fill in random input till the end-2022-12-29 at 02h13m12s258ms UTC/random_answers_screenshots-2022-12-29 at 02h13m13s676ms UTC/random-1672279994312.jpg +0 -0
  57. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I fill in random input till the end-2022-12-29 at 02h13m12s258ms UTC/random_answers_screenshots-2022-12-29 at 02h13m13s676ms UTC/random-1672279994892.jpg +0 -0
  58. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I fill in random input till the end-2022-12-29 at 02h13m12s258ms UTC/random_answers_screenshots-2022-12-29 at 02h13m13s676ms UTC/random-1672279995594.jpg +0 -0
  59. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I fill in random input till the end-2022-12-29 at 02h13m12s258ms UTC/random_answers_screenshots-2022-12-29 at 02h13m13s676ms UTC/random-1672279996542.jpg +0 -0
  60. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I fill in random input till the end-2022-12-29 at 02h13m12s258ms UTC/random_answers_screenshots-2022-12-29 at 02h13m13s676ms UTC/random-1672279996887.jpg +0 -0
  61. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I fill in random input till the end-2022-12-29 at 02h13m12s258ms UTC/report.txt +13 -0
  62. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I get the page's JSON-2022-12-29 at 02h13m00s023ms UTC/report.txt +71 -0
  63. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I have four name parts-2022-12-29 at 02h09m31s566ms UTC/report.txt +10 -0
  64. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I have three name parts-2022-12-29 at 02h09m27s653ms UTC/report.txt +10 -0
  65. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I have two name parts-2022-12-29 at 02h09m21s065ms UTC/report.txt +10 -0
  66. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I observe things on a single page when I arrive-2022-12-29 at 02h12m35s237ms UTC/report.txt +7 -0
  67. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I see user errors_-2022-12-29 at 02h12m37s167ms UTC/report.txt +9 -0
  68. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I set various values-2022-12-29 at 02h11m10s659ms UTC/report.txt +58 -0
  69. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I set various values-2022-12-29 at 02h11m10s659ms UTC/simple-doc.pdf +0 -0
  70. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I sign in-2022-12-29 at 02h10m54s349ms UTC/report.txt +7 -0
  71. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I take a screenshot-2022-12-29 at 02h13m01s434ms UTC/report.txt +6 -0
  72. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I take a screenshot-2022-12-29 at 02h13m01s434ms UTC/screenshot-some-screenshot-on-upload-files-2022-12-29 at 02h13m02s597ms UTC.jpg +0 -0
  73. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I upload files with a table-2022-12-29 at 02h15m13s490ms UTC/report.txt +19 -0
  74. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/I want to go to a full arbitrary url-2022-12-29 at 02h10m58s362ms UTC/report.txt +6 -0
  75. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Interview name includes url args with a wait-2022-12-29 at 02h10m52s195ms UTC/report.txt +6 -0
  76. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Interview name includes url args-2022-12-29 at 02h10m48s849ms UTC/report.txt +6 -0
  77. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Latin-Fail with missing language link-2022-12-29 at 02h13m40s278ms UTC/report.txt +10 -0
  78. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Latin-Fail with wrong page id-2022-12-29 at 02h13m41s379ms UTC/report.txt +10 -0
  79. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Proxy and regular vars are mixed-2022-12-29 at 02h14m51s648ms UTC/report.txt +35 -0
  80. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Report doesn't complain about `there_is_another _ False` in a table_-2022-12-29 at 02h14m36s652ms UTC/report.txt +16 -0
  81. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Report lists unused table rows-2022-12-29 at 02h14m29s563ms UTC/report.txt +65 -0
  82. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Report still shows page id when I tap to continue without setting any fields-2022-12-29 at 02h14m25s620ms UTC/report.txt +22 -0
  83. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Server finishes reloading-2022-12-29 at 02h11m08s477ms UTC/report.txt +6 -0
  84. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Sign in to server successfully-2022-12-29 at 02h14m34s127ms UTC/report.txt +6 -0
  85. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Story table accidentally uses the opposite double or single quotes-2022-12-29 at 02h15m11s462ms UTC/report.txt +18 -0
  86. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Table MISSING trigger column-2022-12-29 at 02h14m41s898ms UTC/report.txt +33 -0
  87. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Table has no header row, MISSING trigger column-2022-12-29 at 02h14m47s483ms UTC/report.txt +33 -0
  88. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Table has no header row, has trigger column-2022-12-29 at 02h14m44s776ms UTC/report.txt +37 -0
  89. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Table has trigger column-2022-12-29 at 02h14m39s081ms UTC/report.txt +37 -0
  90. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Test _Then I cannot continue_-2022-12-29 at 02h12m48s119ms UTC/report.txt +9 -0
  91. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Test _Then I can/342/200/231t continue_ with an apostrophe-2022-12-29 at 02h12m52s553ms UTC/report.txt" +9 -0
  92. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Test _Then I do not continue_-2022-12-29 at 02h12m50s316ms UTC/report.txt +9 -0
  93. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Test _Then I don't continue_ with a single quote-2022-12-29 at 02h12m45s907ms UTC/report.txt +9 -0
  94. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Test _Then I don/342/200/231t continue_ with an apostrophe-2022-12-29 at 02h12m54s800ms UTC/report.txt" +9 -0
  95. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/Warn when there are too many names-2022-12-29 at 02h14m22s722ms UTC/report.txt +8 -0
  96. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/debug_log.txt +2373 -0
  97. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/handles settings from Github secrets-2022-12-29 at 02h12m13s413ms UTC/report.txt +19 -0
  98. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/report.txt +1439 -0
  99. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/tap element with an error-2022-12-29 at 02h12m18s853ms UTC/report.txt +10 -0
  100. package/alkiln-2022-12-29 at 02h09m19s552ms UTC/tap elements-2022-12-29 at 02h12m16s677ms UTC/report.txt +6 -0
  101. package/docassemble/__init__.py +5 -0
  102. package/lib/scope.js +183 -119
  103. package/lib/steps.js +28 -0
  104. package/lib/utils/files.js +3 -3
  105. package/lib/utils/session_vars.js +5 -3
  106. package/package.json +5 -4
  107. package/setup.py +60 -0
  108. package/tests/unit_tests/fields.fixtures.js +79 -103
  109. package/tests/unit_tests/getAllFields.test.js +18 -13
  110. package/tests/unit_tests/getSafeScenarioFilename.test.js +2 -2
  111. package/tests/unit_tests/html.fixtures.js +0 -1
  112. package/tests/features/assembly_line.feature +0 -43
  113. package/tests/features/establishing_steps.feature +0 -62
  114. package/tests/features/example_test.feature +0 -11
  115. package/tests/features/interactive_steps.feature +0 -134
  116. package/tests/features/observation_steps.feature +0 -153
  117. package/tests/features/random_input.feature +0 -31
  118. package/tests/features/reload.feature +0 -155
  119. package/tests/features/reports.feature +0 -546
  120. package/tests/features/some_png_1.png +0 -0
  121. package/tests/features/some_png_2.png +0 -0
  122. package/tests/features/story_table_formats.feature +0 -125
  123. package/tests/features/story_tables.feature +0 -91
@@ -0,0 +1,10 @@
1
+
2
+ ---------------
3
+ Scenario: tap element with an error
4
+ Tags: @interactive @i4 @tap-elements @tabs @error
5
+ ---------------
6
+ Trying to load the interview
7
+
8
+ ERROR: Couldn't find the tab with id "#Tests-not_there-tab" on the page, is there a typo?
9
+
10
+ **-- Scenario Failed --**
@@ -0,0 +1,6 @@
1
+
2
+ ---------------
3
+ Scenario: tap elements
4
+ Tags: @interactive @i3 @tap-elements @tabs
5
+ ---------------
6
+ Trying to load the interview
@@ -0,0 +1,5 @@
1
+ try:
2
+ __import__('pkg_resources').declare_namespace(__name__)
3
+ except ImportError:
4
+ __path__ = __import__('pkgutil').extend_path(__path__, __name__)
5
+
package/lib/scope.js CHANGED
@@ -54,8 +54,34 @@ let is_secret_var = function(row) {
54
54
  return original.flags !== undefined && original.flags.secret_name !== undefined;
55
55
  }
56
56
 
57
+ let fromBase64 = function(base64_str) {
58
+ /* Takes a base64 encoded string and returns it decoded.
59
+ * Note that this can take strings with invalid base 64 characters,
60
+ * but will change those characters to `/`s instead.
61
+ */
62
+ let buffer = Buffer.from( base64_str, 'base64' );
63
+ return buffer.toString( `utf-8` );
64
+ }
65
+
66
+ let toBase64 = function ( utf8_str, exclude_padding=true) {
67
+ /* Encoded a given string (@param utf8_str) in base64.
68
+ * @param exclude_padding {boolean} - if true, strips off `=` characters
69
+ * that are added as padding while base64 encoding (docassemble doesn't
70
+ * include those characters, and uses the UTF-7 base64 encoding
71
+ * (RFC-2152)). For more details, see
72
+ * https://github.com/nodejs/node/issues/6107#issuecomment-207177131
73
+ */
74
+ if ( typeof( utf8_str ) !== `string` ) { // Prevent an error
75
+ return `Will+Never+Match+Existing+DOM+Node==`;
76
+ }
77
+ let buffer = Buffer.from( utf8_str, 'utf-8' );
78
+ let base64str = buffer.toString( `base64` )
79
+ return exclude_padding ? base64str.replace(/=/g, '') : base64str;
80
+ }
81
+
57
82
  let proxies_regex = /(\bx\.)|(\bx\[)|(\[[ijklmn]\])/g;
58
- let misc_artifacts_dir = `_alkiln_test-misc_artifacts`;
83
+ let da_base64_regex = /^[A-Za-z0-9+/]+$/;
84
+ let misc_artifacts_dir = `_alkiln-misc_artifacts`;
59
85
 
60
86
  module.exports = {
61
87
  trigger_not_needed_flag: `ALKiln: no trigger variable needed`,
@@ -620,7 +646,7 @@ module.exports = {
620
646
  * - Birthdate fields that will be showing up relatively soon
621
647
  * - Time fields?
622
648
  */
623
- let base64_var_name = await scope.toBase64( scope, { utf8_str: var_name });
649
+ let base64_var_name = toBase64( var_name );
624
650
 
625
651
  // Will catch some radio and checkbox inputs too, which will then be rejected
626
652
  // Note: I think `showif`s with `code` don't use `data-saveas`
@@ -659,12 +685,14 @@ module.exports = {
659
685
  return to_manipulate;
660
686
  }, // Ends scope.setVar()
661
687
 
688
+ continue_button_selector: `fieldset.da-field-buttons button[type="submit"].btn-primary`,
689
+ signature_selector: `fieldset .dasigsave`,
662
690
  continue: async function ( scope ) {
663
691
  /* Presses whatever button it finds that might lead to the next page. */
664
692
  // Any selectors I find seem somewhat precarious.
665
693
  let elem = await Promise.race([
666
- scope.page.waitForSelector(`fieldset.da-field-buttons button[type="submit"]`), // other pages (this is the most consistent way)
667
- scope.page.waitForSelector(`fieldset .dasigsave`), // signature page
694
+ scope.page.waitForSelector(scope.continue_button_selector), // other pages (this is the most consistent way)
695
+ scope.page.waitForSelector(scope.signature_selector), // signature page
668
696
  ]);
669
697
  await elem.evaluate( el => { return el.className });
670
698
  // Waits for navigation or user error
@@ -675,15 +703,16 @@ module.exports = {
675
703
  /** Return true if a button that will allow continuing appears
676
704
  * on this page. Otherwise return false. */
677
705
 
678
- // Possible to find this on an event screen with `buttons:`, like an exit button
706
+ // Will only find this with "Continue buttons":
707
+ // * An actual continue button that doesn't set a variable
708
+ // <button class="btn btn-da btn-primary" type="submit">Continue</button>
709
+ // * One that sets a variable:
710
+ // <button type="submit" class="btn btn-da btn-primary" name="Zm9v" value="True">Continue</button>
711
+ // It will not find event screen `buttons:`, like an exit button or a restart button
679
712
  // <button type="submit" class="btn btn-da btn-danger" name="X211bHRpcGxlX2Nob2ljZQ" value="0">exit</button>
680
- // An actual continue button that doesn't set a variable
681
- // <button class="btn btn-da btn-primary" type="submit">Continue</button>
682
- // One that sets a variable:
683
- // <button type="submit" class="btn btn-da btn-primary" name="Zm9v" value="True">Continue</button>
684
713
  // `buttons:` can be used in question blocks as choices
685
- let regular = await scope.page.$( `fieldset.da-field-buttons button[type="submit"]` );
686
- let signature = await scope.page.$( `fieldset .dasigsave` );
714
+ let regular = await scope.page.$(scope.continue_button_selector);
715
+ let signature = await scope.page.$(scope.signature_selector);
687
716
 
688
717
  return regular !== null || signature !== null;
689
718
 
@@ -972,9 +1001,9 @@ module.exports = {
972
1001
  if ( !encoded_trigger_var ) {
973
1002
  // TODO: Add documentation and then link to it in this message.
974
1003
  await scope.addToReport(scope, { type: `warning`, value: `You are missing an element containing the data for the page's trigger variable. Some fields, like loops with index variables, may not work correctly in these tests. See https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing/#a-missing-trigger-variable.` });
975
- encoded_trigger_var = await scope.toBase64( scope, { utf8_str: `None` });
1004
+ encoded_trigger_var = toBase64( `None` );
976
1005
  }
977
- let trigger = await scope.fromBase64( scope, { base64_str: encoded_trigger_var });
1006
+ let trigger = fromBase64( encoded_trigger_var );
978
1007
 
979
1008
 
980
1009
  // Some `show if` fields have `names` like '_field_n' instead of their var name. Ex: _field_1
@@ -1120,7 +1149,18 @@ module.exports = {
1120
1149
  // Inputs like textareas don't have values
1121
1150
  if ( values.length > 0 ) {
1122
1151
  for ( let value of values ){
1123
- possible_table_rows.push({ var: var_name, value, });
1152
+ possible_table_rows.push({ var: var_name, value: value, });
1153
+ // Ignore blanks, and values that certainly aren't base64 encoded like True and False
1154
+ // Modified from https://stackoverflow.com/a/8571649/11416267
1155
+ // DA doesn't use `==` to pad base 64
1156
+ if (value !== 'None' && value !== 'True' && value !== 'False'
1157
+ && value.match(da_base64_regex)) {
1158
+ let value_decoded = fromBase64(value);
1159
+ let values_decoded = scope.decodeFully( scope, {expr_str: value_decoded });
1160
+ for (let possible_val of values_decoded) {
1161
+ possible_table_rows.push({var: var_name, value: possible_val, });
1162
+ }
1163
+ }
1124
1164
  }
1125
1165
  } else {
1126
1166
  possible_table_rows.push({ var: var_name, value: '', });
@@ -1145,11 +1185,11 @@ module.exports = {
1145
1185
 
1146
1186
  let field_like_names = {};
1147
1187
  let field_like_names_encoded = $('input[name="_varnames"]')[0].attribs.value;
1148
- let field_like_names_str = await scope.fromBase64( scope, { base64_str: field_like_names_encoded });
1188
+ let field_like_names_str = fromBase64( field_like_names_encoded );
1149
1189
  let field_like_names_json = JSON.parse( field_like_names_str );
1150
1190
  for ( let field_like_key in field_like_names_json ) {
1151
- let field_like_DOM_name = await scope.fromBase64( scope, { base64_str: field_like_key });
1152
- let field_like_var_name = await scope.fromBase64( scope, { base64_str: field_like_names_json[ field_like_key ]});
1191
+ let field_like_DOM_name = fromBase64( field_like_key );
1192
+ let field_like_var_name = fromBase64( field_like_names_json[ field_like_key ] );
1153
1193
  field_like_names[ field_like_DOM_name ] = field_like_var_name;
1154
1194
  }
1155
1195
 
@@ -1178,14 +1218,11 @@ module.exports = {
1178
1218
  * @param encoded {str} - base64 encoded string of the node
1179
1219
  * @param field_like_names {obj} - e.g. `{ _field_0: 'some_checkbox_var_name' }`
1180
1220
  * @param nota {bool} - is the field a 'none of the above' field? */
1181
- let var_names = [];
1182
- let var_name = '';
1183
-
1184
1221
  // E.g. continue buttons that don't set a variable
1185
- if ( encoded === undefined ) { return var_names; }
1222
+ if ( encoded === undefined ) { return []; }
1186
1223
 
1187
1224
  // Names can be encoded multiple times, so start peeling the layers
1188
- let layer_1_decoded = await scope.fromBase64( scope, { base64_str: encoded });
1225
+ let layer_1_decoded = fromBase64( encoded );
1189
1226
  // If this is of the format `_field_n`, we need to get the actual matching var name.
1190
1227
  if ( field_like_names[ layer_1_decoded ] ) {
1191
1228
  layer_1_decoded = field_like_names[ layer_1_decoded ];
@@ -1200,60 +1237,66 @@ module.exports = {
1200
1237
  // TODO: Should we move this back into `getAllFields()`?
1201
1238
  }
1202
1239
 
1203
- // A lot of checkbox fields have a key hidden inside them. (not yesno type checkboxes)
1204
- let checkbox_match = layer_1_decoded.match( /^(.*)\[[BR]'(.*)'\]/ );
1240
+ return scope.decodeFully(scope, { expr_str: layer_1_decoded, field_like_names, nota });
1241
+ }, // Ends scope.getPossibleVarNames()
1242
+
1243
+ decodeFully: function ( scope, { expr_str, field_like_names = {}, nota} ) {
1244
+ /*
1245
+ * Given a DA variable or value name, make sure it's fully decoded from base64.
1246
+ * @param variable_str {str} - a python variable string, potentially partially base64 encoded
1247
+ * Some examples:
1248
+ * - my_variable
1249
+ * - checkboxes_choices[B'Y2hlY2tib3hfb3RoZXJfb3B0XzI']
1250
+ * - foo[bar][B'Y2hlY2tib3hfb3RoZXJfb3B0XzI']
1251
+ * @param field_like_names {obj} - e.g. `{ _field_0: 'some_checkbox_var_name' }`
1252
+ * @param nota {bool} - is the field a 'none of the above' field?
1253
+ */
1254
+ // Some fields have a key hidden inside them.
1255
+ // This gets the variable name before the final brackets, and the likely
1256
+ // still base64 encoded string inside the brackets
1257
+ let before_and_in_brackets = expr_str.match( /^(.*)\[[BR]'(.*)'\]/ );
1205
1258
 
1206
1259
  // Of the format `foo[B'encoded']` or `foo[R'encoded']`
1260
+ // The B means internal to docassemble, the encoded portion is a string,
1261
+ // and the R means that the python `repr` function was called with a
1262
+ // non-string object
1207
1263
  // See https://github.com/jhpyle/docassemble/blob/595ad0a08572c0188f9ab49b1a6af99a3621cd42/docassemble_base/docassemble/base/standardformatter.py#L2108
1208
1264
 
1209
- // What about something like foo[R'encoded1'][B'encoded2']? Is that a
1210
- // thing? Can they be index numbers? Or just `foo['encoded1']`? What
1211
- // would their format be? The above? `foo[R'actual_choice'][B'encoded']`?
1212
- // `foo['something'][B'encoded']`?
1213
-
1214
1265
  // =====================
1215
1266
  // Ex: checkboxes_choices[B'Y2hlY2tib3hfb3RoZXJfb3B0XzI']
1216
1267
  // Ex: checkboxes_choices[R'Y2hlY2tib3hfb3RoZXJfb3B0XzI']
1217
- if ( checkbox_match ) {
1218
-
1219
- let key_or_name = checkbox_match[1];
1268
+ let possible = [];
1269
+ if ( before_and_in_brackets ) {
1270
+ let key_or_name = before_and_in_brackets[1];
1220
1271
  // If this is of the format `_field_n`, we need to get the actual matching var name.
1221
- if ( field_like_names[ key_or_name ] ) {
1222
- var_name = field_like_names[ key_or_name ];
1223
- } else {
1224
- var_name = key_or_name;
1225
- }
1272
+ let var_name = field_like_names [ key_or_name ] || key_or_name;
1226
1273
 
1227
- // Our own marker for a 'none of the above' checkbox choice
1228
- if ( nota ) { var_names.push( `${ var_name }['None']` ); }
1229
- else {
1230
- // Get the choice name. E.g. option_1, option_2
1231
- let box_layer_1_decoded = await scope.fromBase64( scope, { base64_str: checkbox_match[2] });
1232
- var_names.push( `${ var_name }['${ box_layer_1_decoded }']` );
1274
+ let dictionary_key = before_and_in_brackets[2];
1233
1275
 
1234
- try {
1235
- // The part inside `[B'encoded']` could need one more decoding (or more?)
1236
- // It may have invalid chars if it's not a one-word var name, though, like '_'
1237
- let box_layer_2_decoded = await scope.fromBase64( scope, { base64_str: box_layer_1_decoded });
1238
- var_names.push(`${ var_name }['${ box_layer_2_decoded }']`);
1239
- } catch ( err ) {} // It wasn't double encoded. Do nothing.
1276
+ // Our own marker for a 'none of the above' checkbox choice
1277
+ if ( nota ) {
1278
+ possible.push( `${ var_name }['None']` );
1279
+ } else {
1280
+ if (dictionary_key.match(da_base64_regex)) {
1281
+ // Get the choice name. E.g. option_1, option_2
1282
+ let dictionary_key_decoded = fromBase64( dictionary_key );
1283
+ possible.push( `${ var_name }['${ dictionary_key_decoded }']` );
1284
+ if (dictionary_key_decoded.match(da_base64_regex)) {
1285
+ try {
1286
+ // The part inside `[B'encoded']` could need one more decoding.
1287
+ possible.push(`${ var_name }['${ fromBase64( dictionary_key_decoded ) }']`);
1288
+ } catch ( err ) {} // It wasn't double encoded. Do nothing.
1289
+ }
1290
+ }
1240
1291
  }
1241
1292
 
1242
1293
  // =====================
1243
- // Otherwise, just the var name (non checkboxes or yesno checkbox type things)
1294
+ // If bracket regex didn't match, just use var name
1244
1295
  } else {
1245
- var_name = layer_1_decoded;
1246
- var_names.push(`${ layer_1_decoded }`);
1247
- // // Values are sometimes encoded multiple times (check this happens with a plain var name)
1248
- // let layer_2_decoded = await scope.fromBase64( layer_1_decoded );
1249
- // names.var_names.push( layer_2_decoded );
1296
+ possible.push(`${ expr_str }`);
1250
1297
  }
1251
-
1252
- // Detecting valid variable names/characters
1253
- // https://stackoverflow.com/a/23377268
1254
-
1255
- return var_names;
1256
- }, // Ends scope.getPossibleVarNames()
1298
+ return possible;
1299
+ },
1257
1300
 
1258
1301
  getOptionValues: async function ( scope, { $select }) {
1259
1302
  /* Returns all the option node values of the select node. */
@@ -1261,35 +1304,11 @@ module.exports = {
1261
1304
  let option_nodes = $select.find( `option` );
1262
1305
  for ( let opt of option_nodes ) {
1263
1306
  options.push( opt.attribs.value );
1264
- // Dropdown options created with objects will be encoded
1265
- let decoded = await scope.fromBase64( scope, { base64_str: opt.attribs.value });
1266
- options.push( decoded );
1307
+ // Dropdowns created w/ objects are base64 encoded; but it's handled in getPossibleTableRowMatches
1267
1308
  }
1268
- // TODO: These may be encoded? Just 'decode' and append those vals too.
1269
- // TODO: Keep those decoded values somewhere for setting the right
1270
- // option in the DOM. Not sure how that can be put together sensically.
1271
1309
  return options;
1272
1310
  }, // Ends scope.getOptionValues()
1273
1311
 
1274
- fromBase64: async function ( scope, { base64_str }) {
1275
- let buffer = Buffer.from( base64_str, 'base64' );
1276
- return buffer.toString( `utf-8` );
1277
- },
1278
-
1279
- toBase64: async function ( scope, { utf8_str }, exclude_padding=true) {
1280
- if ( typeof( utf8_str ) !== `string` ) { // Prevent an error
1281
- return `Will+Never+Match+Existing+DOM+Node==`;
1282
- }
1283
- // Sometimes, converting to base64 results in padding characters ('=')
1284
- // being added to the end. Docassemble does not use these padding
1285
- // characters everywhere, so we allow for stripping them using
1286
- // the exclude_padding argument. For more details, see
1287
- // https://github.com/nodejs/node/issues/6107#issuecomment-207177131
1288
- let buffer = Buffer.from( utf8_str, 'utf-8' );
1289
- let base64str = buffer.toString( `base64` )
1290
- return exclude_padding ? base64str.replace(/=/g, '') : base64str;
1291
- },
1292
-
1293
1312
 
1294
1313
  // TODO: Maybe add `from_story_table` to the var data table (array...)
1295
1314
  getMatchingRows: async function ( scope, { field, var_data }) {
@@ -1481,7 +1500,12 @@ module.exports = {
1481
1500
  await scope.addToReport(scope, { type: `warning`, value: multiple_matches_msg });
1482
1501
  }
1483
1502
 
1484
- if ( session_vars.get_debug() ) { console.log( `final_matching_rows:\n`, JSON.stringify( final_matching_rows )); }
1503
+ if ( session_vars.get_debug() ) {
1504
+ console.log( `final_matching_rows:\n`, JSON.stringify( final_matching_rows ));
1505
+ if ( final_matching_rows.length == 0) {
1506
+ console.log(` field:\n`, JSON.stringify( field ) );
1507
+ }
1508
+ }
1485
1509
  return final_matching_rows;
1486
1510
  }, // Ends scope.getMatchingRows()
1487
1511
 
@@ -1685,8 +1709,6 @@ module.exports = {
1685
1709
  },
1686
1710
  select: async function ( scope, { handle, answer }) {
1687
1711
  // Note: I don't remember what's really going on in here.
1688
- // I will note that values of `select` elements are strings,
1689
- // not variables, so we don't need to check for variable names.
1690
1712
 
1691
1713
  // A dropdown's option value can be one of two things
1692
1714
  // Try to find the element using the first value
@@ -1707,7 +1729,7 @@ module.exports = {
1707
1729
  // If that literal value isn't on the page, it should be a base64 encoded value
1708
1730
  // TODO: Can these be double encoded? Can they be encoded like objects?
1709
1731
  } else {
1710
- let base64_name = await scope.toBase64( scope, { utf8_str: answer });
1732
+ let base64_name = toBase64( answer );
1711
1733
  await handle.select( base64_name );
1712
1734
  }
1713
1735
 
@@ -1783,35 +1805,53 @@ module.exports = {
1783
1805
  /* Try to upload one or more files to a file-type input field.
1784
1806
  * Does not submit, so no danger of timeout with server reload.
1785
1807
  */
1786
-
1787
- // Tests could be in one of two places. Figure out which directory they're in
1788
- // Makes an absolute path from the directory in which `npm run` was used plus our internal folders
1789
- let dir_source = `${ process.cwd() }/docassemble/${ session_vars.get_repo_url().match(/docassemble-(.*)/)[1] }/data/sources`;
1790
- let dir_features = `${ process.cwd() }/tests/features`;
1791
- let dir = null;
1792
-
1793
- // Make sure one of the valid directories exist. This only allows
1794
- // one directory to be used per Step or table row. We don't anticipate
1795
- // future people having a `tests/features` folder, let alone both.
1808
+
1809
+ // Tests should be one place: docassemble/*/data/sources, where * is the only folder in `docassemble`.
1810
+ // Makes an absolute path from the folder in which `npm run` was used plus our internal folders
1811
+ let docassemble_pkg_path = `${ process.cwd() }/docassemble`;
1812
+ let subpackage_name = session_vars.get_package_name().substring(12);
1813
+ let pkg_paths = [];
1796
1814
  try {
1797
- // Try the most common directory first
1798
- fs.accessSync( dir_source );
1799
- dir = dir_source;
1800
- // If they have both directories, let them know which directory will be used.
1801
- try {
1802
- fs.accessSync( dir_features );
1803
- await scope.addToReport( scope, { type: `warning`, value: `You have both a docassemble package 'sources' folder and a 'test/features' folder. These tests will use the package's 'sources' folder.` });
1804
- } catch ( error_features ) { `all's well, do nothing`; }
1815
+ fs.accessSync(docassemble_pkg_path);
1816
+ // https://stackoverflow.com/a/24594123/11416267
1817
+ let dirs = fs.readdirSync(docassemble_pkg_path, {withFileTypes: true}).filter(dirent => dirent.isDirectory()).map(dirent => dirent.name);
1818
+ if (dirs.length < 1) {
1819
+ let msg = `You can store files to upload in 'docassemble/${ subpackage_name }/data/source'. These tests could not find a folder like that starting at ${ process.cwd() }.`;
1820
+ await scope.addToReport( scope, { type: `warning`, value: msg});
1821
+ throw Error(msg); // Most people will want to know the sources folder path;
1822
+ }
1823
+ for (let dir of dirs) {
1824
+ pkg_paths.push(`${docassemble_pkg_path}/${dir}`);
1825
+ }
1826
+ } catch ( error_da ) {
1827
+ let msg = `You can store files to upload in 'docassemble/${ subpackage_name }/data/source'. ` +
1828
+ `These tests could not find a folder like that. The current directory is ` +
1829
+ ` ${ process.cwd() }; is that in the top level folder of your docassemble package? (${ session_vars.get_package_name() })?`;
1830
+ await scope.addToReport( scope, { type: `warning`, value: msg});
1831
+ throw error_da; // Most people will want to know the sources folder path;
1832
+ }
1805
1833
 
1806
- } catch ( error_source ) {
1834
+ let chosen_dir = null;
1835
+ let err_paths = [];
1836
+ for (let pkg_path of pkg_paths) {
1837
+ let dir = `${ pkg_path }/data/sources`;
1807
1838
  try {
1808
- fs.accessSync( dir_features );
1809
- dir = dir_features;
1810
- } catch ( error_features ) {
1811
- await scope.addToReport( scope, { type: `warning`, value: `There are two places where you can store files to upload: "${ dir_source }" and "${ dir_features }". These tests could not find either of those.` });
1812
- throw error_source; // Most people will want to know the sources folder path;
1839
+ fs.accessSync(dir);
1840
+ chosen_dir = dir;
1841
+ break;
1842
+ } catch ( error_full ) {
1843
+ err_paths.push(`'${dir}'`);
1813
1844
  }
1814
1845
  }
1846
+ if (chosen_dir === null) {
1847
+ let msg = `You can store files to upload in ${err_paths.join(", or ")}. These tests could not find any of those folders.`;
1848
+ await scope.addToReport( scope, { type: `warning`, value: msg });
1849
+ throw Error(msg); // Most people will want to know the sources folder path;
1850
+ }
1851
+
1852
+ if (pkg_paths.length > 1 && pkg_paths.length - err_paths.length > 1) {
1853
+ await scope.addToReport( scope, { type: `warning`, value: `You have multiple folders in the 'docassemble' folder (${ pkg_paths }). These tests will use the first folder with a "data/sources" subfolder (${chosen_dir}).` });
1854
+ }
1815
1855
 
1816
1856
  // Allow a typo where someone forgot to include a space after a comma
1817
1857
  let file_names = answer.split(`,`);
@@ -1823,15 +1863,14 @@ module.exports = {
1823
1863
  // If the developer correctly used a ", ", there will be a space at the
1824
1864
  // start of the string. Remove it. If they forgot the space, that's fine too.
1825
1865
  name = name.replace(/^\s/, '');
1826
- let file_path = path.join( dir, name );
1866
+ let file_path = path.join( chosen_dir, name );
1827
1867
 
1828
1868
  try {
1829
1869
  fs.accessSync( file_path );
1830
1870
  paths.push( file_path );
1831
-
1832
1871
  } catch ( err ) {
1833
1872
  // TODO: Should this throw an error?
1834
- await scope.addToReport( scope, { type: `warning`, value: `Could not find "${ name }" in "${ dir }"` });
1873
+ await scope.addToReport( scope, { type: `warning`, value: `Could not find "${ name }" in "${ chosen_dir }"` });
1835
1874
  }
1836
1875
  }
1837
1876
 
@@ -2127,13 +2166,18 @@ module.exports = {
2127
2166
  enhanced_var_data.push( special_row );
2128
2167
 
2129
2168
  // Neutralize a `.there_is_another` row in a story table
2130
- } else if ( from_story_table && var_datum.var.match( /\.there_is_another$/ ) ) {
2169
+ } else if ( from_story_table && var_datum.var.match( /\.there_is_another$/ ) && var_datum.value !== `False`) {
2131
2170
  var_datum.invalid = true;
2132
2171
  // Removing the row will just cause a different error that will take longer to fail and
2133
2172
  // probably add a confusing message to the report, so mutating seems, sadly, more useful.
2134
2173
  var_datum.value = `False`; // The original value is still preserved
2135
2174
  // Warn the developer not to use `.there_is_another`.
2136
- await scope.addToReport( scope, { type: `warning`, value: `The attribute \`.there_is_another\` is invalid in story table tests. Replace it with \`.target_number\` in your \`var\` and \`trigger\` columns. Set the \`value\` to the number of items in that list. This test will now set this row's \`value\` to \`False\`. See https://suffolklitlab.github.io/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing#there_is_another. The row data is\n${ JSON.stringify( var_datum.original )}` });
2175
+ await scope.addToReport( scope, { type: `warning`,
2176
+ value: 'The attribute `.there_is_another` is invalid in story table tests. Replace it with '
2177
+ + '`.target_number` in your `var` and `trigger` columns. Set the `value` to the number of items in '
2178
+ + `that list. This test will now set this row's \`value\` to \`False\`. See `
2179
+ + `https://suffolklitlab.github.io/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing#there_is_another. `
2180
+ + `The row data is\n${ JSON.stringify( var_datum.original )}` });
2137
2181
  }
2138
2182
  }
2139
2183
 
@@ -2821,4 +2865,24 @@ module.exports = {
2821
2865
 
2822
2866
  return all_are_included;
2823
2867
  }, // Ends scope.reportIncludesAllExpected()
2868
+
2869
+ reportDoesNotInclude: async function ( scope, { not_expected=[] }) {
2870
+ /* Tests the behavior of this testing framework.
2871
+ * Test whether the expected values are not included in the test report.
2872
+ *
2873
+ * @param not_expected {array} Arr of strs that should not appear in the report.
2874
+ */
2875
+ let current_report_obj = scope.report.get( scope.scenario_id );
2876
+ let scenario = [ scope.scenario_id, current_report_obj ]; // Mimic how loop works with `Map`
2877
+ let report = await scope.getPrintableScenario( scope, { scenario });
2878
+ let none_included = true;
2879
+ for ( let one_expectation of not_expected ) {
2880
+ if ( report.includes( one_expectation )) {
2881
+ none_included = false;
2882
+ expect( report ).not.to.contain( one_expectation );
2883
+ }
2884
+ }
2885
+
2886
+ return none_included;
2887
+ },
2824
2888
  };
package/lib/steps.js CHANGED
@@ -115,6 +115,7 @@ Before(async (scenario) => {
115
115
  scope.scenario_id = uuidv4();
116
116
  scope.expected_status = null;
117
117
  scope.expected_in_report = null;
118
+ scope.expected_not_in_report = null;
118
119
  scope.server_reload_promise = null;
119
120
 
120
121
  await scope.addReportHeading(scope, {scenario});
@@ -1068,6 +1069,19 @@ Given(/the Scenario report should include/i, async ( report_text ) => {
1068
1069
 
1069
1070
  });
1070
1071
 
1072
+ Given(/the Scenario report should not include/i, async ( report_text ) => {
1073
+ /* WARNING: If a Step will fail, this Step must come first.
1074
+ *
1075
+ * Prepare to test that the Scenario report does not include `report_text`.
1076
+ * The actual test has to be done at the end of the Scenario as some text
1077
+ * may be added to the report there. */
1078
+ if ( !scope.expected_not_in_report ) {
1079
+ scope.expected_not_in_report = [];
1080
+ }
1081
+ scope.expected_not_in_report.push( report_text );
1082
+
1083
+ });
1084
+
1071
1085
  Given(/the final Scenario status should be "(passed|failed)"/i, async ( expected_status ) => {
1072
1086
  /* WARNING: If a Step will fail, this Step must come first.
1073
1087
  *
@@ -1156,6 +1170,20 @@ After(async function(scenario) {
1156
1170
  else { scenario.result.status = `FAILED`; }
1157
1171
  }
1158
1172
 
1173
+ if (scope.expected_not_in_report && scope.expected_not_in_report.length > 0) {
1174
+ let not_expected = scope.expected_not_in_report;
1175
+ // Reset report values no matter what so they don't mess up future scenarios
1176
+ scope.expected_not_in_report = null;
1177
+ let all_were_not_there = await scope.reportDoesNotInclude( scope, { not_expected });
1178
+
1179
+ if ( all_were_not_there ) {
1180
+ scenario.result.status = `PASSED`;
1181
+ } else {
1182
+ scenario.result.status = `FAILED`;
1183
+ }
1184
+
1185
+ }
1186
+
1159
1187
  // If there is a page open, then sign out and close it
1160
1188
  if ( scope.page ) {
1161
1189
  // Catch a possible reload error.
@@ -11,14 +11,14 @@ files.readable_date = function() {
11
11
  */
12
12
  let date = new Date(); // now
13
13
  let day = ("0" + date.getUTCDate()).slice(-2);
14
- let month = ("0" + date.getUTCMonth() + 1).slice(-2);
14
+ let month = ("0" + (date.getUTCMonth() + 1)).slice(-2);
15
15
  let year = date.getUTCFullYear();
16
16
  let hours = ("0" + date.getUTCHours()).slice(-2);
17
17
  let mins = ("0" + date.getUTCMinutes()).slice(-2);
18
18
  let secs = ("0" + date.getUTCSeconds()).slice(-2);
19
19
  let msecs = ("00" + date.getUTCMilliseconds()).slice(-3);
20
20
 
21
- let fancy_str = `${ year }-${ month }-${ day } at ${ hours }hr ${ mins }min ${ secs }sec ${ msecs }ms UTC`;
21
+ let fancy_str = `${ year }-${ month }-${ day } at ${ hours }h${ mins }m${ secs }s${ msecs }ms UTC`;
22
22
  return fancy_str;
23
23
  }; // Ends files.readable_date()
24
24
 
@@ -31,7 +31,7 @@ files.make_artifacts_folder = function (folder_name) {
31
31
  if (folder_name) {
32
32
  folder_name = safe_filename( folder_name );
33
33
  } else {
34
- folder_name = `alkiln_tests_output-${ files.readable_date() }`
34
+ folder_name = `alkiln-${ files.readable_date() }`
35
35
  }
36
36
 
37
37
  fs.mkdirSync( folder_name );
@@ -67,13 +67,15 @@ session_vars.get_branch_name = function () {
67
67
  return branch_name;
68
68
  };
69
69
 
70
- session_vars.get_project_base_name = function () {
70
+ session_vars.get_package_name = function () {
71
71
  /* Make an un-incremented project name based on the repo name and branch name. */
72
72
  let repo_url_without_trailing_slash = session_vars.get_repo_url().replace(/\/$/, '');
73
73
  let repo_name_parts = repo_url_without_trailing_slash.split('/');
74
- let package_name = repo_name_parts[ repo_name_parts.length - 1 ];
74
+ return repo_name_parts[ repo_name_parts.length - 1 ];
75
+ };
75
76
 
76
- let name = `testing${ package_name }${ session_vars.get_branch_name() }`;
77
+ session_vars.get_project_base_name = function () {
78
+ let name = `testing${ session_vars.get_package_name() }${ session_vars.get_branch_name() }`;
77
79
  let sanitized_name = name.replace( /[^A-Za-z0-9]/g, '' );
78
80
  return sanitized_name;
79
81
  };
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "4.9.3",
3
+ "version": "4.10.1",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
7
7
  "test": "npm run unit && npm run langs_setup && npm run cucumber -- $@",
8
8
  "cucumber_base": "run_cucumber(){ ./node_modules/.bin/cucumber-js \"$@\"; status=$?; ./lib/utils/clean_reports.js; return $status; }; run_cucumber",
9
- "cucumber": "run_base(){ npm run cucumber_base -- \"$@\" tests/features/*.feature --retry 1; }; run_base",
9
+ "cucumber": "run_base(){ npm run cucumber_base -- \"$@\" docassemble/*/data/sources/*.feature --retry 1; }; run_base",
10
10
  "cucumber_external": "run_cucumber(){ tmp_path=`pwd`; ( new_path=\"$1\"; shift; cd \"$new_path\"; npm run --prefix \"${tmp_path}\" cucumber_base -- \"$@\" \"$new_path\"/docassemble/*/data/sources/*.feature;) }; run_cucumber",
11
- "langs": "npm run langs_setup && ./node_modules/.bin/cucumber-js --require ./lib/index.js tests/features/*_lang*.feature",
12
- "langs_setup": "./lib/utils/langs.js 'tests/features/*.feature'",
11
+ "langs": "npm run langs_setup && ./node_modules/.bin/cucumber-js --require ./lib/index.js docassemble/*/data/sources/*_lang*.feature",
12
+ "langs_setup": "./lib/utils/langs.js 'docassemble/*/data/sources/*.feature'",
13
13
  "langs_local": "npm run langs_setup && npm run local",
14
14
  "local": "npm run setup && npm run cucumber; npm run takedown",
15
15
  "setup": "./lib/docassemble/setup.js",
@@ -42,6 +42,7 @@
42
42
  "chai": "4.2.0",
43
43
  "cheerio": "1.0.0-rc.10",
44
44
  "dotenv": "8.2.0",
45
+ "deep-equal-in-any-order": "^2.0.0",
45
46
  "mocha": "9.2.0",
46
47
  "puppeteer": "13.1.3",
47
48
  "qs": "6.10.2",