@khanacademy/perseus 6.2.1 → 6.3.0
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.
- package/CHANGELOG.md +22 -0
- package/dist/all-widgets.d.ts +2 -2
- package/dist/all-widgets.js.flow +2 -2
- package/dist/basic-widgets.d.ts +2 -2
- package/dist/basic-widgets.js.flow +2 -2
- package/dist/components/math-input.d.ts +4 -1
- package/dist/components/math-input.js.flow +4 -1
- package/dist/components/tex-buttons.d.ts +5 -1
- package/dist/components/tex-buttons.js.flow +1 -1
- package/dist/es/index.js +172 -205
- package/dist/es/index.js.map +1 -1
- package/dist/extra-widgets.d.ts +2 -2
- package/dist/extra-widgets.js.flow +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +170 -201
- package/dist/index.js.flow +2 -0
- package/dist/index.js.map +1 -1
- package/dist/strings.js +23 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.js.flow +2 -2
- package/dist/util/geometry.d.ts +25 -0
- package/dist/util/geometry.js.flow +37 -0
- package/dist/widgets/categorizer.d.ts +2 -2
- package/dist/widgets/categorizer.js.flow +2 -2
- package/dist/widgets/cs-program.d.ts +2 -2
- package/dist/widgets/cs-program.js.flow +2 -2
- package/dist/widgets/definition.d.ts +2 -2
- package/dist/widgets/definition.js.flow +2 -2
- package/dist/widgets/dropdown.d.ts +2 -2
- package/dist/widgets/dropdown.js.flow +2 -2
- package/dist/widgets/explanation.d.ts +2 -2
- package/dist/widgets/explanation.js.flow +2 -2
- package/dist/widgets/expression.d.ts +2 -2
- package/dist/widgets/expression.js.flow +2 -2
- package/dist/widgets/graded-group-set.d.ts +2 -2
- package/dist/widgets/graded-group-set.js.flow +2 -2
- package/dist/widgets/graded-group.d.ts +2 -2
- package/dist/widgets/graded-group.js.flow +2 -2
- package/dist/widgets/grapher.d.ts +2 -2
- package/dist/widgets/grapher.js.flow +2 -2
- package/dist/widgets/group.d.ts +2 -2
- package/dist/widgets/group.js.flow +2 -2
- package/dist/widgets/iframe.d.ts +2 -2
- package/dist/widgets/iframe.js.flow +2 -2
- package/dist/widgets/image.d.ts +2 -2
- package/dist/widgets/image.js.flow +2 -2
- package/dist/widgets/input-number.d.ts +2 -2
- package/dist/widgets/input-number.js.flow +2 -2
- package/dist/widgets/interaction.d.ts +2 -2
- package/dist/widgets/interaction.js.flow +2 -2
- package/dist/widgets/interactive-graph.d.ts +4 -12
- package/dist/widgets/interactive-graph.js.flow +4 -7
- package/dist/widgets/label-image.d.ts +2 -2
- package/dist/widgets/label-image.js.flow +2 -2
- package/dist/widgets/lights-puzzle.d.ts +2 -2
- package/dist/widgets/lights-puzzle.js.flow +2 -2
- package/dist/widgets/matcher.d.ts +2 -2
- package/dist/widgets/matcher.js.flow +2 -2
- package/dist/widgets/matrix.d.ts +2 -2
- package/dist/widgets/matrix.js.flow +2 -2
- package/dist/widgets/measurer.d.ts +2 -2
- package/dist/widgets/measurer.js.flow +2 -2
- package/dist/widgets/molecule.d.ts +2 -2
- package/dist/widgets/molecule.js.flow +2 -2
- package/dist/widgets/number-line.d.ts +2 -2
- package/dist/widgets/number-line.js.flow +2 -2
- package/dist/widgets/numeric-input.d.ts +2 -2
- package/dist/widgets/numeric-input.js.flow +2 -2
- package/dist/widgets/orderer.d.ts +2 -2
- package/dist/widgets/orderer.js.flow +2 -2
- package/dist/widgets/passage-ref-target.d.ts +2 -2
- package/dist/widgets/passage-ref-target.js.flow +2 -2
- package/dist/widgets/passage-ref.d.ts +2 -2
- package/dist/widgets/passage-ref.js.flow +2 -2
- package/dist/widgets/passage.d.ts +2 -2
- package/dist/widgets/passage.js.flow +2 -2
- package/dist/widgets/plotter.d.ts +2 -2
- package/dist/widgets/plotter.js.flow +2 -2
- package/dist/widgets/radio.d.ts +2 -2
- package/dist/widgets/radio.js.flow +2 -2
- package/dist/widgets/reaction-diagram.d.ts +2 -2
- package/dist/widgets/reaction-diagram.js.flow +2 -2
- package/dist/widgets/sequence.d.ts +2 -2
- package/dist/widgets/sequence.js.flow +2 -2
- package/dist/widgets/simulator.d.ts +2 -2
- package/dist/widgets/simulator.js.flow +2 -2
- package/dist/widgets/sorter.d.ts +2 -2
- package/dist/widgets/sorter.js.flow +2 -2
- package/dist/widgets/table.d.ts +2 -2
- package/dist/widgets/table.js.flow +2 -2
- package/dist/widgets/transformer.d.ts +2 -2
- package/dist/widgets/transformer.js.flow +2 -2
- package/dist/widgets/unit.d.ts +2 -2
- package/dist/widgets/unit.js.flow +2 -2
- package/dist/widgets/video.d.ts +2 -2
- package/dist/widgets/video.js.flow +2 -2
- package/package.json +4 -5
- package/src/components/__tests__/math-input.test.tsx +60 -0
- package/src/components/math-input.tsx +88 -118
- package/src/components/prop-check-box.tsx +0 -1
- package/src/components/tex-buttons.tsx +73 -12
- package/src/index.ts +2 -0
- package/src/types.ts +19 -2
- package/src/util/geometry.ts +293 -0
- package/src/widgets/__tests__/numeric-input.test.ts +38 -0
- package/src/widgets/expression.tsx +0 -1
- package/src/widgets/grapher.tsx +0 -1
- package/src/widgets/image.tsx +0 -1
- package/src/widgets/input-number.tsx +0 -1
- package/src/widgets/interactive-graph.tsx +20 -288
- package/src/widgets/matrix.tsx +0 -1
- package/src/widgets/numeric-input.tsx +6 -2
- package/src/widgets/transformer.tsx +0 -1
- package/tsconfig-build.json +0 -1
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/__tests__/mock-asset-loading-widget.d.ts +0 -59
- package/dist/__tests__/mock-asset-loading-widget.js.flow +0 -70
- package/dist/__tests__/mock-widget.d.ts +0 -30
- package/dist/__tests__/mock-widget.js.flow +0 -39
- package/dist/__tests__/test-items/image-item.d.ts +0 -14
- package/dist/__tests__/test-items/image-item.js.flow +0 -20
- package/dist/__tests__/test-items/input-number-1-item.d.ts +0 -9
- package/dist/__tests__/test-items/input-number-1-item.js.flow +0 -15
- package/dist/__tests__/test-items/input-number-2-item.d.ts +0 -9
- package/dist/__tests__/test-items/input-number-2-item.js.flow +0 -15
- package/dist/__tests__/test-items/table-item.d.ts +0 -9
- package/dist/__tests__/test-items/table-item.js.flow +0 -15
- package/dist/interactive2/__tests__/movable-mock.d.ts +0 -5
- package/dist/interactive2/__tests__/movable-mock.js.flow +0 -11
- package/dist/widgets/__stories__/test-keypad-context-wrapper.d.ts +0 -6
- package/dist/widgets/__stories__/test-keypad-context-wrapper.js.flow +0 -12
- package/dist/widgets/__tests__/renderQuestion.d.ts +0 -13
- package/dist/widgets/__tests__/renderQuestion.js.flow +0 -28
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A collection of geomtry-related utility functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {number as knumber} from "@khanacademy/kmath";
|
|
6
|
+
import _ from "underscore";
|
|
7
|
+
|
|
8
|
+
import Util from "../util";
|
|
9
|
+
|
|
10
|
+
import type {Coord, Line} from "../interactive2/types";
|
|
11
|
+
|
|
12
|
+
const {eq, deepEq} = Util;
|
|
13
|
+
|
|
14
|
+
// This should really be a readonly tuple of [number, number]
|
|
15
|
+
export type Range = [number, number];
|
|
16
|
+
|
|
17
|
+
export type SineCoefficient = [
|
|
18
|
+
number, // amplitude
|
|
19
|
+
number, // angularFrequency
|
|
20
|
+
number, // phase
|
|
21
|
+
number, // verticalOffset
|
|
22
|
+
];
|
|
23
|
+
export type QuadraticCoefficient = [number, number, number]; // a, b, c
|
|
24
|
+
|
|
25
|
+
export function sign(val: number): 0 | 1 | -1 {
|
|
26
|
+
if (eq(val, 0)) {
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
return val > 0 ? 1 : -1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function ccw(a: Coord, b: Coord, c: Coord): number {
|
|
33
|
+
return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function collinear(a: Coord, b: Coord, c: Coord): boolean {
|
|
37
|
+
return eq(ccw(a, b, c), 0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Given rect bounding points A and B, whether point C is inside the rect
|
|
41
|
+
function pointInRect(a: Coord, b: Coord, c: Coord): boolean {
|
|
42
|
+
return (
|
|
43
|
+
c[0] <= Math.max(a[0], b[0]) &&
|
|
44
|
+
c[0] >= Math.min(a[0], b[0]) &&
|
|
45
|
+
c[1] <= Math.max(a[1], b[1]) &&
|
|
46
|
+
c[1] >= Math.min(a[1], b[1])
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Whether line segment AB intersects line segment CD
|
|
51
|
+
// http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
|
|
52
|
+
export function intersects(ab: Line, cd: Line): boolean {
|
|
53
|
+
const triplets = [
|
|
54
|
+
[ab[0], ab[1], cd[0]] as const,
|
|
55
|
+
[ab[0], ab[1], cd[1]] as const,
|
|
56
|
+
[cd[0], cd[1], ab[0]] as const,
|
|
57
|
+
[cd[0], cd[1], ab[1]] as const,
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const orientations = _.map(triplets, function (triplet) {
|
|
61
|
+
return sign(ccw(...triplet));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (
|
|
65
|
+
orientations[0] !== orientations[1] &&
|
|
66
|
+
orientations[2] !== orientations[3]
|
|
67
|
+
) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < 4; i++) {
|
|
72
|
+
if (orientations[i] === 0 && pointInRect(...triplets[i])) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function vector(a, b) {
|
|
81
|
+
return _.map(_.zip(a, b), function (pair) {
|
|
82
|
+
return pair[0] - pair[1];
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function magnitude(v: ReadonlyArray<Coord>): number {
|
|
87
|
+
return Math.sqrt(
|
|
88
|
+
_.reduce(
|
|
89
|
+
v,
|
|
90
|
+
function (memo, el) {
|
|
91
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'Coord' is not assignable to parameter of type 'number'.
|
|
92
|
+
return memo + Math.pow(el, 2);
|
|
93
|
+
},
|
|
94
|
+
0,
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function dotProduct(a: Coord, b: Coord): number {
|
|
100
|
+
return _.reduce(
|
|
101
|
+
_.zip(a, b),
|
|
102
|
+
function (memo, pair) {
|
|
103
|
+
return memo + pair[0] * pair[1];
|
|
104
|
+
},
|
|
105
|
+
0,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function sideLengths(coords: ReadonlyArray<Coord>): ReadonlyArray<number> {
|
|
110
|
+
const segments = _.zip(coords, rotate(coords));
|
|
111
|
+
return segments.map(function (segment) {
|
|
112
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
113
|
+
return magnitude(vector(...segment));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Based on http://math.stackexchange.com/a/151149
|
|
118
|
+
export function angleMeasures(
|
|
119
|
+
coords: ReadonlyArray<Coord>,
|
|
120
|
+
): ReadonlyArray<number> {
|
|
121
|
+
const triplets = _.zip(rotate(coords, -1), coords, rotate(coords, 1));
|
|
122
|
+
|
|
123
|
+
const offsets = _.map(triplets, function (triplet) {
|
|
124
|
+
const p = vector(triplet[1], triplet[0]);
|
|
125
|
+
const q = vector(triplet[2], triplet[1]);
|
|
126
|
+
// @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'Coord'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'. | TS2345 - Argument of type 'number[]' is not assignable to parameter of type 'readonly Coord[]'.
|
|
127
|
+
const raw = Math.acos(dotProduct(p, q) / (magnitude(p) * magnitude(q)));
|
|
128
|
+
// @ts-expect-error [FEI-5003] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
129
|
+
return sign(ccw(...triplet)) > 0 ? raw : -raw;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const sum = _.reduce(
|
|
133
|
+
offsets,
|
|
134
|
+
function (memo, arg) {
|
|
135
|
+
return memo + arg;
|
|
136
|
+
},
|
|
137
|
+
0,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
return _.map(offsets, function (offset) {
|
|
141
|
+
return sum > 0 ? Math.PI - offset : Math.PI + offset;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Whether two polygons are similar (or if specified, congruent)
|
|
146
|
+
export function similar(
|
|
147
|
+
coords1: ReadonlyArray<Coord>,
|
|
148
|
+
coords2: ReadonlyArray<Coord>,
|
|
149
|
+
tolerance: number,
|
|
150
|
+
): boolean {
|
|
151
|
+
if (coords1.length !== coords2.length) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const n = coords1.length;
|
|
156
|
+
|
|
157
|
+
const angles1 = angleMeasures(coords1);
|
|
158
|
+
const angles2 = angleMeasures(coords2);
|
|
159
|
+
|
|
160
|
+
const sides1 = sideLengths(coords1);
|
|
161
|
+
const sides2 = sideLengths(coords2);
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < 2 * n; i++) {
|
|
164
|
+
let angles = angles2.slice();
|
|
165
|
+
let sides = sides2.slice();
|
|
166
|
+
|
|
167
|
+
// Reverse angles and sides to allow matching reflected polygons
|
|
168
|
+
if (i >= n) {
|
|
169
|
+
angles.reverse();
|
|
170
|
+
sides.reverse();
|
|
171
|
+
// Since sides are calculated from two coordinates,
|
|
172
|
+
// simply reversing results in an off by one error
|
|
173
|
+
// @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
174
|
+
sides = rotate(sides, 1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
178
|
+
angles = rotate(angles, i);
|
|
179
|
+
// @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
|
|
180
|
+
sides = rotate(sides, i);
|
|
181
|
+
|
|
182
|
+
if (deepEq(angles1, angles)) {
|
|
183
|
+
const sidePairs = _.zip(sides1, sides);
|
|
184
|
+
|
|
185
|
+
const factors = _.map(sidePairs, function (pair) {
|
|
186
|
+
return pair[0] / pair[1];
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const same = _.all(factors, function (factor) {
|
|
190
|
+
return eq(factors[0], factor);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const congruentEnough = _.all(sidePairs, function (pair) {
|
|
194
|
+
return knumber.equal(pair[0], pair[1], tolerance);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
if (same && congruentEnough) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Given triangle with sides ABC return angle opposite side C in degrees
|
|
207
|
+
export function lawOfCosines(a: number, b: number, c: number): number {
|
|
208
|
+
return (Math.acos((a * a + b * b - c * c) / (2 * a * b)) * 180) / Math.PI;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function canonicalSineCoefficients([
|
|
212
|
+
amplitude,
|
|
213
|
+
angularFrequency,
|
|
214
|
+
phase,
|
|
215
|
+
verticalOffset,
|
|
216
|
+
]: [any, any, any, any]) {
|
|
217
|
+
// For a curve of the form f(x) = a * Sin(b * x - c) + d,
|
|
218
|
+
// this function ensures that a, b > 0, and c is its
|
|
219
|
+
// smallest possible positive value.
|
|
220
|
+
|
|
221
|
+
// Guarantee a > 0
|
|
222
|
+
if (amplitude < 0) {
|
|
223
|
+
amplitude *= -1;
|
|
224
|
+
angularFrequency *= -1;
|
|
225
|
+
phase *= -1;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const period = 2 * Math.PI;
|
|
229
|
+
// Guarantee b > 0
|
|
230
|
+
if (angularFrequency < 0) {
|
|
231
|
+
angularFrequency *= -1;
|
|
232
|
+
phase *= -1;
|
|
233
|
+
phase += period / 2;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Guarantee c is smallest possible positive value
|
|
237
|
+
while (phase > 0) {
|
|
238
|
+
phase -= period;
|
|
239
|
+
}
|
|
240
|
+
while (phase < 0) {
|
|
241
|
+
phase += period;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return [amplitude, angularFrequency, phase, verticalOffset];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// e.g. rotate([1, 2, 3]) -> [2, 3, 1]
|
|
248
|
+
export function rotate<T>(
|
|
249
|
+
array: ReadonlyArray<T>,
|
|
250
|
+
n?: number,
|
|
251
|
+
): ReadonlyArray<T> {
|
|
252
|
+
n = typeof n === "undefined" ? 1 : n % array.length;
|
|
253
|
+
return array.slice(n).concat(array.slice(0, n));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function getLineEquation(first: Coord, second: Coord): string {
|
|
257
|
+
if (eq(first[0], second[0])) {
|
|
258
|
+
return "x = " + first[0].toFixed(3);
|
|
259
|
+
}
|
|
260
|
+
const m = (second[1] - first[1]) / (second[0] - first[0]);
|
|
261
|
+
const b = first[1] - m * first[0];
|
|
262
|
+
return "y = " + m.toFixed(3) + "x + " + b.toFixed(3);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Stolen from the wikipedia article
|
|
266
|
+
// http://en.wikipedia.org/wiki/Line-line_intersection
|
|
267
|
+
export function getLineIntersection(
|
|
268
|
+
// TODO(LP-10725): update these to be 2-tuples
|
|
269
|
+
firstPoints: ReadonlyArray<Coord>,
|
|
270
|
+
secondPoints: ReadonlyArray<Coord>,
|
|
271
|
+
): string {
|
|
272
|
+
const x1 = firstPoints[0][0];
|
|
273
|
+
const y1 = firstPoints[0][1];
|
|
274
|
+
const x2 = firstPoints[1][0];
|
|
275
|
+
const y2 = firstPoints[1][1];
|
|
276
|
+
const x3 = secondPoints[0][0];
|
|
277
|
+
const y3 = secondPoints[0][1];
|
|
278
|
+
const x4 = secondPoints[1][0];
|
|
279
|
+
const y4 = secondPoints[1][1];
|
|
280
|
+
|
|
281
|
+
const determinant = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
|
282
|
+
|
|
283
|
+
if (Math.abs(determinant) < 1e-9) {
|
|
284
|
+
return "Lines are parallel";
|
|
285
|
+
}
|
|
286
|
+
const x =
|
|
287
|
+
((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) /
|
|
288
|
+
determinant;
|
|
289
|
+
const y =
|
|
290
|
+
((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) /
|
|
291
|
+
determinant;
|
|
292
|
+
return "Intersection: (" + x.toFixed(3) + ", " + y.toFixed(3) + ")";
|
|
293
|
+
}
|
|
@@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event";
|
|
|
3
3
|
|
|
4
4
|
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
5
5
|
import * as Dependencies from "../../dependencies";
|
|
6
|
+
import {errors} from "../../util/answer-types";
|
|
6
7
|
import {
|
|
7
8
|
question1AndAnswer,
|
|
8
9
|
multipleAnswers,
|
|
@@ -212,6 +213,43 @@ describe("static function validate", () => {
|
|
|
212
213
|
`);
|
|
213
214
|
});
|
|
214
215
|
|
|
216
|
+
// Don't default to validating the answer as a pi answer
|
|
217
|
+
// if answerForm isn't set on the answer.
|
|
218
|
+
// The answer value, userInput.currentValue, and
|
|
219
|
+
// the omission of answerForms in the answer are
|
|
220
|
+
// important to the test.
|
|
221
|
+
// https://khanacademy.atlassian.net/browse/LC-691
|
|
222
|
+
it("doesn't default to validating pi", () => {
|
|
223
|
+
const rubric: Rubric = {
|
|
224
|
+
answers: [
|
|
225
|
+
{
|
|
226
|
+
maxError: null,
|
|
227
|
+
message: "",
|
|
228
|
+
simplify: "required",
|
|
229
|
+
status: "correct",
|
|
230
|
+
strict: true,
|
|
231
|
+
value: 45.289,
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
labelText: "",
|
|
235
|
+
size: "normal",
|
|
236
|
+
static: false,
|
|
237
|
+
coefficient: false,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const userInput = {
|
|
241
|
+
// (pi / 12) * 173 = 45.291
|
|
242
|
+
// within the 0.01 margin of error
|
|
243
|
+
// to trigger the pi validation flow
|
|
244
|
+
currentValue: "45.282",
|
|
245
|
+
} as const;
|
|
246
|
+
|
|
247
|
+
const score = NumericInput.validate(userInput, rubric);
|
|
248
|
+
|
|
249
|
+
expect(score.message).not.toBe(errors.APPROXIMATED_PI_ERROR);
|
|
250
|
+
expect(score.message?.includes("pi")).toBeFalsy();
|
|
251
|
+
});
|
|
252
|
+
|
|
215
253
|
it("with a strict answer", () => {
|
|
216
254
|
const rubric: Rubric = {
|
|
217
255
|
answers: [
|
package/src/widgets/grapher.tsx
CHANGED
package/src/widgets/image.tsx
CHANGED