@pie-element/hotspot 4.8.0 → 4.8.1-beta.587

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.
Files changed (95) hide show
  1. package/CHANGELOG.md +474 -0
  2. package/configure/CHANGELOG.md +417 -0
  3. package/configure/lib/button.js.map +1 -1
  4. package/configure/lib/defaults.js +24 -2
  5. package/configure/lib/defaults.js.map +1 -1
  6. package/configure/lib/hotspot-container.js +44 -23
  7. package/configure/lib/hotspot-container.js.map +1 -1
  8. package/configure/lib/hotspot-drawable.js +11 -5
  9. package/configure/lib/hotspot-drawable.js.map +1 -1
  10. package/configure/lib/hotspot-palette.js +1 -1
  11. package/configure/lib/hotspot-palette.js.map +1 -1
  12. package/configure/lib/hotspot-polygon.js +1 -1
  13. package/configure/lib/hotspot-polygon.js.map +1 -1
  14. package/configure/lib/hotspot-rectangle.js +1 -1
  15. package/configure/lib/hotspot-rectangle.js.map +1 -1
  16. package/configure/lib/index.js +17 -3
  17. package/configure/lib/index.js.map +1 -1
  18. package/configure/lib/root.js +133 -27
  19. package/configure/lib/root.js.map +1 -1
  20. package/configure/lib/upload-control.js.map +1 -1
  21. package/configure/lib/utils.js +18 -4
  22. package/configure/lib/utils.js.map +1 -1
  23. package/configure/package.json +4 -4
  24. package/configure/src/__tests__/__snapshots__/hotspot-container.test.js.snap +2 -1
  25. package/configure/src/__tests__/__snapshots__/root.test.js.snap +56 -4
  26. package/configure/src/__tests__/hotspot-container.test.js +98 -71
  27. package/configure/src/__tests__/hotspot-drawable.test.js +48 -49
  28. package/configure/src/__tests__/index.test.js +28 -51
  29. package/configure/src/__tests__/root.test.js +162 -82
  30. package/configure/src/__tests__/utils.test.js +112 -98
  31. package/configure/src/button.jsx +4 -4
  32. package/configure/src/defaults.js +36 -18
  33. package/configure/src/hotspot-container.jsx +107 -97
  34. package/configure/src/hotspot-drawable.jsx +59 -53
  35. package/configure/src/hotspot-palette.jsx +12 -25
  36. package/configure/src/hotspot-polygon.jsx +20 -22
  37. package/configure/src/hotspot-rectangle.jsx +5 -15
  38. package/configure/src/index.js +32 -18
  39. package/configure/src/root.jsx +120 -51
  40. package/configure/src/upload-control.jsx +9 -20
  41. package/configure/src/utils.js +51 -22
  42. package/controller/CHANGELOG.md +61 -0
  43. package/controller/lib/defaults.js +2 -2
  44. package/controller/lib/defaults.js.map +1 -1
  45. package/controller/lib/index.js +57 -9
  46. package/controller/lib/index.js.map +1 -1
  47. package/controller/lib/utils.js.map +1 -1
  48. package/controller/package.json +2 -2
  49. package/controller/src/__tests__/index.test.js +99 -55
  50. package/controller/src/defaults.js +5 -9
  51. package/controller/src/index.js +58 -34
  52. package/controller/src/utils.js +9 -6
  53. package/docs/config-schema.json +217 -0
  54. package/docs/config-schema.json.md +169 -0
  55. package/docs/demo/config.js +2 -2
  56. package/docs/demo/generate.js +54 -35
  57. package/docs/demo/session.js +2 -2
  58. package/docs/pie-schema.json +59 -0
  59. package/docs/pie-schema.json.md +47 -0
  60. package/lib/hotspot/container.js +44 -7
  61. package/lib/hotspot/container.js.map +1 -1
  62. package/lib/hotspot/icons.js.map +1 -1
  63. package/lib/hotspot/image.js +35 -8
  64. package/lib/hotspot/image.js.map +1 -1
  65. package/lib/hotspot/index.js +72 -18
  66. package/lib/hotspot/index.js.map +1 -1
  67. package/lib/hotspot/polygon.js +51 -32
  68. package/lib/hotspot/polygon.js.map +1 -1
  69. package/lib/hotspot/rectangle.js +51 -32
  70. package/lib/hotspot/rectangle.js.map +1 -1
  71. package/lib/index.js +4 -1
  72. package/lib/index.js.map +1 -1
  73. package/lib/session-updater.js.map +1 -1
  74. package/package.json +7 -4
  75. package/src/__tests__/__snapshots__/{container-test.jsx.snap → container.test.jsx.snap} +8 -0
  76. package/src/__tests__/__snapshots__/image.test.jsx.snap +43 -0
  77. package/src/__tests__/__snapshots__/{polygon-test.jsx.snap → polygon.test.jsx.snap} +11 -0
  78. package/src/__tests__/__snapshots__/{rectangle-test.jsx.snap → rectangle.test.jsx.snap} +11 -0
  79. package/src/__tests__/container.test.jsx +214 -0
  80. package/src/__tests__/{image-test.jsx → image.test.jsx} +10 -2
  81. package/src/__tests__/polygon.test.jsx +235 -0
  82. package/src/__tests__/rectangle.test.jsx +237 -0
  83. package/src/__tests__/{session-updater-test.jsx → session-updater.test.jsx} +22 -31
  84. package/src/hotspot/container.jsx +60 -23
  85. package/src/hotspot/icons.js +4 -2
  86. package/src/hotspot/image.jsx +26 -13
  87. package/src/hotspot/index.jsx +73 -33
  88. package/src/hotspot/polygon.jsx +51 -40
  89. package/src/hotspot/rectangle.jsx +51 -40
  90. package/src/index.js +10 -18
  91. package/src/session-updater.js +1 -1
  92. package/src/__tests__/__snapshots__/image-test.jsx.snap +0 -21
  93. package/src/__tests__/container-test.jsx +0 -143
  94. package/src/__tests__/polygon-test.jsx +0 -76
  95. package/src/__tests__/rectangle-test.jsx +0 -77
@@ -41,20 +41,17 @@ describe('controller', () => {
41
41
  });
42
42
 
43
43
  describe('outcome partialScoring test', () => {
44
-
45
- describe('hanging controller', ()=> {
46
-
44
+ describe('hanging controller', () => {
47
45
  it('doesnt hang if answers is missing', async () => {
48
- const response = await outcome({}, {id: "1"}, {});
49
- expect(response).toEqual({score: 0, empty: true});
46
+ const response = await outcome({}, { id: '1' }, {});
47
+ expect(response).toEqual({ score: 0, empty: true });
50
48
  });
51
49
 
52
50
  it('doesnt hang if the rest is missing', async () => {
53
- const response = await outcome({}, {id: "1", answers: {}}, {});
54
- expect(response).toEqual({score: 0 });
51
+ const response = await outcome({}, { id: '1', answers: {} }, {});
52
+ expect(response).toEqual({ score: 0 });
55
53
  });
56
-
57
- })
54
+ });
58
55
 
59
56
  beforeEach(() => {
60
57
  const rectangles = question.shapes.rectangles.concat({
@@ -72,11 +69,7 @@ describe('controller', () => {
72
69
 
73
70
  const assertOutcome = (message, extra, sessionValue, env, expected) => {
74
71
  it(message, async () => {
75
- const result = await outcome(
76
- { ...question, ...extra },
77
- sessionValue,
78
- env
79
- );
72
+ const result = await outcome({ ...question, ...extra }, sessionValue, env);
80
73
  expect(result).toEqual(expect.objectContaining(expected));
81
74
  });
82
75
  };
@@ -86,7 +79,7 @@ describe('controller', () => {
86
79
  { partialScoring: true },
87
80
  { answers: [{ id: '2' }] },
88
81
  { mode: 'evaluate' },
89
- { score: 0.2 }
82
+ { score: 0 },
90
83
  );
91
84
 
92
85
  assertOutcome(
@@ -94,7 +87,7 @@ describe('controller', () => {
94
87
  { partialScoring: false },
95
88
  { answers: [{ id: '2' }] },
96
89
  { mode: 'evaluate' },
97
- { score: 0 }
90
+ { score: 0 },
98
91
  );
99
92
 
100
93
  assertOutcome(
@@ -102,7 +95,7 @@ describe('controller', () => {
102
95
  { partialScoring: false },
103
96
  { answers: [{ id: '2' }] },
104
97
  { mode: 'evaluate', partialScoring: true },
105
- { score: 0 }
98
+ { score: 0 },
106
99
  );
107
100
 
108
101
  assertOutcome(
@@ -110,7 +103,7 @@ describe('controller', () => {
110
103
  { partialScoring: true },
111
104
  { answers: [{ id: '2' }] },
112
105
  { mode: 'evaluate', partialScoring: false },
113
- { score: 0 }
106
+ { score: 0 },
114
107
  );
115
108
  });
116
109
 
@@ -136,10 +129,16 @@ describe('controller', () => {
136
129
 
137
130
  describe('partial scoring', () => {
138
131
  beforeEach(() => {
139
- const rectangles = question.shapes.rectangles.concat({
140
- id: '5',
141
- correct: true,
142
- });
132
+ const rectangles = question.shapes.rectangles.concat(
133
+ {
134
+ id: '5',
135
+ correct: true,
136
+ },
137
+ {
138
+ id: '6',
139
+ correct: true,
140
+ },
141
+ );
143
142
  question = {
144
143
  ...question,
145
144
  partialScoring: true,
@@ -149,42 +148,101 @@ describe('controller', () => {
149
148
  },
150
149
  };
151
150
  });
152
- it('returns a score of 0.2', async () => {
151
+ it('returns a score of 0', async () => {
153
152
  const result = await outcome(question, { answers: [{ id: '2' }] });
154
- expect(result.score).toEqual(0.2);
153
+ expect(result.score).toEqual(0);
155
154
  });
156
155
 
157
- it('returns a score of 0.4', async () => {
156
+ it('returns a score of 0.25 for 1/4 correct answers and 0 incorrect answers', async () => {
158
157
  const result = await outcome(question, {
159
158
  answers: [{ id: '2' }, { id: '5' }],
160
159
  });
161
- expect(result.score).toEqual(0.4);
160
+ expect(result.score).toEqual(0.25);
162
161
  });
163
162
 
164
- it('returns a score of 0.4, even if answers are not in order', async () => {
163
+ it('returns a score of 0.25, even if answers are not in order', async () => {
165
164
  const result = await outcome(question, {
166
165
  answers: [{ id: '5' }, { id: '2' }],
167
166
  });
168
- expect(result.score).toEqual(0.4);
167
+ expect(result.score).toEqual(0.25);
169
168
  });
170
169
 
171
- it('returns a score of 0.6', async () => {
172
- const result = await outcome(question, { answers: [{ id: '5' }] });
173
- expect(result.score).toEqual(0.6);
170
+ it('returns a score of 0.5 for 2/4 correct answers and 0 incorrect answers', async () => {
171
+ const result = await outcome(question, { answers: [{ id: '5' }, { id: '4' }] });
172
+ expect(result.score).toEqual(0.5);
174
173
  });
175
174
 
176
- it('returns a score of 1', async () => {
175
+ it('returns a score of 0.75 for 3/4 correct answers and 0 incorrect answers', async () => {
176
+ const result = await outcome(question, { answers: [{ id: '1' }, { id: '5' }, { id: '4' }] });
177
+ expect(result.score).toEqual(0.75);
178
+ });
179
+
180
+ it('returns a score of 1 for 4/4 correct answers and 0 incorrect answers', async () => {
177
181
  const result = await outcome(question, {
178
- answers: [{ id: '1' }, { id: '4' }, { id: '5' }],
182
+ answers: [{ id: '1' }, { id: '4' }, { id: '5' }, { id: '6' }],
179
183
  });
180
184
  expect(result.score).toEqual(1);
181
185
  });
186
+
187
+ it('returns a score of 0.25 for 1/4 correct answers and 2 incorrect answers', async () => {
188
+ const result = await outcome(question, {
189
+ answers: [{ id: '1' }, { id: '2' }, { id: '3' }],
190
+ });
191
+ expect(result.score).toEqual(0.25);
192
+ });
193
+
194
+ it('returns a score of 0.5 for 2/4 correct answers and 2 incorrect answers', async () => {
195
+ const result = await outcome(question, {
196
+ answers: [{ id: '1' }, { id: '4' }, { id: '2' }, { id: '3' }],
197
+ });
198
+ expect(result.score).toEqual(0.5);
199
+ });
200
+
201
+ it('returns a score of 0.5 for 2/4 correct answers and 2 incorrect answers', async () => {
202
+ const result = await outcome(question, {
203
+ answers: [{ id: '1' }, { id: '4' }, { id: '2' }, { id: '3' }],
204
+ });
205
+ expect(result.score).toEqual(0.5);
206
+ });
207
+
208
+ it('returns a score of 0.75 for 3/4 correct answers and 1 incorrect answer', async () => {
209
+ const result = await outcome(question, {
210
+ answers: [{ id: '6' }, { id: '4' }, { id: '1' }, { id: '3' }],
211
+ });
212
+ expect(result.score).toEqual(0.75);
213
+ });
214
+
215
+ it('returns a score of 0.75 for 3/4 correct answers and 1 incorrect answer', async () => {
216
+ const result = await outcome(question, {
217
+ answers: [{ id: '6' }, { id: '4' }, { id: '1' }, { id: '3' }],
218
+ });
219
+ expect(result.score).toEqual(0.75);
220
+ });
221
+
222
+ it('returns a score of 0.5 for 3/4 correct answers and 2 incorrect answer', async () => {
223
+ const result = await outcome(question, {
224
+ answers: [{ id: '6' }, { id: '4' }, { id: '2' }, { id: '3' }, { id: '1' }],
225
+ });
226
+ expect(result.score).toEqual(0.5);
227
+ });
228
+
229
+ it('returns a score of 0.75 for 4/4 correct answers and 1 incorrect answer', async () => {
230
+ const result = await outcome(question, {
231
+ answers: [{ id: '6' }, { id: '4' }, { id: '2' }, { id: '5' }, { id: '1' }],
232
+ });
233
+ expect(result.score).toEqual(0.75);
234
+ });
235
+
236
+ it('returns a score of 0.5 for 4/4 correct answers and 2 incorrect answers', async () => {
237
+ const result = await outcome(question, {
238
+ answers: [{ id: '6' }, { id: '4' }, { id: '2' }, { id: '5' }, { id: '1' }, { id: '3' }],
239
+ });
240
+ expect(result.score).toEqual(0.5);
241
+ });
182
242
  });
183
243
 
184
244
  const returnOutcome = (session) => {
185
- it(`returns empty: true when session is ${JSON.stringify(
186
- session
187
- )}`, async () => {
245
+ it(`returns empty: true when session is ${JSON.stringify(session)}`, async () => {
188
246
  const result = await outcome(question, session);
189
247
  expect(result).toEqual({ score: 0, empty: true });
190
248
  });
@@ -233,15 +291,9 @@ describe('controller', () => {
233
291
 
234
292
  it('returns shapes', () => {
235
293
  expect(result.shapes.rectangles).toEqual(
236
- expect.arrayContaining([
237
- { id: '1', correct: true },
238
- { id: '2' },
239
- { id: '3' },
240
- ])
241
- );
242
- expect(result.shapes.polygons).toEqual(
243
- expect.arrayContaining([{ id: '4', correct: true }])
294
+ expect.arrayContaining([{ id: '1', correct: true }, { id: '2' }, { id: '3' }]),
244
295
  );
296
+ expect(result.shapes.polygons).toEqual(expect.arrayContaining([{ id: '4', correct: true }]));
245
297
  });
246
298
 
247
299
  it('does not return responseCorrect', () => {
@@ -271,15 +323,9 @@ describe('controller', () => {
271
323
 
272
324
  it('returns choices w/ correct', () => {
273
325
  expect(result.shapes.rectangles).toEqual(
274
- expect.arrayContaining([
275
- { id: '1', correct: true },
276
- { id: '2' },
277
- { id: '3' },
278
- ])
279
- );
280
- expect(result.shapes.polygons).toEqual(
281
- expect.arrayContaining([{ id: '4', correct: true }])
326
+ expect.arrayContaining([{ id: '1', correct: true }, { id: '2' }, { id: '3' }]),
282
327
  );
328
+ expect(result.shapes.polygons).toEqual(expect.arrayContaining([{ id: '4', correct: true }]));
283
329
  });
284
330
 
285
331
  it('returns is response correct', () => {
@@ -290,9 +336,7 @@ describe('controller', () => {
290
336
 
291
337
  describe('isResponseCorrect', () => {
292
338
  const returnIsResponseCorect = (session) => {
293
- it(`response is not correct if session is ${JSON.stringify(
294
- session
295
- )}`, () => {
339
+ it(`response is not correct if session is ${JSON.stringify(session)}`, () => {
296
340
  expect(isResponseCorrect(question, session)).toEqual(false);
297
341
  });
298
342
  };
@@ -3,20 +3,16 @@ export default {
3
3
  imageUrl: '',
4
4
  shapes: {
5
5
  rectangles: [],
6
- polygons: []
6
+ polygons: [],
7
7
  },
8
8
  multipleCorrect: true,
9
9
  partialScoring: false,
10
10
  dimensions: {
11
11
  height: 0,
12
- width: 0
12
+ width: 0,
13
13
  },
14
- hotspotColor: 'rgba(137, 183, 244, 0.65)',
15
- hotspotList: [
16
- 'rgba(137, 183, 244, 0.65)'
17
- ],
14
+ hotspotColor: 'rgba(137, 183, 244, 0.25)',
15
+ hotspotList: ['rgba(137, 183, 244, 0.25)'],
18
16
  outlineColor: 'blue',
19
- outlineList: [
20
- 'blue'
21
- ]
17
+ outlineList: ['blue'],
22
18
  };
@@ -8,7 +8,7 @@ import defaults from './defaults';
8
8
 
9
9
  const log = debug('pie-elements:hotspot:controller');
10
10
 
11
- export const normalize = question => ({
11
+ export const normalize = (question) => ({
12
12
  promptEnabled: true,
13
13
  rationaleEnabled: true,
14
14
  teacherInstructionsEnabled: true,
@@ -19,19 +19,11 @@ export const normalize = question => ({
19
19
 
20
20
  export function model(question, session, env) {
21
21
  const normalizedQuestion = normalize(question);
22
- const {
23
- imageUrl,
24
- dimensions,
25
- hotspotColor,
26
- multipleCorrect,
27
- outlineColor,
28
- partialScoring,
29
- prompt,
30
- shapes
31
- } = normalizedQuestion;
22
+ const { imageUrl, dimensions, hotspotColor, multipleCorrect, outlineColor, partialScoring, prompt, shapes } =
23
+ normalizedQuestion;
32
24
  const { rectangles, polygons } = shapes || {};
33
25
 
34
- return new Promise(resolve => {
26
+ return new Promise((resolve) => {
35
27
  const out = {
36
28
  disabled: env.mode !== 'gather',
37
29
  mode: env.mode,
@@ -46,17 +38,16 @@ export function model(question, session, env) {
46
38
  // eslint-disable-next-line no-unused-vars
47
39
  rectangles: (rectangles || []).map(({ index, ...rectProps }) => ({ ...rectProps })),
48
40
  // eslint-disable-next-line no-unused-vars
49
- polygons: (polygons || []).map(({ index, ...polyProps }) => ({ ...polyProps }))
41
+ polygons: (polygons || []).map(({ index, ...polyProps }) => ({ ...polyProps })),
50
42
  },
51
- responseCorrect:
52
- env.mode === 'evaluate'
53
- ? isResponseCorrect(normalizedQuestion, session)
54
- : undefined
43
+ responseCorrect: env.mode === 'evaluate' ? isResponseCorrect(normalizedQuestion, session) : undefined,
55
44
  };
56
45
 
57
46
  if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {
58
47
  out.rationale = normalizedQuestion.rationaleEnabled ? normalizedQuestion.rationale : null;
59
- out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled ? normalizedQuestion.teacherInstructions : null;
48
+ out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled
49
+ ? normalizedQuestion.teacherInstructions
50
+ : null;
60
51
  } else {
61
52
  out.rationale = null;
62
53
  out.teacherInstructions = null;
@@ -71,7 +62,7 @@ export function model(question, session, env) {
71
62
  }
72
63
 
73
64
  export const createDefaultModel = (model = {}) =>
74
- new Promise(resolve => {
65
+ new Promise((resolve) => {
75
66
  resolve({
76
67
  ...defaults,
77
68
  ...model,
@@ -93,25 +84,35 @@ const getScore = (config, session, env = {}) => {
93
84
  }
94
85
 
95
86
  let correctAnswers = 0;
87
+ let selectedChoices = 0;
96
88
 
97
89
  const choices = [...rectangles, ...polygons];
98
90
 
99
- choices.forEach(shape => {
100
- const selected = answers && answers.filter(answer => answer.id === shape.id)[0];
91
+ const correctChoices = choices.filter((choice) => choice.correct);
92
+
93
+ choices.forEach((shape) => {
94
+ const selected = answers && answers.filter((answer) => answer.id === shape.id)[0];
101
95
  const correctlySelected = shape.correct && selected;
102
- const correctlyUnselected = !shape.correct && !selected;
103
96
 
104
- if (correctlySelected || correctlyUnselected) {
97
+ if (selected) {
98
+ selectedChoices += 1;
99
+ }
100
+
101
+ if (correctlySelected) {
105
102
  correctAnswers += 1;
106
103
  }
107
104
  });
108
105
 
109
- const str = (correctAnswers / choices.length).toFixed(2);
110
- return parseFloat(str);
106
+ const extraAnswers = selectedChoices > correctChoices.length ? selectedChoices - correctChoices.length : 0;
107
+
108
+ const total = correctChoices.length === 0 ? 1 : correctChoices.length;
109
+ const str = ((correctAnswers - extraAnswers) / total).toFixed(2);
110
+
111
+ return str < 0 ? 0 : parseFloat(str);
111
112
  };
112
113
 
113
114
  export function outcome(config, session, env = {}) {
114
- return new Promise(resolve => {
115
+ return new Promise((resolve) => {
115
116
  log('outcome...');
116
117
 
117
118
  if (!session || isEmpty(session)) {
@@ -122,7 +123,7 @@ export function outcome(config, session, env = {}) {
122
123
  const score = getScore(config, session, env);
123
124
  resolve({ score });
124
125
  } else {
125
- resolve({score: 0, empty: true})
126
+ resolve({ score: 0, empty: true });
126
127
  }
127
128
  });
128
129
  }
@@ -130,7 +131,7 @@ export function outcome(config, session, env = {}) {
130
131
  const returnShapesCorrect = (shapes) => {
131
132
  let answers = [];
132
133
 
133
- shapes.forEach(i => {
134
+ shapes.forEach((i) => {
134
135
  const { correct, id } = i;
135
136
  if (correct) {
136
137
  answers.push({ id });
@@ -140,7 +141,7 @@ const returnShapesCorrect = (shapes) => {
140
141
  };
141
142
 
142
143
  export const createCorrectResponseSession = (question, env) => {
143
- return new Promise(resolve => {
144
+ return new Promise((resolve) => {
144
145
  if (env.mode !== 'evaluate' && env.role === 'instructor') {
145
146
  const { shapes: { rectangles = [], polygons = {} } = {} } = question;
146
147
 
@@ -148,14 +149,37 @@ export const createCorrectResponseSession = (question, env) => {
148
149
  const polygonsCorrect = returnShapesCorrect(polygons);
149
150
 
150
151
  resolve({
151
- answers: [
152
- ...rectangleCorrect,
153
- ...polygonsCorrect
154
- ],
155
- id: '1'
152
+ answers: [...rectangleCorrect, ...polygonsCorrect],
153
+ id: '1',
156
154
  });
157
155
  } else {
158
156
  resolve(null);
159
157
  }
160
158
  });
161
159
  };
160
+
161
+ export const validate = (model = {}, config = {}) => {
162
+ const { shapes } = model;
163
+ const { minShapes = 2, maxShapes, maxSelections } = config;
164
+ const errors = {};
165
+
166
+ const allShapes = Object.values(shapes || {}).reduce((acc, shape) => [...acc, ...shape], []);
167
+
168
+ const nbOfSelections = (allShapes || []).reduce((acc, shape) => (shape.correct ? acc + 1 : acc), 0);
169
+
170
+ const nbOfShapes = (allShapes || []).length;
171
+
172
+ if (nbOfShapes < minShapes) {
173
+ errors.shapesError = `There should be at least ${minShapes} shapes defined.`;
174
+ } else if (nbOfShapes > maxShapes) {
175
+ errors.shapesError = `No more than ${maxShapes} shapes should be defined.`;
176
+ }
177
+
178
+ if (nbOfSelections < 1) {
179
+ errors.selectionsError = 'There should be at least 1 shape selected.';
180
+ } else if (nbOfSelections > maxSelections) {
181
+ errors.selectionsError = `No more than ${maxSelections} shapes should be selected.`;
182
+ }
183
+
184
+ return errors;
185
+ };
@@ -1,13 +1,16 @@
1
1
  import isEqual from 'lodash/isEqual';
2
2
  import isEmpty from 'lodash/isEmpty';
3
3
 
4
- export const getCorrectResponse = (choices) => choices
5
- .filter(c => c.correct)
6
- .map(c => c.id)
7
- .sort();
4
+ export const getCorrectResponse = (choices) =>
5
+ choices
6
+ .filter((c) => c.correct)
7
+ .map((c) => c.id)
8
+ .sort();
8
9
 
9
10
  export const isResponseCorrect = (question, session) => {
10
- const { shapes: { rectangles, polygons } } = question;
11
+ const {
12
+ shapes: { rectangles, polygons },
13
+ } = question;
11
14
  const choices = [...rectangles, ...polygons];
12
15
  let correctResponseIds = getCorrectResponse(choices);
13
16
 
@@ -16,7 +19,7 @@ export const isResponseCorrect = (question, session) => {
16
19
  }
17
20
 
18
21
  if (session.answers && session.answers.length) {
19
- let answerIds = (session.answers || []).map(a => a.id);
22
+ let answerIds = (session.answers || []).map((a) => a.id);
20
23
 
21
24
  return isEqual(answerIds.sort(), correctResponseIds);
22
25
  } else if (!(correctResponseIds && correctResponseIds.length)) {