diff --git a/curriculum/schema/__snapshots__/challenge-schema.test.mjs.snap b/curriculum/schema/__snapshots__/challenge-schema.test.mjs.snap new file mode 100644 index 00000000000..24e03d0e1c3 --- /dev/null +++ b/curriculum/schema/__snapshots__/challenge-schema.test.mjs.snap @@ -0,0 +1,2304 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`challenge schema > should not be changed without informing the mobile team 1`] = ` +{ + "keys": { + "assignments": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 21, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "items": [ + { + "type": "string", + }, + ], + "type": "array", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "items": [ + { + "type": "string", + }, + ], + "type": "array", + }, + }, + ], + }, + "bilibiliIds": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 11, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "keys": { + "aid": { + "flags": { + "presence": "required", + }, + "type": "number", + }, + "bvid": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "cid": { + "flags": { + "presence": "required", + }, + "type": "number", + }, + }, + "type": "object", + }, + }, + ], + }, + "block": { + "flags": { + "presence": "required", + }, + "rules": [ + { + "args": { + "regex": "/^[a-z0-9-]+$/", + }, + "name": "pattern", + }, + ], + "type": "string", + }, + "blockId": { + "rules": [ + { + "args": { + "options": { + "name": "valid mongo id", + }, + "regex": "/^[0-9a-fA-F]{24}$/", + }, + "name": "pattern", + }, + ], + "type": "string", + }, + "blockLabel": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + "full-stack-open", + "a1-professional-spanish", + "responsive-web-design-v9", + "javascript-v9", + "front-end-development-libraries-v9", + "python-v9", + "relational-databases-v9", + "back-end-development-and-apis-v9", + "full-stack-developer-v9", + "a1-professional-chinese", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "optional", + }, + "type": "any", + }, + "ref": { + "path": [ + "superBlock", + ], + }, + "then": { + "allow": [ + "workshop", + "lab", + "lecture", + "review", + "quiz", + "exam", + "warm-up", + "learn", + "practice", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + }, + ], + }, + "blockLayout": { + "allow": [ + "challenge-list", + "challenge-grid", + "dialogue-grid", + "link", + "project-list", + "legacy-challenge-list", + "legacy-link", + "legacy-challenge-grid", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "certification": { + "rules": [ + { + "args": { + "regex": "/^[a-z0-9-/]+$/", + }, + "name": "pattern", + }, + ], + "type": "string", + }, + "challengeFiles": { + "items": [ + { + "keys": { + "contents": { + "allow": [ + "", + ], + "type": "string", + }, + "editableRegionBoundaries": { + "items": [ + { + "type": "number", + }, + ], + "type": "array", + }, + "error": { + "allow": [ + null, + ], + "flags": { + "only": true, + }, + "type": "any", + }, + "ext": { + "type": "string", + }, + "fileKey": { + "type": "string", + }, + "head": { + "allow": [ + "", + ], + "type": "string", + }, + "history": { + "items": [ + { + "allow": [ + "", + ], + "type": "string", + }, + ], + "type": "array", + }, + "id": { + "allow": [ + "", + ], + "type": "string", + }, + "name": { + "type": "string", + }, + "path": { + "type": "string", + }, + "seed": { + "allow": [ + "", + ], + "type": "string", + }, + "tail": { + "allow": [ + "", + ], + "type": "string", + }, + }, + "type": "object", + }, + ], + "type": "array", + }, + "challengeOrder": { + "type": "number", + }, + "challengeType": { + "flags": { + "presence": "required", + }, + "rules": [ + { + "args": { + "limit": 0, + }, + "name": "min", + }, + { + "args": { + "limit": 31, + }, + "name": "max", + }, + ], + "type": "number", + }, + "chapter": { + "type": "string", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + "full-stack-open", + "a1-professional-spanish", + "responsive-web-design-v9", + "javascript-v9", + "front-end-development-libraries-v9", + "python-v9", + "relational-databases-v9", + "back-end-development-and-apis-v9", + "full-stack-developer-v9", + "a1-professional-chinese", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "optional", + }, + "type": "any", + }, + "ref": { + "path": [ + "superBlock", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "type": "any", + }, + }, + ], + }, + "dashedName": { + "rules": [ + { + "args": { + "regex": "/^[a-z0-9-]+$/", + }, + "name": "pattern", + }, + ], + "type": "string", + }, + "demoType": { + "allow": [ + "onClick", + "onLoad", + ], + "flags": { + "only": true, + }, + "type": "string", + }, + "description": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 7, + 11, + 19, + 22, + 31, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "allow": [ + "", + ], + "type": "string", + }, + }, + ], + }, + "disableLoopProtectPreview": { + "flags": { + "presence": "required", + }, + "type": "boolean", + }, + "disableLoopProtectTests": { + "flags": { + "presence": "required", + }, + "type": "boolean", + }, + "explanation": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 19, + 22, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "type": "string", + }, + }, + ], + }, + "fillInTheBlank": { + "keys": { + "blanks": { + "flags": { + "presence": "required", + }, + "items": [ + { + "keys": { + "answer": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "feedback": { + "allow": [ + null, + ], + "type": "string", + }, + }, + "type": "object", + }, + ], + "type": "array", + }, + "inputType": { + "allow": [ + "pinyin-tone", + "pinyin-to-hanzi", + ], + "flags": { + "only": true, + "presence": "optional", + }, + "type": "string", + }, + "sentence": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + "forumTopicId": { + "type": "number", + }, + "hasEditableBoundaries": { + "type": "boolean", + }, + "helpCategory": { + "allow": [ + "JavaScript", + "HTML-CSS", + "Python", + "Backend Development", + "C-Sharp", + "English", + "Odin", + "Euler", + "Rosetta", + "Chinese Curriculum", + "Spanish Curriculum", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "hooks": { + "keys": { + "afterAll": { + "allow": [ + "", + ], + "type": "string", + }, + "afterEach": { + "allow": [ + "", + ], + "type": "string", + }, + "beforeAll": { + "allow": [ + "", + ], + "type": "string", + }, + "beforeEach": { + "allow": [ + "", + ], + "type": "string", + }, + }, + "type": "object", + }, + "id": { + "flags": { + "presence": "required", + }, + "rules": [ + { + "args": { + "options": { + "name": "valid mongo id", + }, + "regex": "/^[0-9a-fA-F]{24}$/", + }, + "name": "pattern", + }, + ], + "type": "string", + }, + "inputType": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 22, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "forbidden", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + "a1-professional-chinese", + "a2-professional-chinese", + ], + "flags": { + "only": true, + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "forbidden", + }, + "type": "any", + }, + "ref": { + "path": [ + "superBlock", + ], + }, + "then": { + "allow": [ + "pinyin-tone", + "pinyin-to-hanzi", + ], + "flags": { + "only": true, + "presence": "optional", + }, + "type": "string", + }, + }, + ], + }, + }, + ], + }, + "instructions": { + "type": "string", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 10, + 13, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "allow": [ + "", + ], + "type": "string", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "rules": [ + { + "args": { + "limit": 1, + }, + "name": "min", + }, + ], + "type": "string", + }, + }, + ], + }, + "isExam": { + "type": "boolean", + }, + "isLastChallengeInBlock": { + "flags": { + "presence": "required", + }, + "type": "boolean", + }, + "lang": { + "type": "string", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + "a2-english-for-developers", + "b1-english-for-developers", + "a1-professional-spanish", + "a2-professional-spanish", + "a1-professional-chinese", + "a2-professional-chinese", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "forbidden", + }, + "type": "any", + }, + "ref": { + "path": [ + "superBlock", + ], + }, + "then": { + "allow": [ + "en-US", + "es", + "zh-CN", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + }, + ], + }, + "module": { + "type": "string", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + "full-stack-open", + "a1-professional-spanish", + "responsive-web-design-v9", + "javascript-v9", + "front-end-development-libraries-v9", + "python-v9", + "relational-databases-v9", + "back-end-development-and-apis-v9", + "full-stack-developer-v9", + "a1-professional-chinese", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "optional", + }, + "type": "any", + }, + "ref": { + "path": [ + "superBlock", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "type": "any", + }, + }, + ], + }, + "msTrophyId": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 18, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + ], + }, + "nodules": { + "items": [ + { + "keys": { + "data": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + "interactiveEditor", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "ref": { + "path": [ + "type", + ], + }, + "then": { + "items": [ + { + "keys": { + "contents": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "contentsHtml": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "ext": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "name": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + ], + "type": "array", + }, + }, + ], + }, + "type": { + "allow": [ + "paragraph", + "interactiveEditor", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + }, + "type": "object", + }, + ], + "type": "array", + }, + "notes": { + "allow": [ + "", + ], + "type": "string", + }, + "order": { + "type": "number", + }, + "prerequisites": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 17, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "items": [ + { + "keys": { + "id": { + "flags": { + "presence": "required", + }, + "rules": [ + { + "args": { + "options": { + "name": "valid mongo id", + }, + "regex": "/^[0-9a-fA-F]{24}$/", + }, + "name": "pattern", + }, + ], + "type": "string", + }, + "title": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + ], + "type": "array", + }, + }, + ], + }, + "questions": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 11, + 19, + 15, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "rules": [ + { + "args": { + "limit": 0, + }, + "name": "length", + }, + ], + "type": "array", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "items": [ + { + "keys": { + "answers": { + "flags": { + "presence": "required", + }, + "items": [ + { + "keys": { + "answer": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "audioId": { + "allow": [ + null, + ], + "type": "string", + }, + "feedback": { + "allow": [ + null, + ], + "type": "string", + }, + }, + "type": "object", + }, + ], + "rules": [ + { + "args": { + "comparator": "answer", + }, + "name": "unique", + }, + ], + "type": "array", + }, + "solution": { + "flags": { + "presence": "required", + }, + "rules": [ + { + "args": { + "limit": 1, + }, + "name": "min", + }, + { + "args": { + "limit": { + "ref": { + "path": [ + "answers", + "length", + ], + }, + }, + }, + "name": "max", + }, + ], + "type": "number", + }, + "text": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + ], + "rules": [ + { + "args": { + "limit": 1, + }, + "name": "min", + }, + ], + "type": "array", + }, + }, + ], + }, + "quizzes": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 8, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "otherwise": { + "flags": { + "presence": "forbidden", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "items": [ + { + "keys": { + "questions": { + "flags": { + "presence": "required", + }, + "items": [ + { + "keys": { + "answer": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "audioData": { + "keys": { + "audio": { + "keys": { + "filename": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "finishTimestamp": { + "type": "number", + }, + "startTimestamp": { + "type": "number", + }, + }, + "type": "object", + }, + "transcript": { + "items": [ + { + "keys": { + "character": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "text": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + ], + "type": "array", + }, + }, + "type": "object", + }, + "distractors": { + "flags": { + "presence": "required", + }, + "items": [ + { + "allow": [ + { + "ref": { + "ancestor": 2, + "path": [ + "answer", + ], + }, + }, + ], + "flags": { + "only": true, + "presence": "forbidden", + }, + "type": "any", + }, + { + "flags": { + "presence": "required", + }, + "type": "string", + }, + ], + "rules": [ + { + "args": { + "limit": 3, + }, + "name": "length", + }, + { + "name": "unique", + }, + ], + "type": "array", + }, + "text": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + ], + "preferences": { + "messages": { + "array.invalidLength": "Quiz must have exactly 10 or 20 questions.", + }, + }, + "rules": [ + { + "args": { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "array", + }, + }, + "type": "object", + }, + ], + "rules": [ + { + "args": { + "limit": 1, + }, + "name": "min", + }, + ], + "type": "array", + }, + }, + ], + }, + "required": { + "items": [ + { + "keys": { + "crossDomain": { + "type": "boolean", + }, + "link": { + "type": "string", + }, + "raw": { + "type": "boolean", + }, + "src": { + "type": "string", + }, + }, + "type": "object", + }, + ], + "type": "array", + }, + "saveSubmissionToDB": { + "type": "boolean", + }, + "scene": { + "keys": { + "commands": { + "items": [ + { + "keys": { + "background": { + "allow": [ + "cafe.png", + "chaos.png", + "company1-boardroom.png", + "company1-breakroom.png", + "company1-center.png", + "company1-dining.png", + "company1-lydia-cubicle.png", + "company1-parking.png", + "company1-reception.png", + "company1-roof.png", + "company2-boardroom.png", + "company2-breakroom.png", + "company2-center.png", + "company2-dining.png", + "company2-lydia-cubicle.png", + "company2-parking.png", + "company2-reception.png", + "company2-roof.png", + "company3-boardmeeting.png", + "company3-breakroom.png", + "company3-center.png", + "company3-dining.png", + "company3-lydia-cubicle.png", + "company3-parking.png", + "company3-reception.png", + "company3-roof.png", + "cubicle.png", + "desk.png", + "hacker-space-cafe.png", + "hacker-space.png", + "interview-room1.png", + "interview-room2.png", + "interview-room3.png", + "living-room.png", + ], + "flags": { + "only": true, + }, + "type": "string", + }, + "character": { + "allow": [ + "Alice", + "Amy", + "Anna", + "Bob", + "Brian", + "Candidate", + "David", + "Delivery Man", + "Expert", + "Jake", + "James", + "Jessica", + "Jim", + "Josh", + "Linda", + "Lisa", + "Maria", + "Mark", + "Riker", + "Sarah", + "Second Candidate", + "Sophie", + "Tom", + "Alex", + "Ángela", + "Camila", + "Carlos", + "Elena", + "Esteban", + "Joaquín", + "Julieta", + "Luis", + "Luna", + "Marisol", + "Mateo", + "Noelia", + "René", + "Sebastián", + "Diego", + "Valeria", + "Chen Na", + "Li Hong", + "Li Ping", + "Lin Yating", + "Liu Ming", + "Wang Hua", + "Zhang Wei", + "Zhou Yongjie", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "string", + }, + "dialogue": { + "keys": { + "align": { + "allow": [ + "left", + "center", + "right", + ], + "flags": { + "only": true, + }, + "type": "string", + }, + "text": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + "finishTime": { + "preferences": { + "convert": false, + }, + "type": "number", + }, + "opacity": { + "preferences": { + "convert": false, + }, + "type": "number", + }, + "position": { + "keys": { + "x": { + "flags": { + "presence": "required", + }, + "preferences": { + "convert": false, + }, + "type": "number", + }, + "y": { + "flags": { + "presence": "required", + }, + "preferences": { + "convert": false, + }, + "type": "number", + }, + "z": { + "flags": { + "presence": "required", + }, + "preferences": { + "convert": false, + }, + "type": "number", + }, + }, + "type": "object", + }, + "startTime": { + "flags": { + "presence": "required", + }, + "preferences": { + "convert": false, + }, + "type": "number", + }, + }, + "type": "object", + }, + ], + "preferences": { + "messages": { + "array.unique": "Dialogues must not have overlapping times.", + }, + }, + "rules": [ + { + "args": { + "comparator": [Function], + }, + "name": "unique", + }, + ], + "type": "array", + }, + "setup": { + "flags": { + "presence": "required", + }, + "keys": { + "alwaysShowDialogue": { + "type": "boolean", + }, + "audio": { + "flags": { + "presence": "required", + }, + "keys": { + "filename": { + "allow": [ + "1.1-1.mp3", + "1.1-2.mp3", + "1.1-3.mp3", + "1.1-4.mp3", + "1.1-5.mp3", + "1.2-1.mp3", + "1.2-2.mp3", + "1.2-3.mp3", + "1.2-4.mp3", + "1.2-5.mp3", + "1.3-1.mp3", + "1.3-2.mp3", + "1.3-3.mp3", + "1.3-4.mp3", + "1.3-5.mp3", + "2.1-1.mp3", + "2.1-2.mp3", + "2.1-3.mp3", + "2.1-4.mp3", + "2.1-5.mp3", + "2.2-1.mp3", + "2.2-2.mp3", + "2.2-3.mp3", + "2.2-4.mp3", + "2.2-5.mp3", + "2.3-1.mp3", + "2.3-2.mp3", + "2.3-3.mp3", + "2.3-4.mp3", + "2.3-5.mp3", + "3.1-1.mp3", + "3.1-2.mp3", + "3.1-3.mp3", + "3.1-4.mp3", + "3.1-5.mp3", + "3.2-1.mp3", + "3.2-2.mp3", + "3.2-3.mp3", + "3.2-4.mp3", + "3.2-5.mp3", + "3.3-1.mp3", + "3.3-2.mp3", + "3.3-3.mp3", + "3.3-4.mp3", + "3.3-5.mp3", + "4.1-1.mp3", + "4.1-2.mp3", + "4.1-3.mp3", + "4.1-4.mp3", + "4.1-5.mp3", + "4.2-1.mp3", + "4.2-2.mp3", + "4.2-3.mp3", + "4.2-4.mp3", + "4.2-5.mp3", + "4.3-1.mp3", + "4.3-2.mp3", + "4.3-3.mp3", + "4.3-4.mp3", + "4.3-5.mp3", + "5.1-1.mp3", + "5.1-2.mp3", + "5.1-3.mp3", + "5.2-1.mp3", + "5.2-2.mp3", + "5.2-3.mp3", + "5.3-1.mp3", + "5.3-2.mp3", + "5.3-3.mp3", + "6.1-1.mp3", + "6.1-2.mp3", + "6.1-3.mp3", + "6.2-1.mp3", + "6.2-2.mp3", + "6.2-3.mp3", + "6.3-1.mp3", + "6.3-2.mp3", + "6.3-3.mp3", + "7.1-1.mp3", + "7.1-2.mp3", + "7.1-3.mp3", + "7.2-1.mp3", + "7.2-2.mp3", + "7.2-3.mp3", + "7.3-1.mp3", + "7.3-2.mp3", + "7.3-3.mp3", + "8.1-1.mp3", + "8.1-2.mp3", + "8.1-3.mp3", + "8.2-1.mp3", + "8.2-2.mp3", + "8.2-3.mp3", + "8.3-1.mp3", + "8.3-2.mp3", + "8.3-3.mp3", + "9.1-1.mp3", + "9.1-2.mp3", + "9.1-3.mp3", + "9.2-1.mp3", + "9.2-2.mp3", + "9.2-3.mp3", + "9.3-1.mp3", + "9.3-2.mp3", + "9.3-3.mp3", + "B1_1-1.mp3", + "B1_1-2.mp3", + "B1_1-3.mp3", + "B1_2-1.mp3", + "B1_2-2.mp3", + "B1_2-3.mp3", + "B1_3-1.mp3", + "B1_3-2.mp3", + "B1_3-3.mp3", + "B1_4-1.mp3", + "B1_4-2.mp3", + "B1_4-3.mp3", + "B1_4-4.mp3", + "B1_5-1.mp3", + "B1_5-2.mp3", + "B1_5-3.mp3", + "B1_6-1.mp3", + "B1_6-2.mp3", + "B1_6-3.mp3", + "B1_7-1.mp3", + "B1_7-2.mp3", + "B1_7-3.mp3", + "B1_8-1.mp3", + "B1_8-2.mp3", + "B1_8-3.mp3", + "B1_9-1.mp3", + "B1_9-2.mp3", + "B1_9-3.mp3", + "B1_10-1.mp3", + "B1_10-2.mp3", + "B1_10-3.mp3", + "B1_11-1.mp3", + "B1_11-2.mp3", + "B1_11-3.mp3", + "B1_12-1.mp3", + "B1_12-2.mp3", + "B1_12-3.mp3", + "B1_13-1.mp3", + "B1_13-2.mp3", + "B1_13-3.mp3", + "B1_14-1.mp3", + "B1_14-2.mp3", + "B1_14-3.mp3", + "B1_15-1.mp3", + "B1_15-2.mp3", + "B1_15-3.mp3", + "B1_16-1.mp3", + "B1_16-2.mp3", + "B1_16-3.mp3", + "B1_17-1.mp3", + "B1_17-2.mp3", + "B1_17-3.mp3", + "B1_18-1.mp3", + "B1_18-2.mp3", + "B1_18-3.mp3", + "B1_19-1.mp3", + "B1_19-2.mp3", + "B1_19-3.mp3", + "B1_20-1.mp3", + "B1_20-2.mp3", + "B1_20-3.mp3", + "B1_21-1.mp3", + "B1_21-2.mp3", + "B1_21-3.mp3", + "B1_22-1.mp3", + "B1_22-2.mp3", + "B1_22-3.mp3", + "B1_23-1.mp3", + "B1_23-2.mp3", + "B1_23-3.mp3", + "B1_24-1.mp3", + "B1_24-2.mp3", + "B1_24-3.mp3", + "ES_A1_spanish_greetings_in_the_morning.mp3", + "ES_A1_spanish_greetings_in_the_afternoon.mp3", + "ES_A1_spanish_greetings_in_the_evening.mp3", + "ES_A1_spanish_greetings_camila_practice.mp3", + "ES_A1_spanish_meet_luna.mp3", + "ES_A1_spanish_meet_mateo.mp3", + "ES_A1_spanish_meet_julieta.mp3", + "ES_A1_spanish_meet_mateo_practice.mp3", + "ES_A1_spanish_introducing_yourself_vocabulary.mp3", + "ES_A1_spanish_meet_angela_and_basti.mp3", + "ES_A1_spanish_meet_camila_and_mateo.mp3", + "ES_A1_first_questions_numbers.mp3", + "ES_A1_first_questions_warmup.mp3", + "ES_A1_alphabet.mp3", + "ES_A1_alphabet_practice.mp3", + "ES_A1_andescommerce_company_profile.mp3", + "ES_A1_basic_personal_details_numbers_10_29.mp3", + "ES_A1_basic_personal_details_numbers_mixed.mp3", + "ES_A1_basti_asks_luna_about_carlos_dialogue.mp3", + "ES_A1_basti_personal_details.mp3", + "ES_A1_diego_mini_biography.mp3", + "ES_A1_esteban_asks_angela_about_luis_dialogue.mp3", + "ES_A1_camila_asks_basti_about_marisol_dialogue.mp3", + "ES_A1_carlos_workplace_profile.mp3", + "ES_A1_company_departments_and_employees.mp3", + "ES_A1_company_details_fill_in.mp3", + "ES_A1_company_facts_short.mp3", + "ES_A1_describing_a_company_basics_vocabulary.mp3", + "ES_A1_describing_company_and_people_numbers_practice.mp3", + "ES_A1_elena_asks_esteban_about_diego_dialogue.mp3", + "ES_A1_elena_talks_about_her_company.mp3", + "ES_A1_esteban_personal_records_dialogue_with_angela.mp3", + "ES_A1_describing_company_and_people_numbers_21_100.mp3", + "ES_A1_joaquin_workplace_profile.mp3", + "ES_A1_julieta_personal_details.mp3", + "ES_A1_luis_workplace_profile.mp3", + "ES_A1_luna_personal_data_form.mp3", + "ES_A1_marisol_mini_biography.mp3", + "ES_A1_mateo_asks_julieta_about_valeria_dialogue.mp3", + "ES_A1_mateo_camila_phone_number_dialogue.mp3", + "ES_A1_mateo_email_dialogue_with_camila.mp3", + "ES_A1_noelia_mini_biography.mp3", + "ES_A1_personal_details_just_questions.mp3", + "ES_A1_questions_about_joaquin_workplace_profile.mp3", + "ES_A1_questions_about_noelia_mini_biography.mp3", + "ES_A1_sebastian_asks_elena_about_her_company.mp3", + "ES_A1_spanish_fundamentals_numbers_0_9.mp3", + "ES_A1_spanish_fundamentals_numbers_mixed.mp3", + "ES_A1_valeria_mini_biography.mp3", + "ES_A1_vowels.mp3", + "ES_A1_vowels_aiu.mp3", + "ES_A1_vowels_oiu.mp3", + "ES_A1_vowels_uae.mp3", + "ES_A1_warm_up_describing_people_at_work.mp3", + "ES_A1_what_the_company_does.mp3", + "ZH_A1_greetings_and_introductions_wanghua.mp3", + "ZH_A1_greetings_and_introductions_liuming.mp3", + "ZH_A1_greetings_and_introductions_chenna.mp3", + "ZH_A1_greetings_and_introductions_dialogue.mp3", + "ZH_A1_greetings_and_introductions_warm_up.mp3", + "ZH_A1_chinese_fundamentals_hello_world.mp3", + "ZH_A1_pinyin_single_finals.mp3", + "ZH_A1_pinyin_initials.mp3", + "ZH_A1_pinyin_compound_finals.mp3", + "ZH_A1_pinyin_nasal_finals.mp3", + "ZH_A1_pinyin_special_spelling_rules.mp3", + "ZH_A1_pinyin_practice.mp3", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "string", + }, + "finishTimestamp": { + "preferences": { + "convert": false, + }, + "type": "number", + }, + "startTime": { + "flags": { + "presence": "required", + }, + "preferences": { + "convert": false, + }, + "type": "number", + }, + "startTimestamp": { + "preferences": { + "convert": false, + }, + "type": "number", + }, + }, + "type": "object", + }, + "background": { + "allow": [ + "cafe.png", + "chaos.png", + "company1-boardroom.png", + "company1-breakroom.png", + "company1-center.png", + "company1-dining.png", + "company1-lydia-cubicle.png", + "company1-parking.png", + "company1-reception.png", + "company1-roof.png", + "company2-boardroom.png", + "company2-breakroom.png", + "company2-center.png", + "company2-dining.png", + "company2-lydia-cubicle.png", + "company2-parking.png", + "company2-reception.png", + "company2-roof.png", + "company3-boardmeeting.png", + "company3-breakroom.png", + "company3-center.png", + "company3-dining.png", + "company3-lydia-cubicle.png", + "company3-parking.png", + "company3-reception.png", + "company3-roof.png", + "cubicle.png", + "desk.png", + "hacker-space-cafe.png", + "hacker-space.png", + "interview-room1.png", + "interview-room2.png", + "interview-room3.png", + "living-room.png", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "string", + }, + "characters": { + "flags": { + "presence": "required", + }, + "items": [ + { + "keys": { + "character": { + "allow": [ + "Alice", + "Amy", + "Anna", + "Bob", + "Brian", + "Candidate", + "David", + "Delivery Man", + "Expert", + "Jake", + "James", + "Jessica", + "Jim", + "Josh", + "Linda", + "Lisa", + "Maria", + "Mark", + "Riker", + "Sarah", + "Second Candidate", + "Sophie", + "Tom", + "Alex", + "Ángela", + "Camila", + "Carlos", + "Elena", + "Esteban", + "Joaquín", + "Julieta", + "Luis", + "Luna", + "Marisol", + "Mateo", + "Noelia", + "René", + "Sebastián", + "Diego", + "Valeria", + "Chen Na", + "Li Hong", + "Li Ping", + "Lin Yating", + "Liu Ming", + "Wang Hua", + "Zhang Wei", + "Zhou Yongjie", + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "string", + }, + "opacity": { + "preferences": { + "convert": false, + }, + "type": "number", + }, + "position": { + "flags": { + "presence": "required", + }, + "keys": { + "x": { + "flags": { + "presence": "required", + }, + "preferences": { + "convert": false, + }, + "type": "number", + }, + "y": { + "flags": { + "presence": "required", + }, + "preferences": { + "convert": false, + }, + "type": "number", + }, + "z": { + "flags": { + "presence": "required", + }, + "preferences": { + "convert": false, + }, + "type": "number", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + ], + "rules": [ + { + "args": { + "limit": 1, + }, + "name": "min", + }, + ], + "type": "array", + }, + }, + "type": "object", + }, + }, + "type": "object", + }, + "showSpeakingButton": { + "type": "boolean", + }, + "solutions": { + "items": [ + { + "items": [ + { + "keys": { + "contents": { + "allow": [ + "", + ], + "type": "string", + }, + "editableRegionBoundaries": { + "items": [ + { + "type": "number", + }, + ], + "type": "array", + }, + "error": { + "allow": [ + null, + ], + "flags": { + "only": true, + }, + "type": "any", + }, + "ext": { + "type": "string", + }, + "fileKey": { + "type": "string", + }, + "head": { + "allow": [ + "", + ], + "type": "string", + }, + "history": { + "items": [ + { + "allow": [ + "", + ], + "type": "string", + }, + ], + "type": "array", + }, + "id": { + "allow": [ + "", + ], + "type": "string", + }, + "name": { + "type": "string", + }, + "path": { + "type": "string", + }, + "seed": { + "allow": [ + "", + ], + "type": "string", + }, + "tail": { + "allow": [ + "", + ], + "type": "string", + }, + }, + "type": "object", + }, + ], + "rules": [ + { + "args": { + "limit": 1, + }, + "name": "min", + }, + ], + "type": "array", + }, + ], + "type": "array", + }, + "sourceLocation": { + "type": "string", + }, + "suborder": { + "type": "number", + }, + "superBlock": { + "rules": [ + { + "args": { + "regex": "/^[a-z0-9-/]+$/", + }, + "name": "pattern", + }, + ], + "type": "string", + }, + "superOrder": { + "type": "number", + }, + "template": { + "allow": [ + "", + ], + "type": "string", + }, + "tests": { + "flags": { + "presence": "required", + }, + "items": [ + { + "keys": { + "id": { + "allow": [ + "", + ], + "type": "string", + }, + "testString": { + "allow": [ + "", + ], + "flags": { + "presence": "required", + }, + "type": "string", + }, + "text": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + { + "keys": { + "id": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "title": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + "type": "object", + }, + ], + "type": "array", + }, + "title": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + "transcript": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 24, + 11, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "type": "string", + }, + }, + ], + }, + "translationPending": { + "flags": { + "presence": "required", + }, + "type": "boolean", + }, + "url": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 12, + 13, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + ], + }, + "usesMultifileEditor": { + "type": "boolean", + }, + "videoId": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 11, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "flags": { + "presence": "required", + }, + "type": "string", + }, + }, + ], + }, + "videoLocaleIds": { + "type": "any", + "whens": [ + { + "is": { + "allow": [ + { + "override": true, + }, + 11, + ], + "flags": { + "only": true, + "presence": "required", + }, + "type": "any", + }, + "ref": { + "path": [ + "challengeType", + ], + }, + "then": { + "keys": { + "espanol": { + "type": "string", + }, + "italian": { + "type": "string", + }, + "portuguese": { + "type": "string", + }, + }, + "type": "object", + }, + }, + ], + }, + "videoUrl": { + "allow": [ + "", + ], + "type": "string", + }, + }, + "type": "object", +} +`; diff --git a/curriculum/schema/challenge-schema.js b/curriculum/schema/challenge-schema.js index 4f287e9d29f..a7c849e690e 100644 --- a/curriculum/schema/challenge-schema.js +++ b/curriculum/schema/challenge-schema.js @@ -1,20 +1,20 @@ -const Joi = require('joi'); -Joi.objectId = require('joi-objectid')(Joi); +import Joi from 'joi'; +import joiObjectId from 'joi-objectid'; -const { - challengeTypes -} = require('@freecodecamp/shared/config/challenge-types'); -const { +Joi.objectId = joiObjectId(Joi); + +import { challengeTypes } from '@freecodecamp/shared/config/challenge-types'; +import { chapterBasedSuperBlocks, languageSuperBlocks, SuperBlocks -} = require('@freecodecamp/shared/config/curriculum'); -const { +} from '@freecodecamp/shared/config/curriculum'; +import { availableCharacters, availableBackgrounds, availableAudios, availableAlignments -} = require('./scene-assets'); +} from './scene-assets.js'; const slugRE = new RegExp('^[a-z0-9-]+$'); const slugWithSlashRE = new RegExp('^[a-z0-9-/]+$'); @@ -143,7 +143,7 @@ const quizJoi = Joi.object().keys({ .required() }); -const schema = Joi.object().keys({ +export const schema = Joi.object().keys({ block: Joi.string().regex(slugRE).required(), blockId: Joi.objectId(), blockLabel: Joi.when('superBlock', { @@ -395,6 +395,6 @@ const schema = Joi.object().keys({ usesMultifileEditor: Joi.boolean() }); -exports.challengeSchemaValidator = () => { +export const challengeSchemaValidator = () => { return challenge => schema.validate(challenge); }; diff --git a/curriculum/schema/challenge-schema.test.mjs b/curriculum/schema/challenge-schema.test.mjs new file mode 100644 index 00000000000..867ae768459 --- /dev/null +++ b/curriculum/schema/challenge-schema.test.mjs @@ -0,0 +1,9 @@ +import { describe, expect, it } from 'vitest'; + +import { schema } from './challenge-schema.js'; + +describe('challenge schema', () => { + it('should not be changed without informing the mobile team', () => { + expect(schema.describe()).toMatchSnapshot(); + }); +});