@suffolklitlab/alkiln 5.2.0 → 5.2.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/CHANGELOG.md CHANGED
@@ -44,7 +44,20 @@ Format:
44
44
  -->
45
45
  <!-- ## [Unreleased] -->
46
46
 
47
+ ## [5.2.1] - 2023-09-29
48
+
49
+ ### Added
50
+ - New warning message to the developer when their date string doesn't have a `/` in it.
51
+
52
+ ### Changed
53
+ - Told the user the url of the interview ALKiln tried to load. See https://github.com/SuffolkLITLab/ALKiln/issues/696.
54
+
55
+ ### Fixed
56
+ - Restored Assembly Line custom datatype three-parts dates functionality. See https://github.com/SuffolkLITLab/ALKiln/issues/764.
57
+ - Fixed "today" not being converted into a date for custom datatypes.
58
+
47
59
  ## [5.2.0] - 2023-09-16
60
+
48
61
  ### Changed
49
62
  - Make `I tap to continue` case insensitive to allow the author to match the capitalization of the button in Docassemble.
50
63
 
package/lib/scope.js CHANGED
@@ -616,22 +616,22 @@ module.exports = {
616
616
  * https://github.com/puppeteer/puppeteer/issues/299 and
617
617
  */
618
618
 
619
+ // If file has finished downloading
620
+ if ( scope.downloadComplete ) {
621
+ // reset the flag and resolve
622
+ scope.downloadComplete = false;
623
+ return true;
624
+ }
625
+
619
626
  // Stop/resolve if too much time has passed
620
627
  let expirationTime = scope.timeout - 200; // ms
621
628
  if ( !endTime ) { endTime = new Date((new Date()).getTime() + expirationTime); }
622
629
  let diff = endTime.getTime() - Date.now();
623
630
  if ( diff <= 0 ) { return false; }
624
631
 
625
- // If time hasn't expired
626
- if ( scope.downloadComplete ) {
627
- // If download is complete, reset the flag and resolve
628
- scope.downloadComplete = false;
629
- return true;
630
- } else {
631
- // Otherwise wait and then check again
632
- await scope.page.waitForTimeout( 100 );
633
- await scope.detectDownloadComplete(scope, endTime);
634
- }
632
+ // If time hasn't expired, wait and then check again
633
+ await scope.page.waitForTimeout( 100 );
634
+ await scope.detectDownloadComplete(scope, endTime);
635
635
 
636
636
  return false;
637
637
  },
@@ -732,11 +732,6 @@ module.exports = {
732
732
  /* Try to load the page. Should we also pass a `timeout` arg or just
733
733
  * use scope.timeout? */
734
734
 
735
- await scope.addToReport(scope,{
736
- type: `row`,
737
- value: `Trying to load the interview`
738
- });
739
-
740
735
  let interview_url = null;
741
736
  // If the file is already a full url, just use that arbitrary url
742
737
  if ( /^http/.test( file_name ) ) {
@@ -752,7 +747,11 @@ module.exports = {
752
747
  let base_url = await get_base_interview_url();
753
748
  interview_url = `${ base_url }${ file_name }`;
754
749
  }
755
- if ( session_vars.get_debug() ) { console.log( `interview_url:`, interview_url ); }
750
+
751
+ await scope.addToReport(scope,{
752
+ type: `row`,
753
+ value: `Trying to load the interview at "${interview_url}"`
754
+ });
756
755
 
757
756
  if ( !scope.page ) { scope.page = await scope.browser.newPage(); }
758
757
 
@@ -1477,11 +1476,6 @@ module.exports = {
1477
1476
  */
1478
1477
  let row_used = null;
1479
1478
 
1480
- // Skip hidden fields right away. When a date field was above a combobox,
1481
- // interacting with the hidden field caused the date field to open its
1482
- // calendar which blocked input to the combobox
1483
- if ( field.type === `hidden` ) { return row_used; }
1484
-
1485
1479
  let matching_input_rows = await scope.getMatchingRows( scope, { var_data, field });
1486
1480
 
1487
1481
  // Nothing to set if there are no matching table rows
@@ -1597,8 +1591,16 @@ module.exports = {
1597
1591
  row_was_used = await scope.setCustomDatatype[ custom_datatype ]( scope, { handle: custom_handle, answer });
1598
1592
  } else {
1599
1593
  // If no custom datatype, set a regular field
1600
- row_was_used = await scope.enter_answer[ field.tag ]( scope, { handle, answer });
1601
- }
1594
+
1595
+ // Skip hidden fields. When a `show if` date field is above a combobox,
1596
+ // interacting with the hidden field causes the date field to open its
1597
+ // calendar which blocks input to the combobox
1598
+ if ( field.type === `hidden` ) {
1599
+ row_was_used = false;
1600
+ } else {
1601
+ row_was_used = await scope.enter_answer[ field.tag ]( scope, { handle, answer });
1602
+ }
1603
+ } // ends if custom datatype
1602
1604
 
1603
1605
  return row_was_used;
1604
1606
  }, // Ends scope.funnel_the_answer()
@@ -1614,8 +1616,12 @@ module.exports = {
1614
1616
 
1615
1617
  // If it doesn't have the right format
1616
1618
  if ( answer === null || !answer.includes(`/`) ) {
1617
- // Do nothing
1618
- return;
1619
+ // Return field unused.
1620
+ await scope.addToReport( scope, {
1621
+ type: `warning`,
1622
+ value: `Invalid date. Value given: "${ answer }".`
1623
+ });
1624
+ return false;
1619
1625
  }
1620
1626
 
1621
1627
  let date_parts = answer.split(`/`);
@@ -1636,18 +1642,20 @@ module.exports = {
1636
1642
  handle: field,
1637
1643
  answer: date_parts[ field_i ]
1638
1644
  });
1645
+ await handle.press(`Tab`);
1639
1646
  }
1640
1647
 
1641
1648
  } // For each field
1642
1649
 
1643
1650
  await scope.afterStep( scope );
1651
+ return true;
1644
1652
  }, // Ends scope.setCustomDatatype.al_date()
1645
1653
 
1646
1654
  BirthDate: async function ( scope, { handle, answer }) {
1647
1655
  /** Given a date of the format 'mm/dd/yyyy' and the handle of the
1648
1656
  * custom field container, sets the ALToolbox custom birthdate
1649
1657
  * field values. */
1650
-
1658
+ answer = await scope.today_to_date(scope, { date: answer });
1651
1659
  await scope.setCustomDatatype.al_date( scope, { handle, answer })
1652
1660
  }, // Ends scope.setCustomDatatype.BirthDate()
1653
1661
 
@@ -1655,6 +1663,7 @@ module.exports = {
1655
1663
  /** Given a date of the format 'mm/dd/yyyy' and the handle of the
1656
1664
  * custom field container, sets the ALToolbox custom ThreePartsDate
1657
1665
  * field values. */
1666
+ answer = await scope.today_to_date(scope, { date: answer });
1658
1667
  await scope.setCustomDatatype.al_date( scope, { handle, answer })
1659
1668
  }, // Ends scope.setCustomDatatype.ThreePartsDate()
1660
1669
 
@@ -1729,36 +1738,20 @@ module.exports = {
1729
1738
  await scope.afterStep(scope, { waitForShowIf: true });
1730
1739
 
1731
1740
  } else if (type == `date`) {
1732
- let answer_date = answer;
1733
- // If the date is specified in the format "today" or "today + 2" or "today-1"
1734
- // it will need some pre-processing.
1735
- if (answer.indexOf(`today`) !== -1) {
1736
- // Find out how many days to add/subtract
1737
- let date_delta = answer.replace(/\s/g, '');
1738
- date_delta = date_delta.match(/today([+-]\d+)?/)[1];
1739
- date_delta = date_delta == undefined ? 0 : date_delta;
1740
- date_delta = parseInt(date_delta);
1741
- // Get today's date
1742
- answer_date = new Date();
1743
- // Add/subtract required number of days
1744
- answer_date.setDate(answer_date.getDate() + date_delta);
1745
- // Convert to the docassemble date field format: mm/dd/yyyy
1746
- // Reference: https://gomakethings.com/setting-a-date-input-to-todays-date-with-vanilla-js/
1747
- answer_date = ((answer_date.getMonth() + 1).toString().padStart(2, 0) + '/' +
1748
- answer_date.getDate().toString().padStart(2, 0) + '/' +
1749
- answer_date.getFullYear().toString());
1750
- }
1741
+ let answer_date = await scope.today_to_date(scope, { date: answer });
1751
1742
  await scope.setText( scope, { handle, answer: answer_date });
1743
+ await handle.press(`Tab`);
1752
1744
  await scope.afterStep(scope, { waitForShowIf: true });
1753
1745
  } else if (type == `time`) {
1754
1746
  // Authors should enter the time like 12:34 PM, but we shouldn't type ":", space, or M.
1755
1747
  let answer_time = answer.replace(":", "").replace(" ", "").replace("M", "");
1756
1748
  await handle.focus();
1757
1749
  await handle.type( answer );
1758
- await handle.press("Enter");
1750
+ await handle.press(`Enter`);
1759
1751
  await scope.afterStep(scope, { waitForShowIf: true });
1760
1752
  } else {
1761
1753
  await scope.setText( scope, { handle, answer });
1754
+ await handle.press(`Tab`);
1762
1755
  await scope.afterStep(scope, { waitForShowIf: true });
1763
1756
  }
1764
1757
 
@@ -1774,7 +1767,50 @@ module.exports = {
1774
1767
  return;
1775
1768
  },
1776
1769
 
1777
- }, // ends scope.enter_answer
1770
+ }, // ends scope.enter_answer()
1771
+
1772
+ today_to_date: async function ( scope, { date }) {
1773
+ /** Return `date` or, if the date is specified in the format
1774
+ * "today" or "today + 2" or "today-1", return a date of the format
1775
+ * "mm/dd/yyyy".
1776
+ *
1777
+ * @param scope { obj } REQUIRED. Object with test state.
1778
+ * @param date { str } REQUIRED. Input from the developer.
1779
+ *
1780
+ * @returns { str } `date` or "today" math as date str
1781
+ *
1782
+ * WARNING: If the developer is counting on date boundaries to trigger
1783
+ * other logic then timezone differences may interfere with that. They
1784
+ * should:
1785
+ *
1786
+ * 1. Test with a day well within the date range they're testing instead
1787
+ * of near a boundary. For example, test 8 days from now instead of 10.
1788
+ * This is because their test may run around 12am and thus be vulnerable
1789
+ * to failure.
1790
+ * 2. Warn the user explicitly and let them know when they need to file by
1791
+ * in case the user doesn't know and files a few days from the day they filled
1792
+ * out the form.
1793
+ */
1794
+ let valid_date = date;
1795
+ if (date.indexOf(`today`) !== -1) {
1796
+ // Find out how many days to add/subtract
1797
+ let date_delta = date.replace(/\s/g, '');
1798
+ date_delta = date_delta.match(/today([+-]\d+)?/)[1];
1799
+ date_delta = date_delta == undefined ? 0 : date_delta;
1800
+ date_delta = parseInt(date_delta);
1801
+ // Get today's date
1802
+ valid_date = new Date();
1803
+ // Add/subtract required number of days
1804
+ valid_date.setDate(valid_date.getDate() + date_delta);
1805
+ // Convert to the docassemble date field format: mm/dd/yyyy
1806
+ // Reference: https://gomakethings.com/setting-a-date-input-to-todays-date-with-vanilla-js/
1807
+ valid_date = ((valid_date.getMonth() + 1).toString().padStart(2, 0) + '/' +
1808
+ valid_date.getDate().toString().padStart(2, 0) + '/' +
1809
+ valid_date.getFullYear().toString());
1810
+ }
1811
+
1812
+ return valid_date;
1813
+ }, // Ends scope.today_to_date()
1778
1814
 
1779
1815
  uploadFiles: async function ( scope, { handle, answer }) {
1780
1816
  /* Try to upload one or more files to a file-type input field.
package/lib/steps.js CHANGED
@@ -203,6 +203,7 @@ Given(/I start the interview at "([^"]+)"/, {timeout: -1}, async (file_name) =>
203
203
  // at any one time since each step completes synchronously
204
204
 
205
205
  // If response has a file in it that matches the file we are downloading
206
+ // Sadly, 'content-disposition' isn't working. We're not sure what will.
206
207
  if ( response.headers()['content-disposition'] && (response.headers()['content-disposition']).includes(`attachment;filename=${scope.toDownload}`) ) {
207
208
  // We're choosing to avoid removing the event listener in case removal
208
209
  // would prevent downloading other files. The listener should get
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "5.2.0",
3
+ "version": "5.2.1",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {