diff --git a/.eslintrc.js b/.eslintrc.js
index 8cee22a41..5584ffadc 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -106,7 +106,7 @@ module.exports = {
settings: {
react: {
// Must be updated when package.json react version is bumped
- version: '16.9.0'
+ version: '18.2.0'
},
'import/resolver': {
node: {
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 0fcbff51c..0b2ed0c74 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,18 +1,15 @@
{
- "javascript.validate.enable": true,
- "jest.disabledWorkspaceFolders": [
- "spec/javascript/e2e"
- ],
- "editor.codeActionsOnSave": {
- // For ESLint
- "source.fixAll.eslint": true,
- },
- "eslint.format.enable": true,
- "eslint.lintTask.enable": true,
- "eslint.packageManager": "yarn",
- "jest.runAllTestsFirst": false,
- "jest.rootPath": "./",
- "autoimport.sourceRoot": "./app/javascript",
- "autoimport.semicolon": false,
- "autoimport.absolute": true,
-}
\ No newline at end of file
+ "javascript.validate.enable": true,
+ "jest.disabledWorkspaceFolders": ["spec/javascript/e2e"],
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": true
+ },
+ "eslint.format.enable": true,
+ "eslint.lintTask.enable": true,
+ "eslint.packageManager": "yarn",
+ "jest.runAllTestsFirst": false,
+ "jest.rootPath": "./",
+ "autoimport.sourceRoot": "./app/javascript",
+ "autoimport.semicolon": false,
+ "autoimport.absolute": true
+}
diff --git a/Gemfile b/Gemfile
index 20ce2a0fe..7a591b6a8 100644
--- a/Gemfile
+++ b/Gemfile
@@ -82,31 +82,23 @@ gem "sentry-raven"
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
# gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
-# Added at 2017-10-22 23:46:25 -0700 by dkaplan:
gem "devise", "~> 4.9.0"
-# Added at 2017-10-22 23:48:56 -0700 by dkaplan:
gem "omniauth-salesforce", "~> 1.0.5"
gem "omniauth-rails_csrf_protection"
-# Added at 2017-10-24 00:03:53 -0700 by dkaplan:
gem "restforce", "~> 6.2.2"
# handy ruby extensions
gem 'facets', require: false
-# Added at 2017-10-24 08:23:22 -0700 by dkaplan:
gem "slim-rails", "~> 3.1"
-# Added at 2017-10-25 00:30:33 -0700 by dkaplan:
gem "hashie"
-# Added at 2017-10-29 23:29:10 -0700 by dkaplan:
-gem "webpacker", "~> 5.4.4"
+gem "shakapacker", "7.1.0"
-# Added at 2017-10-29 23:57:32 -0700 by dkaplan:
-gem "webpacker-react", "~> 0.3.2"
+gem "react_on_rails", "13.4.0"
-# Added at 2017-11-10 09:14:41 -0800 by dkaplan:
gem "pg", "~> 1.4.6"
gem 'scout_apm'
diff --git a/Gemfile.lock b/Gemfile.lock
index 87c21eb6a..b01056696 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -95,6 +95,7 @@ GEM
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.2.2)
+ connection_pool (2.4.1)
crack (0.4.5)
rexml
crass (1.0.6)
@@ -216,7 +217,7 @@ GEM
nio4r (~> 2.0)
racc (1.6.2)
rack (2.2.6.4)
- rack-proxy (0.7.6)
+ rack-proxy (0.7.7)
rack
rack-test (2.1.0)
rack (>= 1.3)
@@ -269,6 +270,12 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
+ react_on_rails (13.4.0)
+ addressable
+ connection_pool
+ execjs (~> 2.5)
+ rails (>= 5.2)
+ rainbow (~> 3.0)
regexp_parser (2.7.0)
require_all (3.0.0)
responders (3.1.0)
@@ -336,6 +343,11 @@ GEM
sentry-raven (3.1.2)
faraday (>= 1.0)
sexp_processor (4.16.1)
+ shakapacker (7.1.0)
+ activesupport (>= 5.2)
+ rack-proxy (>= 0.6.1)
+ railties (>= 5.2)
+ semantic_range (>= 2.3.0)
simplecov (0.17.1)
docile (~> 1.1)
json (>= 1.8, < 3)
@@ -386,13 +398,6 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
- webpacker (5.4.4)
- activesupport (>= 5.2)
- rack-proxy (>= 0.6.1)
- railties (>= 5.2)
- semantic_range (>= 2.3.0)
- webpacker-react (0.3.2)
- webpacker
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
@@ -434,6 +439,7 @@ DEPENDENCIES
rails_12factor
rails_best_practices
rails_layout
+ react_on_rails (= 13.4.0)
restforce (~> 6.2.2)
rspec-rails (~> 4.0.2)
rubocop
@@ -442,6 +448,7 @@ DEPENDENCIES
scout_apm
selenium-webdriver (~> 3.142.7)
sentry-raven
+ shakapacker (= 7.1.0)
simplecov (~> 0.10, < 0.18)
slim-rails (~> 3.1)
spring
@@ -451,8 +458,6 @@ DEPENDENCIES
vcr
web-console (>= 3.3.0)
webmock
- webpacker (~> 5.4.4)
- webpacker-react (~> 0.3.2)
RUBY VERSION
ruby 3.1.3p185
diff --git a/Procfile.development b/Procfile.development
index 671a1fb39..6d7357aeb 100644
--- a/Procfile.development
+++ b/Procfile.development
@@ -1,2 +1,2 @@
# rails: bin/rails server -p 3000
-webpack: bin/webpack-dev-server --hot
+webpack: bin/shakapacker-dev-server --hot
diff --git a/README.md b/README.md
index ddedd5bff..a09a041a6 100644
--- a/README.md
+++ b/README.md
@@ -10,37 +10,45 @@ Only showing rspec tests for now:
Cross-browser testing done with
## Setup
-* Use Ruby 3.1.3 (Set the version using [RVM](https://rvm.io/rvm/install) or [rbenv](https://github.com/rbenv/rbenv))
-* Install [Bundler](https://github.com/bundler/bundler) `gem install bundler`
-* Use Node v18.12.x (npm v8.19.2)
-* Install Yarn (if you have Homebrew you can run `brew install yarn`)
-* Run `yarn install`
-* Run `bundle install`
+
+- Use Ruby 3.1.3 (Set the version using [RVM](https://rvm.io/rvm/install) or [rbenv](https://github.com/rbenv/rbenv))
+- Install [Bundler](https://github.com/bundler/bundler) `gem install bundler`
+- Use Node v18.12.x (npm v8.19.2)
+- Install Yarn (if you have Homebrew you can run `brew install yarn`)
+- Run `yarn install`
+- Run `bundle install`
- see [here](https://stackoverflow.com/a/19850273/260495) if you have issues installing `pg` gem with Postgres.app, you may need to use: `gem install pg -v 0.21.0 -- --with-pg-config=/Applications/Postgres.app/Contents/Versions/latest/bin/pg_config`
- if you need to run this command make sure you run `bundle install` again following the success of the postgres installation to install the remaining gems
+
* Run `overcommit --install`
* Create a `.env` file in the root directory and ask a team member for access to the local development secrets
* Setup your local database by running `bin/rails db:migrate RAILS_ENV=development`
### VSCode setup
+
We recommend you use VSCode to develop partners. You can use something else, but you're on your own for setting up linting/autocomplete.
#### Installing recommended VSCode extensions
+
Open the partners projects in VSCode, click the extensions tab and filter by recommended extensions, install the extensions under "Workspace recommendations"
#### Configuring VSCode and extensions
+
Necessary configs are defined in [.vscode/settings.json](.vscode/settings.json). you can override those configs or change additional settings by changing the apps user settings (Code -> Preferences -> Settings or using the shortcut `CMD + ,`)
-## To run server
-* `yarn start`
-* Access the app at [http://localhost:3000/](http://localhost:3000/)
+## To run server and client concurrently
+
+- `yarn start`
+- Access the app at [http://localhost:3000/](http://localhost:3000/)
## To update CSS from Pattern Library
-* Checkout your desired commit in your local copy of the [sf-dahlia-pattern-library](https://github.com/SFDigitalServices/sf-dahlia-pattern-library)
-* Run `npm start` in your pattern lib directory
-* In a separate tab, change to the partners directory and run `grunt`
+
+- Checkout your desired commit in your local copy of the [sf-dahlia-pattern-library](https://github.com/SFDigitalServices/sf-dahlia-pattern-library)
+- Run `npm start` in your pattern lib directory
+- In a separate tab, change to the partners directory and run `grunt`
## Linting
+
To lint Ruby code run: `rubocop`
To lint the React code run: `yarn lint`
@@ -48,11 +56,14 @@ To lint the React code run: `yarn lint`
To fix any auto-fixable linting errors run: `yarn lint:fix`
## Visual Studio setup
+
Install the following extensions:
+
- [EsLint](https://github.com/Microsoft/vscode-eslint)
- [Prettier](https://github.com/prettier/prettier-vscode)
To automatically fix linting errors on save, add this to your VSCode workspace settings:
+
```
"editor.codeActionsOnSave": {
// For ESLint
@@ -60,7 +71,6 @@ To automatically fix linting errors on save, add this to your VSCode workspace s
},
```
-
## Rails tests
### Running tests
@@ -73,16 +83,18 @@ If the Salesforce API changes for a request, or if the data sent to the API for
In order to update the cassettes you have to:
-* Go to your failing test.
-* Locate the instruction that is creating the cassette with `VCR.use_cassette`.
-* Remove the cassette specified from `spec/vcr/`
+- Go to your failing test.
+- Locate the instruction that is creating the cassette with `VCR.use_cassette`.
+- Remove the cassette specified from `spec/vcr/`
For example, for:
+
```
VCR.use_cassette('listings/applications_controller/index') do
```
You have to remove:
+
```
spec/vcr/listings/applications_controller/index.yml
```
@@ -107,7 +119,7 @@ _Note: Snapshots should be pushed to the repo_
To view the e2e tests as they're running, set `HEADLESS` to `false` in [this file](https://github.com/SFDigitalServices/sf-dahlia-lap/blob/main/spec/javascript/support/puppeteer/consts.js#L51)
-#### Run server (in a terminal window)
+#### Run server and client concurrently (in a terminal window)
`yarn start`
@@ -115,7 +127,6 @@ To view the e2e tests as they're running, set `HEADLESS` to `false` in [this fil
`yarn e2e`
-
### Running all or individual tests
To run all tests (unit and e2e):
@@ -126,140 +137,66 @@ To run an individual test:
`yarn test:all path/to/test`
-### Writing component unit tests
-#### General best practices
-1. Use [Enzyme](https://enzymejs.github.io/enzyme/docs/api/shallow.html)’s shallow rendering instead of react-test-renderer.create or Enzyme mount for all unit tests (snapshot and otherwise).
- * There are some cases where you’ll need to use mount instead of shallow, like when you need to test componentDidMount functionality or something
-2. Snapshot tests are fine for very simple components, but for anything more complex we should write actual unit tests instead of (or along with) snapshot tests
-
-For more information on why shallow rendering is simpler than full rendering, check out the comments on [this pr](https://github.com/SFDigitalServices/sf-dahlia-lap/pull/386).
-
-#### Shallow vs. Mount explained
-Say you have two components `` and ``:
-```
-const B = ({ className }) => (
-
-)
-
-const A = ({}) => (
-
-
-
-)
-```
-
-`mount()` would create a snapshot that looks like this:
-```
-
-```
-
-`shallow()` would create a snapshot that looks like this:
-```
-
-
-
-```
+### Writing unit tests with React Testing Library
-Shallow rendering is preferred because the snapshot is simpler, and it ensures you're actually writing a unit test, not a test that will search the whole tree.
-
-##### When do you need to use mount rendering?
-- When you need to test functionality of `componentDidMount`
-- When you actually want to write an end-to-end snapshot test that looks at all of the children
- - There are usually better tests to write than this if you have the time
-- When it's less confusing than adding a bunch of `.dive().dive()`'s to your tests
- - For example, if you wanted to check that react-final-form adds the correct error label to an input, it's usually easier to mount render and find the class, rather than traverse the shallow render tree with a whole bunch of dive()s.
-
-#### Unit testing components with form or context
-Shallow rendering is more complicated when you're using connected components, that are wrapped with useContext or work with [react-final-form](https://final-form.org/docs/react-final-form/getting-started)'s form objects.
+#### General best practices
-The [wrapperUtil.js](spec/javascript/testUtils/wrapperUtil.js) contains utils to help with shallow rendering components that use context or form.
+[React Testing Library (RTL)](https://testing-library.com/docs/queries/about) is a library for testing React components in a way that resembles how your app's users would interact with your app. Instead of dealing with instances of rendered React components, your tests will work with actual DOM nodes rendered in a headless version of Chromium
-##### Example: shallow rendering a component that has a form passed in
-Say you want to test a component that looks like this:
-```
-const ComponentThatUsesForm = ({ form }) => (
-
-
-
-
-)
-```
+RTL encourages you to interact with your components in the same way a user would, rather than testing implementation details like props being passed, internal state, or styling. This means RTL tests tend to be more resilient to changes in your app's code and can catch a wider range of bugs.
-You can test it like:
-```
-import { withForm } from 'spec/javascript/testUtils/wrapperUtil.js'
-import ComponentA from '...'
+1. Interact with your components like a user: Use RTL's fireEvent (or userEvent) functions to simulate user interactions like clicking buttons, typing into inputs, and submitting forms. Avoid interacting with your components in ways a user couldn't, like by setting props or state directly.
-test('it renders ComponentA', () => {
- const application = { id: 'appid',
}
+2. Query by specific accessible roles and labels: Use RTL's getByRole, getByLabelText, and other similar functions to select elements in your tests. These functions select elements based on their accessible roles and labels, which is how users find and interact with elements. Avoid selecting elements by their tag name, class name, or other implementation details.
- const wrapper = withForm(application, (form) => )
+ - When trying to query for something, try to be as specific as possible. A button, for example can be queried with `screen.getByRole('button)`, but you could also be more specific and say `screen.getByRole('button, {name: 'click me'})`
+ - `getBy*` and `queryBy*` operate slightly differently, the former will throw an error is nothing is found, while the latter will simply return undefined if no elements are found. If you are querying for something that you know isn't there, then use `expect(screen.queryBy*(element)).not.toBeInTheDocument()`
+ - `[get|query]AllBy*` and `[get|query]By*` are also different, with the former returning an array and the other returning only one (and throwing an error if it finds otherwise).
- expect(wrapper.find(ComponentA)).toHaveLength(1)
-})
-```
+3. Snapshots are a great way to mass verify that a component is being rendered correctly. However, they should be used sparingly because they are both brittle and not a great way to detect something breaking.
+4. Tests should not be overly complex.
+ - If you feel like a test is testing too many different things, or that your test is trying to run a user flow, consider making it an E2E test.
+ - Don't double test something. For example, if a component uses a button that is itself being tested independently, then there is no need to test it in another component, unless the button is being used in a novel way
-##### Example: shallow rendering a component that uses form and context
-Say you want to test a component that looks like this:
-```
-const ComponentThatUsesFormAndContext = ({ form, store }) => {
+#### Debugging
- return (
-
-
-
-
- )
-}
+Some things to consider when debugging:
-export withContext(ComponentThatUsesFormAndContext)
-```
+- Are you missing a context provider that you can mock using jest?
+- Should an action or assertion be wrapped in `act`?
+- Are you using getBy when you should be using queryBy?
+- Is the test inheriting something from a beforeEach or from a parent test?
-You can test it like:
-```
-import { shallowWithFormAndContext } from 'spec/javascript/testUtils/wrapperUtil.js'
-import ComponentA from '...'
+The `screen` constant provides a lot of good debugging tools:
-test('it renders ComponentA', () => {
- // Note that the context object must have an application on it, because form expects an application.
- const context = {
- application: { id: 'appid',
}
- }
-
- const wrapper = shallowWithFormAndContext(
- context,
- (form) =>
- )
-
- expect(wrapper.find(ComponentA)).toHaveLength(1)
-})
-```
-
-#### Example files that follow these best practices
-- Non-form component: [StatusHistoryContainer.test.js](spec/javascript/components/molecules/lease_up_sidebar/StatusHistoryContainer.test.js)
-- Form-component: [RentalAssistance.test.js](spec/javascript/components/supplemental_application/sections/RentalAssistance.test.js)
+- `screen.logTestingPlaygroundURL` will output a url that has encoded everything the test was looking at. Copy the url and past it into the browser to see what the test was looking at rendered (without styling). The tool will also help you to find the most effective selector.
+- `screen.debug([component])` will render the component in the terminal in a much more pretty way than console.log will.
+- If you are having issues with only one test in a large testing suite, use `test.only(...)` to only run that single test. The inverse is also true, where using `test.skip(...)` will skip that specific test
## Scripts
### Release scripts
+
More documentation for how these scripts are used during a release in the [partners release process doc](https://sfgovdt.jira.com/wiki/spaces/HOUS/pages/1900544029/Partners+Release+processes+template).
#### 1. create_release_branch
+
Command: `yarn create_release_branch`
This script will:
+
- Create a new branch named `release-`
- Merge it with the latest main
- Open a PR in a browser window
#### 2. print_release_info
+
Command: `yarn print_release_info -u -t `
Instructions for how to get your github access token are printed by running `yarn print_release_info -h`
This script will:
+
- Print release tracker info you can paste into the [Release Tracker doc](https://docs.google.com/spreadsheets/d/1EUvw2ugaFprt8FxlCUa1yWATSn0KKo4FyRfBJWUwE3M/edit#gid=1500049656)
- Output a URL that will create a draft release with description, title, tags, and base branch filled in
@@ -268,6 +205,7 @@ This script will:
You can debug all tests and Rails server in VS code.
Go to debug view (⇧+⌘+D on Mac)
From the dropdown in left top corner pick what you want to debug:
+
- Rails server: for running app
- Jest: for any javascript test
- Rspec: for ruby tests
@@ -279,12 +217,15 @@ For tests you can debug a single file or the whole suite. To enter debug click a
To debug javascript, run Rails server in the prefered way. Go to browser and open inspector (⌥+⌘+I). Go to Sources tab and press ⌘+P to search for a file that you want to debug eg. PaperApplicationForm. Click line number to set a breakpoint in the place you want to debug. You can also add watch expressions, step into or over lines like in VS code debugger.
## React Hooks
+
Wanted to post a basic intro to react hooks here as they will make our code more performant and allow us to use more functional components. I have examples below but you can read more on the [React Documentation](https://reactjs.org/docs/hooks-overview.html)
### useState, useEffect, and useRef Hooks
+
These hooks are all built-in to react by default.
`useState` allows functional components to manage state just like a class component but with a streamlined syntax.
+
```js
// class version
class Dropdown extends React.Component {
@@ -306,9 +247,11 @@ const Dropdown = () => {
}
}
```
+
These components now have the exact same level of control over the expanded flag but the below function has less overhead when mounting and unmounting.
`useEffect` is the hook that allows us to still take advantage of lifecycle events when necessary.
+
```js
const Dropdown = ({ styles }) => {
// The empty array passed as the second param here
diff --git a/app/javascript/components/applications/application_form/PaperApplicationForm.js b/app/javascript/components/applications/application_form/PaperApplicationForm.js
index b1e788cef..0a2745acf 100644
--- a/app/javascript/components/applications/application_form/PaperApplicationForm.js
+++ b/app/javascript/components/applications/application_form/PaperApplicationForm.js
@@ -25,9 +25,7 @@ class PaperApplicationForm extends React.Component {
state = {
loading: false,
submittedValues: {},
- submitType: '',
- genderSpecifyRequired: false,
- orientationOtherRequired: false
+ submitType: ''
}
submitShortForm = async (submittedValues) => {
@@ -107,10 +105,6 @@ class PaperApplicationForm extends React.Component {
errors.demographics.sexual_orientation_other = isOrientationOtherRequired
? 'Sexual Orientation is required'
: undefined
- this.setState({
- genderSpecifyRequired: isGenderSpecifyRequired,
- orientationOtherRequired: isOrientationOtherRequired
- })
}
return errors
}
@@ -127,7 +121,7 @@ class PaperApplicationForm extends React.Component {
mutators={{
...arrayMutators
}}
- render={({ handleSubmit, form, values, visited }) => (
+ render={({ handleSubmit, form, values, visited, errors }) => (