Skip to content

Latest commit

 

History

History

workbench

This project is a user-interface layer for InVEST (Integrated Valuation of Ecosystem Services and Tradeoffs). InVEST can be found at https://github.com/natcap/invest.

The purpose of this project is to provide a single entry-point for all InVEST models, and to be extensible to future models or common auxiliary workflows of an InVEST user.

To develop and launch this Application

  1. from invest/:
  • activate a python environment and install natcap.invest
  1. from invest/workbench/:
    • yarn install
    • yarn start

To package this app for distribution

  1. from invest/:
  • make binaries
  • make userguide
  1. from invest/workbench/:
  • yarn run install
  • yarn run build
  • yarn run dist - Configure the packaging in electron-builder-config.js.

Dependency management in package.json

dependencies should only include node modules used by the main process.

Renderer & preload process dependencies (react, bootstrap, etc) belong in devDependencies. They are required in production, but we want electron-builder to ignore them because they are already packaged via the vite bundle. electron-builder will package everything under dependencies and nothing under devDependencies.

Testing with Jest

Configuration

Jest configuration is in package.json. Tests run in a jsdom environment by default, where a browser API is available but a node API is not. The environment can be toggled to node on a per-file basis using the docblock seen at the top of main.test.js.

Config also includes global mock resets. These trigger before each individual test, so there is no need to cleanup mocks in afterEach blocks. beforeEach blocks within a test file will fire after these global resets, so mock setup can be done in a beforeEach, or in a test block itself.

the global config:

"restoreMocks": false,

Restore unmocked implementations. Ideally, this would be true. And previously it was. But as of jest28 or 29 it behaves differently. Now it restores manual mocks in __mocks__, such as the electron API. That is unhelpful, as we always want that API mocked and there is no way to revert to the original manual mock between tests. Basically, we have this problem: jestjs/jest#10419. Though for us it seems triggered by restoreMocks instead of reset. Setting to false allows __mocks__ to work as expected, but now it no longer restores things like,

const spy = jest.spyOn(ipcRenderer, 'send')
  .mockImplementation(() => Promise.resolve());
...
spy.mockReset(); // now required, and resets to orignal mock defined in __mocks__
"clearMocks": true,

Jest docs suggest restoreMocks should do all the work of clearMocks, but I found this exception and added the clear to the global config: Using jest.spyOn(module, 'foo-method') to keep track of number of times foo-method is called. clearMocks is needed to reset the calls data.

"resetModules": true

Needed to restore to an unmocked module when we mocked it like this: jest.mock('ui_config.js', () => mockUISpec(mockSpec)); Possibly because this is outside control of restoreMocks, which only works on jest.spyOn mocks?

Client logfile locations:

  • Windows: "C:\Users\dmf\AppData\Roaming\invest-workbench\logs"
  • Mac: "~/Library/Logs/invest-workbench/"
  • Linux: "~/.config/invest-workbench/logs/"

Internationalization

See also the internationalization readme in the invest repo.

We are using the javascript internationalization package i18next and its react extension react-i18next. i18next takes in translation resources as a javascript object. It's convenient to store translations in JSON format. Vite automatically serves JSON files as Javascript modules, so we can directly import translations from JSON.

The translations for each language live in workbench/src/renderer/i18n/xx.json. The JSON object in each file maps English messages to translations.

Nothing needs to be done during routine development. As we make changes to the workbench text, it will inevitably get out of sync with the translations, and that's okay. Strings that have no translation will fall back to English. When it's time to update our translations, this is the process:

Getting a new batch of translations

These instructions assume you have defined the two-letter locale code in an environment variable $LL.

  1. Extract messages from the source code:

    i18next "src/main/**/*.{js,jsx}" --output main-messages.json
    i18next "src/renderer/**/*.{js,jsx}" --output renderer-messages.json
    

    This command is provided by the i18next-parser package and configured by workbench/i18next-parser.config.mjs. The output JSON files should contain a JSON object mapping each translated message from the source code to an empty string.

  2. Merge into the existing translation files:

    jq -s add main-messages.json src/main/i18n/$LL.json > tmp.json
    cat tmp.json > src/main/i18n/$LL.json
    jq -s add renderer-messages.json src/renderer/i18n/$LL.json > tmp.json
    cat tmp.json > src/renderer/i18n/$LL.json
    

    This will add new keys into the JSON message catalogs and leave those that already have translations:

    {
       "text that's already been translated": "translation",
       "new text that doesn't have a translation yet": ""
    }
    
  3. Commit the changes:

    git add src/main/i18n/$LL.json src/renderer/i18n/$LL.json
    git commit -m "add new messages into $LL translation files"
    
  4. Convert JSON to PO using i18next-gettext-converter:

    i18next-conv -l $LL -s src/main/i18n/$LL.json -t src/main/i18n/$LL.po
    i18next-conv -l $LL -s src/renderer/i18n/$LL.json -t src/renderer/i18n/$LL.po
    
    
  5. Send src/main/i18n/$LL.po and src/renderer/i18n/$LL.po to the translator and wait to receive a copy with translations added.

  6. Receive the updated PO files from the translator, then convert PO to JSON using i18next-gettext-converter. Replace src/main/i18n/$LL.json and src/renderer/i18n/$LL.json with the updated versions:

    i18next-conv -l $LL -s new_main_translations.po -t src/main/i18n/$LL.json
    i18next-conv -l $LL -s new_renderer_translations.po -t src/renderer/i18n/$LL.json
    
    
  7. Commit the changes:

    git add src/main/i18n/$LL.json src/renderer/i18n/$LL.json
    git commit -m "add new translations for $LL"