1# These tests are auto-generated with test data from:
2# https://github.com/exercism/problem-specifications/tree/main/exercises/react/canonical-data.json
3# File last updated on 2023-07-19
4
5from functools import partial
6import unittest
7
8from react import (
9 InputCell,
10 ComputeCell,
11)
12
13
14class ReactTest(unittest.TestCase):
15 def test_input_cells_have_a_value(self):
16 input = InputCell(10)
17 self.assertEqual(input.value, 10)
18
19 def test_an_input_cell_s_value_can_be_set(self):
20 input = InputCell(4)
21 input.value = 20
22 self.assertEqual(input.value, 20)
23
24 def test_compute_cells_calculate_initial_value(self):
25 input = InputCell(1)
26 output = ComputeCell(
27 [
28 input,
29 ],
30 lambda inputs: inputs[0] + 1,
31 )
32 self.assertEqual(output.value, 2)
33
34 def test_compute_cells_take_inputs_in_the_right_order(self):
35 one = InputCell(1)
36 two = InputCell(2)
37 output = ComputeCell(
38 [
39 one,
40 two,
41 ],
42 lambda inputs: inputs[0] + inputs[1] * 10,
43 )
44 self.assertEqual(output.value, 21)
45
46 def test_compute_cells_update_value_when_dependencies_are_changed(self):
47 input = InputCell(1)
48 output = ComputeCell(
49 [
50 input,
51 ],
52 lambda inputs: inputs[0] + 1,
53 )
54 input.value = 3
55 self.assertEqual(output.value, 4)
56
57 def test_compute_cells_can_depend_on_other_compute_cells(self):
58 input = InputCell(1)
59 times_two = ComputeCell(
60 [
61 input,
62 ],
63 lambda inputs: inputs[0] * 2,
64 )
65 times_thirty = ComputeCell(
66 [
67 input,
68 ],
69 lambda inputs: inputs[0] * 30,
70 )
71 output = ComputeCell(
72 [
73 times_two,
74 times_thirty,
75 ],
76 lambda inputs: inputs[0] + inputs[1],
77 )
78 self.assertEqual(output.value, 32)
79 input.value = 3
80 self.assertEqual(output.value, 96)
81
82 def test_compute_cells_fire_callbacks(self):
83 input = InputCell(1)
84 output = ComputeCell(
85 [
86 input,
87 ],
88 lambda inputs: inputs[0] + 1,
89 )
90 cb1_observer = []
91 callback1 = self.callback_factory(cb1_observer)
92 output.add_callback(callback1)
93 input.value = 3
94 self.assertEqual(cb1_observer[-1], 4)
95
96 def test_callback_cells_only_fire_on_change(self):
97 input = InputCell(1)
98 output = ComputeCell([input], lambda inputs: 111 if inputs[0] < 3 else 222)
99 cb1_observer = []
100 callback1 = self.callback_factory(cb1_observer)
101 output.add_callback(callback1)
102 input.value = 2
103 self.assertEqual(cb1_observer, [])
104 input.value = 4
105 self.assertEqual(cb1_observer[-1], 222)
106
107 def test_callbacks_do_not_report_already_reported_values(self):
108 input = InputCell(1)
109 output = ComputeCell(
110 [
111 input,
112 ],
113 lambda inputs: inputs[0] + 1,
114 )
115 cb1_observer = []
116 callback1 = self.callback_factory(cb1_observer)
117 output.add_callback(callback1)
118 input.value = 2
119 self.assertEqual(cb1_observer[-1], 3)
120 input.value = 3
121 self.assertEqual(cb1_observer[-1], 4)
122
123 def test_callbacks_can_fire_from_multiple_cells(self):
124 input = InputCell(1)
125 plus_one = ComputeCell(
126 [
127 input,
128 ],
129 lambda inputs: inputs[0] + 1,
130 )
131 minus_one = ComputeCell(
132 [
133 input,
134 ],
135 lambda inputs: inputs[0] - 1,
136 )
137 cb1_observer = []
138 cb2_observer = []
139 callback1 = self.callback_factory(cb1_observer)
140 callback2 = self.callback_factory(cb2_observer)
141 plus_one.add_callback(callback1)
142 minus_one.add_callback(callback2)
143 input.value = 10
144 self.assertEqual(cb1_observer[-1], 11)
145 self.assertEqual(cb2_observer[-1], 9)
146
147 def test_callbacks_can_be_added_and_removed(self):
148 input = InputCell(11)
149 output = ComputeCell(
150 [
151 input,
152 ],
153 lambda inputs: inputs[0] + 1,
154 )
155 cb1_observer = []
156 cb2_observer = []
157 cb3_observer = []
158 callback1 = self.callback_factory(cb1_observer)
159 callback2 = self.callback_factory(cb2_observer)
160 callback3 = self.callback_factory(cb3_observer)
161 output.add_callback(callback1)
162 output.add_callback(callback2)
163 input.value = 31
164 self.assertEqual(cb1_observer[-1], 32)
165 self.assertEqual(cb2_observer[-1], 32)
166 output.remove_callback(callback1)
167 output.add_callback(callback3)
168 input.value = 41
169 self.assertEqual(len(cb1_observer), 1)
170 self.assertEqual(cb2_observer[-1], 42)
171 self.assertEqual(cb3_observer[-1], 42)
172
173 def test_removing_a_callback_multiple_times_doesn_t_interfere_with_other_callbacks(
174 self,
175 ):
176 input = InputCell(1)
177 output = ComputeCell(
178 [
179 input,
180 ],
181 lambda inputs: inputs[0] + 1,
182 )
183 cb1_observer = []
184 cb2_observer = []
185 callback1 = self.callback_factory(cb1_observer)
186 callback2 = self.callback_factory(cb2_observer)
187 output.add_callback(callback1)
188 output.add_callback(callback2)
189 output.remove_callback(callback1)
190 output.remove_callback(callback1)
191 output.remove_callback(callback1)
192 input.value = 2
193 self.assertEqual(cb1_observer, [])
194 self.assertEqual(cb2_observer[-1], 3)
195
196 def test_callbacks_should_only_be_called_once_even_if_multiple_dependencies_change(
197 self,
198 ):
199 input = InputCell(1)
200 plus_one = ComputeCell(
201 [
202 input,
203 ],
204 lambda inputs: inputs[0] + 1,
205 )
206 minus_one1 = ComputeCell(
207 [
208 input,
209 ],
210 lambda inputs: inputs[0] - 1,
211 )
212 minus_one2 = ComputeCell(
213 [
214 minus_one1,
215 ],
216 lambda inputs: inputs[0] - 1,
217 )
218 output = ComputeCell(
219 [
220 plus_one,
221 minus_one2,
222 ],
223 lambda inputs: inputs[0] * inputs[1],
224 )
225 cb1_observer = []
226 callback1 = self.callback_factory(cb1_observer)
227 output.add_callback(callback1)
228 input.value = 4
229 self.assertEqual(cb1_observer[-1], 10)
230
231 def test_callbacks_should_not_be_called_if_dependencies_change_but_output_value_doesn_t_change(
232 self,
233 ):
234 input = InputCell(1)
235 plus_one = ComputeCell(
236 [
237 input,
238 ],
239 lambda inputs: inputs[0] + 1,
240 )
241 minus_one = ComputeCell(
242 [
243 input,
244 ],
245 lambda inputs: inputs[0] - 1,
246 )
247 always_two = ComputeCell(
248 [
249 plus_one,
250 minus_one,
251 ],
252 lambda inputs: inputs[0] - inputs[1],
253 )
254 cb1_observer = []
255 callback1 = self.callback_factory(cb1_observer)
256 always_two.add_callback(callback1)
257 input.value = 2
258 self.assertEqual(cb1_observer, [])
259 input.value = 3
260 self.assertEqual(cb1_observer, [])
261 input.value = 4
262 self.assertEqual(cb1_observer, [])
263 input.value = 5
264 self.assertEqual(cb1_observer, [])
265
266 # Utility functions.
267 def callback_factory(self, observer):
268 def callback(observer, value):
269 observer.append(value)
270
271 return partial(callback, observer)