@pie-element/image-cloze-association 10.1.2-next.2 → 10.1.2
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.json +437 -0
- package/CHANGELOG.md +1997 -0
- package/LICENSE.md +5 -0
- package/README.md +1 -0
- package/configure/CHANGELOG.json +197 -0
- package/configure/CHANGELOG.md +1600 -0
- package/configure/lib/defaults.js +86 -0
- package/configure/lib/defaults.js.map +1 -0
- package/configure/lib/index.js +99 -0
- package/configure/lib/index.js.map +1 -0
- package/configure/lib/root.js +135 -0
- package/configure/lib/root.js.map +1 -0
- package/configure/package.json +23 -0
- package/configure/src/__tests__/index.test.js +155 -0
- package/configure/src/defaults.js +59 -0
- package/configure/src/index.js +114 -0
- package/configure/src/root.jsx +116 -0
- package/controller/CHANGELOG.json +137 -0
- package/controller/CHANGELOG.md +1149 -0
- package/controller/lib/defaults.js +14 -0
- package/controller/lib/defaults.js.map +1 -0
- package/controller/lib/index.js +304 -0
- package/controller/lib/index.js.map +1 -0
- package/controller/lib/utils.js +70 -0
- package/controller/lib/utils.js.map +1 -0
- package/controller/package.json +19 -0
- package/controller/src/__tests__/index.test.js +711 -0
- package/controller/src/defaults.js +7 -0
- package/controller/src/index.js +322 -0
- package/controller/src/utils.js +72 -0
- package/docs/config-schema.json +1382 -0
- package/docs/config-schema.json.md +1021 -0
- package/docs/demo/config.js +8 -0
- package/docs/demo/generate.js +74 -0
- package/docs/demo/index.html +1 -0
- package/docs/demo/session.js +16 -0
- package/docs/pie-schema.json +1085 -0
- package/docs/pie-schema.json.md +810 -0
- package/lib/constants.js +12 -0
- package/lib/constants.js.map +1 -0
- package/lib/evaluation-icon.js +60 -0
- package/lib/evaluation-icon.js.map +1 -0
- package/lib/image-container.js +94 -0
- package/lib/image-container.js.map +1 -0
- package/lib/image-drop-target.js +130 -0
- package/lib/image-drop-target.js.map +1 -0
- package/lib/index.js +220 -0
- package/lib/index.js.map +1 -0
- package/lib/interactive-section.js +104 -0
- package/lib/interactive-section.js.map +1 -0
- package/lib/possible-response.js +161 -0
- package/lib/possible-response.js.map +1 -0
- package/lib/possible-responses.js +58 -0
- package/lib/possible-responses.js.map +1 -0
- package/lib/root.js +491 -0
- package/lib/root.js.map +1 -0
- package/lib/static-html-span.js +35 -0
- package/lib/static-html-span.js.map +1 -0
- package/lib/utils-correctness.js +89 -0
- package/lib/utils-correctness.js.map +1 -0
- package/package.json +21 -86
- package/src/__tests__/index.test.js +174 -0
- package/src/__tests__/root.test.jsx +99 -0
- package/src/__tests__/utils.test.js +207 -0
- package/src/constants.js +5 -0
- package/src/evaluation-icon.jsx +54 -0
- package/src/image-container.jsx +90 -0
- package/src/image-drop-target.jsx +140 -0
- package/src/index.js +245 -0
- package/src/interactive-section.jsx +94 -0
- package/src/possible-response.jsx +152 -0
- package/src/possible-responses.jsx +52 -0
- package/src/root.jsx +490 -0
- package/src/static-html-span.jsx +30 -0
- package/src/utils-correctness.js +95 -0
- package/configure.js +0 -2
- package/controller.js +0 -1
- package/dist/author/defaults.d.ts +0 -88
- package/dist/author/defaults.js +0 -58
- package/dist/author/index.d.ts +0 -34
- package/dist/author/index.js +0 -71
- package/dist/author/root.d.ts +0 -14
- package/dist/author/root.js +0 -80
- package/dist/browser/Check-DL1c-mLM.js +0 -10708
- package/dist/browser/Check-DL1c-mLM.js.map +0 -1
- package/dist/browser/author/index.js +0 -38597
- package/dist/browser/author/index.js.map +0 -1
- package/dist/browser/controller/index.js +0 -171
- package/dist/browser/controller/index.js.map +0 -1
- package/dist/browser/delivery/index.js +0 -2699
- package/dist/browser/delivery/index.js.map +0 -1
- package/dist/browser/dist-BphSS14E.js +0 -346
- package/dist/browser/dist-BphSS14E.js.map +0 -1
- package/dist/browser/humps-CZ4RCLab.js +0 -67
- package/dist/browser/humps-CZ4RCLab.js.map +0 -1
- package/dist/browser/image-cloze-association.css +0 -2
- package/dist/controller/defaults.d.ts +0 -16
- package/dist/controller/defaults.js +0 -10
- package/dist/controller/index.d.ts +0 -23
- package/dist/controller/index.js +0 -122
- package/dist/controller/utils.d.ts +0 -14
- package/dist/controller/utils.js +0 -36
- package/dist/delivery/constants.d.ts +0 -14
- package/dist/delivery/evaluation-icon.d.ts +0 -28
- package/dist/delivery/evaluation-icon.js +0 -38
- package/dist/delivery/image-container.d.ts +0 -13
- package/dist/delivery/image-container.js +0 -61
- package/dist/delivery/image-drop-target.d.ts +0 -45
- package/dist/delivery/image-drop-target.js +0 -90
- package/dist/delivery/index.d.ts +0 -20
- package/dist/delivery/index.js +0 -110
- package/dist/delivery/interactive-section.d.ts +0 -15
- package/dist/delivery/interactive-section.js +0 -72
- package/dist/delivery/possible-response.d.ts +0 -34
- package/dist/delivery/possible-response.js +0 -100
- package/dist/delivery/possible-responses.d.ts +0 -31
- package/dist/delivery/possible-responses.js +0 -41
- package/dist/delivery/root.d.ts +0 -21
- package/dist/delivery/root.js +0 -278
- package/dist/delivery/static-html-span.d.ts +0 -14
- package/dist/delivery/static-html-span.js +0 -22
- package/dist/delivery/utils-correctness.d.ts +0 -10
- package/dist/delivery/utils-correctness.js +0 -43
- package/dist/index.d.ts +0 -1
- package/dist/index.iife.d.ts +0 -8
- package/dist/index.iife.js +0 -152
- package/dist/index.js +0 -2
- package/dist/node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.js +0 -16
- package/dist/runtime-support.d.ts +0 -12
- package/dist/runtime-support.js +0 -12
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getUnansweredAnswers = exports.getAnswersCorrectness = void 0;
|
|
7
|
+
var _humps = require("humps");
|
|
8
|
+
// functions also used in controller/src/utils.js
|
|
9
|
+
// camelize keys is needed to convert the keys from snake_case to camelCase
|
|
10
|
+
// this is also done in the controller
|
|
11
|
+
|
|
12
|
+
const getAllCorrectAnswers = (answers, responses) => (answers || []).map(answer => ({
|
|
13
|
+
...answer,
|
|
14
|
+
isCorrect: (responses[answer.containerIndex] && responses[answer.containerIndex].images || []).includes(answer.value)
|
|
15
|
+
}));
|
|
16
|
+
const getValidAnswer = (answer, response) => (response[answer.containerIndex].images || []).filter(res => res === answer.value);
|
|
17
|
+
const getUniqueCorrectAnswers = (answers, validResponses) => {
|
|
18
|
+
let finalAnswers = answers;
|
|
19
|
+
answers.forEach(answer1 => {
|
|
20
|
+
const valuesToParse = answers.filter(answer2 => answer2.value === answer1.value && answer2.containerIndex === answer1.containerIndex);
|
|
21
|
+
if (valuesToParse.length > 1) {
|
|
22
|
+
// point only to duplicates but first
|
|
23
|
+
valuesToParse.shift();
|
|
24
|
+
// mark duplicates as incorrect
|
|
25
|
+
valuesToParse.forEach((value, index) => {
|
|
26
|
+
finalAnswers = (finalAnswers || []).map(finalAnswer => {
|
|
27
|
+
if (finalAnswer.id === value.id) {
|
|
28
|
+
let valid = getValidAnswer(finalAnswer, validResponses);
|
|
29
|
+
return {
|
|
30
|
+
...finalAnswer,
|
|
31
|
+
isCorrect: valid.length > index + 1
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return finalAnswer;
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return finalAnswers;
|
|
40
|
+
};
|
|
41
|
+
const getUnansweredAnswers = (answers, validation) => {
|
|
42
|
+
const camelizedValidation = (0, _humps.camelizeKeys)(validation);
|
|
43
|
+
const {
|
|
44
|
+
validResponse: {
|
|
45
|
+
value
|
|
46
|
+
} = {}
|
|
47
|
+
} = camelizedValidation;
|
|
48
|
+
return (value || []).reduce((unanswered, response, index) => {
|
|
49
|
+
const isAnswered = !!answers.find(answer => answer.containerIndex === index);
|
|
50
|
+
response.images = response.images || [];
|
|
51
|
+
if (!isAnswered) {
|
|
52
|
+
return [...unanswered, {
|
|
53
|
+
id: `unanswered-${index}`,
|
|
54
|
+
value: response.images[0] || '',
|
|
55
|
+
containerIndex: index,
|
|
56
|
+
isCorrect: !response.images.length ? undefined : false,
|
|
57
|
+
hidden: true
|
|
58
|
+
}];
|
|
59
|
+
}
|
|
60
|
+
return unanswered;
|
|
61
|
+
}, []);
|
|
62
|
+
};
|
|
63
|
+
exports.getUnansweredAnswers = getUnansweredAnswers;
|
|
64
|
+
const getAnswersCorrectness = (answers, validation) => {
|
|
65
|
+
const camelizedValidation = (0, _humps.camelizeKeys)(validation);
|
|
66
|
+
const {
|
|
67
|
+
validResponse: {
|
|
68
|
+
value
|
|
69
|
+
},
|
|
70
|
+
altResponses
|
|
71
|
+
} = camelizedValidation;
|
|
72
|
+
const allCorrect = getAllCorrectAnswers(answers, value);
|
|
73
|
+
const uniqueAnswers = getUniqueCorrectAnswers(allCorrect, value);
|
|
74
|
+
const noOfCorrect = uniqueAnswers.filter(answer => answer.isCorrect).length;
|
|
75
|
+
|
|
76
|
+
// Look for alternate correct responses if there are incorrect responses.
|
|
77
|
+
if (noOfCorrect < uniqueAnswers.length && altResponses && altResponses.length) {
|
|
78
|
+
const altUniqueStack = (altResponses || []).map(altResponse => {
|
|
79
|
+
const altValue = altResponse.value;
|
|
80
|
+
const altAllCorrect = getAllCorrectAnswers(answers, altValue);
|
|
81
|
+
return getUniqueCorrectAnswers(altAllCorrect, altValue);
|
|
82
|
+
});
|
|
83
|
+
// Return the one with most correct answers.
|
|
84
|
+
return altUniqueStack.sort((a, b) => b.filter(c => c.isCorrect).length - a.filter(c => c.isCorrect).length)[0];
|
|
85
|
+
}
|
|
86
|
+
return uniqueAnswers;
|
|
87
|
+
};
|
|
88
|
+
exports.getAnswersCorrectness = getAnswersCorrectness;
|
|
89
|
+
//# sourceMappingURL=utils-correctness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils-correctness.js","names":["_humps","require","getAllCorrectAnswers","answers","responses","map","answer","isCorrect","containerIndex","images","includes","value","getValidAnswer","response","filter","res","getUniqueCorrectAnswers","validResponses","finalAnswers","forEach","answer1","valuesToParse","answer2","length","shift","index","finalAnswer","id","valid","getUnansweredAnswers","validation","camelizedValidation","camelizeKeys","validResponse","reduce","unanswered","isAnswered","find","undefined","hidden","exports","getAnswersCorrectness","altResponses","allCorrect","uniqueAnswers","noOfCorrect","altUniqueStack","altResponse","altValue","altAllCorrect","sort","a","b","c"],"sources":["../src/utils-correctness.js"],"sourcesContent":["// functions also used in controller/src/utils.js\n// camelize keys is needed to convert the keys from snake_case to camelCase\n// this is also done in the controller\nimport { camelizeKeys } from 'humps';\n\nconst getAllCorrectAnswers = (answers, responses) =>\n (answers || []).map((answer) => ({\n ...answer,\n isCorrect: ((responses[answer.containerIndex] && responses[answer.containerIndex].images) || []).includes(\n answer.value,\n ),\n }));\n\nconst getValidAnswer = (answer, response) =>\n (response[answer.containerIndex].images || []).filter((res) => res === answer.value);\n\nconst getUniqueCorrectAnswers = (answers, validResponses) => {\n let finalAnswers = answers;\n\n answers.forEach((answer1) => {\n const valuesToParse = answers.filter(\n (answer2) => answer2.value === answer1.value && answer2.containerIndex === answer1.containerIndex,\n );\n\n if (valuesToParse.length > 1) {\n // point only to duplicates but first\n valuesToParse.shift();\n // mark duplicates as incorrect\n valuesToParse.forEach((value, index) => {\n finalAnswers = (finalAnswers || []).map((finalAnswer) => {\n if (finalAnswer.id === value.id) {\n let valid = getValidAnswer(finalAnswer, validResponses);\n\n return {\n ...finalAnswer,\n isCorrect: valid.length > index + 1,\n };\n }\n return finalAnswer;\n });\n });\n }\n });\n return finalAnswers;\n};\n\nexport const getUnansweredAnswers = (answers, validation) => {\n const camelizedValidation = camelizeKeys(validation);\n const { validResponse: { value } = {} } = camelizedValidation;\n\n return (value || []).reduce((unanswered, response, index) => {\n const isAnswered = !!answers.find((answer) => answer.containerIndex === index);\n response.images = response.images || [];\n\n if (!isAnswered) {\n return [\n ...unanswered,\n {\n id: `unanswered-${index}`,\n value: response.images[0] || '',\n containerIndex: index,\n isCorrect: !response.images.length ? undefined : false,\n hidden: true,\n },\n ];\n }\n\n return unanswered;\n }, []);\n};\n\nexport const getAnswersCorrectness = (answers, validation) => {\n const camelizedValidation = camelizeKeys(validation);\n const {\n validResponse: { value },\n altResponses,\n } = camelizedValidation;\n\n const allCorrect = getAllCorrectAnswers(answers, value);\n const uniqueAnswers = getUniqueCorrectAnswers(allCorrect, value);\n const noOfCorrect = uniqueAnswers.filter((answer) => answer.isCorrect).length;\n\n // Look for alternate correct responses if there are incorrect responses.\n if (noOfCorrect < uniqueAnswers.length && altResponses && altResponses.length) {\n const altUniqueStack = (altResponses || []).map((altResponse) => {\n const altValue = altResponse.value;\n\n const altAllCorrect = getAllCorrectAnswers(answers, altValue);\n return getUniqueCorrectAnswers(altAllCorrect, altValue);\n });\n // Return the one with most correct answers.\n return altUniqueStack.sort((a, b) => b.filter((c) => c.isCorrect).length - a.filter((c) => c.isCorrect).length)[0];\n }\n return uniqueAnswers;\n};\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,101 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-element/image-cloze-association",
|
|
3
|
-
"
|
|
3
|
+
"repository": "pie-framework/pie-elements",
|
|
4
|
+
"version": "10.1.2",
|
|
4
5
|
"description": "",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
5
9
|
"dependencies": {
|
|
6
10
|
"@dnd-kit/core": "6.3.1",
|
|
7
11
|
"@emotion/react": "^11.14.0",
|
|
8
12
|
"@emotion/style": "^0.8.0",
|
|
9
13
|
"@mui/icons-material": "^7.3.4",
|
|
10
14
|
"@mui/material": "^7.3.4",
|
|
11
|
-
"@pie-
|
|
12
|
-
"@pie-lib/
|
|
13
|
-
"@pie-lib/
|
|
14
|
-
"@pie-lib/
|
|
15
|
+
"@pie-framework/pie-player-events": "^0.1.0",
|
|
16
|
+
"@pie-lib/correct-answer-toggle": "4.0.3",
|
|
17
|
+
"@pie-lib/drag": "4.0.3",
|
|
18
|
+
"@pie-lib/math-rendering": "5.0.2",
|
|
19
|
+
"@pie-lib/render-ui": "6.1.1",
|
|
20
|
+
"@pie-lib/translator": "4.0.2",
|
|
21
|
+
"classnames": "^2.2.6",
|
|
15
22
|
"humps": "^2.0.1",
|
|
16
23
|
"prop-types": "^15.8.1",
|
|
17
|
-
"react
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"@pie-lib/config-ui": "13.0.4-next.30",
|
|
21
|
-
"@pie-lib/editable-html-tip-tap": "2.1.2-next.30",
|
|
22
|
-
"@pie-element/shared-controller-utils": "0.1.1-next.1",
|
|
23
|
-
"@pie-element/shared-lodash": "0.1.1-next.0",
|
|
24
|
-
"clsx": "^2.1.1",
|
|
25
|
-
"@pie-element/shared-math-rendering-mathjax": "0.1.1-next.0",
|
|
26
|
-
"@pie-element/shared-player-events": "0.1.0"
|
|
27
|
-
},
|
|
28
|
-
"peerDependencies": {
|
|
29
|
-
"react": "^18.0.0",
|
|
30
|
-
"react-dom": "^18.0.0"
|
|
31
|
-
},
|
|
32
|
-
"exports": {
|
|
33
|
-
".": {
|
|
34
|
-
"types": "./dist/index.d.ts",
|
|
35
|
-
"default": "./dist/index.js"
|
|
36
|
-
},
|
|
37
|
-
"./delivery": {
|
|
38
|
-
"types": "./dist/delivery/index.d.ts",
|
|
39
|
-
"default": "./dist/delivery/index.js"
|
|
40
|
-
},
|
|
41
|
-
"./browser/delivery": {
|
|
42
|
-
"default": "./dist/browser/delivery/index.js"
|
|
43
|
-
},
|
|
44
|
-
"./author": {
|
|
45
|
-
"types": "./dist/author/index.d.ts",
|
|
46
|
-
"default": "./dist/author/index.js"
|
|
47
|
-
},
|
|
48
|
-
"./browser/author": {
|
|
49
|
-
"default": "./dist/browser/author/index.js"
|
|
50
|
-
},
|
|
51
|
-
"./configure": {
|
|
52
|
-
"types": "./dist/author/index.d.ts",
|
|
53
|
-
"default": "./dist/author/index.js"
|
|
54
|
-
},
|
|
55
|
-
"./controller": {
|
|
56
|
-
"types": "./dist/controller/index.d.ts",
|
|
57
|
-
"default": "./dist/controller/index.js"
|
|
58
|
-
},
|
|
59
|
-
"./controller.js": {
|
|
60
|
-
"types": "./dist/controller/index.d.ts",
|
|
61
|
-
"default": "./dist/controller/index.js"
|
|
62
|
-
},
|
|
63
|
-
"./browser/controller": {
|
|
64
|
-
"default": "./dist/browser/controller/index.js"
|
|
65
|
-
},
|
|
66
|
-
"./runtime-support": {
|
|
67
|
-
"types": "./dist/runtime-support.d.ts",
|
|
68
|
-
"default": "./dist/runtime-support.js"
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
"type": "module",
|
|
72
|
-
"main": "./dist/index.js",
|
|
73
|
-
"types": "./dist/index.d.ts",
|
|
74
|
-
"files": [
|
|
75
|
-
"configure.js",
|
|
76
|
-
"controller.js",
|
|
77
|
-
"dist"
|
|
78
|
-
],
|
|
79
|
-
"sideEffects": false,
|
|
80
|
-
"devDependencies": {
|
|
81
|
-
"vite": "^8.0.1",
|
|
82
|
-
"typescript": "^5.9.3",
|
|
83
|
-
"@vitejs/plugin-react": "^6.0.1",
|
|
84
|
-
"@types/react": "^18.2.0",
|
|
85
|
-
"@types/react-dom": "^18.2.0"
|
|
24
|
+
"react": "18.3.1",
|
|
25
|
+
"react-dom": "18.3.1",
|
|
26
|
+
"react-transition-group": "^4.4.5"
|
|
86
27
|
},
|
|
28
|
+
"author": "pie framework developers",
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"gitHead": "206ff17a94e8e197744d059f97ad2b9a1bc9145d",
|
|
87
31
|
"scripts": {
|
|
88
|
-
"
|
|
89
|
-
"dev": "bun x vite",
|
|
90
|
-
"demo": "bun x vite --mode demo",
|
|
91
|
-
"test": "bun x vitest run"
|
|
32
|
+
"postpublish": "../../scripts/postpublish"
|
|
92
33
|
},
|
|
93
|
-
"
|
|
94
|
-
|
|
95
|
-
"configure": "@pie-element/image-cloze-association/configure",
|
|
96
|
-
"browserSharedDependencies": {
|
|
97
|
-
"react": "18.2.0",
|
|
98
|
-
"react-dom": "18.2.0"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
34
|
+
"main": "lib/index.js",
|
|
35
|
+
"module": "src/index.js"
|
|
101
36
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { createRoot } from 'react-dom/client';
|
|
4
|
+
import { ModelSetEvent, SessionChangedEvent } from '@pie-framework/pie-player-events';
|
|
5
|
+
import { ImageClozeAssociationComponent } from '../root';
|
|
6
|
+
|
|
7
|
+
jest.mock('@pie-lib/math-rendering', () => ({ renderMath: jest.fn() }));
|
|
8
|
+
|
|
9
|
+
jest.mock('@dnd-kit/core', () => ({
|
|
10
|
+
DragOverlay: ({ children }) => <div>{children}</div>,
|
|
11
|
+
useDraggable: () => ({
|
|
12
|
+
setNodeRef: jest.fn(),
|
|
13
|
+
attributes: {},
|
|
14
|
+
listeners: {},
|
|
15
|
+
}),
|
|
16
|
+
useDroppable: () => ({
|
|
17
|
+
setNodeRef: jest.fn(),
|
|
18
|
+
isOver: false,
|
|
19
|
+
}),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock('@pie-lib/drag', () => ({
|
|
23
|
+
DragProvider: ({ children }) => <div>{children}</div>,
|
|
24
|
+
ICADroppablePlaceholder: ({ children }) => <div>{children}</div>,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const mockRender = jest.fn();
|
|
28
|
+
const mockUnmount = jest.fn();
|
|
29
|
+
const mockCreateRoot = jest.fn(() => ({
|
|
30
|
+
render: mockRender,
|
|
31
|
+
unmount: mockUnmount,
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
jest.mock('react-dom/client', () => ({
|
|
35
|
+
createRoot: (...args) => mockCreateRoot(...args),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
jest.mock('../index', () => {
|
|
39
|
+
const { ModelSetEvent, SessionChangedEvent } = require('@pie-framework/pie-player-events');
|
|
40
|
+
|
|
41
|
+
class MockHTMLElement {
|
|
42
|
+
constructor() {
|
|
43
|
+
this._root = null;
|
|
44
|
+
this._model = null;
|
|
45
|
+
this._session = null;
|
|
46
|
+
this.dispatchEvent = jest.fn();
|
|
47
|
+
this.audioComplete = false;
|
|
48
|
+
this.tagName = 'image-cloze-association';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
querySelector() {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
__esModule: true,
|
|
58
|
+
default: class ImageClozeAssociation extends MockHTMLElement {
|
|
59
|
+
constructor() {
|
|
60
|
+
super();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
set model(m) {
|
|
64
|
+
this._model = m;
|
|
65
|
+
this.dispatchEvent(new ModelSetEvent(this.tagName.toLowerCase(), this.isComplete(), !!this._model));
|
|
66
|
+
this._render();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
set session(s) {
|
|
70
|
+
this._session = s;
|
|
71
|
+
this._render();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
updateAnswer = (answers) => {
|
|
75
|
+
this._session = { ...this._session, answers };
|
|
76
|
+
this.dispatchEvent(new SessionChangedEvent(this.tagName.toLowerCase(), this.isComplete()));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
isComplete() {
|
|
80
|
+
const { responseAreasToBeFilled } = this._model || {};
|
|
81
|
+
if (!this._session || !this._session.answers) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const { answers } = this._session;
|
|
85
|
+
if (!Array.isArray(answers)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const filledResponseAreas = [...new Map(answers.map((item) => [item.containerIndex, item])).values()].length;
|
|
89
|
+
return filledResponseAreas >= responseAreasToBeFilled;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
_render() {
|
|
93
|
+
if (!this._root) {
|
|
94
|
+
this._root = mockCreateRoot(global.document.createElement('div'));
|
|
95
|
+
}
|
|
96
|
+
this._root.render(null);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const ImageClozeAssociation = require('../index').default;
|
|
103
|
+
|
|
104
|
+
describe('image-cloze-association', () => {
|
|
105
|
+
beforeEach(() => {
|
|
106
|
+
mockRender.mockClear();
|
|
107
|
+
mockCreateRoot.mockClear();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('events', () => {
|
|
111
|
+
describe('model', () => {
|
|
112
|
+
it('dispatches model set event', () => {
|
|
113
|
+
const el = new ImageClozeAssociation();
|
|
114
|
+
el.tagName = 'ica-el';
|
|
115
|
+
el.model = {};
|
|
116
|
+
expect(el.dispatchEvent).toBeCalledWith(new ModelSetEvent('ica-el', false, true));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('calls render', () => {
|
|
120
|
+
const el = new ImageClozeAssociation();
|
|
121
|
+
el.model = {};
|
|
122
|
+
const rootInstance = mockCreateRoot.mock.results[0].value;
|
|
123
|
+
expect(rootInstance.render).toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('updateAnswer', () => {
|
|
128
|
+
it('dispatches session changed event - add answer', () => {
|
|
129
|
+
const el = new ImageClozeAssociation();
|
|
130
|
+
el.tagName = 'ica-el';
|
|
131
|
+
el.model = {
|
|
132
|
+
responseAreasToBeFilled: 1,
|
|
133
|
+
};
|
|
134
|
+
el.session = { answers: [] };
|
|
135
|
+
el.updateAnswer([{ id: '1', containerIndex: 0, value: '' }]);
|
|
136
|
+
expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', true));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('dispatches session changed event - remove answer', () => {
|
|
140
|
+
const el = new ImageClozeAssociation();
|
|
141
|
+
el.tagName = 'ica-el';
|
|
142
|
+
el.model = {
|
|
143
|
+
responseAreasToBeFilled: 1,
|
|
144
|
+
};
|
|
145
|
+
el.session = { answers: [{ id: '1', containerIndex: 0, value: '' }] };
|
|
146
|
+
el.updateAnswer([]);
|
|
147
|
+
expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', false));
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('dispatches session changed event - add/remove answer', () => {
|
|
151
|
+
const el = new ImageClozeAssociation();
|
|
152
|
+
el.tagName = 'ica-el';
|
|
153
|
+
el.model = {
|
|
154
|
+
responseAreasToBeFilled: 2,
|
|
155
|
+
};
|
|
156
|
+
el.session = { answers: [] };
|
|
157
|
+
el.updateAnswer([{ id: '1', containerIndex: 0, value: '' }]);
|
|
158
|
+
expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', false));
|
|
159
|
+
|
|
160
|
+
el.updateAnswer([
|
|
161
|
+
{ id: '1', containerIndex: 0, value: '' },
|
|
162
|
+
{ id: '2', containerIndex: 1, value: '' },
|
|
163
|
+
]);
|
|
164
|
+
expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', true));
|
|
165
|
+
|
|
166
|
+
el.updateAnswer([{ id: '2', containerIndex: 1, value: '' }]);
|
|
167
|
+
expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', false));
|
|
168
|
+
|
|
169
|
+
el.updateAnswer([]);
|
|
170
|
+
expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', false));
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { ImageClozeAssociationComponent as Root } from '../root';
|
|
4
|
+
|
|
5
|
+
jest.mock('@dnd-kit/core', () => ({
|
|
6
|
+
DragOverlay: ({ children }) => <div>{children}</div>,
|
|
7
|
+
useDraggable: () => ({
|
|
8
|
+
setNodeRef: jest.fn(),
|
|
9
|
+
attributes: {},
|
|
10
|
+
listeners: {},
|
|
11
|
+
}),
|
|
12
|
+
useDroppable: () => ({
|
|
13
|
+
setNodeRef: jest.fn(),
|
|
14
|
+
isOver: false,
|
|
15
|
+
}),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.mock('@pie-lib/drag', () => ({
|
|
19
|
+
DragProvider: ({ children }) => <div>{children}</div>,
|
|
20
|
+
ICADroppablePlaceholder: ({ children }) => <div>{children}</div>,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const model = {
|
|
24
|
+
possibleResponses: ['firstImage', 'secondImage'],
|
|
25
|
+
responseContainers: [
|
|
26
|
+
{ index: 0, x: 0, y: 0, width: '10%', height: '10%' },
|
|
27
|
+
{ index: 1, x: 20, y: 20, width: '10%', height: '10%' },
|
|
28
|
+
],
|
|
29
|
+
duplicateResponses: false,
|
|
30
|
+
maxResponsePerZone: 1,
|
|
31
|
+
image: { src: 'test.jpg', width: 100, height: 100 },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
describe('Root', () => {
|
|
35
|
+
const updateAnswer = jest.fn();
|
|
36
|
+
|
|
37
|
+
const mkWrapper = (opts = {}) => {
|
|
38
|
+
const props = {
|
|
39
|
+
model,
|
|
40
|
+
session: { answers: [] },
|
|
41
|
+
updateAnswer,
|
|
42
|
+
...opts,
|
|
43
|
+
};
|
|
44
|
+
return render(<Root {...props} />);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const createInstance = (opts = {}) => {
|
|
48
|
+
const props = {
|
|
49
|
+
model,
|
|
50
|
+
session: { answers: [] },
|
|
51
|
+
updateAnswer,
|
|
52
|
+
...opts,
|
|
53
|
+
};
|
|
54
|
+
const instance = new Root(props);
|
|
55
|
+
|
|
56
|
+
// Mock setState to execute updates immediately for testing
|
|
57
|
+
instance.setState = jest.fn((state) => {
|
|
58
|
+
Object.assign(instance.state, typeof state === 'function' ? state(instance.state) : state);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return instance;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
describe('initialization', () => {
|
|
65
|
+
it('initializes with correct possible responses', () => {
|
|
66
|
+
const instance = createInstance();
|
|
67
|
+
expect(instance.state.possibleResponses).toEqual([
|
|
68
|
+
{ value: 'firstImage', id: '0' },
|
|
69
|
+
{ value: 'secondImage', id: '1' },
|
|
70
|
+
]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('handleOnAnswerSelect', () => {
|
|
75
|
+
it('removes response from possibleResponses on answer select', () => {
|
|
76
|
+
const instance = createInstance();
|
|
77
|
+
instance.handleOnAnswerSelect({ value: 'firstImage', id: '0' }, 0);
|
|
78
|
+
expect(instance.state.possibleResponses).toEqual([{ value: 'secondImage', id: '1' }]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('adds response back to possibleResponses on answer remove', () => {
|
|
82
|
+
const instance = createInstance();
|
|
83
|
+
instance.handleOnAnswerSelect({ value: 'firstImage', id: '0' }, 0);
|
|
84
|
+
instance.handleOnAnswerRemove({ value: 'firstImage', id: '0', containerIndex: 0 });
|
|
85
|
+
expect(instance.state.possibleResponses).toEqual([
|
|
86
|
+
{ value: 'secondImage', id: '1' },
|
|
87
|
+
{ value: 'firstImage', id: '0' },
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('preserves id when adding back to possibleResponses', () => {
|
|
92
|
+
const instance = createInstance();
|
|
93
|
+
instance.handleOnAnswerSelect({ value: 'firstImage', id: '0' }, 0);
|
|
94
|
+
instance.handleOnAnswerRemove({ value: 'firstImage', id: '0', containerIndex: 0 });
|
|
95
|
+
expect(instance.state.possibleResponses[1].id).toBe('0');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
});
|