Adding more API tests

This commit is contained in:
Jonathan Putney
2019-11-14 13:17:16 -05:00
parent 1636f8b443
commit 360d30bdf0
9 changed files with 449 additions and 111 deletions

View File

@@ -26,15 +26,16 @@ export default class AICC extends Scorm12API {
* *
* @param {string} CMIElement * @param {string} CMIElement
* @param {any} value * @param {any} value
* @param {boolean} foundFirstIndex
* @return {object} * @return {object}
*/ */
getChildElement(CMIElement, value) { getChildElement(CMIElement, value, foundFirstIndex) {
let newChild = super.getChildElement(CMIElement); let newChild = super.getChildElement(CMIElement, value, foundFirstIndex);
if (!newChild) { if (!newChild) {
if (this.stringContains(CMIElement, 'cmi.evaluation.comments')) { if (this.stringMatches(CMIElement, 'cmi\\.evaluation\\.comments\\.\\d')) {
newChild = new CMIEvaluationCommentsObject(this); newChild = new CMIEvaluationCommentsObject(this);
} else if (this.stringContains(CMIElement, 'cmi.student_data.tries')) { } else if (this.stringMatches(CMIElement, 'cmi\\.student_data\\.tries\\.\\d')) {
newChild = new CMITriesObject(this); newChild = new CMITriesObject(this);
} }
} }

View File

@@ -30,6 +30,9 @@ export default class BaseAPI {
* @param {object} error_codes * @param {object} error_codes
*/ */
constructor(error_codes) { constructor(error_codes) {
if (new.target === BaseAPI) {
throw new TypeError('Cannot construct BaseAPI instances directly');
}
this.currentState = api_constants.STATE_NOT_INITIALIZED; this.currentState = api_constants.STATE_NOT_INITIALIZED;
this.apiLogLevel = api_constants.LOG_LEVEL_ERROR; this.apiLogLevel = api_constants.LOG_LEVEL_ERROR;
this.lastErrorCode = 0; this.lastErrorCode = 0;
@@ -109,7 +112,7 @@ export default class BaseAPI {
callbackName: String, callbackName: String,
checkTerminated: boolean, checkTerminated: boolean,
CMIElement: String) { CMIElement: String) {
let returnValue = ''; let returnValue;
if (this.checkState(checkTerminated, if (this.checkState(checkTerminated,
this.#error_codes.RETRIEVE_BEFORE_INIT, this.#error_codes.RETRIEVE_BEFORE_INIT,
@@ -351,8 +354,22 @@ export default class BaseAPI {
* @param {string} tester String to check for * @param {string} tester String to check for
* @return {boolean} * @return {boolean}
*/ */
stringContains(str: String, tester: String) { stringMatches(str: String, tester: String) {
return str.indexOf(tester) > -1; return str && tester && str.match(tester);
}
/**
* Check to see if the specific object has the given property
* @param {*} refObject
* @param {string} attribute
* @return {boolean}
* @private
*/
_checkObjectHasProperty(refObject, attribute: String) {
return Object.hasOwnProperty.call(refObject, attribute) ||
Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(refObject), attribute) ||
(attribute in refObject);
} }
/** /**
@@ -362,9 +379,10 @@ export default class BaseAPI {
* @param {(string|number)} _errorNumber * @param {(string|number)} _errorNumber
* @param {boolean} _detail * @param {boolean} _detail
* @return {string} * @return {string}
* @abstract
*/ */
getLmsErrorMessageDetails(_errorNumber, _detail) { getLmsErrorMessageDetails(_errorNumber, _detail) {
return 'No error'; throw new Error('The getLmsErrorMessageDetails method has not been implemented');
} }
/** /**
@@ -373,9 +391,10 @@ export default class BaseAPI {
* *
* @param {string} _CMIElement * @param {string} _CMIElement
* @return {string} * @return {string}
* @abstract
*/ */
getCMIValue(_CMIElement) { getCMIValue(_CMIElement) {
return ''; throw new Error('The getCMIValue method has not been implemented');
} }
/** /**
@@ -385,9 +404,10 @@ export default class BaseAPI {
* @param {string} _CMIElement * @param {string} _CMIElement
* @param {any} _value * @param {any} _value
* @return {string} * @return {string}
* @abstract
*/ */
setCMIValue(_CMIElement, _value) { setCMIValue(_CMIElement, _value) {
return api_constants.SCORM_FALSE; throw new Error('The setCMIValue method has not been implemented');
} }
/** /**
@@ -408,6 +428,7 @@ export default class BaseAPI {
const structure = CMIElement.split('.'); const structure = CMIElement.split('.');
let refObject = this; let refObject = this;
let returnValue = api_constants.SCORM_FALSE; let returnValue = api_constants.SCORM_FALSE;
let foundFirstIndex = false;
const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`; const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
const invalidErrorCode = scorm2004 ? const invalidErrorCode = scorm2004 ?
@@ -421,12 +442,10 @@ export default class BaseAPI {
if (scorm2004 && (attribute.substr(0, 8) === '{target=') && if (scorm2004 && (attribute.substr(0, 8) === '{target=') &&
(typeof refObject._isTargetValid == 'function')) { (typeof refObject._isTargetValid == 'function')) {
this.throwSCORMError(this.#error_codes.READ_ONLY_ELEMENT); this.throwSCORMError(this.#error_codes.READ_ONLY_ELEMENT);
} else if (!Object.hasOwnProperty.call(refObject, attribute) && } else if (!this._checkObjectHasProperty(refObject, attribute)) {
!Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(refObject), attribute)) {
this.throwSCORMError(invalidErrorCode, invalidErrorMessage); this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
} else { } else {
if (this.stringContains(CMIElement, '.correct_responses')) { if (this.stringMatches(CMIElement, '.correct_responses')) {
this.validateCorrectResponse(CMIElement, value); this.validateCorrectResponse(CMIElement, value);
} }
@@ -452,7 +471,8 @@ export default class BaseAPI {
if (item) { if (item) {
refObject = item; refObject = item;
} else { } else {
const newChild = this.getChildElement(CMIElement, value); const newChild = this.getChildElement(CMIElement, value, foundFirstIndex);
foundFirstIndex = true;
if (!newChild) { if (!newChild) {
this.throwSCORMError(invalidErrorCode, invalidErrorMessage); this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
@@ -494,10 +514,12 @@ export default class BaseAPI {
* *
* @param {string} _CMIElement - unused * @param {string} _CMIElement - unused
* @param {*} _value - unused * @param {*} _value - unused
* @param {boolean} _foundFirstIndex - unused
* @return {*} * @return {*}
* @abstract
*/ */
getChildElement(_CMIElement, _value) { getChildElement(_CMIElement, _value, _foundFirstIndex) {
return null; throw new Error('The getChildElement method has not been implemented');
} }
/** /**
@@ -517,14 +539,20 @@ export default class BaseAPI {
let refObject = this; let refObject = this;
let attribute = null; let attribute = null;
const uninitializedErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) has not been initialized.`;
const invalidErrorMessage = `The data model element passed to ${methodName} (${CMIElement}) is not a valid SCORM data model element.`;
const invalidErrorCode = scorm2004 ?
this.#error_codes.UNDEFINED_DATA_MODEL :
this.#error_codes.GENERAL;
for (let i = 0; i < structure.length; i++) { for (let i = 0; i < structure.length; i++) {
attribute = structure[i]; attribute = structure[i];
if (!scorm2004) { if (!scorm2004) {
if (i === structure.length - 1) { if (i === structure.length - 1) {
if (!{}.hasOwnProperty.call(refObject, attribute)) { if (!this._checkObjectHasProperty(refObject, attribute)) {
this.throwSCORMError(101, this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
'getCMIValue did not find a value for: ' + CMIElement); return;
} }
} }
} else { } else {
@@ -533,15 +561,37 @@ export default class BaseAPI {
const target = String(attribute). const target = String(attribute).
substr(8, String(attribute).length - 9); substr(8, String(attribute).length - 9);
return refObject._isTargetValid(target); return refObject._isTargetValid(target);
} else if (!{}.hasOwnProperty.call(refObject, attribute)) { } else if (!this._checkObjectHasProperty(refObject, attribute)) {
this.throwSCORMError(401, this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
'The data model element passed to GetValue (' + CMIElement + return;
') is not a valid SCORM data model element.');
return '';
} }
} }
refObject = refObject[attribute]; refObject = refObject[attribute];
if (!refObject) {
this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
break;
}
if (refObject instanceof CMIArray) {
const index = parseInt(structure[i + 1], 10);
// SCO is trying to set an item on an array
if (!isNaN(index)) {
const item = refObject.childArray[index];
if (item) {
refObject = item;
} else {
this.throwSCORMError(this.#error_codes.VALUE_NOT_INITIALIZED,
uninitializedErrorMessage);
break;
}
// Have to update i value to skip the array position
i++;
}
}
} }
if (refObject === null || refObject === undefined) { if (refObject === null || refObject === undefined) {
@@ -552,7 +602,7 @@ export default class BaseAPI {
this.throwSCORMError(203); this.throwSCORMError(203);
} }
} }
return ''; return;
} else { } else {
return refObject; return refObject;
} }
@@ -657,7 +707,7 @@ export default class BaseAPI {
* @param {string} success * @param {string} success
*/ */
clearSCORMError(success: String) { clearSCORMError(success: String) {
if (success !== api_constants.SCORM_FALSE) { if (success !== undefined && success !== api_constants.SCORM_FALSE) {
this.lastErrorCode = 0; this.lastErrorCode = 0;
} }
} }
@@ -701,13 +751,24 @@ export default class BaseAPI {
* *
* @return {string} * @return {string}
*/ */
renderCMIToJSON() { renderCMIToJSONString() {
const cmi = this.cmi; const cmi = this.cmi;
// Do we want/need to return fields that have no set value? // Do we want/need to return fields that have no set value?
// return JSON.stringify({ cmi }, (k, v) => v === undefined ? null : v, 2); // return JSON.stringify({ cmi }, (k, v) => v === undefined ? null : v, 2);
return JSON.stringify({cmi}); return JSON.stringify({cmi});
} }
/**
* Returns a JS object representing the current cmi
* @return {object}
*/
renderCMIToJSONObject() {
const cmi = this.cmi;
// Do we want/need to return fields that have no set value?
// return JSON.stringify({ cmi }, (k, v) => v === undefined ? null : v, 2);
return JSON.parse(JSON.stringify(cmi));
}
/** /**
* Throws a SCORM error * Throws a SCORM error
* *

View File

@@ -42,6 +42,7 @@ export default class Scorm12API extends BaseAPI {
* @return {string} bool * @return {string} bool
*/ */
lmsInitialize() { lmsInitialize() {
this.cmi.initialize();
return this.initialize('LMSInitialize', 'LMS was already initialized!', return this.initialize('LMSInitialize', 'LMS was already initialized!',
'LMS is already finished!'); 'LMS is already finished!');
} }
@@ -140,18 +141,19 @@ export default class Scorm12API extends BaseAPI {
* *
* @param {string} CMIElement * @param {string} CMIElement
* @param {*} value * @param {*} value
* @param {boolean} foundFirstIndex
* @return {object} * @return {object}
*/ */
getChildElement(CMIElement, value) { getChildElement(CMIElement, value, foundFirstIndex) {
let newChild; let newChild;
if (this.stringContains(CMIElement, 'cmi.objectives')) { if (this.stringMatches(CMIElement, 'cmi\\.objectives\\.\\d')) {
newChild = new CMIObjectivesObject(this); newChild = new CMIObjectivesObject(this);
} else if (this.stringContains(CMIElement, '.correct_responses')) { } else if (foundFirstIndex && this.stringMatches(CMIElement, 'cmi\\.interactions\\.\\d\\.correct_responses\\.\\d')) {
newChild = new CMIInteractionsCorrectResponsesObject(this); newChild = new CMIInteractionsCorrectResponsesObject(this);
} else if (this.stringContains(CMIElement, '.objectives')) { } else if (foundFirstIndex && this.stringMatches(CMIElement, 'cmi\\.interactions\\.\\d\\.objectives\\.\\d')) {
newChild = new CMIInteractionsObjectivesObject(this); newChild = new CMIInteractionsObjectivesObject(this);
} else if (this.stringContains(CMIElement, 'cmi.interactions')) { } else if (this.stringMatches(CMIElement, 'cmi\\.interactions\\.\\d')) {
newChild = new CMIInteractionsObject(this); newChild = new CMIInteractionsObject(this);
} }

View File

@@ -57,6 +57,7 @@ export default class Scorm2004API extends BaseAPI {
* @return {string} bool * @return {string} bool
*/ */
lmsInitialize() { lmsInitialize() {
this.cmi.initialize();
return this.initialize('Initialize'); return this.initialize('Initialize');
} }
@@ -137,14 +138,15 @@ export default class Scorm2004API extends BaseAPI {
* *
* @param {string} CMIElement * @param {string} CMIElement
* @param {any} value * @param {any} value
* @param {boolean} foundFirstIndex
* @return {any} * @return {any}
*/ */
getChildElement(CMIElement, value) { getChildElement(CMIElement, value, foundFirstIndex) {
let newChild; let newChild;
if (this.stringContains(CMIElement, 'cmi.objectives')) { if (this.stringMatches(CMIElement, 'cmi\\.objectives\\.\\d')) {
newChild = new CMIObjectivesObject(this); newChild = new CMIObjectivesObject(this);
} else if (this.stringContains(CMIElement, '.correct_responses')) { } else if (foundFirstIndex && this.stringMatches(CMIElement, 'cmi\\.interactions\\.\\d\\.correct_responses\\.\\d')) {
const parts = CMIElement.split('.'); const parts = CMIElement.split('.');
const index = Number(parts[2]); const index = Number(parts[2]);
const interaction = this.cmi.interactions.childArray[index]; const interaction = this.cmi.interactions.childArray[index];
@@ -181,13 +183,13 @@ export default class Scorm2004API extends BaseAPI {
if (this.lastErrorCode === 0) { if (this.lastErrorCode === 0) {
newChild = new CMIInteractionsCorrectResponsesObject(this); newChild = new CMIInteractionsCorrectResponsesObject(this);
} }
} else if (this.stringContains(CMIElement, '.objectives')) { } else if (foundFirstIndex && this.stringMatches(CMIElement, 'cmi\\.interactions\\.\\d\\.objectives\\.\\d')) {
newChild = new CMIInteractionsObjectivesObject(this); newChild = new CMIInteractionsObjectivesObject(this);
} else if (this.stringContains(CMIElement, 'cmi.interactions')) { } else if (this.stringMatches(CMIElement, 'cmi\\.interactions\\.\\d')) {
newChild = new CMIInteractionsObject(this); newChild = new CMIInteractionsObject(this);
} else if (this.stringContains(CMIElement, 'cmi.comments_from_learner')) { } else if (this.stringMatches(CMIElement, 'cmi\\.comments_from_learner\\.\\d')) {
newChild = new CMICommentsFromLearnerObject(this); newChild = new CMICommentsFromLearnerObject(this);
} else if (this.stringContains(CMIElement, 'cmi.comments_from_lms')) { } else if (this.stringMatches(CMIElement, 'cmi\\.comments_from_lms\\.\\d')) {
newChild = new CMICommentsFromLMSObject(this); newChild = new CMICommentsFromLMSObject(this);
} }

View File

@@ -59,6 +59,15 @@ export class BaseCMI {
jsonString = false; jsonString = false;
#initialized = false; #initialized = false;
/**
* Constructor for BaseCMI, just marks the class as abstract
*/
constructor() {
if (new.target === BaseCMI) {
throw new TypeError('Cannot construct BaseCMI instances directly');
}
}
/** /**
* Getter for #initialized * Getter for #initialized
* @return {boolean} * @return {boolean}
@@ -101,22 +110,17 @@ export class CMIScore extends BaseCMI {
}) { }) {
super(); super();
this.#_children = score_children ? this.#_children = score_children ||
score_children :
scorm12_constants.score_children; scorm12_constants.score_children;
this.#_score_range = !score_range ? false : scorm12_regex.score_range; this.#_score_range = !score_range ? false : scorm12_regex.score_range;
this.#max = (max || max === '') ? max : '100'; this.#max = (max || max === '') ? max : '100';
this.#_invalid_error_code = invalidErrorCode ? this.#_invalid_error_code = invalidErrorCode ||
invalidErrorCode :
scorm12_error_codes.INVALID_SET_VALUE; scorm12_error_codes.INVALID_SET_VALUE;
this.#_invalid_type_code = invalidTypeCode ? this.#_invalid_type_code = invalidTypeCode ||
invalidTypeCode :
scorm12_error_codes.TYPE_MISMATCH; scorm12_error_codes.TYPE_MISMATCH;
this.#_invalid_range_code = invalidRangeCode ? this.#_invalid_range_code = invalidRangeCode ||
invalidRangeCode :
scorm12_error_codes.VALUE_OUT_OF_RANGE; scorm12_error_codes.VALUE_OUT_OF_RANGE;
this.#_decimal_regex = decimalRegex ? this.#_decimal_regex = decimalRegex ||
decimalRegex :
scorm12_regex.CMIDecimal; scorm12_regex.CMIDecimal;
} }
@@ -249,7 +253,6 @@ export class CMIArray extends BaseCMI {
/** /**
* Getter for _children * Getter for _children
* @return {*} * @return {*}
* @private
*/ */
get _children() { get _children() {
return this.#_children; return this.#_children;
@@ -258,7 +261,6 @@ export class CMIArray extends BaseCMI {
/** /**
* Setter for _children. Just throws an error. * Setter for _children. Just throws an error.
* @param {string} _children * @param {string} _children
* @private
*/ */
set _children(_children) { set _children(_children) {
throw new ValidationError(this.#errorCode); throw new ValidationError(this.#errorCode);
@@ -267,7 +269,6 @@ export class CMIArray extends BaseCMI {
/** /**
* Getter for _count * Getter for _count
* @return {number} * @return {number}
* @private
*/ */
get _count() { get _count() {
return this.childArray.length; return this.childArray.length;
@@ -276,7 +277,6 @@ export class CMIArray extends BaseCMI {
/** /**
* Setter for _count. Just throws an error. * Setter for _count. Just throws an error.
* @param {number} _count * @param {number} _count
* @private
*/ */
set _count(_count) { set _count(_count) {
throw new ValidationError(this.#errorCode); throw new ValidationError(this.#errorCode);

View File

@@ -39,26 +39,35 @@ function throwInvalidValueError() {
* Helper method, no reason to have to pass the same error codes every time * Helper method, no reason to have to pass the same error codes every time
* @param {*} value * @param {*} value
* @param {string} regexPattern * @param {string} regexPattern
* @param {boolean} allowEmptyString
* @return {boolean} * @return {boolean}
*/ */
export function check12ValidFormat(value: String, regexPattern: String) { export function check12ValidFormat(
value: String,
regexPattern: String,
allowEmptyString?: boolean) {
return checkValidFormat(value, regexPattern, return checkValidFormat(value, regexPattern,
scorm12_error_codes.TYPE_MISMATCH); scorm12_error_codes.TYPE_MISMATCH, allowEmptyString);
} }
/** /**
* Helper method, no reason to have to pass the same error codes every time * Helper method, no reason to have to pass the same error codes every time
* @param {*} value * @param {*} value
* @param {string} rangePattern * @param {string} rangePattern
* @param {boolean} allowEmptyString
* @return {boolean} * @return {boolean}
*/ */
export function check12ValidRange(value: any, rangePattern: String) { export function check12ValidRange(
value: any,
rangePattern: String,
allowEmptyString?: boolean) {
return checkValidRange(value, rangePattern, return checkValidRange(value, rangePattern,
scorm12_error_codes.VALUE_OUT_OF_RANGE); scorm12_error_codes.VALUE_OUT_OF_RANGE, allowEmptyString);
} }
/** /**
* Class representing the cmi object for SCORM 1.2 * Class representing the cmi object for SCORM 1.2
* @extends BaseCMI
*/ */
export class CMI extends BaseCMI { export class CMI extends BaseCMI {
#_children = ''; #_children = '';
@@ -138,7 +147,6 @@ export class CMI extends BaseCMI {
/** /**
* Getter for #_version * Getter for #_version
* @return {string} * @return {string}
* @private
*/ */
get _version() { get _version() {
return this.#_version; return this.#_version;
@@ -147,7 +155,6 @@ export class CMI extends BaseCMI {
/** /**
* Setter for #_version. Just throws an error. * Setter for #_version. Just throws an error.
* @param {string} _version * @param {string} _version
* @private
*/ */
set _version(_version) { set _version(_version) {
throwInvalidValueError(); throwInvalidValueError();
@@ -156,7 +163,6 @@ export class CMI extends BaseCMI {
/** /**
* Getter for #_children * Getter for #_children
* @return {string} * @return {string}
* @private
*/ */
get _children() { get _children() {
return this.#_children; return this.#_children;
@@ -165,7 +171,6 @@ export class CMI extends BaseCMI {
/** /**
* Setter for #_version. Just throws an error. * Setter for #_version. Just throws an error.
* @param {string} _children * @param {string} _children
* @private
*/ */
set _children(_children) { set _children(_children) {
throwInvalidValueError(); throwInvalidValueError();
@@ -244,6 +249,7 @@ export class CMI extends BaseCMI {
/** /**
* Class representing the cmi.core object * Class representing the cmi.core object
* @extends BaseCMI
*/ */
class CMICore extends BaseCMI { class CMICore extends BaseCMI {
/** /**
@@ -511,6 +517,7 @@ class CMICore extends BaseCMI {
/** /**
* Class representing SCORM 1.2's cmi.objectives object * Class representing SCORM 1.2's cmi.objectives object
* @extends CMIArray
*/ */
class CMIObjectives extends CMIArray { class CMIObjectives extends CMIArray {
/** /**
@@ -526,6 +533,7 @@ class CMIObjectives extends CMIArray {
/** /**
* Class representing SCORM 1.2's cmi.student_data object * Class representing SCORM 1.2's cmi.student_data object
* @extends BaseCMI
*/ */
export class CMIStudentData extends BaseCMI { export class CMIStudentData extends BaseCMI {
#_children; #_children;
@@ -642,6 +650,7 @@ export class CMIStudentData extends BaseCMI {
/** /**
* Class representing SCORM 1.2's cmi.student_preference object * Class representing SCORM 1.2's cmi.student_preference object
* @extends BaseCMI
*/ */
class CMIStudentPreference extends BaseCMI { class CMIStudentPreference extends BaseCMI {
/** /**
@@ -777,6 +786,7 @@ class CMIStudentPreference extends BaseCMI {
/** /**
* Class representing SCORM 1.2's cmi.interactions object * Class representing SCORM 1.2's cmi.interactions object
* @extends BaseCMI
*/ */
class CMIInteractions extends CMIArray { class CMIInteractions extends CMIArray {
/** /**
@@ -792,6 +802,7 @@ class CMIInteractions extends CMIArray {
/** /**
* Class representing SCORM 1.2's cmi.interactions.n object * Class representing SCORM 1.2's cmi.interactions.n object
* @extends BaseCMI
*/ */
export class CMIInteractionsObject extends BaseCMI { export class CMIInteractionsObject extends BaseCMI {
/** /**
@@ -915,7 +926,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} student_response * @param {string} student_response
*/ */
set student_response(student_response) { set student_response(student_response) {
if (check12ValidFormat(student_response, regex.CMIFeedback)) { if (check12ValidFormat(student_response, regex.CMIFeedback, true)) {
this.#student_response = student_response; this.#student_response = student_response;
} }
} }
@@ -993,6 +1004,7 @@ export class CMIInteractionsObject extends BaseCMI {
/** /**
* Class representing SCORM 1.2's cmi.objectives.n object * Class representing SCORM 1.2's cmi.objectives.n object
* @extends BaseCMI
*/ */
export class CMIObjectivesObject extends BaseCMI { export class CMIObjectivesObject extends BaseCMI {
/** /**
@@ -1074,6 +1086,7 @@ export class CMIObjectivesObject extends BaseCMI {
/** /**
* Class representing SCORM 1.2's cmi.interactions.n.objectives.n object * Class representing SCORM 1.2's cmi.interactions.n.objectives.n object
* @extends BaseCMI
*/ */
export class CMIInteractionsObjectivesObject extends BaseCMI { export class CMIInteractionsObjectivesObject extends BaseCMI {
/** /**
@@ -1123,6 +1136,7 @@ export class CMIInteractionsObjectivesObject extends BaseCMI {
/** /**
* Class representing SCORM 1.2's cmi.interactions.correct_responses.n object * Class representing SCORM 1.2's cmi.interactions.correct_responses.n object
* @extends BaseCMI
*/ */
export class CMIInteractionsCorrectResponsesObject extends BaseCMI { export class CMIInteractionsCorrectResponsesObject extends BaseCMI {
/** /**
@@ -1147,7 +1161,7 @@ export class CMIInteractionsCorrectResponsesObject extends BaseCMI {
* @param {string} pattern * @param {string} pattern
*/ */
set pattern(pattern) { set pattern(pattern) {
if (check12ValidFormat(pattern, regex.CMIFeedback)) { if (check12ValidFormat(pattern, regex.CMIFeedback, true)) {
this.#pattern = pattern; this.#pattern = pattern;
} }
} }

View File

@@ -15,14 +15,6 @@ export class ValidationError extends Error {
#errorCode; #errorCode;
/**
* Getter for #errorCode
* @return {number}
*/
get errorCode() {
return this.#errorCode;
}
/** /**
* Trying to override the default Error message * Trying to override the default Error message
* @return {string} * @return {string}

View File

@@ -4,44 +4,245 @@ import Scorm12API from '../src/Scorm12API';
import * as h from './api_helpers'; import * as h from './api_helpers';
import {scorm12_error_codes} from '../src/constants/error_codes'; import {scorm12_error_codes} from '../src/constants/error_codes';
const api = () => {
const API = new Scorm12API();
API.apiLogLevel = 1;
return API;
};
const apiInitialized = () => {
const API = api();
API.lmsInitialize();
return API;
};
describe('SCORM 1.2 API Tests', () => { describe('SCORM 1.2 API Tests', () => {
describe('Pre-Initialization', () => { describe('setCMIValue()', () => {
describe('LMSSetValue', () => { describe('Invalid Sets - Should Always Fail', () => {
const api = () => { h.checkSetCMIValue({
const API = new Scorm12API(); api: api(),
API.apiLogLevel = 1; fieldName: 'cmi._version',
return API; expectedError: scorm12_error_codes.INVALID_SET_VALUE,
};
const apiInitialized = () => {
const API = api();
API.lmsInitialize();
return API;
};
describe('Should throw errors', () => {
h.checkWrite({
api: api(),
fieldName: 'cmi.objectives.0.id',
expectedError: scorm12_error_codes.STORE_BEFORE_INIT,
});
h.checkWrite({
api: api(),
fieldName: 'cmi.interactions.0.id',
expectedError: scorm12_error_codes.STORE_BEFORE_INIT,
});
}); });
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi._children',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi.core._children',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi.core.score._children',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi.objectives._children',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi.objectives._count',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi.interactions._children',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi.interactions._count',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi.interactions.0.objectives._count',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
h.checkSetCMIValue({
api: api(),
fieldName: 'cmi.interactions.0.correct_responses._count',
expectedError: scorm12_error_codes.INVALID_SET_VALUE,
});
});
describe('Should succeed', () => { describe('Invalid Sets - Should Fail After Initialization', () => {
h.checkWrite({ h.checkSetCMIValue({
api: apiInitialized(), api: apiInitialized(),
fieldName: 'cmi.objectives.0.id', fieldName: 'cmi.launch_data',
valueToTest: 'AAA', expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
}); });
h.checkWrite({ h.checkSetCMIValue({
api: apiInitialized(), api: apiInitialized(),
fieldName: 'cmi.interactions.0.id', fieldName: 'cmi.comments_from_lms',
valueToTest: 'AAA', expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
}); });
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.core.student_id',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.core.student_name',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.core.credit',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.core.entry',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.core.total_time',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.core.lesson_mode',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.student_data.mastery_score',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.student_data.max_time_allowed',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
h.checkSetCMIValue({
api: apiInitialized(),
fieldName: 'cmi.student_data.time_limit_action',
expectedError: scorm12_error_codes.READ_ONLY_ELEMENT,
});
});
});
describe('LMSGetValue()', () => {
describe('Invalid Properties - Should Always Fail', () => {
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.core.close',
expectedError: scorm12_error_codes.GENERAL,
errorThrown: false,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.exit',
expectedError: scorm12_error_codes.GENERAL,
errorThrown: false,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.entry',
expectedError: scorm12_error_codes.GENERAL,
errorThrown: false,
});
});
describe('Write-Only Properties - Should Always Fail', () => {
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.core.exit',
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.core.session_time',
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.id',
initializeFirst: true,
initializationValue: 'AAA',
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.time',
initializeFirst: true,
initializationValue: '12:59:59',
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.type',
initializeFirst: true,
initializationValue: 'true-false',
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.weighting',
initializeFirst: true,
initializationValue: '0',
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.student_response',
initializeFirst: true,
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.result',
initializeFirst: true,
initializationValue: 'correct',
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.latency',
initializeFirst: true,
initializationValue: '01:59:59.99',
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
h.checkLMSGetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.correct_responses.0.pattern',
initializeFirst: true,
expectedError: scorm12_error_codes.WRITE_ONLY_ELEMENT,
});
});
});
describe('LMSSetValue()', () => {
describe('Uninitialized - Should Fail', () => {
h.checkLMSSetValue({
api: api(),
fieldName: 'cmi.objectives.0.id',
expectedError: scorm12_error_codes.STORE_BEFORE_INIT,
});
h.checkLMSSetValue({
api: api(),
fieldName: 'cmi.interactions.0.id',
expectedError: scorm12_error_codes.STORE_BEFORE_INIT,
});
});
describe('Initialized - Should Succeed', () => {
h.checkLMSSetValue({
api: apiInitialized(),
fieldName: 'cmi.objectives.0.id',
valueToTest: 'AAA',
});
h.checkLMSSetValue({
api: apiInitialized(),
fieldName: 'cmi.interactions.0.id',
valueToTest: 'AAA',
}); });
}); });
}); });

View File

@@ -1,18 +1,83 @@
import {describe, it} from 'mocha'; import {describe, it} from 'mocha';
import {expect} from 'chai'; import {expect} from 'chai';
export const checkWrite = ( export const checkLMSSetValue = (
{ {
api, api,
fieldName, fieldName,
valueToTest = 'xxx', valueToTest = 'xxx',
expectedError = 0, expectedError = 0,
errorThrown = false,
}) => { }) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
const status = expectedError > 0 ? 'fail to' : 'successfully'; const status = expectedError > 0 ? 'fail to' : 'successfully';
it(`Should ${status} write to ${fieldName}`, () => { it(`Should ${status} set value for ${fieldName}`, () => {
eval(`api.lmsSetValue('${fieldName}', '${valueToTest}')`); if (errorThrown) {
expect(String(api.lastErrorCode)).to.equal(String(expectedError)); expect(() => api.setValue(fieldName, valueToTest)).
to.throw(String(expectedError));
} else {
api.setValue(fieldName, valueToTest);
expect(String(api.lmsGetLastError())).to.equal(String(expectedError));
}
});
});
};
export const checkLMSGetValue = (
{
api,
fieldName,
expectedValue = '',
initializeFirst = false,
initializationValue = '',
expectedError = 0,
errorThrown = true,
}) => {
describe(`Field: ${fieldName}`, () => {
const status = expectedError > 0 ? 'fail to' : 'successfully';
if (initializeFirst) {
api.setCMIValue(fieldName, initializationValue);
}
it(`Should ${status} get value for ${fieldName}`, () => {
if (expectedError > 0) {
if (errorThrown) {
expect(() => api.lmsGetValue(fieldName)).
to.throw(String(expectedError));
} else {
api.lmsGetValue(fieldName);
expect(String(api.lmsGetLastError())).to.equal(String(expectedError));
}
} else {
expect(api.lmsGetValue(fieldName)).to.equal(expectedValue);
}
});
});
};
export const checkSetCMIValue = (
{
api,
fieldName,
valueToTest = 'xxx',
expectedError = 0,
errorThrown = true,
}) => {
describe(`Field: ${fieldName}`, () => {
const status = expectedError > 0 ? 'fail to' : 'successfully';
it(`Should ${status} set CMI value for ${fieldName}`, () => {
if (expectedError > 0) {
if (errorThrown) {
expect(() => api.setCMIValue(fieldName, valueToTest)).
to.throw(String(expectedError));
} else {
api.setCMIValue(fieldName, valueToTest);
expect(String(api.lmsGetLastError())).to.equal(String(expectedError));
}
} else {
expect(() => api.setCMIValue(fieldName, valueToTest)).to.not.throw();
}
}); });
}); });
}; };