Removed circular dependencies between APIs and CMI objects

No longer directly setting error codes on the API when a read/write or data validation fails, throwing exceptions instead that can then be caught by the APi.
This commit is contained in:
Jonathan Putney
2019-11-12 11:17:41 -05:00
parent 73ff260097
commit 9e8896fa32
10 changed files with 1114 additions and 868 deletions

View File

@@ -14,8 +14,6 @@ const api_constants = {
LOG_LEVEL_NONE: 5, LOG_LEVEL_NONE: 5,
}; };
let _self;
/** /**
* Base API class for AICC, SCORM 1.2, and SCORM 2004. Should be considered * Base API class for AICC, SCORM 1.2, and SCORM 2004. Should be considered
* abstract, and never initialized on it's own. * abstract, and never initialized on it's own.
@@ -31,14 +29,13 @@ export default class BaseAPI {
* @param {object} error_codes * @param {object} error_codes
*/ */
constructor(error_codes) { constructor(error_codes) {
_self = this; this.currentState = api_constants.STATE_NOT_INITIALIZED;
_self.currentState = api_constants.STATE_NOT_INITIALIZED; this.apiLogLevel = api_constants.LOG_LEVEL_ERROR;
_self.apiLogLevel = api_constants.LOG_LEVEL_ERROR; this.lastErrorCode = 0;
_self.lastErrorCode = 0; this.listenerArray = [];
_self.listenerArray = [];
_self.#timeout = null; this.#timeout = null;
_self.#error_codes = error_codes; this.#error_codes = error_codes;
} }
/** /**
@@ -54,20 +51,20 @@ export default class BaseAPI {
terminationMessage?: String) { terminationMessage?: String) {
let returnValue = api_constants.SCORM_FALSE; let returnValue = api_constants.SCORM_FALSE;
if (_self.isInitialized()) { if (this.isInitialized()) {
_self.throwSCORMError(_self.#error_codes.INITIALIZED, initializeMessage); this.throwSCORMError(this.#error_codes.INITIALIZED, initializeMessage);
} else if (_self.isTerminated()) { } else if (this.isTerminated()) {
_self.throwSCORMError(_self.#error_codes.TERMINATED, terminationMessage); this.throwSCORMError(this.#error_codes.TERMINATED, terminationMessage);
} else { } else {
_self.currentState = api_constants.STATE_INITIALIZED; this.currentState = api_constants.STATE_INITIALIZED;
_self.lastErrorCode = 0; this.lastErrorCode = 0;
returnValue = api_constants.SCORM_TRUE; returnValue = api_constants.SCORM_TRUE;
_self.processListeners(callbackName); this.processListeners(callbackName);
} }
_self.apiLog(callbackName, null, 'returned: ' + returnValue, this.apiLog(callbackName, null, 'returned: ' + returnValue,
api_constants.LOG_LEVEL_INFO); api_constants.LOG_LEVEL_INFO);
_self.clearSCORMError(returnValue); this.clearSCORMError(returnValue);
return returnValue; return returnValue;
} }
@@ -83,18 +80,18 @@ export default class BaseAPI {
checkTerminated: boolean) { checkTerminated: boolean) {
let returnValue = api_constants.SCORM_FALSE; let returnValue = api_constants.SCORM_FALSE;
if (_self.checkState(checkTerminated, if (this.checkState(checkTerminated,
_self.#error_codes.TERMINATION_BEFORE_INIT, this.#error_codes.TERMINATION_BEFORE_INIT,
_self.#error_codes.MULTIPLE_TERMINATION)) { this.#error_codes.MULTIPLE_TERMINATION)) {
if (checkTerminated) _self.lastErrorCode = 0; if (checkTerminated) this.lastErrorCode = 0;
_self.currentState = api_constants.STATE_TERMINATED; this.currentState = api_constants.STATE_TERMINATED;
returnValue = api_constants.SCORM_TRUE; returnValue = api_constants.SCORM_TRUE;
_self.processListeners(callbackName); this.processListeners(callbackName);
} }
_self.apiLog(callbackName, null, 'returned: ' + returnValue, this.apiLog(callbackName, null, 'returned: ' + returnValue,
api_constants.LOG_LEVEL_INFO); api_constants.LOG_LEVEL_INFO);
_self.clearSCORMError(returnValue); this.clearSCORMError(returnValue);
return returnValue; return returnValue;
} }
@@ -113,17 +110,17 @@ export default class BaseAPI {
CMIElement: String) { CMIElement: String) {
let returnValue = ''; let returnValue = '';
if (_self.checkState(checkTerminated, if (this.checkState(checkTerminated,
_self.#error_codes.RETRIEVE_BEFORE_INIT, this.#error_codes.RETRIEVE_BEFORE_INIT,
_self.#error_codes.RETRIEVE_AFTER_TERM)) { this.#error_codes.RETRIEVE_AFTER_TERM)) {
if (checkTerminated) _self.lastErrorCode = 0; if (checkTerminated) this.lastErrorCode = 0;
returnValue = _self.getCMIValue(CMIElement); returnValue = this.getCMIValue(CMIElement);
_self.processListeners(callbackName, CMIElement); this.processListeners(callbackName, CMIElement);
} }
_self.apiLog(callbackName, CMIElement, ': returned: ' + returnValue, this.apiLog(callbackName, CMIElement, ': returned: ' + returnValue,
api_constants.LOG_LEVEL_INFO); api_constants.LOG_LEVEL_INFO);
_self.clearSCORMError(returnValue); this.clearSCORMError(returnValue);
return returnValue; return returnValue;
} }
@@ -144,17 +141,17 @@ export default class BaseAPI {
value) { value) {
let returnValue = ''; let returnValue = '';
if (_self.checkState(checkTerminated, _self.#error_codes.STORE_BEFORE_INIT, if (this.checkState(checkTerminated, this.#error_codes.STORE_BEFORE_INIT,
_self.#error_codes.STORE_AFTER_TERM)) { this.#error_codes.STORE_AFTER_TERM)) {
if (checkTerminated) _self.lastErrorCode = 0; if (checkTerminated) this.lastErrorCode = 0;
returnValue = _self.setCMIValue(CMIElement, value); returnValue = this.setCMIValue(CMIElement, value);
_self.processListeners(callbackName, CMIElement, value); this.processListeners(callbackName, CMIElement, value);
} }
_self.apiLog(callbackName, CMIElement, this.apiLog(callbackName, CMIElement,
': ' + value + ': result: ' + returnValue, ': ' + value + ': result: ' + returnValue,
api_constants.LOG_LEVEL_INFO); api_constants.LOG_LEVEL_INFO);
_self.clearSCORMError(returnValue); this.clearSCORMError(returnValue);
return returnValue; return returnValue;
} }
@@ -170,16 +167,16 @@ export default class BaseAPI {
checkTerminated: boolean) { checkTerminated: boolean) {
let returnValue = api_constants.SCORM_FALSE; let returnValue = api_constants.SCORM_FALSE;
if (_self.checkState(checkTerminated, _self.#error_codes.COMMIT_BEFORE_INIT, if (this.checkState(checkTerminated, this.#error_codes.COMMIT_BEFORE_INIT,
_self.#error_codes.COMMIT_AFTER_TERM)) { this.#error_codes.COMMIT_AFTER_TERM)) {
if (checkTerminated) _self.lastErrorCode = 0; if (checkTerminated) this.lastErrorCode = 0;
returnValue = api_constants.SCORM_TRUE; returnValue = api_constants.SCORM_TRUE;
_self.processListeners(callbackName); this.processListeners(callbackName);
} }
_self.apiLog(callbackName, null, 'returned: ' + returnValue, this.apiLog(callbackName, null, 'returned: ' + returnValue,
api_constants.LOG_LEVEL_INFO); api_constants.LOG_LEVEL_INFO);
_self.clearSCORMError(returnValue); this.clearSCORMError(returnValue);
return returnValue; return returnValue;
} }
@@ -190,11 +187,11 @@ export default class BaseAPI {
* @return {string} * @return {string}
*/ */
getLastError(callbackName: String) { getLastError(callbackName: String) {
const returnValue = String(_self.lastErrorCode); const returnValue = String(this.lastErrorCode);
_self.processListeners(callbackName); this.processListeners(callbackName);
_self.apiLog(callbackName, null, 'returned: ' + returnValue, this.apiLog(callbackName, null, 'returned: ' + returnValue,
api_constants.LOG_LEVEL_INFO); api_constants.LOG_LEVEL_INFO);
return returnValue; return returnValue;
@@ -211,11 +208,11 @@ export default class BaseAPI {
let returnValue = ''; let returnValue = '';
if (CMIErrorCode !== null && CMIErrorCode !== '') { if (CMIErrorCode !== null && CMIErrorCode !== '') {
returnValue = _self.getLmsErrorMessageDetails(CMIErrorCode); returnValue = this.getLmsErrorMessageDetails(CMIErrorCode);
_self.processListeners(callbackName); this.processListeners(callbackName);
} }
_self.apiLog(callbackName, null, 'returned: ' + returnValue, this.apiLog(callbackName, null, 'returned: ' + returnValue,
api_constants.LOG_LEVEL_INFO); api_constants.LOG_LEVEL_INFO);
return returnValue; return returnValue;
@@ -232,11 +229,11 @@ export default class BaseAPI {
let returnValue = ''; let returnValue = '';
if (CMIErrorCode !== null && CMIErrorCode !== '') { if (CMIErrorCode !== null && CMIErrorCode !== '') {
returnValue = _self.getLmsErrorMessageDetails(CMIErrorCode, true); returnValue = this.getLmsErrorMessageDetails(CMIErrorCode, true);
_self.processListeners(callbackName); this.processListeners(callbackName);
} }
_self.apiLog(callbackName, null, 'returned: ' + returnValue, this.apiLog(callbackName, null, 'returned: ' + returnValue,
api_constants.LOG_LEVEL_INFO); api_constants.LOG_LEVEL_INFO);
return returnValue; return returnValue;
@@ -254,11 +251,11 @@ export default class BaseAPI {
checkTerminated: boolean, checkTerminated: boolean,
beforeInitError: number, beforeInitError: number,
afterTermError?: number) { afterTermError?: number) {
if (_self.isNotInitialized()) { if (this.isNotInitialized()) {
_self.throwSCORMError(beforeInitError); this.throwSCORMError(beforeInitError);
return false; return false;
} else if (checkTerminated && _self.isTerminated()) { } else if (checkTerminated && this.isTerminated()) {
_self.throwSCORMError(afterTermError); this.throwSCORMError(afterTermError);
return false; return false;
} }
@@ -278,9 +275,9 @@ export default class BaseAPI {
CMIElement: String, CMIElement: String,
logMessage: String, logMessage: String,
messageLevel: number) { messageLevel: number) {
logMessage = _self.formatMessage(functionName, CMIElement, logMessage); logMessage = this.formatMessage(functionName, CMIElement, logMessage);
if (messageLevel >= _self.apiLogLevel) { if (messageLevel >= this.apiLogLevel) {
switch (messageLevel) { switch (messageLevel) {
case api_constants.LOG_LEVEL_ERROR: case api_constants.LOG_LEVEL_ERROR:
console.error(logMessage); console.error(logMessage);
@@ -402,8 +399,8 @@ export default class BaseAPI {
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 ?
_self.#error_codes.UNDEFINED_DATA_MODEL : this.#error_codes.UNDEFINED_DATA_MODEL :
_self.#error_codes.GENERAL; this.#error_codes.GENERAL;
for (let i = 0; i < structure.length; i++) { for (let i = 0; i < structure.length; i++) {
const attribute = structure[i]; const attribute = structure[i];
@@ -411,15 +408,15 @@ export default class BaseAPI {
if (i === structure.length - 1) { if (i === structure.length - 1) {
if (scorm2004 && (attribute.substr(0, 8) === '{target=') && if (scorm2004 && (attribute.substr(0, 8) === '{target=') &&
(typeof refObject._isTargetValid == 'function')) { (typeof refObject._isTargetValid == 'function')) {
_self.throwSCORMError(_self.#error_codes.READ_ONLY_ELEMENT); this.throwSCORMError(this.#error_codes.READ_ONLY_ELEMENT);
} else if (!{}.hasOwnProperty.call(refObject, attribute)) { } else if (!{}.hasOwnProperty.call(refObject, attribute)) {
_self.throwSCORMError(invalidErrorCode, invalidErrorMessage); this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
} else { } else {
if (_self.stringContains(CMIElement, '.correct_responses')) { if (this.stringContains(CMIElement, '.correct_responses')) {
_self.validateCorrectResponse(CMIElement, value); this.validateCorrectResponse(CMIElement, value);
} }
if (!scorm2004 || _self.lastErrorCode === 0) { if (!scorm2004 || this.lastErrorCode === 0) {
refObject[attribute] = value; refObject[attribute] = value;
returnValue = api_constants.SCORM_TRUE; returnValue = api_constants.SCORM_TRUE;
} }
@@ -427,7 +424,7 @@ export default class BaseAPI {
} else { } else {
refObject = refObject[attribute]; refObject = refObject[attribute];
if (!refObject) { if (!refObject) {
_self.throwSCORMError(invalidErrorCode, invalidErrorMessage); this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
break; break;
} }
@@ -441,10 +438,10 @@ export default class BaseAPI {
if (item) { if (item) {
refObject = item; refObject = item;
} else { } else {
const newChild = _self.getChildElement(CMIElement, value); const newChild = this.getChildElement(CMIElement, value);
if (!newChild) { if (!newChild) {
_self.throwSCORMError(invalidErrorCode, invalidErrorMessage); this.throwSCORMError(invalidErrorCode, invalidErrorMessage);
} else { } else {
refObject.childArray.push(newChild); refObject.childArray.push(newChild);
refObject = newChild; refObject = newChild;
@@ -459,7 +456,7 @@ export default class BaseAPI {
} }
if (returnValue === api_constants.SCORM_FALSE) { if (returnValue === api_constants.SCORM_FALSE) {
_self.apiLog(methodName, null, this.apiLog(methodName, null,
`There was an error setting the value for: ${CMIElement}, value of: ${value}`, `There was an error setting the value for: ${CMIElement}, value of: ${value}`,
api_constants.LOG_LEVEL_WARNING); api_constants.LOG_LEVEL_WARNING);
} }
@@ -512,7 +509,7 @@ export default class BaseAPI {
if (!scorm2004) { if (!scorm2004) {
if (i === structure.length - 1) { if (i === structure.length - 1) {
if (!{}.hasOwnProperty.call(refObject, attribute)) { if (!{}.hasOwnProperty.call(refObject, attribute)) {
_self.throwSCORMError(101, this.throwSCORMError(101,
'getCMIValue did not find a value for: ' + CMIElement); 'getCMIValue did not find a value for: ' + CMIElement);
} }
} }
@@ -523,7 +520,7 @@ export default class BaseAPI {
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 (!{}.hasOwnProperty.call(refObject, attribute)) {
_self.throwSCORMError(401, this.throwSCORMError(401,
'The data model element passed to GetValue (' + CMIElement + 'The data model element passed to GetValue (' + CMIElement +
') is not a valid SCORM data model element.'); ') is not a valid SCORM data model element.');
return ''; return '';
@@ -536,9 +533,9 @@ export default class BaseAPI {
if (refObject === null || refObject === undefined) { if (refObject === null || refObject === undefined) {
if (!scorm2004) { if (!scorm2004) {
if (attribute === '_children') { if (attribute === '_children') {
_self.throwSCORMError(202); this.throwSCORMError(202);
} else if (attribute === '_count') { } else if (attribute === '_count') {
_self.throwSCORMError(203); this.throwSCORMError(203);
} }
} }
return ''; return '';
@@ -553,7 +550,7 @@ export default class BaseAPI {
* @return {boolean} * @return {boolean}
*/ */
isInitialized() { isInitialized() {
return _self.currentState === api_constants.STATE_INITIALIZED; return this.currentState === api_constants.STATE_INITIALIZED;
} }
/** /**
@@ -562,7 +559,7 @@ export default class BaseAPI {
* @return {boolean} * @return {boolean}
*/ */
isNotInitialized() { isNotInitialized() {
return _self.currentState === api_constants.STATE_NOT_INITIALIZED; return this.currentState === api_constants.STATE_NOT_INITIALIZED;
} }
/** /**
@@ -571,7 +568,7 @@ export default class BaseAPI {
* @return {boolean} * @return {boolean}
*/ */
isTerminated() { isTerminated() {
return _self.currentState === api_constants.STATE_TERMINATED; return this.currentState === api_constants.STATE_TERMINATED;
} }
/** /**
@@ -595,7 +592,7 @@ export default class BaseAPI {
CMIElement = listenerName.replace(functionName + '.', ''); CMIElement = listenerName.replace(functionName + '.', '');
} }
_self.listenerArray.push({ this.listenerArray.push({
functionName: functionName, functionName: functionName,
CMIElement: CMIElement, CMIElement: CMIElement,
callback: callback, callback: callback,
@@ -611,8 +608,8 @@ export default class BaseAPI {
* @param {*} value * @param {*} value
*/ */
processListeners(functionName: String, CMIElement: String, value: any) { processListeners(functionName: String, CMIElement: String, value: any) {
for (let i = 0; i < _self.listenerArray.length; i++) { for (let i = 0; i < this.listenerArray.length; i++) {
const listener = _self.listenerArray[i]; const listener = this.listenerArray[i];
const functionsMatch = listener.functionName === functionName; const functionsMatch = listener.functionName === functionName;
const listenerHasCMIElement = !!listener.CMIElement; const listenerHasCMIElement = !!listener.CMIElement;
const CMIElementsMatch = listener.CMIElement === CMIElement; const CMIElementsMatch = listener.CMIElement === CMIElement;
@@ -631,13 +628,13 @@ export default class BaseAPI {
*/ */
throwSCORMError(errorNumber: number, message: String) { throwSCORMError(errorNumber: number, message: String) {
if (!message) { if (!message) {
message = _self.getLmsErrorMessageDetails(errorNumber); message = this.getLmsErrorMessageDetails(errorNumber);
} }
_self.apiLog('throwSCORMError', null, errorNumber + ': ' + message, this.apiLog('throwSCORMError', null, errorNumber + ': ' + message,
api_constants.LOG_LEVEL_ERROR); api_constants.LOG_LEVEL_ERROR);
_self.lastErrorCode = String(errorNumber); this.lastErrorCode = String(errorNumber);
} }
/** /**
@@ -647,7 +644,7 @@ export default class BaseAPI {
*/ */
clearSCORMError(success: String) { clearSCORMError(success: String) {
if (success !== api_constants.SCORM_FALSE) { if (success !== api_constants.SCORM_FALSE) {
_self.lastErrorCode = 0; this.lastErrorCode = 0;
} }
} }
@@ -658,7 +655,7 @@ export default class BaseAPI {
* @param {string} CMIElement * @param {string} CMIElement
*/ */
loadFromJSON(json, CMIElement) { loadFromJSON(json, CMIElement) {
if (!_self.isNotInitialized()) { if (!this.isNotInitialized()) {
console.error( console.error(
'loadFromJSON can only be called before the call to lmsInitialize.'); 'loadFromJSON can only be called before the call to lmsInitialize.');
return; return;
@@ -673,13 +670,13 @@ export default class BaseAPI {
if (value['childArray']) { if (value['childArray']) {
for (let i = 0; i < value['childArray'].length; i++) { for (let i = 0; i < value['childArray'].length; i++) {
_self.loadFromJSON(value['childArray'][i], this.loadFromJSON(value['childArray'][i],
currentCMIElement + '.' + i); currentCMIElement + '.' + i);
} }
} else if (value.constructor === Object) { } else if (value.constructor === Object) {
_self.loadFromJSON(value, currentCMIElement); this.loadFromJSON(value, currentCMIElement);
} else { } else {
_self.setCMIValue(currentCMIElement, value); this.setCMIValue(currentCMIElement, value);
} }
} }
} }
@@ -691,68 +688,28 @@ export default class BaseAPI {
* @return {string} * @return {string}
*/ */
renderCMIToJSON() { renderCMIToJSON() {
const cmi = _self.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});
} }
/**
* Check if the value matches the proper format. If not, throw proper error code.
*
* @param {string} value
* @param {string} regexPattern
* @return {boolean}
*/
checkValidFormat(value: String, regexPattern: String) {
const formatRegex = new RegExp(regexPattern);
if (!value || !value.match(formatRegex)) {
_self.throwSCORMError(_self.#error_codes.TYPE_MISMATCH);
return false;
}
return true;
}
/**
* Check if the value matches the proper range. If not, throw proper error code.
*
* @param {*} value
* @param {string} rangePattern
* @return {boolean}
*/
checkValidRange(value: any, rangePattern: String) {
const ranges = rangePattern.split('#');
value = value * 1.0;
if (value >= ranges[0]) {
if ((ranges[1] === '*') || (value <= ranges[1])) {
_self.clearSCORMError(api_constants.SCORM_TRUE);
return true;
} else {
_self.throwSCORMError(_self.#error_codes.VALUE_OUT_OF_RANGE);
return false;
}
} else {
_self.throwSCORMError(_self.#error_codes.VALUE_OUT_OF_RANGE);
return false;
}
}
/** /**
* Throws a SCORM error * Throws a SCORM error
* *
* @param {number} when - the number of milliseconds to wait before committing * @param {number} when - the number of milliseconds to wait before committing
*/ */
scheduleCommit(when: number) { scheduleCommit(when: number) {
_self.#timeout = new ScheduledCommit(this, when); this.#timeout = new ScheduledCommit(this, when);
} }
/** /**
* Clears and cancels any currently scheduled commits * Clears and cancels any currently scheduled commits
*/ */
clearScheduledCommit() { clearScheduledCommit() {
if (_self.#timeout) { if (this.#timeout) {
_self.#timeout.cancel(); this.#timeout.cancel();
_self.#timeout = null; this.#timeout = null;
} }
} }
} }
@@ -771,26 +728,26 @@ class ScheduledCommit {
* @param {number} when * @param {number} when
*/ */
constructor(API: any, when: number) { constructor(API: any, when: number) {
_self.#API = API; this.#API = API;
_self.#timeout = setTimeout(_self.#wrapper, when); this.#timeout = setTimeout(this.#wrapper, when);
} }
/** /**
* Cancel any currently scheduled commit * Cancel any currently scheduled commit
*/ */
cancel() { cancel() {
_self.#cancelled = true; this.#cancelled = true;
if (_self.#timeout) { if (this.#timeout) {
clearTimeout(_self.#timeout); clearTimeout(this.#timeout);
} }
} }
/** /**
* Wrap the API commit call to check if the call has already been cancelled * Wrap the API commit call to check if the call has already been cancelled
*/ */
#wrapper = () => { #wrapper() {
if (!_self.#cancelled) { if (!this.#cancelled) {
_self.#API.commit(); this.#API.commit();
} }
}; }
} }

View File

@@ -18,12 +18,11 @@ import {valid_languages} from './constants/language_constants';
import {scorm2004_regex} from './regex'; import {scorm2004_regex} from './regex';
const constants = scorm2004_constants; const constants = scorm2004_constants;
let _self;
/** /**
* API class for SCORM 2004 * API class for SCORM 2004
*/ */
class Scorm2004API extends BaseAPI { export default class Scorm2004API extends BaseAPI {
#version: '1.0'; #version: '1.0';
/** /**
@@ -32,19 +31,18 @@ class Scorm2004API extends BaseAPI {
constructor() { constructor() {
super(scorm2004_error_codes); super(scorm2004_error_codes);
_self = this; this.cmi = new CMI(this);
_self.cmi = new CMI(_self); this.adl = new ADL(this);
_self.adl = new ADL(_self);
// Rename functions to match 2004 Spec and expose to modules // Rename functions to match 2004 Spec and expose to modules
_self.Initialize = _self.lmsInitialize; this.Initialize = this.lmsInitialize;
_self.Terminate = _self.lmsTerminate; this.Terminate = this.lmsTerminate;
_self.GetValue = _self.lmsGetValue; this.GetValue = this.lmsGetValue;
_self.SetValue = _self.lmsSetValue; this.SetValue = this.lmsSetValue;
_self.Commit = _self.lmsCommit; this.Commit = this.lmsCommit;
_self.GetLastError = _self.lmsGetLastError; this.GetLastError = this.lmsGetLastError;
_self.GetErrorString = _self.lmsGetErrorString; this.GetErrorString = this.lmsGetErrorString;
_self.GetDiagnostic = _self.lmsGetDiagnostic; this.GetDiagnostic = this.lmsGetDiagnostic;
} }
/** /**
@@ -59,14 +57,14 @@ class Scorm2004API extends BaseAPI {
* @return {string} bool * @return {string} bool
*/ */
lmsInitialize() { lmsInitialize() {
return _self.initialize('Initialize'); return this.initialize('Initialize');
} }
/** /**
* @return {string} bool * @return {string} bool
*/ */
lmsTerminate() { lmsTerminate() {
return _self.terminate('Terminate', true); return this.terminate('Terminate', true);
} }
/** /**
@@ -74,7 +72,7 @@ class Scorm2004API extends BaseAPI {
* @return {string} * @return {string}
*/ */
lmsGetValue(CMIElement) { lmsGetValue(CMIElement) {
return _self.getValue('GetValue', true, CMIElement); return this.getValue('GetValue', true, CMIElement);
} }
/** /**
@@ -83,7 +81,7 @@ class Scorm2004API extends BaseAPI {
* @return {string} * @return {string}
*/ */
lmsSetValue(CMIElement, value) { lmsSetValue(CMIElement, value) {
return _self.setValue('SetValue', true, CMIElement, value); return this.setValue('SetValue', true, CMIElement, value);
} }
/** /**
@@ -92,7 +90,7 @@ class Scorm2004API extends BaseAPI {
* @return {string} bool * @return {string} bool
*/ */
lmsCommit() { lmsCommit() {
return _self.commit('Commit'); return this.commit('Commit');
} }
/** /**
@@ -101,7 +99,7 @@ class Scorm2004API extends BaseAPI {
* @return {string} * @return {string}
*/ */
lmsGetLastError() { lmsGetLastError() {
return _self.getLastError('GetLastError'); return this.getLastError('GetLastError');
} }
/** /**
@@ -111,7 +109,7 @@ class Scorm2004API extends BaseAPI {
* @return {string} * @return {string}
*/ */
lmsGetErrorString(CMIErrorCode) { lmsGetErrorString(CMIErrorCode) {
return _self.getErrorString('GetErrorString', CMIErrorCode); return this.getErrorString('GetErrorString', CMIErrorCode);
} }
/** /**
@@ -121,7 +119,7 @@ class Scorm2004API extends BaseAPI {
* @return {string} * @return {string}
*/ */
lmsGetDiagnostic(CMIErrorCode) { lmsGetDiagnostic(CMIErrorCode) {
return _self.getDiagnostic('GetDiagnostic', CMIErrorCode); return this.getDiagnostic('GetDiagnostic', CMIErrorCode);
} }
/** /**
@@ -131,7 +129,7 @@ class Scorm2004API extends BaseAPI {
* @param {any} value * @param {any} value
*/ */
setCMIValue(CMIElement, value) { setCMIValue(CMIElement, value) {
_self._commonSetCMIValue('SetValue', true, CMIElement, value); this._commonSetCMIValue('SetValue', true, CMIElement, value);
} }
/** /**
@@ -144,23 +142,23 @@ class Scorm2004API extends BaseAPI {
getChildElement(CMIElement, value) { getChildElement(CMIElement, value) {
let newChild; let newChild;
if (_self.stringContains(CMIElement, 'cmi.objectives')) { if (this.stringContains(CMIElement, 'cmi.objectives')) {
newChild = new CMIObjectivesObject(this); newChild = new CMIObjectivesObject(this);
} else if (_self.stringContains(CMIElement, '.correct_responses')) { } else if (this.stringContains(CMIElement, '.correct_responses')) {
const parts = CMIElement.split('.'); const parts = CMIElement.split('.');
const index = Number(parts[2]); const index = Number(parts[2]);
const interaction = _self.cmi.interactions.childArray[index]; const interaction = this.cmi.interactions.childArray[index];
if (typeof interaction.type === 'undefined') { if (typeof interaction.type === 'undefined') {
_self.throwSCORMError(scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED); this.throwSCORMError(scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
} else { } else {
const interaction_type = interaction.type; const interaction_type = interaction.type;
const interaction_count = interaction.correct_responses._count; const interaction_count = interaction.correct_responses._count;
if (interaction_type === 'choice') { if (interaction_type === 'choice') {
for (let i = 0; i < interaction_count && _self.lastErrorCode === for (let i = 0; i < interaction_count && this.lastErrorCode ===
0; i++) { 0; i++) {
const response = interaction.correct_responses.childArray[i]; const response = interaction.correct_responses.childArray[i];
if (response.pattern === value) { if (response.pattern === value) {
_self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE);
} }
} }
} }
@@ -174,22 +172,22 @@ class Scorm2004API extends BaseAPI {
} }
if (nodes.length > 0 && nodes.length <= response_type.max) { if (nodes.length > 0 && nodes.length <= response_type.max) {
_self.#checkCorrectResponseValue(interaction_type, nodes, value); this.#checkCorrectResponseValue(interaction_type, nodes, value);
} else if (nodes.length > response_type.max) { } else if (nodes.length > response_type.max) {
_self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE,
'Data Model Element Pattern Too Long'); 'Data Model Element Pattern Too Long');
} }
} }
if (_self.lastErrorCode === 0) { if (this.lastErrorCode === 0) {
newChild = new CMIInteractionsCorrectResponsesObject(this); newChild = new CMIInteractionsCorrectResponsesObject(this);
} }
} else if (_self.stringContains(CMIElement, '.objectives')) { } else if (this.stringContains(CMIElement, '.objectives')) {
newChild = new CMIInteractionsObjectivesObject(this); newChild = new CMIInteractionsObjectivesObject(this);
} else if (_self.stringContains(CMIElement, 'cmi.interactions')) { } else if (this.stringContains(CMIElement, 'cmi.interactions')) {
newChild = new CMIInteractionsObject(this); newChild = new CMIInteractionsObject(this);
} else if (_self.stringContains(CMIElement, 'cmi.comments_from_learner')) { } else if (this.stringContains(CMIElement, 'cmi.comments_from_learner')) {
newChild = new CMICommentsFromLearnerObject(this); newChild = new CMICommentsFromLearnerObject(this);
} else if (_self.stringContains(CMIElement, 'cmi.comments_from_lms')) { } else if (this.stringContains(CMIElement, 'cmi.comments_from_lms')) {
newChild = new CMICommentsFromLMSObject(this); newChild = new CMICommentsFromLMSObject(this);
} }
@@ -205,15 +203,15 @@ class Scorm2004API extends BaseAPI {
const parts = CMIElement.split('.'); const parts = CMIElement.split('.');
const index = Number(parts[2]); const index = Number(parts[2]);
const pattern_index = Number(parts[4]); const pattern_index = Number(parts[4]);
const interaction = _self.cmi.interactions.childArray[index]; const interaction = this.cmi.interactions.childArray[index];
const interaction_type = interaction.type; const interaction_type = interaction.type;
const interaction_count = interaction.correct_responses._count; const interaction_count = interaction.correct_responses._count;
if (interaction_type === 'choice') { if (interaction_type === 'choice') {
for (let i = 0; i < interaction_count && _self.lastErrorCode === 0; i++) { for (let i = 0; i < interaction_count && this.lastErrorCode === 0; i++) {
const response = interaction.correct_responses.childArray[i]; const response = interaction.correct_responses.childArray[i];
if (response.pattern === value) { if (response.pattern === value) {
_self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE); this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE);
} }
} }
} }
@@ -229,26 +227,26 @@ class Scorm2004API extends BaseAPI {
} }
if (nodes.length > 0 && nodes.length <= response_type.max) { if (nodes.length > 0 && nodes.length <= response_type.max) {
_self.#checkCorrectResponseValue(interaction_type, nodes, value); this.#checkCorrectResponseValue(interaction_type, nodes, value);
} else if (nodes.length > response_type.max) { } else if (nodes.length > response_type.max) {
_self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE,
'Data Model Element Pattern Too Long'); 'Data Model Element Pattern Too Long');
} }
if (_self.lastErrorCode === 0 && if (this.lastErrorCode === 0 &&
(!response_type.duplicate || (!response_type.duplicate ||
!_self.#checkDuplicatedPattern(interaction.correct_responses, !this.#checkDuplicatedPattern(interaction.correct_responses,
pattern_index, value)) || pattern_index, value)) ||
(_self.lastErrorCode === 0 && value === '')) { (this.lastErrorCode === 0 && value === '')) {
// do nothing, we want the inverse // do nothing, we want the inverse
} else { } else {
if (_self.lastErrorCode === 0) { if (this.lastErrorCode === 0) {
_self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE,
'Data Model Element Pattern Already Exists'); 'Data Model Element Pattern Already Exists');
} }
} }
} else { } else {
_self.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE, this.throwSCORMError(scorm2004_error_codes.GENERAL_SET_FAILURE,
'Data Model Element Collection Limit Reached'); 'Data Model Element Collection Limit Reached');
} }
} }
@@ -260,7 +258,7 @@ class Scorm2004API extends BaseAPI {
* @return {*} * @return {*}
*/ */
getCMIValue(CMIElement) { getCMIValue(CMIElement) {
return _self._commonGetCMIValue('GetValue', true, CMIElement); return this._commonGetCMIValue('GetValue', true, CMIElement);
} }
/** /**
@@ -311,10 +309,10 @@ class Scorm2004API extends BaseAPI {
#checkCorrectResponseValue = (interaction_type, nodes, value) => { #checkCorrectResponseValue = (interaction_type, nodes, value) => {
const response = correct_responses[interaction_type]; const response = correct_responses[interaction_type];
const formatRegex = new RegExp(response.format); const formatRegex = new RegExp(response.format);
for (let i = 0; i < nodes.length && _self.lastErrorCode === 0; i++) { for (let i = 0; i < nodes.length && this.lastErrorCode === 0; i++) {
if (interaction_type.match( if (interaction_type.match(
'^(fill-in|long-fill-in|matching|performance|sequencing)$')) { '^(fill-in|long-fill-in|matching|performance|sequencing)$')) {
nodes[i] = _self.#removeCorrectResponsePrefixes(nodes[i]); nodes[i] = this.#removeCorrectResponsePrefixes(nodes[i]);
} }
if (response.delimiter2 !== undefined) { if (response.delimiter2 !== undefined) {
@@ -322,30 +320,30 @@ class Scorm2004API extends BaseAPI {
if (values.length === 2) { if (values.length === 2) {
const matches = values[0].match(formatRegex); const matches = values[0].match(formatRegex);
if (!matches) { if (!matches) {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} else { } else {
if (!values[1].match(new RegExp(response.format2))) { if (!values[1].match(new RegExp(response.format2))) {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} }
} }
} else { } else {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} }
} else { } else {
const matches = nodes[i].match(formatRegex); const matches = nodes[i].match(formatRegex);
if ((!matches && value !== '') || if ((!matches && value !== '') ||
(!matches && interaction_type === 'true-false')) { (!matches && interaction_type === 'true-false')) {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} else { } else {
if (interaction_type === 'numeric' && nodes.length > 1) { if (interaction_type === 'numeric' && nodes.length > 1) {
if (Number(nodes[0]) > Number(nodes[1])) { if (Number(nodes[0]) > Number(nodes[1])) {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} }
} else { } else {
if (nodes[i] !== '' && response.unique) { if (nodes[i] !== '' && response.unique) {
for (let j = 0; j < i && _self.lastErrorCode === 0; j++) { for (let j = 0; j < i && this.lastErrorCode === 0; j++) {
if (nodes[i] === nodes[j]) { if (nodes[i] === nodes[j]) {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} }
} }
} }
@@ -377,7 +375,7 @@ class Scorm2004API extends BaseAPI {
const lang = langMatches[3]; const lang = langMatches[3];
if (lang !== undefined && lang.length > 0) { if (lang !== undefined && lang.length > 0) {
if (valid_languages[lang.toLowerCase()] === undefined) { if (valid_languages[lang.toLowerCase()] === undefined) {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} }
} }
} }
@@ -386,7 +384,7 @@ class Scorm2004API extends BaseAPI {
case 'case_matters': case 'case_matters':
if (!seenLang && !seenOrder && !seenCase) { if (!seenLang && !seenOrder && !seenCase) {
if (matches[3] !== 'true' && matches[3] !== 'false') { if (matches[3] !== 'true' && matches[3] !== 'false') {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} }
} }
@@ -395,7 +393,7 @@ class Scorm2004API extends BaseAPI {
case 'order_matters': case 'order_matters':
if (!seenCase && !seenLang && !seenOrder) { if (!seenCase && !seenLang && !seenOrder) {
if (matches[3] !== 'true' && matches[3] !== 'false') { if (matches[3] !== 'true' && matches[3] !== 'false') {
_self.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH); this.throwSCORMError(scorm2004_error_codes.TYPE_MISMATCH);
} }
} }
@@ -417,8 +415,8 @@ class Scorm2004API extends BaseAPI {
*/ */
replaceWithAnotherScormAPI(newAPI) { replaceWithAnotherScormAPI(newAPI) {
// Data Model // Data Model
_self.cmi = newAPI.cmi; this.cmi = newAPI.cmi;
_self.adl = newAPI.adl; this.adl = newAPI.adl;
} }
/** /**
@@ -427,8 +425,8 @@ class Scorm2004API extends BaseAPI {
* @return {string} ISO8601 Duration * @return {string} ISO8601 Duration
*/ */
getCurrentTotalTime() { getCurrentTotalTime() {
const totalTime = _self.cmi.total_time; const totalTime = this.cmi.total_time;
const sessionTime = _self.cmi.session_time; const sessionTime = this.cmi.session_time;
return Util.addTwoDurations(totalTime, sessionTime, return Util.addTwoDurations(totalTime, sessionTime,
scorm2004_regex.CMITimespan); scorm2004_regex.CMITimespan);

View File

@@ -3,31 +3,38 @@ import {BaseCMI, CMIArray, CMIScore} from './common';
import {aicc_constants} from '../constants/api_constants'; import {aicc_constants} from '../constants/api_constants';
import {aicc_regex} from '../regex'; import {aicc_regex} from '../regex';
import {scorm12_error_codes} from '../constants/error_codes'; import {scorm12_error_codes} from '../constants/error_codes';
import {ValidationError} from '../exceptions';
import {check12ValidFormat} from './scorm12_cmi';
import {throwReadOnlyError} from './scorm12_cmi';
import {throwWriteOnlyError} from './scorm12_cmi';
const constants = aicc_constants; const constants = aicc_constants;
const regex = aicc_regex; const regex = aicc_regex;
/**
* Sets a READ_ONLY error on the API
* @param {AICC} API
*/
function throwReadOnlyError(API) {
API.throwSCORMError(scorm12_error_codes.READ_ONLY_ELEMENT);
}
/** /**
* CMI Class for AICC * CMI Class for AICC
*/ */
export class CMI extends Scorm12CMI.CMI { export class CMI extends Scorm12CMI.CMI {
/** /**
* Constructor for AICC CMI object * Constructor for AICC CMI object
* @param {AICC} API * @param {boolean} initialized
*/ */
constructor(API) { constructor(initialized: boolean) {
super(API, constants.cmi_children); super(constants.cmi_children);
this.student_data = new AICCCMIStudentData(API); if (initialized) this.initialize();
this.evaluation = new CMIEvaluation(API);
this.student_data = new AICCCMIStudentData();
this.evaluation = new CMIEvaluation();
}
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
super.initialize();
this.student_data?.initialize();
this.evaluation?.initialize();
} }
} }
@@ -37,12 +44,19 @@ export class CMI extends Scorm12CMI.CMI {
class CMIEvaluation extends BaseCMI { class CMIEvaluation extends BaseCMI {
/** /**
* Constructor for AICC Evaluation object * Constructor for AICC Evaluation object
* @param {AICC} API
*/ */
constructor(API) { constructor() {
super(API); super();
this.comments = new CMIEvaluationComments(API); this.comments = new CMIEvaluationComments();
}
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
super.initialize();
this.comments?.initialize();
} }
} }
@@ -52,10 +66,9 @@ class CMIEvaluation extends BaseCMI {
class CMIEvaluationComments extends CMIArray { class CMIEvaluationComments extends CMIArray {
/** /**
* Constructor for AICC Evaluation Comments object * Constructor for AICC Evaluation Comments object
* @param {AICC} API
*/ */
constructor(API) { constructor() {
super(API, constants.comments_children, super(constants.comments_children,
scorm12_error_codes.INVALID_SET_VALUE); scorm12_error_codes.INVALID_SET_VALUE);
} }
} }
@@ -66,12 +79,19 @@ class CMIEvaluationComments extends CMIArray {
class AICCCMIStudentData extends Scorm12CMI.CMIStudentData { class AICCCMIStudentData extends Scorm12CMI.CMIStudentData {
/** /**
* Constructor for AICC StudentData object * Constructor for AICC StudentData object
* @param {AICC} API
*/ */
constructor(API) { constructor() {
super(API, constants.student_data_children); super(constants.student_data_children);
this.tries = new CMITries(API); this.tries = new CMITries();
}
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
super.initialize();
this.tries?.initialize();
} }
#tries_during_lesson = ''; #tries_during_lesson = '';
@@ -86,13 +106,13 @@ class AICCCMIStudentData extends Scorm12CMI.CMIStudentData {
/** /**
* Setter for #tries_during_lesson. Sets an error if trying to set after * Setter for #tries_during_lesson. Sets an error if trying to set after
* API initialization. * initialization.
* @param {string} tries_during_lesson * @param {string} tries_during_lesson
*/ */
set tries_during_lesson(tries_during_lesson) { set tries_during_lesson(tries_during_lesson) {
this.API.isNotInitialized() ? !this.initialized ?
this.#tries_during_lesson = tries_during_lesson : this.#tries_during_lesson = tries_during_lesson :
throwReadOnlyError(this.API); throwReadOnlyError();
} }
} }
@@ -102,10 +122,9 @@ class AICCCMIStudentData extends Scorm12CMI.CMIStudentData {
export class CMITries extends CMIArray { export class CMITries extends CMIArray {
/** /**
* Constructor for inline Tries Array class * Constructor for inline Tries Array class
* @param {AICC} API
*/ */
constructor(API) { constructor() {
super(API, aicc_constants.tries_children); super(aicc_constants.tries_children);
} }
} }
@@ -115,12 +134,19 @@ export class CMITries extends CMIArray {
export class CMITriesObject extends BaseCMI { export class CMITriesObject extends BaseCMI {
/** /**
* Constructor for AICC Tries object * Constructor for AICC Tries object
* @param {AICC} API
*/ */
constructor(API) { constructor() {
super(API); super();
this.score = new CMIScore(API); this.score = new CMIScore();
}
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
super.initialize();
this.score?.initialize();
} }
#status = ''; #status = '';
@@ -139,7 +165,7 @@ export class CMITriesObject extends BaseCMI {
* @param {string} status * @param {string} status
*/ */
set status(status) { set status(status) {
if (this.API.checkValidFormat(status, regex.CMIStatus2)) { if (check12ValidFormat(status, regex.CMIStatus2)) {
this.#status = status; this.#status = status;
} }
} }
@@ -157,7 +183,7 @@ export class CMITriesObject extends BaseCMI {
* @param {string} time * @param {string} time
*/ */
set time(time) { set time(time) {
if (this.API.checkValidFormat(time, regex.CMITime)) { if (check12ValidFormat(time, regex.CMITime)) {
this.#time = time; this.#time = time;
} }
} }
@@ -169,10 +195,9 @@ export class CMITriesObject extends BaseCMI {
export class CMIEvaluationCommentsObject extends BaseCMI { export class CMIEvaluationCommentsObject extends BaseCMI {
/** /**
* Constructor for Evaluation Comments * Constructor for Evaluation Comments
* @param {AICC} API
*/ */
constructor(API) { constructor() {
super(API); super();
} }
#content = ''; #content = '';
@@ -192,7 +217,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI {
* @param {string} content * @param {string} content
*/ */
set content(content) { set content(content) {
if (this.API.checkValidFormat(content, regex.CMIString256)) { if (check12ValidFormat(content, regex.CMIString256)) {
this.#content = content; this.#content = content;
} }
} }
@@ -210,7 +235,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI {
* @param {string} location * @param {string} location
*/ */
set location(location) { set location(location) {
if (this.API.checkValidFormat(location, regex.CMIString256)) { if (check12ValidFormat(location, regex.CMIString256)) {
this.#location = location; this.#location = location;
} }
} }
@@ -228,7 +253,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI {
* @param {string} time * @param {string} time
*/ */
set time(time) { set time(time) {
if (this.API.checkValidFormat(time, regex.CMITime)) { if (check12ValidFormat(time, regex.CMITime)) {
this.#time = time; this.#time = time;
} }
} }
@@ -240,10 +265,9 @@ export class CMIEvaluationCommentsObject extends BaseCMI {
export class NAV extends BaseCMI { export class NAV extends BaseCMI {
/** /**
* Constructor for NAV object * Constructor for NAV object
* @param {AICC} API
*/ */
constructor(API) { constructor() {
super(API); super();
} }
#event = ''; #event = '';
@@ -253,9 +277,7 @@ export class NAV extends BaseCMI {
* @return {string} * @return {string}
*/ */
get event() { get event() {
return (!this.jsonString) ? return (!this.jsonString) ? throwWriteOnlyError() : this.#event;
this.API.throwSCORMError(scorm12_error_codes.WRITE_ONLY_ELEMENT) :
this.#event;
} }
/** /**
@@ -263,7 +285,7 @@ export class NAV extends BaseCMI {
* @param {string} event * @param {string} event
*/ */
set event(event) { set event(event) {
if (this.API.checkValidFormat(event, regex.NAVEvent)) { if (check12ValidFormat(event, regex.NAVEvent)) {
this.#event = event; this.#event = event;
} }
} }

View File

@@ -1,20 +1,68 @@
// @flow // @flow
import {scorm12_constants} from '../constants/api_constants'; import {scorm12_constants} from '../constants/api_constants';
import {scorm12_error_codes} from '../constants/error_codes'; import {scorm12_error_codes} from '../constants/error_codes';
import {ValidationError} from '../exceptions';
/**
* Check if the value matches the proper format. If not, throw proper error code.
*
* @param {string} value
* @param {string} regexPattern
* @param {number} errorCode
* @return {boolean}
*/
export function checkValidFormat(
value: String, regexPattern: String, errorCode: number) {
const formatRegex = new RegExp(regexPattern);
if (!value || !value.match(formatRegex)) {
throw new ValidationError(errorCode);
}
return true;
}
/**
* Check if the value matches the proper range. If not, throw proper error code.
*
* @param {*} value
* @param {string} rangePattern
* @param {number} errorCode
* @return {boolean}
*/
export function checkValidRange(
value: any, rangePattern: String, errorCode: number) {
const ranges = rangePattern.split('#');
value = value * 1.0;
if (value >= ranges[0]) {
if ((ranges[1] === '*') || (value <= ranges[1])) {
return true;
} else {
throw new ValidationError(errorCode);
}
} else {
throw new ValidationError(errorCode);
}
}
/** /**
* Base class for API cmi objects * Base class for API cmi objects
*/ */
export class BaseCMI { export class BaseCMI {
jsonString = false; jsonString = false;
API; #initialized = false;
/** /**
* Constructor for base cmi * Getter for #initialized
* @param {BaseAPI} API * @return {boolean}
*/ */
constructor(API: any) { get initialized() {
this.API = API; return this.#initialized;
}
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
this.#initialized = true;
} }
} }
@@ -24,29 +72,48 @@ export class BaseCMI {
export class CMIScore extends BaseCMI { export class CMIScore extends BaseCMI {
/** /**
* Constructor for *.score * Constructor for *.score
* @param {BaseAPI} API
* @param {string} score_children * @param {string} score_children
* @param {string} score_range * @param {string} score_range
* @param {string} max
* @param {number} invalidErrorCode * @param {number} invalidErrorCode
* @param {number} invalidTypeCode
* @param {number} invalidRangeCode
*/ */
constructor(API, score_children?, score_range?, invalidErrorCode) { constructor(
super(API); {
score_children,
score_range,
max,
invalidErrorCode,
invalidTypeCode,
invalidRangeCode,
}) {
super();
this.#_children = score_children ? this.#_children = score_children ?
score_children : score_children :
scorm12_constants.score_children; scorm12_constants.score_children;
this.#_score_range = score_range ? score_range : false; this.#_score_range = score_range ? score_range : false;
this.#max = max ? max : '100';
this.#_invalid_error_code = invalidErrorCode ? this.#_invalid_error_code = invalidErrorCode ?
invalidErrorCode : invalidErrorCode :
scorm12_error_codes.INVALID_SET_VALUE; scorm12_error_codes.INVALID_SET_VALUE;
this.#_invalid_type_code = invalidTypeCode ?
invalidTypeCode :
scorm12_error_codes.TYPE_MISMATCH;
this.#_invalid_range_code = invalidRangeCode ?
invalidRangeCode :
scorm12_error_codes.VALUE_OUT_OF_RANGE;
} }
#_children; #_children;
#_score_range; #_score_range;
#_invalid_error_code; #_invalid_error_code;
#_invalid_type_code;
#_invalid_range_code;
#raw = ''; #raw = '';
#min = ''; #min = '';
#max = '100'; #max;
/** /**
* Getter for _children * Getter for _children
@@ -63,7 +130,7 @@ export class CMIScore extends BaseCMI {
* @private * @private
*/ */
set _children(_children) { set _children(_children) {
this.API.throwSCORMError(this.#_invalid_error_code); throw new ValidationError(this.#_invalid_error_code);
} }
/** /**
@@ -79,9 +146,11 @@ export class CMIScore extends BaseCMI {
* @param {string} raw * @param {string} raw
*/ */
set raw(raw) { set raw(raw) {
if (this.API.checkValidFormat(raw, scorm12_constants.CMIDecimal) && if (checkValidFormat(raw, scorm12_constants.CMIDecimal,
this.#_invalid_type_code) &&
(!this.#_score_range || (!this.#_score_range ||
this.API.checkValidRange(raw, this.#_score_range))) { checkValidRange(raw, this.#_score_range,
this.#_invalid_range_code))) {
this.#raw = raw; this.#raw = raw;
} }
} }
@@ -99,9 +168,11 @@ export class CMIScore extends BaseCMI {
* @param {string} min * @param {string} min
*/ */
set min(min) { set min(min) {
if (this.API.checkValidFormat(min, scorm12_constants.CMIDecimal) && if (checkValidFormat(min, scorm12_constants.CMIDecimal,
this.#_invalid_type_code) &&
(!this.#_score_range || (!this.#_score_range ||
this.API.checkValidRange(min, this.#_score_range))) { checkValidRange(min, this.#_score_range,
this.#_invalid_range_code))) {
this.#min = min; this.#min = min;
} }
} }
@@ -119,9 +190,11 @@ export class CMIScore extends BaseCMI {
* @param {string} max * @param {string} max
*/ */
set max(max) { set max(max) {
if (this.API.checkValidFormat(max, scorm12_constants.CMIDecimal) && if (checkValidFormat(max, scorm12_constants.CMIDecimal,
this.#_invalid_type_code) &&
(!this.#_score_range || (!this.#_score_range ||
this.API.checkValidRange(max, this.#_score_range))) { checkValidRange(max, this.#_score_range,
this.#_invalid_range_code))) {
this.#max = max; this.#max = max;
} }
} }
@@ -145,12 +218,11 @@ export class CMIScore extends BaseCMI {
export class CMIArray extends BaseCMI { export class CMIArray extends BaseCMI {
/** /**
* Constructor cmi *.n arrays * Constructor cmi *.n arrays
* @param {BaseAPI} API
* @param {string} children * @param {string} children
* @param {number} errorCode * @param {number} errorCode
*/ */
constructor({API, children, errorCode}) { constructor({children, errorCode}) {
super(API); super();
this.#_children = children; this.#_children = children;
this.#errorCode = errorCode; this.#errorCode = errorCode;
this.childArray = []; this.childArray = [];
@@ -174,7 +246,7 @@ export class CMIArray extends BaseCMI {
* @private * @private
*/ */
set _children(_children) { set _children(_children) {
this.API.throwSCORMError(this.#errorCode); throw new ValidationError(this.#errorCode);
} }
/** /**
@@ -192,7 +264,7 @@ export class CMIArray extends BaseCMI {
* @private * @private
*/ */
set _count(_count) { set _count(_count) {
this.API.throwSCORMError(this.#errorCode); throw new ValidationError(this.#errorCode);
} }
/** /**

View File

@@ -1,34 +1,60 @@
// @flow // @flow
import {BaseCMI, CMIArray, CMIScore} from './common'; import {
BaseCMI,
checkValidFormat,
checkValidRange,
CMIArray,
CMIScore,
} from './common';
import {scorm12_constants} from '../constants/api_constants'; import {scorm12_constants} from '../constants/api_constants';
import {scorm12_error_codes} from '../constants/error_codes'; import {scorm12_error_codes} from '../constants/error_codes';
import {scorm12_regex} from '../regex'; import {scorm12_regex} from '../regex';
import {ValidationError} from '../exceptions';
const constants = scorm12_constants; const constants = scorm12_constants;
const regex = scorm12_regex; const regex = scorm12_regex;
/** /**
* Helper method for throwing Read Only error * Helper method for throwing Read Only error
* @param {Scorm12API} API
*/ */
function throwReadOnlyError(API) { export function throwReadOnlyError() {
API.throwSCORMError(scorm12_error_codes.READ_ONLY_ELEMENT); throw new ValidationError(scorm12_error_codes.READ_ONLY_ELEMENT);
} }
/** /**
* Helper method for throwing Write Only error * Helper method for throwing Write Only error
* @param {Scorm12API} API
*/ */
function throwWriteOnlyError(API) { export function throwWriteOnlyError() {
API.throwSCORMError(scorm12_error_codes.WRITE_ONLY_ELEMENT); throw new ValidationError(scorm12_error_codes.WRITE_ONLY_ELEMENT);
} }
/** /**
* Helper method for throwing Invalid Set error * Helper method for throwing Invalid Set error
* @param {Scorm12API} API
*/ */
function throwInvalidValueError(API) { function throwInvalidValueError() {
API.throwSCORMError(scorm12_error_codes.INVALID_SET_VALUE); throw new ValidationError(scorm12_error_codes.INVALID_SET_VALUE);
}
/**
* Helper method, no reason to have to pass the same error codes every time
* @param {*} value
* @param {string} regexPattern
* @return {boolean}
*/
export function check12ValidFormat(value: String, regexPattern: String) {
return checkValidFormat(value, regexPattern,
scorm12_error_codes.TYPE_MISMATCH);
}
/**
* Helper method, no reason to have to pass the same error codes every time
* @param {*} value
* @param {string} rangePattern
* @return {boolean}
*/
export function check12ValidRange(value: any, rangePattern: String) {
return checkValidRange(value, rangePattern,
scorm12_error_codes.VALUE_OUT_OF_RANGE);
} }
/** /**
@@ -46,21 +72,33 @@ export class CMI extends BaseCMI {
/** /**
* Constructor for the SCORM 1.2 cmi object * Constructor for the SCORM 1.2 cmi object
* @param {Scorm12API} API
* @param {string} cmi_children * @param {string} cmi_children
* @param {string} student_data * @param {(CMIStudentData|AICCCMIStudentData)} student_data
* @param {boolean} initialized
*/ */
constructor(API, cmi_children, student_data) { constructor(cmi_children, student_data, initialized: boolean) {
super(API); super();
if (initialized) this.initialize();
this.#_children = cmi_children ? cmi_children : constants.cmi_children; this.#_children = cmi_children ? cmi_children : constants.cmi_children;
this.core = new CMICore(API); this.core = new CMICore();
this.objectives = new CMIObjectives(API); this.objectives = new CMIObjectives();
this.student_data = student_data ? this.student_data = student_data ? student_data : new CMIStudentData();
student_data : this.student_preference = new CMIStudentPreference();
new CMIStudentData(API); this.interactions = new CMIInteractions();
this.student_preference = new CMIStudentPreference(API); }
this.interactions = new CMIInteractions(API);
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
super.initialize();
this.core?.initialize();
this.objectives?.initialize();
this.student_data?.initialize();
this.student_preference?.initialize();
this.interactions?.initialize();
} }
/** /**
@@ -112,7 +150,7 @@ export class CMI extends BaseCMI {
* @private * @private
*/ */
set _version(_version) { set _version(_version) {
throwInvalidValueError(this.API); throwInvalidValueError();
} }
/** /**
@@ -130,7 +168,7 @@ export class CMI extends BaseCMI {
* @private * @private
*/ */
set _children(_children) { set _children(_children) {
throwInvalidValueError(this.API); throwInvalidValueError();
} }
/** /**
@@ -146,7 +184,7 @@ export class CMI extends BaseCMI {
* @param {string} suspend_data * @param {string} suspend_data
*/ */
set suspend_data(suspend_data) { set suspend_data(suspend_data) {
if (this.API.checkValidFormat(suspend_data, regex.CMIString4096)) { if (check12ValidFormat(suspend_data, regex.CMIString4096)) {
this.#suspend_data = suspend_data; this.#suspend_data = suspend_data;
} }
} }
@@ -160,13 +198,11 @@ export class CMI extends BaseCMI {
} }
/** /**
* Setter for #launch_data. Can only be called before API initialization. * Setter for #launch_data. Can only be called before initialization.
* @param {string} launch_data * @param {string} launch_data
*/ */
set launch_data(launch_data) { set launch_data(launch_data) {
this.API.isNotInitialized() ? !this.initialized ? this.#launch_data = launch_data : throwReadOnlyError();
this.#launch_data = launch_data :
throwReadOnlyError(this.API);
} }
/** /**
@@ -182,7 +218,7 @@ export class CMI extends BaseCMI {
* @param {string} comments * @param {string} comments
*/ */
set comments(comments) { set comments(comments) {
if (this.API.checkValidFormat(comments, regex.CMIString4096)) { if (check12ValidFormat(comments, regex.CMIString4096)) {
this.#comments = comments; this.#comments = comments;
} }
} }
@@ -196,13 +232,11 @@ export class CMI extends BaseCMI {
} }
/** /**
* Setter for #comments_from_lms. Can only be called before API initialization. * Setter for #comments_from_lms. Can only be called before initialization.
* @param {string} comments_from_lms * @param {string} comments_from_lms
*/ */
set comments_from_lms(comments_from_lms) { set comments_from_lms(comments_from_lms) {
this.API.isNotInitialized() ? !this.initialized ? this.#comments_from_lms = comments_from_lms : throwReadOnlyError();
this.#comments_from_lms = comments_from_lms :
throwReadOnlyError(this.API);
} }
} }
@@ -212,13 +246,26 @@ export class CMI extends BaseCMI {
class CMICore extends BaseCMI { class CMICore extends BaseCMI {
/** /**
* Constructor for cmi.core * Constructor for cmi.core
* @param {Scorm12API} API
*/ */
constructor(API) { constructor() {
super(API); super();
this.score = new Scorm12CMIScore(API, constants.score_children, this.score = new Scorm12CMIScore(
regex.score_range); {
score_children: constants.score_children,
score_range: regex.score_range,
invalidErrorCode: scorm12_error_codes.INVALID_SET_VALUE,
invalidTypeCode: scorm12_error_codes.TYPE_MISMATCH,
invalidRangeCode: scorm12_error_codes.VALUE_OUT_OF_RANGE,
});
}
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
super.initialize();
this.score?.initialize();
} }
#_children = constants.core_children; #_children = constants.core_children;
@@ -248,7 +295,7 @@ class CMICore extends BaseCMI {
* @private * @private
*/ */
set _children(_children) { set _children(_children) {
throwInvalidValueError(this.API); throwInvalidValueError();
} }
/** /**
@@ -260,13 +307,11 @@ class CMICore extends BaseCMI {
} }
/** /**
* Setter for #student_id. Can only be called before API initialization. * Setter for #student_id. Can only be called before initialization.
* @param {string} student_id * @param {string} student_id
*/ */
set student_id(student_id) { set student_id(student_id) {
this.API.isNotInitialized() ? !this.initialized ? this.#student_id = student_id : throwReadOnlyError();
this.#student_id = student_id :
throwReadOnlyError(this.API);
} }
/** /**
@@ -278,13 +323,11 @@ class CMICore extends BaseCMI {
} }
/** /**
* Setter for #student_name. Can only be called before API initialization. * Setter for #student_name. Can only be called before initialization.
* @param {string} student_name * @param {string} student_name
*/ */
set student_name(student_name) { set student_name(student_name) {
this.API.isNotInitialized() ? !this.initialized ? this.#student_name = student_name : throwReadOnlyError();
this.#student_name = student_name :
throwReadOnlyError(this.API);
} }
/** /**
@@ -300,7 +343,7 @@ class CMICore extends BaseCMI {
* @param {string} lesson_location * @param {string} lesson_location
*/ */
set lesson_location(lesson_location) { set lesson_location(lesson_location) {
if (this.API.checkValidFormat(lesson_location, regex.CMIString256)) { if (check12ValidFormat(lesson_location, regex.CMIString256)) {
this.#lesson_location = lesson_location; this.#lesson_location = lesson_location;
} }
} }
@@ -314,13 +357,11 @@ class CMICore extends BaseCMI {
} }
/** /**
* Setter for #credit. Can only be called before API initialization. * Setter for #credit. Can only be called before initialization.
* @param {string} credit * @param {string} credit
*/ */
set credit(credit) { set credit(credit) {
this.API.isNotInitialized() ? !this.initialized ? this.#credit = credit : throwReadOnlyError();
this.#credit = credit :
throwReadOnlyError(this.API);
} }
/** /**
@@ -336,7 +377,7 @@ class CMICore extends BaseCMI {
* @param {string} lesson_status * @param {string} lesson_status
*/ */
set lesson_status(lesson_status) { set lesson_status(lesson_status) {
if (this.API.checkValidFormat(lesson_status, regex.CMIStatus)) { if (check12ValidFormat(lesson_status, regex.CMIStatus)) {
this.#lesson_status = lesson_status; this.#lesson_status = lesson_status;
} }
} }
@@ -350,13 +391,11 @@ class CMICore extends BaseCMI {
} }
/** /**
* Setter for #entry. Can only be called before API initialization. * Setter for #entry. Can only be called before initialization.
* @param {string} entry * @param {string} entry
*/ */
set entry(entry) { set entry(entry) {
this.API.isNotInitialized() ? !this.initialized ? this.#entry = entry : throwReadOnlyError();
this.#entry = entry :
throwReadOnlyError(this.API);
} }
/** /**
@@ -368,13 +407,11 @@ class CMICore extends BaseCMI {
} }
/** /**
* Setter for #total_time. Can only be called before API initialization. * Setter for #total_time. Can only be called before initialization.
* @param {string} total_time * @param {string} total_time
*/ */
set total_time(total_time) { set total_time(total_time) {
this.API.isNotInitialized() ? !this.initialized ? this.#total_time = total_time : throwReadOnlyError();
this.#total_time = total_time :
throwReadOnlyError(this.API);
} }
/** /**
@@ -386,13 +423,11 @@ class CMICore extends BaseCMI {
} }
/** /**
* Setter for #lesson_mode. Can only be called before API initialization. * Setter for #lesson_mode. Can only be called before initialization.
* @param {string} lesson_mode * @param {string} lesson_mode
*/ */
set lesson_mode(lesson_mode) { set lesson_mode(lesson_mode) {
this.API.isNotInitialized() ? !this.initialized ? this.#lesson_mode = lesson_mode : throwReadOnlyError();
this.#lesson_mode = lesson_mode :
throwReadOnlyError(this.API);
} }
/** /**
@@ -400,7 +435,7 @@ class CMICore extends BaseCMI {
* @return {*} * @return {*}
*/ */
get exit() { get exit() {
return (!this.jsonString) ? throwWriteOnlyError(this.API) : this.#exit; return (!this.jsonString) ? throwWriteOnlyError() : this.#exit;
} }
/** /**
@@ -408,7 +443,7 @@ class CMICore extends BaseCMI {
* @param {string} exit * @param {string} exit
*/ */
set exit(exit) { set exit(exit) {
if (this.API.checkValidFormat(exit, regex.CMIExit)) { if (check12ValidFormat(exit, regex.CMIExit)) {
this.#exit = exit; this.#exit = exit;
} }
} }
@@ -418,9 +453,7 @@ class CMICore extends BaseCMI {
* @return {*} * @return {*}
*/ */
get session_time() { get session_time() {
return (!this.jsonString) ? return (!this.jsonString) ? throwWriteOnlyError() : this.#session_time;
throwWriteOnlyError(this.API) :
this.#session_time;
} }
/** /**
@@ -428,7 +461,7 @@ class CMICore extends BaseCMI {
* @param {string} session_time * @param {string} session_time
*/ */
set session_time(session_time) { set session_time(session_time) {
if (this.API.checkValidFormat(session_time, regex.CMITimespan)) { if (check12ValidFormat(session_time, regex.CMITimespan)) {
this.#session_time = session_time; this.#session_time = session_time;
} }
} }
@@ -478,11 +511,9 @@ class CMICore extends BaseCMI {
class CMIObjectives extends CMIArray { class CMIObjectives extends CMIArray {
/** /**
* Constructor for cmi.objectives * Constructor for cmi.objectives
* @param {Scorm12API} API
*/ */
constructor(API) { constructor() {
super({ super({
API: API,
children: constants.objectives_children, children: constants.objectives_children,
errorCode: scorm12_error_codes.INVALID_SET_VALUE, errorCode: scorm12_error_codes.INVALID_SET_VALUE,
}); });
@@ -500,11 +531,10 @@ export class CMIStudentData extends BaseCMI {
/** /**
* Constructor for cmi.student_data * Constructor for cmi.student_data
* @param {Scorm12API} API
* @param {string} student_data_children * @param {string} student_data_children
*/ */
constructor(API, student_data_children) { constructor(student_data_children) {
super(API); super();
this.#_children = student_data_children ? this.#_children = student_data_children ?
student_data_children : student_data_children :
@@ -526,7 +556,7 @@ export class CMIStudentData extends BaseCMI {
* @private * @private
*/ */
set _children(_children) { set _children(_children) {
throwInvalidValueError(this.API); throwInvalidValueError();
} }
/** /**
@@ -538,13 +568,11 @@ export class CMIStudentData extends BaseCMI {
} }
/** /**
* Setter for #master_score. Can only be called before API initialization. * Setter for #master_score. Can only be called before initialization.
* @param {string} mastery_score * @param {string} mastery_score
*/ */
set mastery_score(mastery_score) { set mastery_score(mastery_score) {
this.API.isNotInitialized() ? !this.initialized ? this.#mastery_score = mastery_score : throwReadOnlyError();
this.#mastery_score = mastery_score :
throwReadOnlyError(this.API);
} }
/** /**
@@ -556,13 +584,11 @@ export class CMIStudentData extends BaseCMI {
} }
/** /**
* Setter for #max_time_allowed. Can only be called before API initialization. * Setter for #max_time_allowed. Can only be called before initialization.
* @param {string} max_time_allowed * @param {string} max_time_allowed
*/ */
set max_time_allowed(max_time_allowed) { set max_time_allowed(max_time_allowed) {
this.API.isNotInitialized() ? !this.initialized ? this.#max_time_allowed = max_time_allowed : throwReadOnlyError();
this.#max_time_allowed = max_time_allowed :
throwReadOnlyError(this.API);
} }
/** /**
@@ -574,13 +600,11 @@ export class CMIStudentData extends BaseCMI {
} }
/** /**
* Setter for #time_limit_action. Can only be called before API initialization. * Setter for #time_limit_action. Can only be called before initialization.
* @param {string} time_limit_action * @param {string} time_limit_action
*/ */
set time_limit_action(time_limit_action) { set time_limit_action(time_limit_action) {
this.API.isNotInitialized() ? !this.initialized ? this.#time_limit_action = time_limit_action : throwReadOnlyError();
this.#time_limit_action = time_limit_action :
throwReadOnlyError(this.API);
} }
/** /**
@@ -612,10 +636,9 @@ export class CMIStudentData extends BaseCMI {
class CMIStudentPreference extends BaseCMI { class CMIStudentPreference extends BaseCMI {
/** /**
* Constructor for cmi.student_preference * Constructor for cmi.student_preference
* @param {Scorm12API} API
*/ */
constructor(API) { constructor() {
super(API); super();
} }
#_children = constants.student_preference_children; #_children = constants.student_preference_children;
@@ -639,7 +662,7 @@ class CMIStudentPreference extends BaseCMI {
* @private * @private
*/ */
set _children(_children) { set _children(_children) {
throwInvalidValueError(this.API); throwInvalidValueError();
} }
/** /**
@@ -655,8 +678,8 @@ class CMIStudentPreference extends BaseCMI {
* @param {string} audio * @param {string} audio
*/ */
set audio(audio) { set audio(audio) {
if (this.API.checkValidFormat(audio, regex.CMISInteger) && if (check12ValidFormat(audio, regex.CMISInteger) &&
this.API.checkValidRange(audio, regex.audio_range)) { check12ValidRange(audio, regex.audio_range)) {
this.#audio = audio; this.#audio = audio;
} }
} }
@@ -674,7 +697,7 @@ class CMIStudentPreference extends BaseCMI {
* @param {string} language * @param {string} language
*/ */
set language(language) { set language(language) {
if (this.API.checkValidFormat(language, regex.CMIString256)) { if (check12ValidFormat(language, regex.CMIString256)) {
this.#language = language; this.#language = language;
} }
} }
@@ -692,8 +715,8 @@ class CMIStudentPreference extends BaseCMI {
* @param {string} speed * @param {string} speed
*/ */
set speed(speed) { set speed(speed) {
if (this.API.checkValidFormat(speed, regex.CMISInteger) && if (check12ValidFormat(speed, regex.CMISInteger) &&
this.API.checkValidRange(speed, regex.speed_range)) { check12ValidRange(speed, regex.speed_range)) {
this.#speed = speed; this.#speed = speed;
} }
} }
@@ -711,8 +734,8 @@ class CMIStudentPreference extends BaseCMI {
* @param {string} text * @param {string} text
*/ */
set text(text) { set text(text) {
if (this.API.checkValidFormat(text, regex.CMISInteger) && if (check12ValidFormat(text, regex.CMISInteger) &&
this.API.checkValidRange(text, regex.text_range)) { check12ValidRange(text, regex.text_range)) {
this.#text = text; this.#text = text;
} }
} }
@@ -748,11 +771,9 @@ class CMIStudentPreference extends BaseCMI {
class CMIInteractions extends CMIArray { class CMIInteractions extends CMIArray {
/** /**
* Constructor for cmi.interactions * Constructor for cmi.interactions
* @param {Scorm12API} API
*/ */
constructor(API) { constructor() {
super({ super({
API: API,
children: constants.interactions_children, children: constants.interactions_children,
errorCode: scorm12_error_codes.INVALID_SET_VALUE, errorCode: scorm12_error_codes.INVALID_SET_VALUE,
}); });
@@ -765,23 +786,29 @@ class CMIInteractions extends CMIArray {
export class CMIInteractionsObject extends BaseCMI { export class CMIInteractionsObject extends BaseCMI {
/** /**
* Constructor for cmi.interactions.n object * Constructor for cmi.interactions.n object
* @param {Scorm12API} API
*/ */
constructor(API) { constructor() {
super(API); super();
this.objectives = new CMIArray({ this.objectives = new CMIArray({
API: API, errorCode: scorm12_error_codes.INVALID_SET_VALUE,
errorCode: 402,
children: constants.objectives_children, children: constants.objectives_children,
}); });
this.correct_responses = new CMIArray({ this.correct_responses = new CMIArray({
API: API, errorCode: scorm12_error_codes.INVALID_SET_VALUE,
errorCode: 402,
children: constants.correct_responses_children, children: constants.correct_responses_children,
}); });
} }
/**
* Called when the API has been initialized after the CMI has been created
*/
initialize() {
super.initialize();
this.objectives?.initialize();
this.correct_responses?.initialize();
}
#id: ''; #id: '';
#time: ''; #time: '';
#type: ''; #type: '';
@@ -795,7 +822,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*} * @return {*}
*/ */
get id() { get id() {
return (!this.jsonString) ? throwWriteOnlyError(this.API) : this.#id; return (!this.jsonString) ? throwWriteOnlyError() : this.#id;
} }
/** /**
@@ -803,7 +830,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} id * @param {string} id
*/ */
set id(id) { set id(id) {
if (this.API.checkValidFormat(id, regex.CMIIdentifier)) { if (check12ValidFormat(id, regex.CMIIdentifier)) {
this.#id = id; this.#id = id;
} }
} }
@@ -813,7 +840,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*} * @return {*}
*/ */
get time() { get time() {
return (!this.jsonString) ? throwWriteOnlyError(this.API) : this.#time; return (!this.jsonString) ? throwWriteOnlyError() : this.#time;
} }
/** /**
@@ -821,7 +848,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} time * @param {string} time
*/ */
set time(time) { set time(time) {
if (this.API.checkValidFormat(time, regex.CMITime)) { if (check12ValidFormat(time, regex.CMITime)) {
this.#time = time; this.#time = time;
} }
} }
@@ -831,7 +858,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*} * @return {*}
*/ */
get type() { get type() {
return (!this.jsonString) ? throwWriteOnlyError(this.API) : this.#type; return (!this.jsonString) ? throwWriteOnlyError() : this.#type;
} }
/** /**
@@ -839,7 +866,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} type * @param {string} type
*/ */
set type(type) { set type(type) {
if (this.API.checkValidFormat(type, regex.CMIType)) { if (check12ValidFormat(type, regex.CMIType)) {
this.#type = type; this.#type = type;
} }
} }
@@ -850,7 +877,7 @@ export class CMIInteractionsObject extends BaseCMI {
*/ */
get weighting() { get weighting() {
return (!this.jsonString) ? return (!this.jsonString) ?
throwWriteOnlyError(this.API) : throwWriteOnlyError() :
this.#weighting; this.#weighting;
} }
@@ -859,8 +886,8 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} weighting * @param {string} weighting
*/ */
set weighting(weighting) { set weighting(weighting) {
if (this.API.checkValidFormat(weighting, regex.CMIDecimal) && if (check12ValidFormat(weighting, regex.CMIDecimal) &&
this.API.checkValidRange(weighting, regex.weighting_range)) { check12ValidRange(weighting, regex.weighting_range)) {
this.#weighting = weighting; this.#weighting = weighting;
} }
} }
@@ -870,9 +897,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*} * @return {*}
*/ */
get student_response() { get student_response() {
return (!this.jsonString) ? return (!this.jsonString) ? throwWriteOnlyError() : this.#student_response;
throwWriteOnlyError(this.API) :
this.#student_response;
} }
/** /**
@@ -880,7 +905,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 (this.API.checkValidFormat(student_response, regex.CMIFeedback)) { if (check12ValidFormat(student_response, regex.CMIFeedback)) {
this.#student_response = student_response; this.#student_response = student_response;
} }
} }
@@ -890,9 +915,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*} * @return {*}
*/ */
get result() { get result() {
return (!this.jsonString) ? return (!this.jsonString) ? throwWriteOnlyError() : this.#result;
throwWriteOnlyError(this.API) :
this.#result;
} }
/** /**
@@ -900,7 +923,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} result * @param {string} result
*/ */
set result(result) { set result(result) {
if (this.API.checkValidFormat(result, regex.CMIResult)) { if (check12ValidFormat(result, regex.CMIResult)) {
this.#result = result; this.#result = result;
} }
} }
@@ -910,9 +933,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*} * @return {*}
*/ */
get latency() { get latency() {
return (!this.jsonString) ? return (!this.jsonString) ? throwWriteOnlyError() : this.#latency;
throwWriteOnlyError(this.API) :
this.#latency;
} }
/** /**
@@ -920,7 +941,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} latency * @param {string} latency
*/ */
set latency(latency) { set latency(latency) {
if (this.API.checkValidFormat(latency, regex.CMITimespan)) { if (check12ValidFormat(latency, regex.CMITimespan)) {
this.#latency = latency; this.#latency = latency;
} }
} }
@@ -966,12 +987,11 @@ export class CMIInteractionsObject extends BaseCMI {
export class CMIObjectivesObject extends BaseCMI { export class CMIObjectivesObject extends BaseCMI {
/** /**
* Constructor for cmi.objectives.n * Constructor for cmi.objectives.n
* @param {Scorm12API} API
*/ */
constructor(API) { constructor() {
super(API); super();
this.score = new Scorm12CMIScore(API); this.score = new Scorm12CMIScore();
} }
#id: ''; #id: '';
@@ -990,7 +1010,7 @@ export class CMIObjectivesObject extends BaseCMI {
* @param {string} id * @param {string} id
*/ */
set id(id) { set id(id) {
if (this.API.checkValidFormat(id, regex.CMIIdentifier)) { if (check12ValidFormat(id, regex.CMIIdentifier)) {
this.#id = id; this.#id = id;
} }
} }
@@ -1008,7 +1028,7 @@ export class CMIObjectivesObject extends BaseCMI {
* @param {string} status * @param {string} status
*/ */
set status(status) { set status(status) {
if (this.API.checkValidFormat(status, regex.CMIStatus2)) { if (check12ValidFormat(status, regex.CMIStatus2)) {
this.#status = status; this.#status = status;
} }
} }
@@ -1041,10 +1061,9 @@ export class CMIObjectivesObject extends BaseCMI {
export class CMIInteractionsObjectivesObject extends BaseCMI { export class CMIInteractionsObjectivesObject extends BaseCMI {
/** /**
* Constructor for cmi.interactions.n.objectives.n * Constructor for cmi.interactions.n.objectives.n
* @param {Scorm12API} API
*/ */
constructor(API) { constructor() {
super(API); super();
} }
#id: ''; #id: '';
@@ -1062,7 +1081,7 @@ export class CMIInteractionsObjectivesObject extends BaseCMI {
* @param {string} id * @param {string} id
*/ */
set id(id) { set id(id) {
if (this.API.checkValidFormat(id, regex.CMIIdentifier)) { if (check12ValidFormat(id, regex.CMIIdentifier)) {
this.#id = id; this.#id = id;
} }
} }
@@ -1091,10 +1110,9 @@ export class CMIInteractionsObjectivesObject extends BaseCMI {
export class CMIInteractionsCorrectResponsesObject extends BaseCMI { export class CMIInteractionsCorrectResponsesObject extends BaseCMI {
/** /**
* Constructor for cmi.interactions.correct_responses.n * Constructor for cmi.interactions.correct_responses.n
* @param {Scorm12API} API
*/ */
constructor(API) { constructor() {
super(API); super();
} }
#pattern: ''; #pattern: '';
@@ -1112,7 +1130,7 @@ export class CMIInteractionsCorrectResponsesObject extends BaseCMI {
* @param {string} pattern * @param {string} pattern
*/ */
set pattern(pattern) { set pattern(pattern) {
if (this.API.checkValidFormat(pattern, regex.CMIFeedback)) { if (check12ValidFormat(pattern, regex.CMIFeedback)) {
this.#pattern = pattern; this.#pattern = pattern;
} }
} }

File diff suppressed because it is too large Load Diff

33
src/exceptions.js Normal file
View File

@@ -0,0 +1,33 @@
// @flow
/**
* Data Validation Exception
*/
export class ValidationError extends Error {
/**
* Constructor to take in an error message and code
* @param {number} errorCode
*/
constructor(errorCode: number) {
super(errorCode);
this.#errorCode = errorCode;
}
#errorCode;
/**
* Getter for #errorCode
* @return {number}
*/
get errorCode() {
return this.#errorCode;
}
/**
* Trying to override the default Error message
* @return {string}
*/
get message() {
return this.#errorCode + '';
}
}

View File

@@ -1,29 +1,27 @@
import {expect, assert} from 'chai'; import {expect} from 'chai';
import {describe, it, beforeEach, afterEach} from 'mocha'; import {describe, it, beforeEach, afterEach} from 'mocha';
import AICC from '../../src/AICC';
import {aicc_constants} from '../../src/constants/api_constants'; import {aicc_constants} from '../../src/constants/api_constants';
import {scorm12_error_codes} from '../../src/constants/error_codes'; import {scorm12_error_codes} from '../../src/constants/error_codes';
import {CMI} from '../../src/cmi/aicc_cmi';
let API; let cmi;
const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => { const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
it(`Should be able to write upto ${limit} characters to ${fieldName}`, it(`Should be able to write upto ${limit} characters to ${fieldName}`,
() => { () => {
eval(`API.${fieldName} = 'x'.repeat(${limit})`); expect(() => eval(`${fieldName} = 'x'.repeat(${limit})`)).
expect(0).to.equal(API.lastErrorCode); to.not.throw();
}); });
it(`Should fail to write more than ${limit} characters to ${fieldName}`, it(`Should fail to write more than ${limit} characters to ${fieldName}`,
() => { () => {
eval(`API.${fieldName} = 'x'.repeat(${limit + 1})`); expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)).
expect(scorm12_error_codes.TYPE_MISMATCH + ''). to.throw(scorm12_error_codes.TYPE_MISMATCH + '');
to.
equal(API.lastErrorCode);
}); });
}); });
}; };
@@ -31,14 +29,12 @@ const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => {
const checkInvalidSet = ({fieldName, expectedValue = ''}) => { const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
it(`Should fail to write to ${fieldName}`, () => { it(`Should fail to write to ${fieldName}`, () => {
eval(`API.${fieldName} = 'xxx'`); expect(() => eval(`${fieldName} = 'xxx'`)).
expect(API.lastErrorCode). to.throw(scorm12_error_codes.INVALID_SET_VALUE + '');
to.
equal(scorm12_error_codes.INVALID_SET_VALUE + '');
}); });
}); });
}; };
@@ -46,14 +42,12 @@ const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
const checkReadOnly = ({fieldName, expectedValue = ''}) => { const checkReadOnly = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
it(`Should fail to write to ${fieldName}`, () => { it(`Should fail to write to ${fieldName}`, () => {
eval(`API.${fieldName} = 'xxx'`); expect(() => eval(`${fieldName} = 'xxx'`)).
expect(API.lastErrorCode). to.throw(scorm12_error_codes.READ_ONLY_ELEMENT + '');
to.
equal(scorm12_error_codes.READ_ONLY_ELEMENT + '');
}); });
}); });
}; };
@@ -61,7 +55,7 @@ const checkReadOnly = ({fieldName, expectedValue = ''}) => {
const checkRead = ({fieldName, expectedValue = ''}) => { const checkRead = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
}); });
}; };
@@ -69,12 +63,12 @@ const checkRead = ({fieldName, expectedValue = ''}) => {
const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) => { const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
it(`Should successfully write to ${fieldName}`, () => { it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`); expect(() => eval(`${fieldName} = '${valueToTest}'`)).
expect(API.lastErrorCode).to.equal(0); to.not.throw();
}); });
}); });
}; };
@@ -82,15 +76,12 @@ const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'})
const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => { const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should fail to read from ${fieldName}`, () => { it(`Should fail to read from ${fieldName}`, () => {
eval(`API.${fieldName}`); expect(() => eval(`${fieldName}`)).
expect(API.lastErrorCode). to.throw(scorm12_error_codes.WRITE_ONLY_ELEMENT + '');
to.
equal(scorm12_error_codes.WRITE_ONLY_ELEMENT + '');
}); });
it(`Should successfully write to ${fieldName}`, () => { it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`); expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
expect(API.lastErrorCode).to.equal(0);
}); });
}); });
}; };
@@ -98,8 +89,7 @@ const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
const checkWrite = ({fieldName, valueToTest = 'xxx'}) => { const checkWrite = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should successfully write to ${fieldName}`, () => { it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`); expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
expect(API.lastErrorCode).to.equal(0);
}); });
}); });
}; };
@@ -110,8 +100,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
if ({}.hasOwnProperty.call(validValues, idx)) { if ({}.hasOwnProperty.call(validValues, idx)) {
it(`Should successfully write '${validValues[idx]}' to ${fieldName}`, it(`Should successfully write '${validValues[idx]}' to ${fieldName}`,
() => { () => {
eval(`API.${fieldName} = '${validValues[idx]}'`); expect(() => eval(`${fieldName} = '${validValues[idx]}'`)).
expect(API.lastErrorCode).to.equal(0); to.not.throw();
}); });
} }
} }
@@ -120,8 +110,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
if ({}.hasOwnProperty.call(invalidValues, idx)) { if ({}.hasOwnProperty.call(invalidValues, idx)) {
it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`, it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`,
() => { () => {
eval(`API.${fieldName} = '${invalidValues[idx]}'`); expect(() => eval(`${fieldName} = '${invalidValues[idx]}'`)).
expect(API.lastErrorCode).to.equal(expectedError + ''); to.throw(expectedError + '');
}); });
} }
} }
@@ -130,30 +120,12 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
describe('AICC CMI Tests', () => { describe('AICC CMI Tests', () => {
describe('CMI Spec Tests', () => { describe('CMI Spec Tests', () => {
beforeEach('Create the API object', () => {
API = new AICC();
API.lmsInitialize();
});
afterEach('Destroy API object', () => {
API = null;
});
it('lmsInitialize should create CMI object', () => {
assert(API.cmi !== undefined, 'CMI object is created');
});
it('Exporting CMI to JSON produces proper Object', () => {
expect(
JSON.parse(API.renderCMIToJSON()).cmi?.core !== undefined,
).to.be.true;
});
describe('Pre-Initialize Tests', () => { describe('Pre-Initialize Tests', () => {
beforeEach('Create the API object', () => { beforeEach('Create the API object', () => {
API = new AICC(); cmi = new CMI();
}); });
afterEach('Destroy API object', () => { afterEach('Destroy API object', () => {
API = null; cmi = null;
}); });
/** /**
@@ -409,11 +381,11 @@ describe('AICC CMI Tests', () => {
describe('Post-Initialize Tests', () => { describe('Post-Initialize Tests', () => {
beforeEach('Create the API object', () => { beforeEach('Create the API object', () => {
API = new AICC(); cmi = new CMI();
API.lmsInitialize(); cmi.initialize();
}); });
afterEach('Destroy API object', () => { afterEach('Destroy API object', () => {
API = null; cmi = null;
}); });
/** /**

View File

@@ -1,29 +1,27 @@
import {expect, assert} from 'chai'; import {expect} from 'chai';
import {describe, it, beforeEach, afterEach} from 'mocha'; import {describe, it, beforeEach, afterEach} from 'mocha';
import Scorm12API from '../../src/Scorm12API';
import {scorm12_constants} from '../../src/constants/api_constants'; import {scorm12_constants} from '../../src/constants/api_constants';
import {scorm12_error_codes} from '../../src/constants/error_codes'; import {scorm12_error_codes} from '../../src/constants/error_codes';
import {CMI} from '../../src/cmi/scorm12_cmi';
let API; let cmi;
const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => { const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
it(`Should be able to write upto ${limit} characters to ${fieldName}`, it(`Should be able to write upto ${limit} characters to ${fieldName}`,
() => { () => {
eval(`API.${fieldName} = 'x'.repeat(${limit})`); expect(() => eval(`${fieldName} = 'x'.repeat(${limit})`)).
expect(0).to.equal(API.lastErrorCode); to.not.throw();
}); });
it(`Should fail to write more than ${limit} characters to ${fieldName}`, it(`Should fail to write more than ${limit} characters to ${fieldName}`,
() => { () => {
eval(`API.${fieldName} = 'x'.repeat(${limit + 1})`); expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)).
expect(scorm12_error_codes.TYPE_MISMATCH + ''). to.throw(scorm12_error_codes.TYPE_MISMATCH + '');
to.
equal(API.lastErrorCode);
}); });
}); });
}; };
@@ -31,14 +29,12 @@ const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => {
const checkInvalidSet = ({fieldName, expectedValue = ''}) => { const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
it(`Should fail to write to ${fieldName}`, () => { it(`Should fail to write to ${fieldName}`, () => {
eval(`API.${fieldName} = 'xxx'`); expect(() => eval(`${fieldName} = 'xxx'`)).
expect(API.lastErrorCode). to.throw(scorm12_error_codes.INVALID_SET_VALUE + '');
to.
equal(scorm12_error_codes.INVALID_SET_VALUE + '');
}); });
}); });
}; };
@@ -46,14 +42,12 @@ const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
const checkReadOnly = ({fieldName, expectedValue = ''}) => { const checkReadOnly = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
it(`Should fail to write to ${fieldName}`, () => { it(`Should fail to write to ${fieldName}`, () => {
eval(`API.${fieldName} = 'xxx'`); expect(() => eval(`${fieldName} = 'xxx'`)).
expect(API.lastErrorCode). to.throw(scorm12_error_codes.READ_ONLY_ELEMENT + '');
to.
equal(scorm12_error_codes.READ_ONLY_ELEMENT + '');
}); });
}); });
}; };
@@ -61,7 +55,7 @@ const checkReadOnly = ({fieldName, expectedValue = ''}) => {
const checkRead = ({fieldName, expectedValue = ''}) => { const checkRead = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
}); });
}; };
@@ -69,12 +63,12 @@ const checkRead = ({fieldName, expectedValue = ''}) => {
const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) => { const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => { it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`API.${fieldName}`)).to.equal(expectedValue); expect(eval(`${fieldName}`)).to.equal(expectedValue);
}); });
it(`Should successfully write to ${fieldName}`, () => { it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`); expect(() => eval(`${fieldName} = '${valueToTest}'`)).
expect(API.lastErrorCode).to.equal(0); to.not.throw();
}); });
}); });
}; };
@@ -82,15 +76,12 @@ const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'})
const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => { const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should fail to read from ${fieldName}`, () => { it(`Should fail to read from ${fieldName}`, () => {
eval(`API.${fieldName}`); expect(() => eval(`${fieldName}`)).
expect(API.lastErrorCode). to.throw(scorm12_error_codes.WRITE_ONLY_ELEMENT + '');
to.
equal(scorm12_error_codes.WRITE_ONLY_ELEMENT + '');
}); });
it(`Should successfully write to ${fieldName}`, () => { it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`); expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
expect(API.lastErrorCode).to.equal(0);
}); });
}); });
}; };
@@ -98,8 +89,7 @@ const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
const checkWrite = ({fieldName, valueToTest = 'xxx'}) => { const checkWrite = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => { describe(`Field: ${fieldName}`, () => {
it(`Should successfully write to ${fieldName}`, () => { it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`); expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
expect(API.lastErrorCode).to.equal(0);
}); });
}); });
}; };
@@ -110,8 +100,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
if ({}.hasOwnProperty.call(validValues, idx)) { if ({}.hasOwnProperty.call(validValues, idx)) {
it(`Should successfully write '${validValues[idx]}' to ${fieldName}`, it(`Should successfully write '${validValues[idx]}' to ${fieldName}`,
() => { () => {
eval(`API.${fieldName} = '${validValues[idx]}'`); expect(() => eval(`${fieldName} = '${validValues[idx]}'`)).
expect(API.lastErrorCode).to.equal(0); to.not.throw();
}); });
} }
} }
@@ -120,8 +110,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
if ({}.hasOwnProperty.call(invalidValues, idx)) { if ({}.hasOwnProperty.call(invalidValues, idx)) {
it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`, it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`,
() => { () => {
eval(`API.${fieldName} = '${invalidValues[idx]}'`); expect(() => eval(`${fieldName} = '${invalidValues[idx]}'`)).
expect(API.lastErrorCode).to.equal(expectedError + ''); to.throw(expectedError + '');
}); });
} }
} }
@@ -130,30 +120,12 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
describe('SCORM 1.2 CMI Tests', () => { describe('SCORM 1.2 CMI Tests', () => {
describe('CMI Spec Tests', () => { describe('CMI Spec Tests', () => {
beforeEach('Create the API object', () => {
API = new Scorm12API();
API.lmsInitialize();
});
afterEach('Destroy API object', () => {
API = null;
});
it('lmsInitialize should create CMI object', () => {
assert(API.cmi !== undefined, 'CMI object is created');
});
it('Exporting CMI to JSON produces proper Object', () => {
expect(
JSON.parse(API.renderCMIToJSON()).cmi?.core !== undefined,
).to.be.true;
});
describe('Pre-Initialize Tests', () => { describe('Pre-Initialize Tests', () => {
beforeEach('Create the API object', () => { beforeEach('Create the API object', () => {
API = new Scorm12API(); cmi = new CMI();
}); });
afterEach('Destroy API object', () => { afterEach('Destroy API object', () => {
API = null; cmi = null;
}); });
/** /**
@@ -408,11 +380,11 @@ describe('SCORM 1.2 CMI Tests', () => {
describe('Post-Initialize Tests', () => { describe('Post-Initialize Tests', () => {
beforeEach('Create the API object', () => { beforeEach('Create the API object', () => {
API = new Scorm12API(); cmi = new CMI();
API.lmsInitialize(); cmi.initialize();
}); });
afterEach('Destroy API object', () => { afterEach('Destroy API object', () => {
API = null; cmi = null;
}); });
/** /**

View File

@@ -0,0 +1,156 @@
import {expect} from 'chai';
import {describe, it, beforeEach, afterEach} from 'mocha';
import {scorm2004_error_codes} from '../../src/constants/error_codes';
import {scorm2004_constants} from '../../src/constants/api_constants';
import {CMI} from '../../src/cmi/scorm2004_cmi';
let cmi;
const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`${fieldName}`)).to.equal(expectedValue);
});
it(`Should be able to write upto ${limit} characters to ${fieldName}`,
() => {
expect(() => eval(`${fieldName} = 'x'.repeat(${limit})`)).
to.not.throw();
});
it(`Should fail to write more than ${limit} characters to ${fieldName}`,
() => {
expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)).
to.throw(scorm2004_error_codes.TYPE_MISMATCH + '');
});
});
};
const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`${fieldName}`)).to.equal(expectedValue);
});
it(`Should fail to write to ${fieldName}`, () => {
expect(() => eval(`${fieldName} = 'xxx'`)).
to.throw(scorm2004_error_codes.INVALID_SET_VALUE + '');
});
});
};
const checkReadOnly = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`${fieldName}`)).to.equal(expectedValue);
});
it(`Should fail to write to ${fieldName}`, () => {
expect(() => eval(`${fieldName} = 'xxx'`)).
to.throw(scorm2004_error_codes.READ_ONLY_ELEMENT + '');
});
});
};
const checkRead = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`${fieldName}`)).to.equal(expectedValue);
});
});
};
const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should be able to read from ${fieldName}`, () => {
expect(eval(`${fieldName}`)).to.equal(expectedValue);
});
it(`Should successfully write to ${fieldName}`, () => {
expect(() => eval(`${fieldName} = '${valueToTest}'`)).
to.not.throw();
});
});
};
const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should fail to read from ${fieldName}`, () => {
expect(() => eval(`${fieldName}`)).
to.throw(scorm2004_error_codes.WRITE_ONLY_ELEMENT + '');
});
it(`Should successfully write to ${fieldName}`, () => {
expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
});
});
};
const checkWrite = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should successfully write to ${fieldName}`, () => {
expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
});
});
};
const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}) => {
describe(`Field: ${fieldName}`, () => {
for (const idx in validValues) {
if ({}.hasOwnProperty.call(validValues, idx)) {
it(`Should successfully write '${validValues[idx]}' to ${fieldName}`,
() => {
expect(() => eval(`${fieldName} = '${validValues[idx]}'`)).
to.not.throw();
});
}
}
for (const idx in invalidValues) {
if ({}.hasOwnProperty.call(invalidValues, idx)) {
it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`,
() => {
expect(() => eval(`${fieldName} = '${invalidValues[idx]}'`)).
to.throw(expectedError + '');
});
}
}
});
};
describe('SCORM 2004 CMI Tests', () => {
describe('CMI Spec Tests', () => {
describe('Pre-Initialize Tests', () => {
beforeEach('Create the API object', () => {
cmi = new CMI();
});
afterEach('Destroy API object', () => {
cmi = null;
});
/**
* Base CMI Properties
*/
checkReadOnly({fieldName: 'cmi._version', expectedValue: '1.0'});
checkReadOnly({
fieldName: 'cmi._children',
expectedValue: scorm2004_constants.cmi_children,
});
checkValidValues({
fieldName: 'cmi.completion_status',
expectedError: scorm2004_error_codes.TYPE_MISMATCH,
validValues: [
'completed',
'incomplete',
'not attempted',
'unknown',
],
invalidValues: [
'complete',
'passed',
'failed',
],
});
});
});
});