Skip to content

Commit

Permalink
Merge branch 'topic/react18' into display-name
Browse files Browse the repository at this point in the history
  • Loading branch information
rpiaggio authored Mar 6, 2024
2 parents 387efd7 + 3e88f01 commit e67690f
Show file tree
Hide file tree
Showing 69 changed files with 2,172 additions and 507 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Includes a router, testing utils, performance utils, more.
* [scastie](https://github.com/scalacenter/scastie) - An interactive playground for Scala [https://scastie.scala-lang.org](https://scastie.scala-lang.org)

##### Requirements:
* React ≥ 17
* React ≥ 18
* Scala ≥ 2.13
* Scala.JS ≥ 1.10

Expand Down
6 changes: 5 additions & 1 deletion bin/ci
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ dryrun=
# dryrun=-n

# See how much memory is available
free -h
if [ "$(uname)" == "Darwin" ]; then # https://apple.stackexchange.com/a/94258
vm_stat | perl -ne '/page size of (\d+)/ and $size=$1; /Pages\s+([^:]+)[^\d]+(\d+)/ and printf("%-16s % 16.2f Mi\n", "$1:", $2 * $size / 1048576);'
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
free -h
fi
echo

# Test upstream
Expand Down
8 changes: 6 additions & 2 deletions bin/update_react_version
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#!/bin/bash
cd "$(dirname "$0")/../library" || exit 1
cd "$(dirname "$0")/.." || exit 1

[ $# -ne 1 ] && echo "Usage: $0 <version>" && exit 1
ver="$1"

perl -pi -e 's/(?<![a-zA-Z0-9_-])[1-3]\d\.\d+\.\d+(?![a-zA-Z0-9_-])/'"$ver"'/g' \
{doc,project}/*.* build.sbt *.md gh-pages/*.html \
*.md \
doc/*.md \
library/build.sbt \
library/ghpages/html/*.html \
library/project/*.* \
&& git diff -U0

2 changes: 2 additions & 0 deletions doc/HOOKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ object Example {
| `useRef()` | `.useRefToAnyVdom` <br> `.useRefToVdom[DomType]` <br> `.useRefToScalaComponent(component)` <br> `.useRefToScalaComponent[P, S, B]` <br> `.useRefToJsComponent(component)` <br> `.useRefToJsComponent[P, S]` <br> `.useRefToJsComponentWithMountedFacade[P, S, F]` |
| `useRef(initialValue)` | `.useRef(initialValue)` |
| `useState(initialState)` <br> `useState(() => initialState)` | `.useState(initialState)` |
| `useId()` | `.useId` |
| `useTransition` | `.useTransition` |
| Custom hook <br> `useBlah(i)` | `.custom(useBlah(i))`

Note: The reason that `[deps]` on the JS side becomes `(deps)` on the Scala side,
Expand Down
171 changes: 84 additions & 87 deletions doc/TESTING.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Testing
=======
# Testing

This file describes testing functionality provided by React.JS and scalajs-react.
<br>It is plenty for simple and small unit tests.
Expand All @@ -10,6 +9,7 @@ For larger and/or complicated tests, **it is highly recommended to use
for how to write tests for real-world scalajs-react applications.

#### Contents

- [Setup](#setup)
- [`ReactTestUtils`](#reacttestutils)
- [`Simulate` and `Simulation`](#simulate-and-simulation)
Expand All @@ -18,69 +18,70 @@ for how to write tests for real-world scalajs-react applications.
- [`Test Scripts`](#test-scripts)
- [Fatal React warnings](#fatal-react-warnings)

Setup
=====
# Setup

1. Install PhantomJS.

2. Add the following to SBT:

```scala
// scalajs-react test module
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "test" % "2.1.1" % Test

// React JS itself.
// NOTE: Requires react-with-addons.js instead of just react.js
jsDependencies +=
```scala
// scalajs-react test module
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "test" % "2.1.1" % Test

"org.webjars.npm" % "react-dom" % "17.0.2" % Test
/ "umd/react-dom-test-utils.development.js"
minified "umd/react-dom-test-utils.production.min.js"
dependsOn "umd/react-dom.development.js"
commonJSName "ReactTestUtils"
```
// React JS itself.
// NOTE: Requires react-with-addons.js instead of just react.js
jsDependencies +=

"org.webjars.npm" % "react-dom" % "18.2.0" % Test
/ "umd/react-dom-test-utils.development.js"
minified "umd/react-dom-test-utils.production.min.js"
dependsOn "umd/react-dom.development.js"
commonJSName "ReactTestUtils"
```

`ReactTestUtils`
================
# `ReactTestUtils`

The main bucket of testing utilities lies in `japgolly.scalajs.react.test.ReactTestUtils`.

Half of the methods delegate to React.JS's [React.addons.TestUtils](https://facebook.github.io/react/docs/test-utils.html)
(for which there is a raw facade in `japgolly.scalajs.react.test.raw.ReactAddonsTestUtils` if you're interested).

The other half are new functions added specifically in scalajs-react.
* Rendering into DOM with auto-removal
* `withRendered[M, A](u: Unmounted[M], intoBody: Boolean)(f: M => A): A`
* `withRenderedIntoDocument[M, A](u: Unmounted[M])(f: M => A): A`
* `withRenderedIntoBody[M, A](u: Unmounted[M])(f: M => A): A`
* `withNewBodyElement[A](use: Element => A): A`
* `newBodyElement(): Element`
* `removeNewBodyElement(e: Element): Unit`
* `renderIntoBody[M, A](u: Unmounted[M]): M`
* Asynchronously rendering into DOM with auto-removal
* `withRenderedAsync[M, A](u: Unmounted[M], intoBody: Boolean)(f: M => Future[A]): Future[A]`
* `withRenderedIntoDocumentAsync[M, A](u: Unmounted[M])(f: M => Future[A]): Future[A]`
* `withRenderedIntoBodyAsync[M, A](u: Unmounted[M])(f: M => Future[A]): Future[A]`
* `withNewBodyElementAsync[A](use: Element => Future[A]): Future[A]`
* Mounted props modification
* `replaceProps(component, mounted)(newProps: P): mounted'`
* `modifyProps(component, mounted)(f: P => P): mounted'`
* Other
* `removeReactInternals(html: String): String` - Removes internal annotations from HTML that React inserts.

- Rendering into DOM with auto-removal
- `withRendered[M, A](u: Unmounted[M], intoBody: Boolean)(f: M => A): A`
- `withRenderedIntoDocument[M, A](u: Unmounted[M])(f: M => A): A`
- `withRenderedIntoBody[M, A](u: Unmounted[M])(f: M => A): A`
- `withNewBodyElement[A](use: Element => A): A`
- `newBodyElement(): Element`
- `removeNewBodyElement(e: Element): Unit`
- `renderIntoBody[M, A](u: Unmounted[M]): M`
- Asynchronously rendering into DOM with auto-removal
- `withRenderedAsync[M, A](u: Unmounted[M], intoBody: Boolean)(f: M => Future[A]): Future[A]`
- `withRenderedIntoDocumentAsync[M, A](u: Unmounted[M])(f: M => Future[A]): Future[A]`
- `withRenderedIntoBodyAsync[M, A](u: Unmounted[M])(f: M => Future[A]): Future[A]`
- `withNewBodyElementAsync[A](use: Element => Future[A]): Future[A]`
- Mounted props modification
- `replaceProps(component, mounted)(newProps: P): mounted'`
- `modifyProps(component, mounted)(f: P => P): mounted'`
- Other
- `removeReactInternals(html: String): String` - Removes internal annotations from HTML that React inserts.

There's only one magic implicit method this time around:
Mounted components get `.outerHtmlScrubbed()` which is shorthand for
`ReactTestUtils.removeReactInternals(m.getDOMNode.outerHTML)`.

`Simulate` and `Simulation`
===========================
# `Simulate` and `Simulation`

To make event simulation easier, certain event types have dedicated, strongly-typed case classes to wrap event data. For example, JS like

```js
// JavaScript
ReactAddons.TestUtils.Simulate.change(t, {target: {value: "Hi"}})
ReactAddons.TestUtils.Simulate.change(t, { target: { value: "Hi" } });
```

becomes

```scala
// Scala
Simulate.change(t, SimEvent.Change(value = "Hi"))
Expand All @@ -94,6 +95,7 @@ If you'd like more composability and/or purity there's also `Simulation` which
represents action (without a target). It does nothing until `.run` is called and a target is provided.

Example:

```scala
val a = Simulation.focus
val b = Simulation.change(SimEvent.Change(value = "hi"))
Expand All @@ -110,14 +112,13 @@ val s = Simulation.focusChangeBlur("hi")
s run component
```


Testing props changes
=====================
# Testing props changes

When you want to simulate a parent component re-rendering a child component with different props,
you can test the child directly using `ReactTestUtils.{modify,replace}Props`.

Example of code to test:

```scala
class CP {
var prev = "none"
Expand All @@ -131,6 +132,7 @@ val CP = ScalaComponent.builder[String]("asd")
```

Example test case:

```scala
ReactTestUtils.withRenderedIntoDocument(CP("start")) { m =>
assert(m.outerHtmlScrubbed(), "<div>none → start</div>")
Expand All @@ -143,16 +145,15 @@ ReactTestUtils.withRenderedIntoDocument(CP("start")) { m =>
}
```


`ReactTestVar`
==============
# `ReactTestVar`

A `ReactTestVar[A]` is a wrapper around a `var a: A` that:
* can produce a `StateSnapshot[A]` with or without `Reusability`
* can produce a `StateAccess[A]`
* retains history when modified
* can perform arbitrary actions when modified
* can be reset

- can produce a `StateSnapshot[A]` with or without `Reusability`
- can produce a `StateAccess[A]`
- retains history when modified
- can perform arbitrary actions when modified
- can be reset

It's useful for testing components that accept `StateSnapshot[A]`/`StateAccess[A]` instances
in their props.
Expand Down Expand Up @@ -207,62 +208,58 @@ ReactTestUtils.withRenderedIntoDocument(component(testVar.stateAccess)) { m =>
}
```


Test Scripts
============
# Test Scripts

It's possible to write test scripts like

1. *click this*
2. *verify that*
3. *press the Back button*
4. *type name*
5. *press Enter*
1. _click this_
2. _verify that_
3. _press the Back button_
4. _type name_
5. _press Enter_

In case you missed the notice at the top of the file, that functionality is provided in a sister library called
[Scala Test-State](https://github.com/japgolly/test-state).

See [this example](https://github.com/japgolly/test-state/tree/master/example-react)
for how to write tests for real-world scalajs-react applications.


Fatal React warnings
====================
# Fatal React warnings

The easiest way to make `ReactTestUtils` to turn React warnings into runtime exceptions,
is via a [config option](./CONFIG.md#testwarningsreact).

Alternatively, you can do any of the following...

* Wrapping a test
- Wrapping a test

```scala
import japgolly.scalajs.react.test.ReactTestUtilsConfig
ReactTestUtilsConfig.AroundReact.fatalReactWarnings {
// test code here
}
```
```scala
import japgolly.scalajs.react.test.ReactTestUtilsConfig
ReactTestUtilsConfig.AroundReact.fatalReactWarnings {
// test code here
}
```

* Installing for all `ReactTestUtils` usage
- Installing for all `ReactTestUtils` usage

```scala
import japgolly.scalajs.react.test.ReactTestUtilsConfig
ReactTestUtilsConfig.aroundReact.set(
ReactTestUtilsConfig.AroundReact.fatalReactWarnings)
```
```scala
import japgolly.scalajs.react.test.ReactTestUtilsConfig
ReactTestUtilsConfig.aroundReact.set(
ReactTestUtilsConfig.AroundReact.fatalReactWarnings)
```

* Installing outside of test code
- Installing outside of test code

```scala
import japgolly.scalajs.react.util.ConsoleHijack
ConsoleHijack.fatalReactWarnings.install()
```
```scala
import japgolly.scalajs.react.util.ConsoleHijack
ConsoleHijack.fatalReactWarnings.install()
```

* Wrapping non-test code
- Wrapping non-test code

```scala
import japgolly.scalajs.react.util.ConsoleHijack
ConsoleHijack.fatalReactWarnings {
// code here
}
```
```scala
import japgolly.scalajs.react.util.ConsoleHijack
ConsoleHijack.fatalReactWarnings {
// code here
}
```
Loading

0 comments on commit e67690f

Please sign in to comment.