diff --git a/.github/workflows/release-part-1.yml b/.github/workflows/release-part-1.yml index 4b6351e85f..e60ca9cd73 100644 --- a/.github/workflows/release-part-1.yml +++ b/.github/workflows/release-part-1.yml @@ -53,7 +53,24 @@ jobs: perl -0777 -i -pe \ "s/Unreleased Changes\n------------------/..\n Unreleased Changes\n ------------------\n\n${HEADER}\n${UNDERLINE}/g" \ HISTORY.rst + + - name: Install dependencies + run: pip install rst2html5 + + - name: Generate changelog.html + run: rst2html5 HISTORY.rst workbench/changelog.html + + - name: Update package.json version + uses: BellCubeDev/update-package-version-by-release-tag@v2 + with: + version: ${{ inputs.version }} + package-json-path: workbench/package.json + + - name: Commit updated HISTORY.rst, changelog.html, and package.json + run: | git add HISTORY.rst + git add workbench/changelog.html + git add workbench/package.json git commit -m "Committing the $VERSION release." - name: Tag and push diff --git a/HISTORY.rst b/HISTORY.rst index 069c285555..3727f646e4 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -48,6 +48,21 @@ Unreleased Changes reflect changes in how InVEST is installed on modern systems, and also to include images of the InVEST workbench instead of just broken links. https://github.com/natcap/invest/issues/1660 +* Workbench + * Several small updates to the model input form UI to improve usability + and visual consistency (https://github.com/natcap/invest/issues/912). + * Fixed a bug that caused the application to crash when attempting to + open a workspace without a valid logfile + (https://github.com/natcap/invest/issues/1598). + * Fixed a bug that was allowing readonly workspace directories on Windows + (https://github.com/natcap/invest/issues/1599). + * Fixed a bug that, in certain scenarios, caused a datastack to be saved + with relative paths when the Relative Paths checkbox was left unchecked + (https://github.com/natcap/invest/issues/1609). + * Improved error handling when a datastack cannot be saved with relative + paths across drives (https://github.com/natcap/invest/issues/1608). + * The InVEST changelog now displays in the Workbench the first time a new + version is launched (https://github.com/natcap/invest/issues/1368). * Coastal Vulnerability * Fixed a regression where an AOI with multiple features could raise a TypeError after intersecting with the landmass polygon. @@ -75,19 +90,6 @@ Unreleased Changes * The model now works as expected when the user provides an LULC raster that does not have a nodata value defined. https://github.com/natcap/invest/issues/1293 -* Workbench - * Several small updates to the model input form UI to improve usability - and visual consistency (https://github.com/natcap/invest/issues/912). - * Fixed a bug that caused the application to crash when attempting to - open a workspace without a valid logfile - (https://github.com/natcap/invest/issues/1598). - * Fixed a bug that was allowing readonly workspace directories on Windows - (https://github.com/natcap/invest/issues/1599). - * Fixed a bug that, in certain scenarios, caused a datastack to be saved - with relative paths when the Relative Paths checkbox was left unchecked - (https://github.com/natcap/invest/issues/1609). - * Improved error handling when a datastack cannot be saved with relative - paths across drives (https://github.com/natcap/invest/issues/1608). 3.14.2 (2024-05-29) ------------------- diff --git a/Makefile b/Makefile index 97af79b9f5..a75c58f220 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ PYTHON_ARCH := $(shell $(PYTHON) -c "import sys; print('x86' if sys.maxsize <= 2 GSUTIL := gsutil SIGNTOOL := SignTool +RST2HTML5 := rst2html5 # local directory names DIST_DIR := dist @@ -73,6 +74,8 @@ DIST_DATA_DIR := $(DIST_DIR)/data BUILD_DIR := build WORKBENCH := workbench WORKBENCH_DIST_DIR := $(WORKBENCH)/dist +CHANGELOG_SRC := HISTORY.rst +CHANGELOG_DEST := $(WORKBENCH)/changelog.html # The fork name and user here are derived from the git path on github. # The fork name will need to be set manually (e.g. make FORKNAME=natcap/invest) @@ -141,6 +144,7 @@ help: @echo " binaries to build pyinstaller binaries" @echo " apidocs to build HTML API documentation" @echo " userguide to build HTML version of the users guide" + @echo " changelog to build HTML version of the changelog" @echo " python_packages to build natcap.invest wheel and source distributions" @echo " codesign_mac to sign the mac disk image using the codesign utility" @echo " codesign_windows to sign the windows installer using the SignTool utility" @@ -366,6 +370,9 @@ deploy: @echo "Application binaries (if they were created) can be downloaded from:" @echo " * $(DOWNLOAD_DIR_URL)" +changelog: + $(RST2HTML5) $(CHANGELOG_SRC) $(CHANGELOG_DEST) + # Notes on Makefile development # # * Use the -drR to show the decision tree (and none of the implicit rules) diff --git a/requirements-dev.txt b/requirements-dev.txt index 6325f1f62d..75b210cc1b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -26,3 +26,4 @@ requests coverage xlwt build # pip-only +rst2html5 diff --git a/workbench/changelog.html b/workbench/changelog.html new file mode 100644 index 0000000000..7f2b62bb56 --- /dev/null +++ b/workbench/changelog.html @@ -0,0 +1,3233 @@ + + + + + + + + +
+

Unreleased Changes

+ +
+
+

3.14.2 (2024-05-29)

+ +
+
+

3.14.1 (2023-12-18)

+ +
+
+

3.14.0 (2023-09-08)

+ +
+
+

3.13.0 (2023-03-17)

+ +
+
+

3.12.1 (2022-12-16)

+ +
+
+

3.12.0 (2022-08-31)

+ +
+
+

3.11.0 (2022-05-24)

+ +
+
+

3.10.2 (2022-02-08)

+ +
+
+

3.10.1 (2022-01-06)

+ +
+
+

3.10.0 (2022-01-04)

+ +
+
+

3.9.2 (2021-10-29)

+ +
+
+

3.9.1 (2021-09-22)

+ +
+
+

3.9.0 (2020-12-11)

+ +
+
+

3.8.9 (2020-09-15)

+ +
+
+

3.8.8 (2020-09-04)

+ +
+
+

3.8.7 (2020-07-17)

+ +
+
+

3.8.6 (2020-07-03)

+ +
+
+

3.8.5 (2020-06-26)

+ +
+
+

3.8.4 (2020-06-05)

+ +
+
+

3.8.3 (2020-05-29)

+ +
+
+

3.8.2 (2020-05-15)

+ +
+
+

3.8.1 (2020-05-08)

+ +
+
+

3.8.0 (2020-02-07)

+ +
+
+

3.7.0 (2019-05-09)

+ +
+
+

3.6.0 (2019-01-30)

+ +
+
+

3.5.0 (2018-08-14)

+ +
+
+

3.4.4 (2018-03-26)

+ +
+
+

3.4.3 (2018-03-26)

+ +
+
+

3.4.2 (2017-12-15)

+ +
+
+

3.4.1 (2017-12-11)

+ +
+
+

3.4.0 (2017-12-03)

+ +
+
+

3.3.3 (2017-02-06)

+ +
+
+

3.3.2 (2016-10-17)

+ +
+
+

3.3.1 (2016-06-13)

+ +
+
+

3.3.0 (2016-03-14)

+ +
+
+

3.2.0 (2015-05-31)

+

InVEST 3.2.0 is a major release with the addition of several experimental models and tools as well as an upgrade to the PyGeoprocessing core:

+ +
+
+

3.1.3 (2015-04-23)

+

InVEST 3.1.3 is a hotfix release patching a memory blocking issue resolved in PyGeoprocessing version 0.2.1. Users might have experienced slow runtimes on SDR or other routed models.

+
+
+

3.1.2 (2015-04-15)

+

InVEST 3.1.2 is a minor release patching issues mostly related to the freshwater routing models and signed GDAL Byte datasets.

+ +
+
+

3.1.1 (2015-03-13)

+

InVEST 3.1.1 is a major performance and memory bug patch to the InVEST toolsuite. We recommend all users upgrade to this version.

+ +

There are side effects that result in sometimes large changes to un calibrated runs of SDR or nutrient. These are related to slightly different flow directions across the landscape and a bug fix on the distance to stream calculation.

+ +
+
+

3.1.0 (2014-11-19)

+

InVEST 3.1.0 (http://www.naturalcapitalproject.org/download.html) is a major software and science milestone that includes an overhauled sedimentation model, long awaited fixes to exponential decay routines in habitat quality and pollination, and a massive update to the underlying hydrological routing routines. The updated sediment model, called SDR (sediment delivery ratio), is part of our continuing effort to improve the science and capabilities of the InVEST tool suite. The SDR model inputs are backwards comparable with the InVEST 3.0.1 sediment model with two additional global calibration parameters and removed the need for the retention efficiency parameter in the biophysical table; most users can run SDR directly with the data they have prepared for previous versions. The biophysical differences between the models are described in a section within the SDR user's guide and represent a superior representation of the hydrological connectivity of the watershed, biophysical parameters that are independent of cell size, and a more accurate representation of sediment retention on the landscape. Other InVEST improvements to include standard bug fixes, performance improvements, and usability features which in part are described below:

+ +
+
+

3.0.1 (2014-05-19)

+ +
+
+

3.0.0 (2014-03-23)

+

The 3.0.0 release of InVEST represents a shift away from the ArcGIS to the InVEST standalone computational platform. The only exception to this shift is the marine coastal protection tier 1 model which is still supported in an ArcGIS toolbox and has no InVEST 3.0 standalone at the moment. Specific changes are detailed below

+ +
+
+

2.6.0 (2013-12-16)

+

The 2.6.0 release of InVEST removes most of the old InVEST models from the Arc toolbox in favor of the new InVEST standalone models. While we have been developing standalone equivalents for the InVEST Arc models since version 2.3.0, this is the first release in which we removed support for the deprecated ArcGIS versions after an internal review of correctness, performance, and stability on the standalones. Additionally, this is one of the last milestones before the InVEST 3.0.0 release later next year which will transition InVEST models away from strict ArcGIS dependence to a standalone form.

+

Specifically, support for the following models have been moved from the ArcGIS toolbox to their Windows based standalones: (1) hydropower/water yield, (2) finfish aquaculture, (3) coastal protection tier 0/coastal vulnerability, (4) wave energy, (5) carbon, (6) habitat quality/biodiversity, (7) pollination, (8) timber, and (9) overlap analysis. Additionally, documentation references to ArcGIS for those models have been replaced with instructions for launching standalone InVEST models from the Windows start menu.

+

This release also addresses minor bugs, documentation updates, performance tweaks, and new functionality to the toolset, including:

+ +
+
+

2.5.6 (2013-09-06)

+

The 2.5.6 release of InVEST that addresses minor bugs, performance tweaks, and new functionality of the InVEST standalone models. Including:

+ +
+
+

2.5.5 (2013-08-06)

+

The 2.5.5 release of InVEST that addresses minor bugs, performance tweaks, and new functionality of the InVEST standalone models. Including:

+
+ +
+

Known Issues:

+
+ +
+
+
+

2.5.4 (2013-06-07)

+

This is a minor release of InVEST that addresses numerous minor bugs and performance tweaks in the InVEST 3.0 models. Including:

+
+ +
+

Known Issues:

+
+ +
+
+
+

2.5.3 (2013-03-21)

+

This is a minor release of InVEST that fixes an issue with the HRA model that caused ArcGIS versions of the model to fail when calculating habitat maps for risk hotspots. This upgrade is strongly recommended for users of InVEST 2.5.1 or 2.5.2.

+
+
+

2.5.2 (2013-03-17)

+

This is a minor release of InVEST that fixes an issue with the HRA sample data that caused ArcGIS versions of the model to fail on the training data. There is no need to upgrade for most users unless you are doing InVEST training.

+
+
+

2.5.1 (2013-03-12)

+

This is a minor release of InVEST that does not add any new models, but does add additional functionality, stability, and increased performance to one of the InVEST 3.0 standalones:

+
+ +
+

Additionally, this minor release fixes a bug in the InVEST user interface where collapsible containers became entirely non-interactive.

+
+
+

2.5.0 (2013-03-08)

+

This a major release of InVEST that includes new standalone versions (ArcGIS is not required) our models as well as additional functionality, stability, and increased performance to many of the existing models. This release is timed to support our group's annual training event at Stanford University. We expect to release InVEST 2.5.1 a couple of weeks after to address any software issues that arise during the training. See the release notes below for details of the release, and please contact richsharp@stanford.edu for any issues relating to software:

+
+ +
+
    +
  • The InVEST 3.0 carbon model will generate inaccurate sequestration results if the extents of the current and future maps don't align. This will be fixed in InVEST 2.5.1; in the meanwhile a workaround is to clip both LULCs so they have identical overlaps.
  • +
  • A user reported an unstable run of InVEST 3.0 water yield. We are not certain what is causing the issue, but we do have a fix that will go out in InVEST 2.5.1.
  • +
  • At the moment the InVEST standalones do not run on Windows XP. This appears to be related to an incompatibility between Windows XP and GDAL, the an open source gis library we use to create and read GIS data. At the moment we are uncertain if we will be able to fix this bug in future releases, but will pass along more information in the future.
  • +
+
+
+
+
+

2.4.5 (2013-02-01)

+

This is a minor release of InVEST that does not add any new models, but does add additional functionality, stability, and increased performance to many of the InVEST 3.0 standalones:

+
+ +
+
+
+

2.4.4 (2012-10-24)

+ +
+
+

2.4.3 (2012-10-19)

+ +
+
+

2.4.2 (2012-10-15)

+ +
+
+

2.4.1 (2012-10-08)

+ +
+
+

2.4.0 (2012-10-05)

+

Changes in InVEST 2.4.0

+

General:

+

This is a major release which releases two additional beta versions of the InVEST models in the InVEST 3.0 framework. Additionally, this release introduces start menu shortcuts for all available InVEST 3.0 beta models. Existing InVEST 2.x models can still be found in the included Arc toolbox.

+

Existing InVEST models migrated to the 3.0 framework in this release include:

+ +

Updates to ArcGIS models:

+ +

Additionally there are a handful of minor fixes and feature enhancements:

+ +
+
+

2.3.0 (2012-08-02)

+

Changes in InVEST 2.3.0

+

General:

+

This is a major release which releases several beta versions of the InVEST models in the InVEST 3.0 framework. These models run as standalones, but a GIS platform is needed to edit and view the data inputs and outputs. Until InVEST 3.0 is released the original ArcGIS based versions of these tools will remain the release.

+

Existing InVEST models migrated to the 3.0 framework in this release include:

+ +

This release also includes the new models which only exist in the 3.0 framework:

+ +

InVEST models in the 3.0 framework from previous releases that now have a standalone executable include:

+ +

Additionally there are a handful of other minor fixes and feature enhancements since the previous release:

+ +
+
+

2.2.2 (2012-03-03)

+

Changes in InVEST 2.2.2

+

General:

+

This is a minor release which fixes the following defects:

+
+
-Fixed an issue with sediment retention model where large watersheds
+
allowed loading per cell was incorrectly rounded to integer values.
+
-Fixed bug where changing the threshold didn't affect the retention output
+
because function was incorrectly rounded to integer values.
+
+

-Added total water yield in meters cubed to to output table by watershed.

+
+
-Fixed bug where smaller than default (2000) resolutions threw an error about
+
not being able to find the field in "unitynew". With non-default resolution, "unitynew" was created without an attribute table, so one was created by force.
+
-Removed mention of beta state and ecoinformatics from header of software
+
license.
+
-Modified overlap analysis toolbox so it reports an error directly in the
+
toolbox if the workspace name is too long.
+
+
+
+

2.2.1 (2012-01-26)

+

Changes in InVEST 2.2.1

+

General:

+

This is a minor release which fixes the following defects:

+

-A variety of miscellaneous bugs were fixed that were causing crashes of the Coastal Protection model in Arc 9.3. -Fixed an issue in the Pollination model that was looking for an InVEST1005 directory. -The InVEST "models only" release had an entry for the InVEST 3.0 Beta tools, but was missing the underlying runtime. This has been added to the models only 2.2.1 release at the cost of a larger installer. -The default InVEST ArcMap document wouldn't open in ArcGIS 9.3. It can now be opened by Arc 9.3 and above. -Minor updates to the Coastal Protection user's guide.

+
+
+

2.2.0 (2011-12-22)

+

In this release we include updates to the habitat risk assessment model, updates to Coastal Vulnerability Tier 0 (previously named Coastal Protection), and a new tier 1 Coastal Vulnerability tool. Additionally, we are releasing a beta version of our 3.0 platform that includes the terrestrial timber and carbon models.

+

See the "Marine Models" and "InVEST 3.0 Beta" sections below for more details.

+

Marine Models

+
    +
  1. Marine Python Extension Check +

    This tool has been updated to include extension requirements for the new Coastal Protection T1 model. It also reflects changes to the Habitat Risk Assessment and Coastal Protection T0 models, as they no longer require the PythonWin extension.

    +
  2. +
  3. Habitat Risk Assessment (HRA) +

    This model has been updated and is now part of three-step toolset. The first step is a new Ratings Survey Tool which eliminates the need for Microsoft Excel when users are providing habitat-stressor ratings. This Survey Tool now allows users to up- and down-weight the importance of various criteria. For step 2, a copy of the Grid the Seascape tool has been placed in the HRA toolset. In the last step, users will run the HRA model which includes the following updates:

    +
      +
    • New habitat outputs classifying risk as low, medium, and high
    • +
    • Model run status updates (% complete) in the message window
    • +
    • Improved habitat risk plots embedded in the output HTML
    • +
    +
  4. +
  5. Coastal Protection +

    This module is now split into sub-models, each with two parts. The first sub-model is Coastal Vulnerability (Tier 0) and the new addition is Coastal Protection (Tier 1).

    +

    Coastal Vulnerability (T0) Step 1) Fetch Calculator - there are no updates to this tool. Step 2) Vulnerability Index

    +
      +
    • Wave Exposure: In this version of the model, we define wave exposure for sites facing the open ocean as the maximum of the weighted average of wave's power coming from the ocean or generated by local winds. We weight wave power coming from each of the 16 equiangular sector by the percent of time that waves occur in that sector, and based on whether or not fetch in that sector exceeds 20km. For sites that are sheltered, wave exposure is the average of wave power generated by the local storm winds weighted by the percent occurrence of those winds in each sector. This new method takes into account the seasonality of wind and wave patterns (storm waves generally come from a preferential direction), and helps identify regions that are not exposed to powerful waves although they are open to the ocean (e.g. the leeside of islands).
    • +
    • Natural Habitats: The ranking is now computed using the rank of all natural habitats present in front of a segment, and we weight the lowest ranking habitat 50% more than all other habitats. Also, rankings and protective distance information are to be provided by CSV file instead of Excel. With this new method, shoreline segments that have more habitats than others will have a lower risk of inundation and/or erosion during storms.
    • +
    • Structures: The model has been updated to now incorporate the presence of structures by decreasing the ranking of shoreline segments that adjoin structures.
    • +
    +

    Coastal Protection (T1) - This is a new model which plots the amount of sandy beach erosion or consolidated bed scour that backshore regions experience in the presence or absence of natural habitats. It is composed of two steps: a Profile Generator and Nearshore Waves and Erosion. It is recommended to run the Profile Generator before the Nearshore Waves and Erosion model.

    +

    Step 1) Profile Generator: This tool helps the user generate a 1-dimensional bathymetric and topographic profile perpendicular to the shoreline at the user-defined location. This model provides plenty of guidance for building backshore profiles for beaches, marshes and mangroves. It will help users modify bathymetry profiles that they already have, or can generate profiles for sandy beaches if the user has not bathymetric data. Also, the model estimates and maps the location of natural habitats present in front of the region of interest. Finally, it provides sample wave and wind data that can be later used in the Nearshore Waves and Erosion model, based on computed fetch values and default Wave Watch III data.

    +

    Step 2) Nearshore Waves and Erosion: This model estimates profiles of beach erosion or values of rates of consolidated bed scour at a site as a function of the type of habitats present in the area of interest. The model takes into account the protective effects of vegetation, coral and oyster reefs, and sand dunes. It also shows the difference of protection provided when those habitats are present, degraded, or gone.

    +
  6. +
  7. Aesthetic Quality +

    This model no longer requires users to provide a projection for Overlap Analysis. Instead, it uses the projection from the user-specified Area of Interest (AOI) polygon. Additionally, the population estimates for this model have been fixed.

    +
  8. +
+

InVEST 3.0 Beta

+

The 2.2.0 release includes a preliminary version of our InVEST 3.0 beta platform. It is included as a toolset named "InVEST 3.0 Beta" in the InVEST220.tbx. It is currently only supported with ArcGIS 10. To launch an InVEST 3.0 beta tool, double click on the desired tool in the InVEST 3.0 toolset then click "Ok" on the Arc toolbox screen that opens. The InVEST 3.0 tool panel has inputs very similar to the InVEST 2.2.0 versions of the tools with the following modifications:

+
+
InVEST 3.0 Carbon:
+
+
    +
  • Fixes a minor bug in the 2.2 version that ignored floating point values in carbon pool inputs.
  • +
  • Separation of carbon model into a biophysical and valuation model.
  • +
  • Calculates carbon storage and sequestration at the minimum resolution of the input maps.
  • +
  • Runtime efficiency improved by an order of magnitude.
  • +
  • User interface streamlined including dynamic activation of inputs based on user preference, direct link to documentation, and recall of inputs based on user's previous run.
  • +
+
+
InVEST 3.0 Timber:
+
+
    +
  • User interface streamlined including dynamic activation of inputs based on user preference, direct link to documentation, and recall of inputs based on user's previous run.
  • +
+
+
+
+
+

2.1.1 (2011-10-17)

+

Changes in InVEST 2.1.1

+

General:

+

This is a minor release which fixes the following defects:

+

-A truncation error was fixed on nutrient retention and sedimentation model that involved division by the number of cells in a watershed. Now correctly calculates floating point division. -Minor typos were fixed across the user's guide.

+
+
+

2.1 Beta (2011-05-11)

+

Updates to InVEST Beta

+

InVEST 2.1 . Beta

+

Changes in InVEST 2.1

+

General:

+

1. InVEST versioning We have altered our versioning scheme. Integer changes will reflect major changes (e.g. the addition of marine models warranted moving from 1.x to 2.0). An increment in the digit after the primary decimal indicates major new features (e.g the addition of a new model) or major revisions. For example, this release is numbered InVEST 2.1 because two new models are included). We will add another decimal to reflect minor feature revisions or bug fixes. For example, InVEST 2.1.1 will likely be out soon as we are continually working to improve our tool. 2. HTML guide With this release, we have migrated the entire InVEST users. guide to an HTML format. The HTML version will output a pdf version for use off-line, printing, etc.

+

MARINE MODELS

+

1.Marine Python Extension Check

+

-This tool has been updated to allow users to select the marine models they intend to run. Based on this selection, it will provide a summary of which Python and ArcGIS extensions are necessary and if the Python extensions have been successfully installed on the user.s machine.

+

2.Grid the Seascape (GS)

+

-This tool has been created to allow marine model users to generate an seascape analysis grid within a specified area of interest (AOI).

+

-It only requires an AOI and cell size (in meters) as inputs, and produces a polygon grid which can be used as inputs for the Habitat Risk Assessment and Overlap Analysis models.

+
    +
  1. Coastal Protection
  2. +
+ +
    +
  1. Habitat Risk Assessment (HRA)
  2. +
+

This new model allows users to assess the risk posed to coastal and marine habitats by human activities and the potential consequences of exposure for the delivery of ecosystem services and biodiversity. The HRA model is suited to screening the risk of current and future human activities in order to prioritize management strategies that best mitigate risk.

+
    +
  1. Overlap Analysis
  2. +
+

This new model maps current human uses in and around the seascape and summarizes the relative importance of various regions for particular activities. The model was designed to produce maps that can be used to identify marine and coastal areas that are most important for human use, in particular recreation and fisheries, but also other activities.

+

FRESHWATER MODELS

+

All Freshwater models now support ArcMap 10.

+

Sample data:

+
    +
  1. Bug fix for error in Water_Tables.mdb Biophysical table where many field values were shifted over one column relative to the correct field name.
  2. +
  3. Bug fix for incorrect units in erosivity layer.
  4. +
+

Hydropower:

+

1.In Water Yield, new output tables have been added containing mean biophysical outputs (precipitation, actual and potential evapotranspiration, water yield) for each watershed and sub-watershed.

+

Water Purification:

+
    +
  1. The Water Purification Threshold table now allows users to specify separate thresholds for nitrogen and phosphorus. Field names thresh_n and thresh_p replace the old ann_load.
  2. +
  3. The Nutrient Retention output tables nutrient_watershed.dbf and nutrient_subwatershed.dbf now include a column for nutrient retention per watershed/sub-watershed.
  4. +
  5. In Nutrient Retention, some output file names have changed.
  6. +
  7. The user's guide has been updated to explain more accurately the inclusion of thresholds in the biophysical service estimates.
  8. +
+

Sedimentation:

+
    +
  1. The Soil Loss output tables sediment_watershed.dbf and sediment_subwatershed.dbf now include a column for sediment retention per watershed/sub-watershed.
  2. +
  3. In Soil Loss, some output file names have changed.
  4. +
  5. The default input value for Slope Threshold is now 75.
  6. +
  7. The user's guide has been updated to explain more accurately the inclusion of thresholds in the biophysical service estimates.
  8. +
  9. Valuation: Bug fix where the present value was not being applied correctly.
  10. +
+
+
+

2.0 Beta (2011-02-14)

+

Changes in InVEST 2.0

+

InVEST 1.005 is a minor release with the following modification:

+
    +
  1. Aesthetic Quality +

    This new model allows users to determine the locations from which new nearshore or offshore features can be seen. It generates viewshed maps that can be used to identify the visual footprint of new offshore development.

    +
  2. +
  3. Coastal Vulnerability +

    This new model produces maps of coastal human populations and a coastal exposure to erosion and inundation index map. These outputs can be used to understand the relative contributions of different variables to coastal exposure and to highlight the protective services offered by natural habitats.

    +
  4. +
  5. Aquaculture +

    This new model is used to evaluate how human activities (e.g., addition or removal of farms, changes in harvest management practices) and climate change (e.g., change in sea surface temperature) may affect the production and economic value of aquacultured Atlantic salmon.

    +
  6. +
  7. Wave Energy +

    This new model provides spatially explicit information, showing potential areas for siting Wave Energy conversion (WEC) facilities with the greatest energy production and value. This site- and device-specific information for the WEC facilities can then be used to identify and quantify potential trade-offs that may arise when siting WEC facilities.

    +
  8. +
  9. Avoided Reservoir Sedimentation +
      +
    • The name of this model has been changed to the Sediment Retention model.
    • +
    • We have added a water quality valuation model for sediment retention. The user now has the option to select avoided dredge cost analysis, avoided water treatment cost analysis or both. The water quality valuation approach is the same as that used in the Water Purification: Nutrient Retention model.
    • +
    • The threshold information for allowed sediment loads (TMDL, dead volume, etc.) are now input in a stand alone table instead of being included in the valuation table. This adjusts the biophysical service output for any social allowance of pollution. Previously, the adjustment was only done in the valuation model.
    • +
    • The watersheds and sub-watershed layers are now input as shapefiles instead of rasters.
    • +
    • Final outputs are now aggregated to the sub-basin scale. The user must input a sub-basin shapefile. We provide the Hydro 1K dataset as a starting option. See users guide for changes to many file output names.
    • +
    • Users are strongly advised not to interpret pixel-scale outputs for hydrological understanding or decision-making of any kind. Pixel outputs should only be used for calibration/validation or model checking.
    • +
    +
  10. +
  11. Hydropower Production +
      +
    • The watersheds and sub-watershed layers are now input as shapefiles instead of rasters.
    • +
    • Final outputs are now aggregated to the sub-basin scale. The user must input a sub-basin shapefile. We provide the Hydro 1K dataset as a starting option. See users guide for changes to many file output names.
    • +
    • Users are strongly advised not to interpret pixel-scale outputs for hydrological understanding or decision-making of any kind. Pixel outputs should only be used for calibration/validation or model checking.
    • +
    • The calibration constant for each watershed is now input in a stand-alone table instead of being included in the valuation table. This makes running the water scarcity model simpler.
    • +
    +
  12. +
  13. Water Purification: Nutrient Retention +
      +
    • The threshold information for allowed pollutant levels (TMDL, etc.) are now input in a stand alone table instead of being included in the valuation table. This adjusts the biophysical service output for any social allowance of pollution. Previously, the adjustment was only done in the valuation model.
    • +
    • The watersheds and sub-watershed layers are now input as shapefiles instead of rasters.
    • +
    • Final outputs are now aggregated to the sub-basin scale. The user must input a sub-basin shapefile. We provide the Hydro 1K dataset as a starting option. See users guide for changes to many file output names.
    • +
    • Users are strongly advised not to interpret pixel-scale outputs for hydrological understanding or decision-making of any kind. Pixel outputs should only be used for calibration/validation or model checking.
    • +
    +
  14. +
  15. Carbon Storage and Sequestration +

    The model now outputs an aggregate sum of the carbon storage.

    +
  16. +
  17. Habitat Quality and Rarity +

    This model had an error while running ReclassByACII if the land cover codes were not sorted alphabetically. This has now been corrected and it sorts the reclass file before running the reclassification

    +

    The model now outputs an aggregate sum of the habitat quality.

    +
  18. +
  19. Pollination +

    In this version, the pollination model accepts an additional parameter which indicated the proportion of a crops yield that is attributed to wild pollinators.

    +
  20. +
+
+ + \ No newline at end of file diff --git a/workbench/package.json b/workbench/package.json index c89365511a..e7c2ccaf4f 100644 --- a/workbench/package.json +++ b/workbench/package.json @@ -1,6 +1,6 @@ { "name": "invest-workbench", - "version": "0.1.0", + "version": "3.14.2", "description": "Models that map and value the goods and services from nature that sustain and fulfill human life", "main": "build/main/main.js", "homepage": "./", diff --git a/workbench/src/main/ipcMainChannels.js b/workbench/src/main/ipcMainChannels.js index f908d7c33b..98ad85112d 100644 --- a/workbench/src/main/ipcMainChannels.js +++ b/workbench/src/main/ipcMainChannels.js @@ -1,4 +1,5 @@ export const ipcMainChannels = { + BASE_URL: 'base-url', CHANGE_LANGUAGE: 'change-language', CHECK_FILE_PERMISSIONS: 'check-file-permissions', CHECK_STORAGE_TOKEN: 'check-storage-token', @@ -12,6 +13,7 @@ export const ipcMainChannels = { INVEST_RUN: 'invest-run', INVEST_VERSION: 'invest-version', IS_FIRST_RUN: 'is-first-run', + IS_NEW_VERSION: 'is-new-version', LOGGER: 'logger', OPEN_EXTERNAL_URL: 'open-external-url', OPEN_PATH: 'open-path', diff --git a/workbench/src/main/main.js b/workbench/src/main/main.js index 5b5b78f90f..7357302054 100644 --- a/workbench/src/main/main.js +++ b/workbench/src/main/main.js @@ -1,5 +1,5 @@ import path from 'path'; - +import i18n from './i18n/i18n'; // eslint-disable-next-line import/no-extraneous-dependencies import { app, @@ -9,35 +9,37 @@ import { ipcMain } from 'electron'; +import BASE_URL from './baseUrl'; import { createPythonFlaskProcess, getFlaskIsReady, - shutdownPythonProcess + shutdownPythonProcess, } from './createPythonFlaskProcess'; import findInvestBinaries from './findInvestBinaries'; -import setupDownloadHandlers from './setupDownloadHandlers'; -import setupDialogs from './setupDialogs'; -import setupContextMenu from './setupContextMenu'; +import { ipcMainChannels } from './ipcMainChannels'; +import ELECTRON_DEV_MODE from './isDevMode'; +import { getLogger } from './logger'; +import menuTemplate from './menubar'; +import pkg from '../../package.json'; +import { settingsStore, setupSettingsHandlers } from './settingsStore'; +import { setupBaseUrl } from './setupBaseUrl'; import setupCheckFilePermissions from './setupCheckFilePermissions'; import { setupCheckFirstRun } from './setupCheckFirstRun'; import { setupCheckStorageToken } from './setupCheckStorageToken'; +import setupContextMenu from './setupContextMenu'; +import setupDialogs from './setupDialogs'; +import setupDownloadHandlers from './setupDownloadHandlers'; +import setupGetElectronPaths from './setupGetElectronPaths'; +import setupGetNCPUs from './setupGetNCPUs'; import { + setupInvestLogReaderHandler, setupInvestRunHandlers, - setupInvestLogReaderHandler } from './setupInvestHandlers'; -import setupGetNCPUs from './setupGetNCPUs'; +import { setupIsNewVersion } from './setupIsNewVersion'; import setupOpenExternalUrl from './setupOpenExternalUrl'; import setupOpenLocalHtml from './setupOpenLocalHtml'; -import { settingsStore, setupSettingsHandlers } from './settingsStore'; -import setupGetElectronPaths from './setupGetElectronPaths'; import setupRendererLogger from './setupRendererLogger'; -import { ipcMainChannels } from './ipcMainChannels'; -import menuTemplate from './menubar'; -import ELECTRON_DEV_MODE from './isDevMode'; -import BASE_URL from './baseUrl'; -import { getLogger } from './logger'; -import i18n from './i18n/i18n'; -import pkg from '../../package.json'; + const logger = getLogger(__filename.split('/').slice(-1)[0]); @@ -87,6 +89,7 @@ export const createWindow = async () => { setupDialogs(); setupCheckFilePermissions(); setupCheckFirstRun(); + setupIsNewVersion(); setupCheckStorageToken(); setupSettingsHandlers(); setupGetElectronPaths(); @@ -94,6 +97,7 @@ export const createWindow = async () => { setupInvestLogReaderHandler(); setupOpenExternalUrl(); setupRendererLogger(); + setupBaseUrl(); await getFlaskIsReady(); const devModeArg = ELECTRON_DEV_MODE ? '--devmode' : ''; diff --git a/workbench/src/main/setupBaseUrl.js b/workbench/src/main/setupBaseUrl.js new file mode 100644 index 0000000000..973e1035f4 --- /dev/null +++ b/workbench/src/main/setupBaseUrl.js @@ -0,0 +1,13 @@ +import { + ipcMain, +} from 'electron'; + +import { ipcMainChannels } from './ipcMainChannels'; + +import baseUrl from './baseUrl'; + +export function setupBaseUrl() { + ipcMain.handle( + ipcMainChannels.BASE_URL, () => baseUrl + ); +} diff --git a/workbench/src/main/setupIsNewVersion.js b/workbench/src/main/setupIsNewVersion.js new file mode 100644 index 0000000000..14472e33e9 --- /dev/null +++ b/workbench/src/main/setupIsNewVersion.js @@ -0,0 +1,52 @@ +import fs from 'fs'; +import path from 'path'; + +import { + app, + ipcMain, +} from 'electron'; + +import { ipcMainChannels } from './ipcMainChannels'; +import { getLogger } from './logger'; +import pkg from '../../package.json'; + +const logger = getLogger(__filename.split('/').slice(-1)[0]); + +export const APP_VERSION_TOKEN = 'app-version-token.txt'; + +/** Determine whether this is the first run of the current running version. + * + * @returns {boolean} true if this version has not run before, otherwise false + */ +export async function isNewVersion() { + // Getting version from package.json is simplest because there is no need to + // spawn an invest process simply to get the version of the installed binary. + const currentVersion = pkg.version; + const userDataPath = app.getPath('userData'); + const tokenPath = path.join(userDataPath, APP_VERSION_TOKEN); + try { + if (fs.existsSync(tokenPath)) { + const tokenString = fs.readFileSync(tokenPath, {encoding: 'utf8'}); + if (tokenString) { + const installedVersionList = tokenString.split(','); + if (installedVersionList.includes(currentVersion)) { + return false; + } + // If current version not found, add it + fs.writeFileSync(tokenPath, `${tokenString},${currentVersion}`); + return true; + } + } + // If file does not exist, create it + fs.writeFileSync(tokenPath, currentVersion); + } catch (error) { + logger.warn(`Unable to write app-version token: ${error}`); + } + return true; +} + +export function setupIsNewVersion() { + ipcMain.handle( + ipcMainChannels.IS_NEW_VERSION, () => isNewVersion() + ); +} diff --git a/workbench/src/renderer/app.jsx b/workbench/src/renderer/app.jsx index e9c5872f98..86810024d8 100644 --- a/workbench/src/renderer/app.jsx +++ b/workbench/src/renderer/app.jsx @@ -24,6 +24,7 @@ import DownloadProgressBar from './components/DownloadProgressBar'; import { getInvestModelNames } from './server_requests'; import InvestJob from './InvestJob'; import { dragOverHandlerNone } from './utils'; +import Changelog from './components/Changelog'; const { ipcRenderer } = window.Workbench.electron; @@ -42,6 +43,8 @@ export default class App extends React.Component { recentJobs: [], showDownloadModal: false, downloadedNofN: null, + showChangelog: false, + changelogDismissed: false, }; this.switchTabs = this.switchTabs.bind(this); this.openInvestModel = this.openInvestModel.bind(this); @@ -65,6 +68,9 @@ export default class App extends React.Component { .includes(job.modelRunName) )), showDownloadModal: this.props.isFirstRun, + // Show changelog if this is a new version, + // but if it's the first run ever, wait until after download modal closes. + showChangelog: this.props.isNewVersion && !this.props.isFirstRun, }); await i18n.changeLanguage(window.Workbench.LANGUAGE); ipcRenderer.on('download-status', (downloadedNofN) => { @@ -92,6 +98,20 @@ export default class App extends React.Component { this.setState({ showDownloadModal: shouldShow, }); + // After close, show changelog if new version and app has just launched + // (i.e., show changelog only once, after the first time the download modal closes). + if (!shouldShow && this.props.isNewVersion && !this.state.changelogDismissed) { + this.setState({ + showChangelog: true, + }); + } + } + + closeChangelogModal() { + this.setState({ + showChangelog: false, + changelogDismissed: true, + }); } /** Push data for a new InvestTab component to an array. @@ -183,6 +203,7 @@ export default class App extends React.Component { openTabIDs, activeTab, showDownloadModal, + showChangelog, downloadedNofN, } = this.state; @@ -277,6 +298,13 @@ export default class App extends React.Component { show={showDownloadModal} closeModal={() => this.showDownloadModal(false)} /> + { + showChangelog && + this.closeChangelogModal()} + /> + } { + async function loadHtml() { + const baseUrl = await ipcRenderer.invoke(ipcMainChannels.BASE_URL); + const response = await fetch(`${baseUrl}/changelog.html`); + if (!response.ok) { + logger.debug(`Error fetching changelog HTML: ${response.status} ${response.statusText}`); + return; + } + try { + const htmlString = await response.text(); + // Find the section whose heading explicitly matches the current version. + const versionStr = pkg.version; + const escapedVersionStr = versionStr.split('.').join('\\.'); + const sectionRegex = new RegExp( + `[\\s]*?

${escapedVersionStr}\\b[\\s\\S]*?

[\\s\\S]*?` + ); + const sectionMatches = htmlString.match(sectionRegex); + if (sectionMatches && sectionMatches.length) { + let latestVersionSection = sectionMatches[0]; + const linkRegex = / { + const openLinkInBrowser = (event) => { + event.preventDefault(); + ipcRenderer.send( + ipcMainChannels.OPEN_EXTERNAL_URL, event.currentTarget.href + ); + }; + document.querySelectorAll('.link-external').forEach(link => { + link.addEventListener('click', openLinkInBrowser); + }); + }, [htmlContent]); + + return ( + + + + {t('New in this version')} + + + + {/* Setting inner HTML in this way is OK because + the HTML content is controlled by our build process + and not, for example, sourced from user input. */} + + + + ); +} + +Changelog.propTypes = { + show: PropTypes.bool.isRequired, + close: PropTypes.func.isRequired, +}; diff --git a/workbench/src/renderer/components/DataDownloadModal/index.jsx b/workbench/src/renderer/components/DataDownloadModal/index.jsx index e55a0192ce..16d2819f76 100644 --- a/workbench/src/renderer/components/DataDownloadModal/index.jsx +++ b/workbench/src/renderer/components/DataDownloadModal/index.jsx @@ -211,6 +211,7 @@ class DataDownloadModal extends React.Component { show={this.props.show} onHide={this.closeDialog} size="lg" + aria-labelledby="download-modal-title" >
@@ -230,7 +231,9 @@ class DataDownloadModal extends React.Component {

{this.state.alertPath}

) - : {t("Download InVEST sample data")} + : + {t("Download InVEST sample data")} + }
diff --git a/workbench/src/renderer/index.jsx b/workbench/src/renderer/index.jsx index 9cf222ea3a..1cb962314b 100644 --- a/workbench/src/renderer/index.jsx +++ b/workbench/src/renderer/index.jsx @@ -9,6 +9,7 @@ import { ipcMainChannels } from '../main/ipcMainChannels'; const { ipcRenderer } = window.Workbench.electron; const isFirstRun = await ipcRenderer.invoke(ipcMainChannels.IS_FIRST_RUN); +const isNewVersion = await ipcRenderer.invoke(ipcMainChannels.IS_NEW_VERSION); const nCPU = await ipcRenderer.invoke(ipcMainChannels.GET_N_CPUS); const root = createRoot(document.getElementById('App')); @@ -16,6 +17,7 @@ root.render( diff --git a/workbench/src/renderer/styles/style.css b/workbench/src/renderer/styles/style.css index a335cabd6f..2068b75921 100644 --- a/workbench/src/renderer/styles/style.css +++ b/workbench/src/renderer/styles/style.css @@ -253,7 +253,7 @@ exceed 100% of window.*/ } .recent-job-card { - width: inherit; + width: inherit; margin-bottom: 1rem; padding: 0; height: fit-content; @@ -617,3 +617,18 @@ input[type=text]::placeholder { .error-boundary .btn { margin: 1rem; } + +/* Changelog modal */ +.link-external { + &:after { + content: ''; + display: inline-block; + width: 1rem; + height: 1rem; + /* Icon is react-icons/md/MdOpenInNew, as a URL-encoded SVG */ + background-image: url("data:image/svg+xml,%3Csvg stroke='currentColor' fill='currentColor' stroke-width='0' viewBox='0 0 24 24' class='mr-1' height='1em' width='1em' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='none' d='M0 0h24v24H0z'%3E%3C/path%3E%3Cpath d='M19 19H5V5h7V3H5a2 2 0 00-2 2v14a2 2 0 002 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z'%3E%3C/path%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: left 0.125rem top 0.125rem; + background-size: 1rem 1rem; + } +} diff --git a/workbench/tests/binary_tests/puppet.test.js b/workbench/tests/binary_tests/puppet.test.js index 54d8d74945..65f24ff355 100644 --- a/workbench/tests/binary_tests/puppet.test.js +++ b/workbench/tests/binary_tests/puppet.test.js @@ -13,6 +13,7 @@ import puppeteer from 'puppeteer-core'; import pkg from '../../package.json'; import { APP_HAS_RUN_TOKEN } from '../../src/main/setupCheckFirstRun'; +import { APP_VERSION_TOKEN } from '../../src/main/setupIsNewVersion'; jest.setTimeout(240000); const PORT = 9009; @@ -26,9 +27,10 @@ let BINARY_PATH; // append to this prefix and the image will be uploaded to github artifacts // E.g. page.screenshot({ path: `${SCREENSHOT_PREFIX}screenshot.png` }) let SCREENSHOT_PREFIX; -// We'll clear this token before launching the app so we can have a +// We'll clear these tokens before launching the app so we can have a // predictable startup page. let APP_HAS_RUN_TOKEN_PATH; +let APP_VERSION_TOKEN_PATH; // On GHA macos, invest validation can time-out reading from os.tmpdir // So on GHA, use the homedir instead. @@ -45,6 +47,9 @@ if (process.platform === 'darwin') { APP_HAS_RUN_TOKEN_PATH = path.join( os.homedir(), 'Library/Application Support', pkg.name, APP_HAS_RUN_TOKEN ); + APP_VERSION_TOKEN_PATH = path.join( + os.homedir(), 'Library/Application Support', pkg.name, APP_VERSION_TOKEN + ); } else if (process.platform === 'win32') { [BINARY_PATH] = glob.sync('./dist/win-unpacked/InVEST*.exe'); SCREENSHOT_PREFIX = path.join( @@ -53,6 +58,9 @@ if (process.platform === 'darwin') { APP_HAS_RUN_TOKEN_PATH = path.join( os.homedir(), 'AppData/Roaming', pkg.name, APP_HAS_RUN_TOKEN ); + APP_VERSION_TOKEN_PATH = path.join( + os.homedir(), 'AppData/Roaming', pkg.name, APP_VERSION_TOKEN + ); } if (!fs.existsSync(BINARY_PATH)) { @@ -97,6 +105,7 @@ afterAll(() => { // https://github.com/facebook/jest/issues/8688 beforeEach(() => { try { fs.unlinkSync(APP_HAS_RUN_TOKEN_PATH); } catch {} + try { fs.unlinkSync(APP_VERSION_TOKEN_PATH); } catch {} // start the invest app and forward stderr to console ELECTRON_PROCESS = spawn( `"${BINARY_PATH}"`, @@ -164,11 +173,22 @@ test('Run a real invest model', async () => { }); await page.screenshot({ path: `${SCREENSHOT_PREFIX}1-page-load.png` }); - const downloadModal = await page.waitForSelector('.modal-dialog'); + const downloadModal = await page.waitForSelector( + 'aria/[name="Download InVEST sample data"][role="dialog"]' + ); const downloadModalCancel = await downloadModal.waitForSelector( 'aria/[name="Cancel"][role="button"]'); await page.waitForTimeout(WAIT_TO_CLICK); // waiting for click handler to be ready await downloadModalCancel.click(); + + const changelogModal = await page.waitForSelector( + 'aria/[name="New in this version"][role="dialog"]' + ); + const changelogModalClose = await changelogModal.waitForSelector( + 'aria/[name="Close modal"][role="button"]'); + await page.waitForTimeout(WAIT_TO_CLICK); // waiting for click handler to be ready + await changelogModalClose.click(); + // We need to get the modelButton from w/in this list-group because there // are buttons with the same name in the Recent Jobs container. const investModels = await page.waitForSelector('.invest-list-group'); @@ -233,12 +253,23 @@ test('Check local userguide links', async () => { page.on('error', (err) => { console.log(err); }); - const downloadModal = await page.waitForSelector('.modal-dialog'); + + const downloadModal = await page.waitForSelector( + 'aria/[name="Download InVEST sample data"][role="dialog"]' + ); const downloadModalCancel = await downloadModal.waitForSelector( 'aria/[name="Cancel"][role="button"]'); await page.waitForTimeout(WAIT_TO_CLICK); // waiting for click handler to be ready await downloadModalCancel.click(); + const changelogModal = await page.waitForSelector( + 'aria/[name="New in this version"][role="dialog"]' + ); + const changelogModalClose = await changelogModal.waitForSelector( + 'aria/[name="Close modal"][role="button"]'); + await page.waitForTimeout(WAIT_TO_CLICK); // waiting for click handler to be ready + await changelogModalClose.click(); + const investList = await page.waitForSelector('.invest-list-group'); const modelButtons = await investList.$$('aria/[role="button"]'); diff --git a/workbench/tests/main/main.test.js b/workbench/tests/main/main.test.js index 2b479b6882..fd0d079408 100644 --- a/workbench/tests/main/main.test.js +++ b/workbench/tests/main/main.test.js @@ -180,12 +180,14 @@ describe('createWindow', () => { test('should register various ipcMain listeners', async () => { await createWindow(); const expectedHandleChannels = [ + ipcMainChannels.BASE_URL, ipcMainChannels.CHANGE_LANGUAGE, ipcMainChannels.CHECK_STORAGE_TOKEN, ipcMainChannels.CHECK_FILE_PERMISSIONS, ipcMainChannels.GET_SETTING, ipcMainChannels.GET_N_CPUS, ipcMainChannels.INVEST_VERSION, + ipcMainChannels.IS_NEW_VERSION, ipcMainChannels.IS_FIRST_RUN, ipcMainChannels.OPEN_PATH, ipcMainChannels.SHOW_OPEN_DIALOG, diff --git a/workbench/tests/renderer/changelog.test.js b/workbench/tests/renderer/changelog.test.js new file mode 100644 index 0000000000..8b1bcd3eba --- /dev/null +++ b/workbench/tests/renderer/changelog.test.js @@ -0,0 +1,77 @@ +import React from 'react'; +import '@testing-library/jest-dom'; +import { render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import App from '../../src/renderer/app'; +import pkg from '../../package.json'; + +describe('Changelog', () => { + const currentVersion = pkg.version; + const nonexistentVersion = 'nonexistent-version'; + beforeEach(() => { + jest.spyOn(window, 'fetch') + .mockResolvedValue({ + ok: true, + text: () => ` + + + +
+

${currentVersion}

+
+
+

${nonexistentVersion}

+
+ + + ` + }); + }); + + test('Changelog modal opens immediately on launch of a new version', async () => { + const { findByRole } = render(); + const changelogModal = await findByRole('dialog', { name: 'New in this version' }); + expect(changelogModal).toBeInTheDocument(); + }); + + test('On first run (of any version), Changelog modal opens after Download modal is closed', async () => { + const { findByRole, getByText } = render(); + + let changelogModalFound = true; + try { + await findByRole('dialog', { name: 'New in this version' }); + } catch { + changelogModalFound = false; + } + expect(changelogModalFound).toBe(false); + + const downloadModal = await findByRole('dialog', { name: 'Download InVEST sample data' }); + expect(downloadModal).toBeInTheDocument(); + + await userEvent.click(getByText('Cancel')); + expect(downloadModal).not.toBeInTheDocument(); + const changelogModal = await findByRole('dialog', { name: 'New in this version' }); + expect(changelogModal).toBeInTheDocument(); + }); + + test('Changelog modal does not open when current version has been run before', async () => { + const { findByRole } = render(); + let changelogModalFound = true; + try { + await findByRole('dialog', { name: 'New in this version' }); + } catch { + changelogModalFound = false; + } + expect(changelogModalFound).toBe(false); + }); + + test('Changelog modal contains only content relevant to the current version', async () => { + const { findByRole, queryByRole } = render(); + const currentVersionSectionHeading = await findByRole('heading', { name: currentVersion }); + expect(currentVersionSectionHeading).toBeInTheDocument(); + + const nonexistentVersionSectionHeading = queryByRole('heading', { name: nonexistentVersion }); + expect(nonexistentVersionSectionHeading).not.toBeInTheDocument(); + }); +}); diff --git a/workbench/vite.config.js b/workbench/vite.config.js index a8c1d4f045..ae4e607992 100644 --- a/workbench/vite.config.js +++ b/workbench/vite.config.js @@ -24,6 +24,7 @@ export default defineConfig({ path.resolve(PROJECT_ROOT, 'splash.html'), path.resolve(PROJECT_ROOT, 'report_a_problem.html'), path.resolve(PROJECT_ROOT, 'about.html'), + path.resolve(PROJECT_ROOT, 'changelog.html'), ], }, emptyOutDir: true,