|
| 1 | +Testing |
| 2 | +======= |
| 3 | + |
| 4 | +##### Contents |
| 5 | +- [Setup](#setup) |
| 6 | +- [`React.addons.TestUtils`](#reactaddonstestutils) |
| 7 | +- [`Simulate` and `Simulation`](#simulate-and-simulation) |
| 8 | +- [`Sel`](#sel) |
| 9 | +- [`DebugJs`](#debugjs) |
| 10 | + |
| 11 | +Setup |
| 12 | +===== |
| 13 | + |
| 14 | +1. Install PhantomJS. |
| 15 | + |
| 16 | +2. Add the following to SBT: |
| 17 | + |
| 18 | + ```scala |
| 19 | + // scalajs-react test module |
| 20 | + libraryDependencies += "com.github.japgolly.scalajs-react" %%% "test" % "0.10.0" % "test" |
| 21 | + |
| 22 | + // React JS itself. |
| 23 | + // NOTE: Requires react-with-addons.js instead of just react.js |
| 24 | + jsDependencies += |
| 25 | + "org.webjars" % "react" % "0.12.2" % "test" / "react-with-addons.js" commonJSName "React" |
| 26 | + |
| 27 | + // Indicate that unit tests will access the DOM |
| 28 | + requiresDOM := true |
| 29 | + |
| 30 | + // Compile tests to JS using fast-optimisation |
| 31 | + scalaJSStage in Test := FastOptStage |
| 32 | + ``` |
| 33 | + |
| 34 | +3. To [workaround](https://github.com/scala-js/scala-js/issues/1555) a [PhantomJS bug](https://github.com/ariya/phantomjs/issues/13112) that causes tests to crash if they write to stderr, copy [`PhantomJS2Env.scala`](../project/PhantomJS2Env.scala) to your `project` directory and add this to SBT: |
| 35 | + |
| 36 | + ```scala |
| 37 | + jsEnv in Test := new PhantomJS2Env(scalaJSPhantomJSClassLoader.value) |
| 38 | + ``` |
| 39 | + |
| 40 | + |
| 41 | +`React.addons.TestUtils` |
| 42 | +======================== |
| 43 | +[React.addons.TestUtils](https://facebook.github.io/react/docs/test-utils.html) is wrapped in Scala and available as `ReactTestUtils`. Usage is unchanged from JS. |
| 44 | + |
| 45 | + |
| 46 | +`Simulate` and `Simulation` |
| 47 | +=========================== |
| 48 | +To make event simulation easier, certain event types have dedicated, strongly-typed case classes to wrap event data. For example, JS like |
| 49 | +```js |
| 50 | +// JavaScript |
| 51 | +React.addons.TestUtils.Simulate.change(t, {target: {value: "Hi"}}) |
| 52 | +``` |
| 53 | +becomes |
| 54 | +```scala |
| 55 | +// Scala |
| 56 | +ReactTestUtils.Simulate.change(t, ChangeEventData(value = "Hi")) |
| 57 | + |
| 58 | +// Or shorter |
| 59 | +ChangeEventData("Hi") simulate t |
| 60 | +``` |
| 61 | + |
| 62 | +Simulations can also be created and composed without a target, using `Simulation`. Example: |
| 63 | +```scala |
| 64 | +val a = Simulation.focus |
| 65 | +val b = Simulation.change(ChangeEventData(value = "hi")) |
| 66 | +val c = Simulation.blur |
| 67 | +val s = a andThen b andThen c |
| 68 | + |
| 69 | +// Or shorter |
| 70 | +val s = Simulation.focus >> ChangeEventData("hi").simulation >> Simulation.blur |
| 71 | + |
| 72 | +// Or even shorter again, using a convenience method |
| 73 | +val s = Simulation.focusChangeBlur("hi") |
| 74 | + |
| 75 | +// Then run it later |
| 76 | +s run component |
| 77 | +``` |
| 78 | + |
| 79 | +`Sel` |
| 80 | +===== |
| 81 | +DOM lookup is much easier than using `ReactTestUtils` directly by instead using `Sel`. |
| 82 | +`Sel` allows you to use a jQuery/CSS-like selector to lookup a DOM element or subset. |
| 83 | +Full examples can be [seen here](src/test/scala/japgolly/scalajs/react/test/SelTest.scala); this is a sample: |
| 84 | +```scala |
| 85 | +val dom = Sel(".inner a.active.new") findIn myComponent |
| 86 | +``` |
| 87 | + |
| 88 | +Note: The syntax is quite limited. It supports tags and classes. It does **not** support Ids. |
| 89 | +jQuery or Sizzle will do a better job. I don't think I thought of that when I wrote `Sel` :) |
| 90 | + |
| 91 | +`DebugJs` |
| 92 | +========= |
| 93 | +[DebugJs](src/main/scala/japgolly/scalajs/react/test/DebugJs.scala) is a dumping ground for functionality useful when testing raw JS. |
| 94 | + |
| 95 | +It doesn't have much but `inspectObject` can be tremendously useful. |
| 96 | + |
| 97 | +Example: |
| 98 | +```scala |
| 99 | +.componentDidMount { $ => |
| 100 | + val dom = $.getDOMNode() |
| 101 | + println(DebugJs inspectObject dom) |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +Output (truncated): |
| 106 | +``` |
| 107 | +[object HTMLCanvasElement] |
| 108 | + [ 1/137] ALLOW_KEYBOARD_INPUT : number = 1 |
| 109 | + [ 2/137] ATTRIBUTE_NODE : number = 2 |
| 110 | + [ 3/137] CDATA_SECTION_NODE : number = 4 |
| 111 | + [ 4/137] COMMENT_NODE : number = 8 |
| 112 | + [ 5/137] DOCUMENT_FRAGMENT_NODE : number = 11 |
| 113 | + [ 6/137] DOCUMENT_NODE : number = 9 |
| 114 | + [ 7/137] DOCUMENT_POSITION_CONTAINED_BY : number = 16 |
| 115 | + [ 8/137] DOCUMENT_POSITION_CONTAINS : number = 8 |
| 116 | + [ 9/137] DOCUMENT_POSITION_DISCONNECTED : number = 1 |
| 117 | + [ 10/137] DOCUMENT_POSITION_FOLLOWING : number = 4 |
| 118 | + [ 11/137] DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC : number = 32 |
| 119 | + [ 12/137] DOCUMENT_POSITION_PRECEDING : number = 2 |
| 120 | + [ 13/137] DOCUMENT_TYPE_NODE : number = 10 |
| 121 | + [ 14/137] ELEMENT_NODE : number = 1 |
| 122 | + [ 15/137] ENTITY_NODE : number = 6 |
| 123 | + [ 16/137] ENTITY_REFERENCE_NODE : number = 5 |
| 124 | + [ 17/137] NOTATION_NODE : number = 12 |
| 125 | + [ 18/137] PROCESSING_INSTRUCTION_NODE : number = 7 |
| 126 | + [ 19/137] TEXT_NODE : number = 3 |
| 127 | + [ 20/137] accessKey : string = |
| 128 | + [ 21/137] addEventListener : function = function addEventListener() { |
| 129 | + [ 22/137] appendChild : function = function appendChild() { |
| 130 | + [ 23/137] attributes : object = [object NamedNodeMap] |
| 131 | + [ 24/137] baseURI : object = null |
| 132 | + [ 25/137] blur : function = function blur() { |
| 133 | + [ 26/137] childElementCount : number = 0 |
| 134 | + [ 27/137] childNodes : object = [object NodeList] |
| 135 | + [ 28/137] children : object = [object HTMLCollection] |
| 136 | + [ 29/137] classList : object = |
| 137 | + [ 30/137] className : string = |
| 138 | + [ 31/137] click : function = function click() { |
| 139 | + [ 32/137] clientHeight : number = 0 |
| 140 | + [ 33/137] clientLeft : number = 0 |
| 141 | + [ 34/137] clientTop : number = 0 |
| 142 | + [ 35/137] clientWidth : number = 0 |
| 143 | + [ 36/137] cloneNode : function = function cloneNode() { |
| 144 | + [ 37/137] compareDocumentPosition : function = function compareDocumentPosition() { |
| 145 | + [ 38/137] contains : function = function contains() { |
| 146 | + [ 39/137] contentEditable : string = inherit |
| 147 | + [ 40/137] dataset : object = [object DOMStringMap] |
| 148 | + [ 41/137] dir : string = |
| 149 | + [ 42/137] dispatchEvent : function = function dispatchEvent() { |
| 150 | + [ 43/137] draggable : boolean = false |
| 151 | + [ 44/137] firstChild : object = null |
| 152 | + [ 45/137] firstElementChild : object = null |
| 153 | + [ 46/137] focus : function = function focus() { |
| 154 | + [ 47/137] getAttribute : function = function getAttribute() { |
| 155 | + [ 48/137] getAttributeNS : function = function getAttributeNS() { |
| 156 | + [ 49/137] getAttributeNode : function = function getAttributeNode() { |
| 157 | + [ 50/137] getAttributeNodeNS : function = function getAttributeNodeNS() { |
| 158 | + [ 51/137] getBoundingClientRect : function = function getBoundingClientRect() { |
| 159 | + [ 52/137] getClientRects : function = function getClientRects() { |
| 160 | + [ 53/137] getContext : function = function getContext() { |
| 161 | + [ 54/137] getElementsByClassName : function = function getElementsByClassName() { |
| 162 | + [ 55/137] getElementsByTagName : function = function getElementsByTagName() { |
| 163 | + [ 56/137] getElementsByTagNameNS : function = function getElementsByTagNameNS() { |
| 164 | + [ 57/137] hasAttribute : function = function hasAttribute() { |
| 165 | + [ 58/137] hasAttributeNS : function = function hasAttributeNS() { |
| 166 | + [ 59/137] hasAttributes : function = function hasAttributes() { |
| 167 | + [ 60/137] hasChildNodes : function = function hasChildNodes() { |
| 168 | + [ 61/137] height : number = 150 |
| 169 | + [ 62/137] hidden : boolean = false |
| 170 | + [ 63/137] id : string = |
| 171 | +... |
| 172 | +``` |
0 commit comments