diff --git a/package-lock.json b/package-lock.json index 3821439..acd2f4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "scorm-again", - "version": "1.4.0", + "version": "1.4.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/Scorm2004API.js b/src/Scorm2004API.js index b548450..dd6194e 100644 --- a/src/Scorm2004API.js +++ b/src/Scorm2004API.js @@ -199,36 +199,14 @@ export default class Scorm2004API extends BaseAPI { this.throwSCORMError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { - const interaction_type = interaction.type; - const interaction_count = interaction.correct_responses._count; - if (interaction_type === 'choice') { - for (let i = 0; i < interaction_count && this.lastErrorCode === - 0; i++) { - const response = interaction.correct_responses.childArray[i]; - if (response.pattern === value) { - this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); - } - } - } + this.checkDuplicateChoiceResponse(interaction, value); - const response_type = correct_responses[interaction_type]; + const response_type = correct_responses[interaction.type]; if (response_type) { - let nodes = []; - if (response_type?.delimiter) { - nodes = String(value).split(response_type.delimiter); - } else { - nodes[0] = value; - } - - if (nodes.length > 0 && nodes.length <= response_type.max) { - this.checkCorrectResponseValue(interaction_type, nodes, value); - } else if (nodes.length > response_type.max) { - this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, - 'Data Model Element Pattern Too Long'); - } + this.checkValidResponseType(response_type, value, interaction.type); } else { this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, - 'Incorrect Response Type: ' + interaction_type); + 'Incorrect Response Type: ' + interaction.type); } } } @@ -252,6 +230,46 @@ export default class Scorm2004API extends BaseAPI { return newChild; } + /** + * Checks for valid response types + * @param {object} response_type + * @param {any} value + * @param {string} interaction_type + */ + checkValidResponseType(response_type, value, interaction_type) { + let nodes = []; + if (response_type?.delimiter) { + nodes = String(value).split(response_type.delimiter); + } else { + nodes[0] = value; + } + + if (nodes.length > 0 && nodes.length <= response_type.max) { + this.checkCorrectResponseValue(interaction_type, nodes, value); + } else if (nodes.length > response_type.max) { + this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, + 'Data Model Element Pattern Too Long'); + } + } + + /** + * Checks for duplicate 'choice' responses. + * @param {CMIInteractionsObject} interaction + * @param {any} value + */ + checkDuplicateChoiceResponse(interaction, value) { + const interaction_count = interaction.correct_responses._count; + if (interaction.type === 'choice') { + for (let i = 0; i < interaction_count && this.lastErrorCode === + 0; i++) { + const response = interaction.correct_responses.childArray[i]; + if (response.pattern === value) { + this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); + } + } + } + } + /** * Validate correct response. * @param {string} CMIElement @@ -263,33 +281,13 @@ export default class Scorm2004API extends BaseAPI { const pattern_index = Number(parts[4]); const interaction = this.cmi.interactions.childArray[index]; - const interaction_type = interaction.type; const interaction_count = interaction.correct_responses._count; - if (interaction_type === 'choice') { - for (let i = 0; i < interaction_count && this.lastErrorCode === 0; i++) { - const response = interaction.correct_responses.childArray[i]; - if (response.pattern === value) { - this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); - } - } - } + this.checkDuplicateChoiceResponse(interaction, value); - const response_type = correct_responses[interaction_type]; + const response_type = correct_responses[interaction.type]; if (typeof response_type.limit === 'undefined' || interaction_count <= response_type.limit) { - let nodes = []; - if (response_type?.delimiter) { - nodes = String(value).split(response_type.delimiter); - } else { - nodes[0] = value; - } - - if (nodes.length > 0 && nodes.length <= response_type.max) { - this.checkCorrectResponseValue(interaction_type, nodes, value); - } else if (nodes.length > response_type.max) { - this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, - 'Data Model Element Pattern Too Long'); - } + this.checkValidResponseType(response_type, value, interaction.type); if (this.lastErrorCode === 0 && (!response_type.duplicate || diff --git a/src/cmi/scorm2004_cmi.js b/src/cmi/scorm2004_cmi.js index 098fd53..c0bfa7c 100644 --- a/src/cmi/scorm2004_cmi.js +++ b/src/cmi/scorm2004_cmi.js @@ -819,7 +819,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} type */ set type(type) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -842,7 +842,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} timestamp */ set timestamp(timestamp) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -865,7 +865,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} weighting */ set weighting(weighting) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -889,8 +889,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} learner_response */ set learner_response(learner_response) { - if (this.initialized && (typeof this.type === 'undefined' || - typeof this.id === 'undefined')) { + if (this.initialized && (this.#type === '' || this.#id === '')) { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -975,7 +974,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} latency */ set latency(latency) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -998,7 +997,7 @@ export class CMIInteractionsObject extends BaseCMI { * @param {string} description */ set description(description) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -1104,7 +1103,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} success_status */ set success_status(success_status) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -1127,7 +1126,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} completion_status */ set completion_status(completion_status) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -1150,7 +1149,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} progress_measure */ set progress_measure(progress_measure) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { @@ -1175,7 +1174,7 @@ export class CMIObjectivesObject extends BaseCMI { * @param {string} description */ set description(description) { - if (this.initialized && typeof this.id === 'undefined') { + if (this.initialized && this.#id === '') { throw new ValidationError( scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); } else { diff --git a/test/Scorm2004API.spec.js b/test/Scorm2004API.spec.js index efa9de6..693c351 100644 --- a/test/Scorm2004API.spec.js +++ b/test/Scorm2004API.spec.js @@ -17,9 +17,7 @@ const api = (settings = {}, startingData = {}) => { }; const apiInitialized = (startingData) => { const API = api(); - if (startingData) { - API.startingData = startingData; - } + API.loadFromJSON(startingData, ''); API.lmsInitialize(); return API; }; @@ -299,61 +297,151 @@ describe('SCORM 2004 API Tests', () => { it('should allow cmi.interactions.0.correct_responses.0.pattern to be set - T/F', () => { const scorm2004API = apiInitialized(); + scorm2004API.setCMIValue('cmi.interactions.0.id', + 'Scene1_Slide3_MultiChoice_0_0'); scorm2004API.setCMIValue('cmi.interactions.0.type', 'true-false'); - scorm2004API.setCMIValue('cmi.interactions.0.correct_responses.0.pattern', 'true'); + scorm2004API.setCMIValue( + 'cmi.interactions.0.correct_responses.0.pattern', 'true'); expect( - String(scorm2004API.lmsGetLastError()) + String(scorm2004API.lmsGetLastError()), ).to.equal(String(0)); }); it('should allow cmi.interactions.10.correct_responses.0.pattern to be set - T/F', () => { const scorm2004API = apiInitialized(); + scorm2004API.setCMIValue('cmi.interactions.0.id', + 'Scene1_Slide3_MultiChoice_0_0'); scorm2004API.setCMIValue('cmi.interactions.0.type', 'true-false'); - scorm2004API.setCMIValue('cmi.interactions.0.correct_responses.0.pattern', 'true'); + scorm2004API.setCMIValue( + 'cmi.interactions.0.correct_responses.0.pattern', 'true'); expect( - String(scorm2004API.lmsGetLastError()) + String(scorm2004API.lmsGetLastError()), ).to.equal(String(0)); }); it('should allow cmi.interactions.0.correct_responses.0.pattern to be set - choice', () => { const scorm2004API = apiInitialized(); - scorm2004API.setCMIValue('cmi.interactions.0.id', 'Scene1_Slide3_MultiChoice_0_0'); + scorm2004API.setCMIValue('cmi.interactions.0.id', + 'Scene1_Slide3_MultiChoice_0_0'); scorm2004API.setCMIValue('cmi.interactions.0.type', 'choice'); - scorm2004API.setCMIValue('cmi.interactions.0.correct_responses.0.pattern', 'VP_on-call_or_President'); + scorm2004API.setCMIValue( + 'cmi.interactions.0.correct_responses.0.pattern', + 'VP_on-call_or_President'); expect( - String(scorm2004API.lmsGetLastError()) + String(scorm2004API.lmsGetLastError()), ).to.equal(String(0)); }); it('should allow cmi.interactions.0.objectives.0.id to be set', () => { const scorm2004API = apiInitialized(); - scorm2004API.setCMIValue('cmi.interactions.0.objectives.0.id', 'ID of the Obj - ID 2'); + scorm2004API.setCMIValue('cmi.interactions.0.objectives.0.id', + 'ID of the Obj - ID 2'); expect( - String(scorm2004API.lmsGetLastError()) + String(scorm2004API.lmsGetLastError()), ).to.equal(String(0)); }); it('should allow cmi.interactions.0.learner_response to be set', () => { const scorm2004API = apiInitialized(); - scorm2004API.setCMIValue('cmi.interactions.0.id', 'Scene1_Slide3_MultiChoice_0_0'); + scorm2004API.setCMIValue('cmi.interactions.0.id', + 'Scene1_Slide3_MultiChoice_0_0'); scorm2004API.setCMIValue('cmi.interactions.0.type', 'choice'); - scorm2004API.setCMIValue('cmi.interactions.0.learner_response', 'VP_on-call_or_President'); + scorm2004API.setCMIValue('cmi.interactions.0.learner_response', + 'VP_on-call_or_President'); expect( - String(scorm2004API.lmsGetLastError()) + String(scorm2004API.lmsGetLastError()), ).to.equal(String(0)); expect( - scorm2004API.getCMIValue('cmi.interactions.0.id') + scorm2004API.getCMIValue('cmi.interactions.0.id'), ).to.equal('Scene1_Slide3_MultiChoice_0_0'); expect( - scorm2004API.getCMIValue('cmi.interactions.0.type') + scorm2004API.getCMIValue('cmi.interactions.0.type'), ).to.equal('choice'); expect( - scorm2004API.getCMIValue('cmi.interactions.0.learner_response') + scorm2004API.getCMIValue('cmi.interactions.0.learner_response'), ).to.equal('VP_on-call_or_President'); }); }); describe('Initialized - Should Fail', () => { + h.checkLMSSetValue({ + api: apiInitialized( + {cmi: {interactions: {'0': {id: 'interaction-id-1'}}}}), + fieldName: 'cmi.interactions.0.type', + valueToTest: 'unknown', + errorThrown: false, + expectedError: scorm2004_error_codes.TYPE_MISMATCH, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.interactions.0.type', + valueToTest: 'true-false', + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.interactions.0.description', + valueToTest: 'this is an interaction', + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.interactions.0.timestamp', + valueToTest: 'PT1S', + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.interactions.0.weighting', + valueToTest: 1.0, + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.interactions.0.learner_response', + valueToTest: 'true', + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.interactions.0.latency', + valueToTest: 'PT1S', + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.objectives.0.success_status', + valueToTest: 'passed', + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.objectives.0.completion_status', + valueToTest: 'completed', + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.objectives.0.progress_measure', + valueToTest: 1.0, + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.objectives.0.description', + valueToTest: 'this is an objective', + errorThrown: false, + expectedError: scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED, + }); h.checkLMSSetValue({ api: apiInitialized(), fieldName: 'cmi.comments_from_lms.0.comment', @@ -375,6 +463,13 @@ describe('SCORM 2004 API Tests', () => { errorThrown: false, expectedError: scorm2004_error_codes.READ_ONLY_ELEMENT, }); + h.checkLMSSetValue({ + api: apiInitialized(), + fieldName: 'cmi.unknown', + valueToTest: 'uknown', + errorThrown: false, + expectedError: scorm2004_error_codes.UNDEFINED_DATA_MODEL, + }); }); });