@khanacademy/perseus 19.1.3 → 19.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,10 +15,6 @@ import objective_ from "./objective_";
15
15
  const assert = InteractiveUtil.assert;
16
16
  const normalizeOptions = InteractiveUtil.normalizeOptions;
17
17
 
18
- // State parameters that should be converted into an array of
19
- // functions
20
- const FUNCTION_ARRAY_OPTIONS = _.keys(MovablePolygonOptions);
21
-
22
18
  // Default "props" and "state". Both are added to this.state and
23
19
  // receive magic getter methods (this.points() etc).
24
20
  // However, properties in DEFAULT_PROPS are updated on `modify()`,
@@ -89,7 +85,6 @@ _.extend(MovablePolygon.prototype, {
89
85
  id: this.state.id,
90
86
  },
91
87
  normalizeOptions(
92
- FUNCTION_ARRAY_OPTIONS,
93
88
  // Defaults are copied from MovablePolygonOptions.*.standard
94
89
  // These defaults are set here instead of DEFAULT_PROPS/STATE
95
90
  // because they:
@@ -124,10 +119,7 @@ _.extend(MovablePolygon.prototype, {
124
119
  update: function (options) {
125
120
  const self = this;
126
121
  const graphie = self.graphie;
127
- const state = _.extend(
128
- self.state,
129
- normalizeOptions(FUNCTION_ARRAY_OPTIONS, options),
130
- );
122
+ const state = _.extend(self.state, normalizeOptions(options));
131
123
 
132
124
  // Default things inside the state.normalStyle object, because
133
125
  // _.extend is not deep.
@@ -0,0 +1,249 @@
1
+ import {fireEvent} from "@testing-library/react";
2
+
3
+ import {Graphie} from "../util/graphie";
4
+
5
+ import {Movable} from "./movable";
6
+
7
+ // Yay for side-effect imports!
8
+ // eslint-disable-next-line import/no-unassigned-import
9
+ import "../util/interactive";
10
+
11
+ describe("Movable", () => {
12
+ const properties = [
13
+ "graphie",
14
+ "state",
15
+ "prevState",
16
+ "cursor",
17
+ "added",
18
+ "isHovering",
19
+ "isMouseOver",
20
+ "isDragging",
21
+ "mouseTarget",
22
+ "_fireEvent",
23
+ "_applyConstraints",
24
+ "draw",
25
+ "listen",
26
+ "unlisten",
27
+ "cloneState",
28
+ "_createDefaultState",
29
+ "modify",
30
+ "grab",
31
+ "update",
32
+ "remove",
33
+ "toBack",
34
+ "toFront",
35
+ ];
36
+
37
+ it.each(properties)("has a %s property", (property) => {
38
+ const dummyGraphie: any = {};
39
+ const movable = new Movable(dummyGraphie, {});
40
+ expect(movable[property]).toBeDefined();
41
+ });
42
+
43
+ const getters = [
44
+ "cursor",
45
+ "added",
46
+ "isHovering",
47
+ "isMouseOver",
48
+ "isDragging",
49
+ "mouseTarget",
50
+ ];
51
+
52
+ it.each(getters)(
53
+ "returns the corresponding state property from %s()",
54
+ (getter) => {
55
+ const dummyGraphie: any = {};
56
+ const movable = new Movable(dummyGraphie, {});
57
+ expect(movable[getter]()).toBe(movable.state[getter]);
58
+ },
59
+ );
60
+
61
+ it("sets default state when constructed", () => {
62
+ const dummyGraphie: any = {};
63
+ const movable = new Movable(dummyGraphie, {});
64
+ expect(movable.state).toEqual({
65
+ id: expect.any(String),
66
+ add: [],
67
+ added: true,
68
+ cursor: null,
69
+ draw: [],
70
+ isDragging: false,
71
+ isHovering: false,
72
+ isMouseOver: false,
73
+ modify: [],
74
+ mouseTarget: null,
75
+ onClick: [],
76
+ onMove: [],
77
+ onMoveEnd: [],
78
+ onMoveStart: [],
79
+ remove: [],
80
+ });
81
+ });
82
+
83
+ it("calls the modify listener when constructed", () => {
84
+ const dummyGraphie: any = {};
85
+ const modifySpy = jest.fn().mockName("modify");
86
+
87
+ const _ = new Movable(dummyGraphie, {modify: modifySpy});
88
+
89
+ const baseExpectedState = {
90
+ add: [],
91
+ added: false,
92
+ cursor: null,
93
+ draw: [],
94
+ id: expect.any(String),
95
+ isDragging: false,
96
+ isHovering: false,
97
+ isMouseOver: false,
98
+ modify: [modifySpy],
99
+ mouseTarget: null,
100
+ onClick: [],
101
+ onMove: [],
102
+ onMoveEnd: [],
103
+ onMoveStart: [],
104
+ remove: [],
105
+ };
106
+ expect(modifySpy.mock.calls).toEqual([
107
+ [baseExpectedState, {}],
108
+ [
109
+ {...baseExpectedState, added: true},
110
+ {...baseExpectedState, added: true},
111
+ ],
112
+ ]);
113
+ });
114
+
115
+ test("cloneState()", () => {
116
+ const movable = new Movable(
117
+ new Graphie(document.createElement("div")),
118
+ {},
119
+ );
120
+
121
+ expect(movable.cloneState()).toEqual(
122
+ expect.objectContaining({
123
+ id: expect.any(String),
124
+ add: [],
125
+ added: true,
126
+ cursor: null,
127
+ draw: [],
128
+ isDragging: false,
129
+ isHovering: false,
130
+ isMouseOver: false,
131
+ modify: [],
132
+ mouseTarget: null,
133
+ onClick: [],
134
+ onMove: [],
135
+ onMoveEnd: [],
136
+ onMoveStart: [],
137
+ remove: [],
138
+ }),
139
+ );
140
+ });
141
+
142
+ test("cloneState() makes a copy", () => {
143
+ const movable = new Movable(
144
+ new Graphie(document.createElement("div")),
145
+ {},
146
+ );
147
+
148
+ expect(movable.cloneState()).not.toBe(movable.state);
149
+ });
150
+
151
+ test("modify() retains original ID", () => {
152
+ const movable = new Movable(
153
+ new Graphie(document.createElement("div")),
154
+ {},
155
+ );
156
+ const id = movable.state.id;
157
+
158
+ movable.modify({added: true});
159
+
160
+ expect(movable.state.id).toBe(id);
161
+ });
162
+
163
+ test("grab() fires onMoveStart", () => {
164
+ const graphie = new Graphie(document.createElement("div"));
165
+ graphie.init({
166
+ range: [
167
+ [-10, 10],
168
+ [-10, 10],
169
+ ],
170
+ });
171
+ const onMoveStart = jest.fn();
172
+ const movable = new Movable(graphie, {onMoveStart});
173
+
174
+ movable.grab([0, 0]);
175
+
176
+ expect(onMoveStart).toHaveBeenCalledWith([0, 0], [0, 0]);
177
+ });
178
+
179
+ test("grab() fires onMove when mouse moves", () => {
180
+ const graphie = new Graphie(document.createElement("div"));
181
+ graphie.init({
182
+ range: [
183
+ [-10, 10],
184
+ [-10, 10],
185
+ ],
186
+ scale: [10, 10],
187
+ });
188
+ const onMove = jest.fn();
189
+ const movable = new Movable(graphie, {onMove});
190
+
191
+ movable.grab([0, 0]);
192
+ // eslint-disable-next-line testing-library/prefer-user-event
193
+ fireEvent.mouseMove(document, {clientX: 120, clientY: 80});
194
+
195
+ expect(onMove).toHaveBeenCalledWith([2, 2], [0, 0]);
196
+ });
197
+
198
+ test("grab() fires onMoveEnd when mouse up", () => {
199
+ const dummyGraphie: any = {};
200
+ const onMoveEnd = jest.fn();
201
+ const movable = new Movable(dummyGraphie, {onMoveEnd});
202
+
203
+ movable.grab([0, 0]);
204
+ // eslint-disable-next-line testing-library/prefer-user-event
205
+ fireEvent.mouseUp(document);
206
+
207
+ expect(onMoveEnd).toHaveBeenCalledWith([0, 0], [0, 0]);
208
+ });
209
+
210
+ test("grab() fires onClick if still hovering when mouse up", () => {
211
+ const dummyGraphie: any = {};
212
+ const onClick = jest.fn();
213
+ const movable = new Movable(dummyGraphie, {onClick});
214
+
215
+ movable.grab([0, 0]);
216
+ // eslint-disable-next-line testing-library/prefer-user-event
217
+ fireEvent.mouseUp(document);
218
+
219
+ expect(onClick).toHaveBeenCalledWith([0, 0], [0, 0]);
220
+ });
221
+
222
+ test("draw() fires a draw event", () => {
223
+ const dummyGraphie: any = {};
224
+ const drawSpy = jest.fn();
225
+ const movable = new Movable(dummyGraphie, {draw: drawSpy});
226
+
227
+ movable.draw();
228
+
229
+ const expectedState = {
230
+ add: [],
231
+ added: true,
232
+ cursor: null,
233
+ draw: [drawSpy],
234
+ id: expect.any(String),
235
+ isDragging: false,
236
+ isHovering: false,
237
+ isMouseOver: false,
238
+ modify: [],
239
+ mouseTarget: null,
240
+ onClick: [],
241
+ onMove: [],
242
+ onMoveEnd: [],
243
+ onMoveStart: [],
244
+ remove: [],
245
+ };
246
+
247
+ expect(drawSpy).toHaveBeenCalledWith(expectedState, expectedState);
248
+ });
249
+ });