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

View File

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

View File

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

View File

@@ -1,20 +1,68 @@
// @flow
import {scorm12_constants} from '../constants/api_constants';
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
*/
export class BaseCMI {
jsonString = false;
API;
#initialized = false;
/**
* Constructor for base cmi
* @param {BaseAPI} API
* Getter for #initialized
* @return {boolean}
*/
constructor(API: any) {
this.API = API;
get initialized() {
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 {
/**
* Constructor for *.score
* @param {BaseAPI} API
* @param {string} score_children
* @param {string} score_range
* @param {string} max
* @param {number} invalidErrorCode
* @param {number} invalidTypeCode
* @param {number} invalidRangeCode
*/
constructor(API, score_children?, score_range?, invalidErrorCode) {
super(API);
constructor(
{
score_children,
score_range,
max,
invalidErrorCode,
invalidTypeCode,
invalidRangeCode,
}) {
super();
this.#_children = score_children ?
score_children :
scorm12_constants.score_children;
this.#_score_range = score_range ? score_range : false;
this.#max = max ? max : '100';
this.#_invalid_error_code = invalidErrorCode ?
invalidErrorCode :
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;
#_score_range;
#_invalid_error_code;
#_invalid_type_code;
#_invalid_range_code;
#raw = '';
#min = '';
#max = '100';
#max;
/**
* Getter for _children
@@ -63,7 +130,7 @@ export class CMIScore extends BaseCMI {
* @private
*/
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
*/
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.API.checkValidRange(raw, this.#_score_range))) {
checkValidRange(raw, this.#_score_range,
this.#_invalid_range_code))) {
this.#raw = raw;
}
}
@@ -99,9 +168,11 @@ export class CMIScore extends BaseCMI {
* @param {string} 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.API.checkValidRange(min, this.#_score_range))) {
checkValidRange(min, this.#_score_range,
this.#_invalid_range_code))) {
this.#min = min;
}
}
@@ -119,9 +190,11 @@ export class CMIScore extends BaseCMI {
* @param {string} 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.API.checkValidRange(max, this.#_score_range))) {
checkValidRange(max, this.#_score_range,
this.#_invalid_range_code))) {
this.#max = max;
}
}
@@ -145,12 +218,11 @@ export class CMIScore extends BaseCMI {
export class CMIArray extends BaseCMI {
/**
* Constructor cmi *.n arrays
* @param {BaseAPI} API
* @param {string} children
* @param {number} errorCode
*/
constructor({API, children, errorCode}) {
super(API);
constructor({children, errorCode}) {
super();
this.#_children = children;
this.#errorCode = errorCode;
this.childArray = [];
@@ -174,7 +246,7 @@ export class CMIArray extends BaseCMI {
* @private
*/
set _children(_children) {
this.API.throwSCORMError(this.#errorCode);
throw new ValidationError(this.#errorCode);
}
/**
@@ -192,7 +264,7 @@ export class CMIArray extends BaseCMI {
* @private
*/
set _count(_count) {
this.API.throwSCORMError(this.#errorCode);
throw new ValidationError(this.#errorCode);
}
/**

View File

@@ -1,34 +1,60 @@
// @flow
import {BaseCMI, CMIArray, CMIScore} from './common';
import {
BaseCMI,
checkValidFormat,
checkValidRange,
CMIArray,
CMIScore,
} from './common';
import {scorm12_constants} from '../constants/api_constants';
import {scorm12_error_codes} from '../constants/error_codes';
import {scorm12_regex} from '../regex';
import {ValidationError} from '../exceptions';
const constants = scorm12_constants;
const regex = scorm12_regex;
/**
* Helper method for throwing Read Only error
* @param {Scorm12API} API
*/
function throwReadOnlyError(API) {
API.throwSCORMError(scorm12_error_codes.READ_ONLY_ELEMENT);
export function throwReadOnlyError() {
throw new ValidationError(scorm12_error_codes.READ_ONLY_ELEMENT);
}
/**
* Helper method for throwing Write Only error
* @param {Scorm12API} API
*/
function throwWriteOnlyError(API) {
API.throwSCORMError(scorm12_error_codes.WRITE_ONLY_ELEMENT);
export function throwWriteOnlyError() {
throw new ValidationError(scorm12_error_codes.WRITE_ONLY_ELEMENT);
}
/**
* Helper method for throwing Invalid Set error
* @param {Scorm12API} API
*/
function throwInvalidValueError(API) {
API.throwSCORMError(scorm12_error_codes.INVALID_SET_VALUE);
function throwInvalidValueError() {
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
* @param {Scorm12API} API
* @param {string} cmi_children
* @param {string} student_data
* @param {(CMIStudentData|AICCCMIStudentData)} student_data
* @param {boolean} initialized
*/
constructor(API, cmi_children, student_data) {
super(API);
constructor(cmi_children, student_data, initialized: boolean) {
super();
if (initialized) this.initialize();
this.#_children = cmi_children ? cmi_children : constants.cmi_children;
this.core = new CMICore(API);
this.objectives = new CMIObjectives(API);
this.student_data = student_data ?
student_data :
new CMIStudentData(API);
this.student_preference = new CMIStudentPreference(API);
this.interactions = new CMIInteractions(API);
this.core = new CMICore();
this.objectives = new CMIObjectives();
this.student_data = student_data ? student_data : new CMIStudentData();
this.student_preference = new CMIStudentPreference();
this.interactions = new CMIInteractions();
}
/**
* 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
*/
set _version(_version) {
throwInvalidValueError(this.API);
throwInvalidValueError();
}
/**
@@ -130,7 +168,7 @@ export class CMI extends BaseCMI {
* @private
*/
set _children(_children) {
throwInvalidValueError(this.API);
throwInvalidValueError();
}
/**
@@ -146,7 +184,7 @@ export class CMI extends BaseCMI {
* @param {string} 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;
}
}
@@ -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
*/
set launch_data(launch_data) {
this.API.isNotInitialized() ?
this.#launch_data = launch_data :
throwReadOnlyError(this.API);
!this.initialized ? this.#launch_data = launch_data : throwReadOnlyError();
}
/**
@@ -182,7 +218,7 @@ export class CMI extends BaseCMI {
* @param {string} comments
*/
set comments(comments) {
if (this.API.checkValidFormat(comments, regex.CMIString4096)) {
if (check12ValidFormat(comments, regex.CMIString4096)) {
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
*/
set comments_from_lms(comments_from_lms) {
this.API.isNotInitialized() ?
this.#comments_from_lms = comments_from_lms :
throwReadOnlyError(this.API);
!this.initialized ? this.#comments_from_lms = comments_from_lms : throwReadOnlyError();
}
}
@@ -212,13 +246,26 @@ export class CMI extends BaseCMI {
class CMICore extends BaseCMI {
/**
* Constructor for cmi.core
* @param {Scorm12API} API
*/
constructor(API) {
super(API);
constructor() {
super();
this.score = new Scorm12CMIScore(API, constants.score_children,
regex.score_range);
this.score = new Scorm12CMIScore(
{
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;
@@ -248,7 +295,7 @@ class CMICore extends BaseCMI {
* @private
*/
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
*/
set student_id(student_id) {
this.API.isNotInitialized() ?
this.#student_id = student_id :
throwReadOnlyError(this.API);
!this.initialized ? this.#student_id = student_id : throwReadOnlyError();
}
/**
@@ -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
*/
set student_name(student_name) {
this.API.isNotInitialized() ?
this.#student_name = student_name :
throwReadOnlyError(this.API);
!this.initialized ? this.#student_name = student_name : throwReadOnlyError();
}
/**
@@ -300,7 +343,7 @@ class CMICore extends BaseCMI {
* @param {string} 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;
}
}
@@ -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
*/
set credit(credit) {
this.API.isNotInitialized() ?
this.#credit = credit :
throwReadOnlyError(this.API);
!this.initialized ? this.#credit = credit : throwReadOnlyError();
}
/**
@@ -336,7 +377,7 @@ class CMICore extends BaseCMI {
* @param {string} 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;
}
}
@@ -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
*/
set entry(entry) {
this.API.isNotInitialized() ?
this.#entry = entry :
throwReadOnlyError(this.API);
!this.initialized ? this.#entry = entry : throwReadOnlyError();
}
/**
@@ -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
*/
set total_time(total_time) {
this.API.isNotInitialized() ?
this.#total_time = total_time :
throwReadOnlyError(this.API);
!this.initialized ? this.#total_time = total_time : throwReadOnlyError();
}
/**
@@ -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
*/
set lesson_mode(lesson_mode) {
this.API.isNotInitialized() ?
this.#lesson_mode = lesson_mode :
throwReadOnlyError(this.API);
!this.initialized ? this.#lesson_mode = lesson_mode : throwReadOnlyError();
}
/**
@@ -400,7 +435,7 @@ class CMICore extends BaseCMI {
* @return {*}
*/
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
*/
set exit(exit) {
if (this.API.checkValidFormat(exit, regex.CMIExit)) {
if (check12ValidFormat(exit, regex.CMIExit)) {
this.#exit = exit;
}
}
@@ -418,9 +453,7 @@ class CMICore extends BaseCMI {
* @return {*}
*/
get session_time() {
return (!this.jsonString) ?
throwWriteOnlyError(this.API) :
this.#session_time;
return (!this.jsonString) ? throwWriteOnlyError() : this.#session_time;
}
/**
@@ -428,7 +461,7 @@ class CMICore extends BaseCMI {
* @param {string} 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;
}
}
@@ -478,11 +511,9 @@ class CMICore extends BaseCMI {
class CMIObjectives extends CMIArray {
/**
* Constructor for cmi.objectives
* @param {Scorm12API} API
*/
constructor(API) {
constructor() {
super({
API: API,
children: constants.objectives_children,
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
});
@@ -500,11 +531,10 @@ export class CMIStudentData extends BaseCMI {
/**
* Constructor for cmi.student_data
* @param {Scorm12API} API
* @param {string} student_data_children
*/
constructor(API, student_data_children) {
super(API);
constructor(student_data_children) {
super();
this.#_children = student_data_children ?
student_data_children :
@@ -526,7 +556,7 @@ export class CMIStudentData extends BaseCMI {
* @private
*/
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
*/
set mastery_score(mastery_score) {
this.API.isNotInitialized() ?
this.#mastery_score = mastery_score :
throwReadOnlyError(this.API);
!this.initialized ? this.#mastery_score = mastery_score : throwReadOnlyError();
}
/**
@@ -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
*/
set max_time_allowed(max_time_allowed) {
this.API.isNotInitialized() ?
this.#max_time_allowed = max_time_allowed :
throwReadOnlyError(this.API);
!this.initialized ? this.#max_time_allowed = max_time_allowed : throwReadOnlyError();
}
/**
@@ -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
*/
set time_limit_action(time_limit_action) {
this.API.isNotInitialized() ?
this.#time_limit_action = time_limit_action :
throwReadOnlyError(this.API);
!this.initialized ? this.#time_limit_action = time_limit_action : throwReadOnlyError();
}
/**
@@ -612,10 +636,9 @@ export class CMIStudentData extends BaseCMI {
class CMIStudentPreference extends BaseCMI {
/**
* Constructor for cmi.student_preference
* @param {Scorm12API} API
*/
constructor(API) {
super(API);
constructor() {
super();
}
#_children = constants.student_preference_children;
@@ -639,7 +662,7 @@ class CMIStudentPreference extends BaseCMI {
* @private
*/
set _children(_children) {
throwInvalidValueError(this.API);
throwInvalidValueError();
}
/**
@@ -655,8 +678,8 @@ class CMIStudentPreference extends BaseCMI {
* @param {string} audio
*/
set audio(audio) {
if (this.API.checkValidFormat(audio, regex.CMISInteger) &&
this.API.checkValidRange(audio, regex.audio_range)) {
if (check12ValidFormat(audio, regex.CMISInteger) &&
check12ValidRange(audio, regex.audio_range)) {
this.#audio = audio;
}
}
@@ -674,7 +697,7 @@ class CMIStudentPreference extends BaseCMI {
* @param {string} language
*/
set language(language) {
if (this.API.checkValidFormat(language, regex.CMIString256)) {
if (check12ValidFormat(language, regex.CMIString256)) {
this.#language = language;
}
}
@@ -692,8 +715,8 @@ class CMIStudentPreference extends BaseCMI {
* @param {string} speed
*/
set speed(speed) {
if (this.API.checkValidFormat(speed, regex.CMISInteger) &&
this.API.checkValidRange(speed, regex.speed_range)) {
if (check12ValidFormat(speed, regex.CMISInteger) &&
check12ValidRange(speed, regex.speed_range)) {
this.#speed = speed;
}
}
@@ -711,8 +734,8 @@ class CMIStudentPreference extends BaseCMI {
* @param {string} text
*/
set text(text) {
if (this.API.checkValidFormat(text, regex.CMISInteger) &&
this.API.checkValidRange(text, regex.text_range)) {
if (check12ValidFormat(text, regex.CMISInteger) &&
check12ValidRange(text, regex.text_range)) {
this.#text = text;
}
}
@@ -748,11 +771,9 @@ class CMIStudentPreference extends BaseCMI {
class CMIInteractions extends CMIArray {
/**
* Constructor for cmi.interactions
* @param {Scorm12API} API
*/
constructor(API) {
constructor() {
super({
API: API,
children: constants.interactions_children,
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
});
@@ -765,23 +786,29 @@ class CMIInteractions extends CMIArray {
export class CMIInteractionsObject extends BaseCMI {
/**
* Constructor for cmi.interactions.n object
* @param {Scorm12API} API
*/
constructor(API) {
super(API);
constructor() {
super();
this.objectives = new CMIArray({
API: API,
errorCode: 402,
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
children: constants.objectives_children,
});
this.correct_responses = new CMIArray({
API: API,
errorCode: 402,
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
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: '';
#time: '';
#type: '';
@@ -795,7 +822,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*}
*/
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
*/
set id(id) {
if (this.API.checkValidFormat(id, regex.CMIIdentifier)) {
if (check12ValidFormat(id, regex.CMIIdentifier)) {
this.#id = id;
}
}
@@ -813,7 +840,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*}
*/
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
*/
set time(time) {
if (this.API.checkValidFormat(time, regex.CMITime)) {
if (check12ValidFormat(time, regex.CMITime)) {
this.#time = time;
}
}
@@ -831,7 +858,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*}
*/
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
*/
set type(type) {
if (this.API.checkValidFormat(type, regex.CMIType)) {
if (check12ValidFormat(type, regex.CMIType)) {
this.#type = type;
}
}
@@ -850,7 +877,7 @@ export class CMIInteractionsObject extends BaseCMI {
*/
get weighting() {
return (!this.jsonString) ?
throwWriteOnlyError(this.API) :
throwWriteOnlyError() :
this.#weighting;
}
@@ -859,8 +886,8 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} weighting
*/
set weighting(weighting) {
if (this.API.checkValidFormat(weighting, regex.CMIDecimal) &&
this.API.checkValidRange(weighting, regex.weighting_range)) {
if (check12ValidFormat(weighting, regex.CMIDecimal) &&
check12ValidRange(weighting, regex.weighting_range)) {
this.#weighting = weighting;
}
}
@@ -870,9 +897,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*}
*/
get student_response() {
return (!this.jsonString) ?
throwWriteOnlyError(this.API) :
this.#student_response;
return (!this.jsonString) ? throwWriteOnlyError() : this.#student_response;
}
/**
@@ -880,7 +905,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} 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;
}
}
@@ -890,9 +915,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*}
*/
get result() {
return (!this.jsonString) ?
throwWriteOnlyError(this.API) :
this.#result;
return (!this.jsonString) ? throwWriteOnlyError() : this.#result;
}
/**
@@ -900,7 +923,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} result
*/
set result(result) {
if (this.API.checkValidFormat(result, regex.CMIResult)) {
if (check12ValidFormat(result, regex.CMIResult)) {
this.#result = result;
}
}
@@ -910,9 +933,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @return {*}
*/
get latency() {
return (!this.jsonString) ?
throwWriteOnlyError(this.API) :
this.#latency;
return (!this.jsonString) ? throwWriteOnlyError() : this.#latency;
}
/**
@@ -920,7 +941,7 @@ export class CMIInteractionsObject extends BaseCMI {
* @param {string} latency
*/
set latency(latency) {
if (this.API.checkValidFormat(latency, regex.CMITimespan)) {
if (check12ValidFormat(latency, regex.CMITimespan)) {
this.#latency = latency;
}
}
@@ -966,12 +987,11 @@ export class CMIInteractionsObject extends BaseCMI {
export class CMIObjectivesObject extends BaseCMI {
/**
* Constructor for cmi.objectives.n
* @param {Scorm12API} API
*/
constructor(API) {
super(API);
constructor() {
super();
this.score = new Scorm12CMIScore(API);
this.score = new Scorm12CMIScore();
}
#id: '';
@@ -990,7 +1010,7 @@ export class CMIObjectivesObject extends BaseCMI {
* @param {string} id
*/
set id(id) {
if (this.API.checkValidFormat(id, regex.CMIIdentifier)) {
if (check12ValidFormat(id, regex.CMIIdentifier)) {
this.#id = id;
}
}
@@ -1008,7 +1028,7 @@ export class CMIObjectivesObject extends BaseCMI {
* @param {string} status
*/
set status(status) {
if (this.API.checkValidFormat(status, regex.CMIStatus2)) {
if (check12ValidFormat(status, regex.CMIStatus2)) {
this.#status = status;
}
}
@@ -1041,10 +1061,9 @@ export class CMIObjectivesObject extends BaseCMI {
export class CMIInteractionsObjectivesObject extends BaseCMI {
/**
* Constructor for cmi.interactions.n.objectives.n
* @param {Scorm12API} API
*/
constructor(API) {
super(API);
constructor() {
super();
}
#id: '';
@@ -1062,7 +1081,7 @@ export class CMIInteractionsObjectivesObject extends BaseCMI {
* @param {string} id
*/
set id(id) {
if (this.API.checkValidFormat(id, regex.CMIIdentifier)) {
if (check12ValidFormat(id, regex.CMIIdentifier)) {
this.#id = id;
}
}
@@ -1091,10 +1110,9 @@ export class CMIInteractionsObjectivesObject extends BaseCMI {
export class CMIInteractionsCorrectResponsesObject extends BaseCMI {
/**
* Constructor for cmi.interactions.correct_responses.n
* @param {Scorm12API} API
*/
constructor(API) {
super(API);
constructor() {
super();
}
#pattern: '';
@@ -1112,7 +1130,7 @@ export class CMIInteractionsCorrectResponsesObject extends BaseCMI {
* @param {string} pattern
*/
set pattern(pattern) {
if (this.API.checkValidFormat(pattern, regex.CMIFeedback)) {
if (check12ValidFormat(pattern, regex.CMIFeedback)) {
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 AICC from '../../src/AICC';
import {aicc_constants} from '../../src/constants/api_constants';
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 = ''}) => {
describe(`Field: ${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}`,
() => {
eval(`API.${fieldName} = 'x'.repeat(${limit})`);
expect(0).to.equal(API.lastErrorCode);
expect(() => eval(`${fieldName} = 'x'.repeat(${limit})`)).
to.not.throw();
});
it(`Should fail to write more than ${limit} characters to ${fieldName}`,
() => {
eval(`API.${fieldName} = 'x'.repeat(${limit + 1})`);
expect(scorm12_error_codes.TYPE_MISMATCH + '').
to.
equal(API.lastErrorCode);
expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)).
to.throw(scorm12_error_codes.TYPE_MISMATCH + '');
});
});
};
@@ -31,14 +29,12 @@ const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => {
const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${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}`, () => {
eval(`API.${fieldName} = 'xxx'`);
expect(API.lastErrorCode).
to.
equal(scorm12_error_codes.INVALID_SET_VALUE + '');
expect(() => eval(`${fieldName} = 'xxx'`)).
to.throw(scorm12_error_codes.INVALID_SET_VALUE + '');
});
});
};
@@ -46,14 +42,12 @@ const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
const checkReadOnly = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${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}`, () => {
eval(`API.${fieldName} = 'xxx'`);
expect(API.lastErrorCode).
to.
equal(scorm12_error_codes.READ_ONLY_ELEMENT + '');
expect(() => eval(`${fieldName} = 'xxx'`)).
to.throw(scorm12_error_codes.READ_ONLY_ELEMENT + '');
});
});
};
@@ -61,7 +55,7 @@ const checkReadOnly = ({fieldName, expectedValue = ''}) => {
const checkRead = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${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'}) => {
describe(`Field: ${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}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`);
expect(API.lastErrorCode).to.equal(0);
expect(() => eval(`${fieldName} = '${valueToTest}'`)).
to.not.throw();
});
});
};
@@ -82,15 +76,12 @@ const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'})
const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should fail to read from ${fieldName}`, () => {
eval(`API.${fieldName}`);
expect(API.lastErrorCode).
to.
equal(scorm12_error_codes.WRITE_ONLY_ELEMENT + '');
expect(() => eval(`${fieldName}`)).
to.throw(scorm12_error_codes.WRITE_ONLY_ELEMENT + '');
});
it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`);
expect(API.lastErrorCode).to.equal(0);
expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
});
});
};
@@ -98,8 +89,7 @@ const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
const checkWrite = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`);
expect(API.lastErrorCode).to.equal(0);
expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
});
});
};
@@ -110,8 +100,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
if ({}.hasOwnProperty.call(validValues, idx)) {
it(`Should successfully write '${validValues[idx]}' to ${fieldName}`,
() => {
eval(`API.${fieldName} = '${validValues[idx]}'`);
expect(API.lastErrorCode).to.equal(0);
expect(() => eval(`${fieldName} = '${validValues[idx]}'`)).
to.not.throw();
});
}
}
@@ -120,8 +110,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
if ({}.hasOwnProperty.call(invalidValues, idx)) {
it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`,
() => {
eval(`API.${fieldName} = '${invalidValues[idx]}'`);
expect(API.lastErrorCode).to.equal(expectedError + '');
expect(() => eval(`${fieldName} = '${invalidValues[idx]}'`)).
to.throw(expectedError + '');
});
}
}
@@ -130,30 +120,12 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
describe('AICC CMI 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', () => {
beforeEach('Create the API object', () => {
API = new AICC();
cmi = new CMI();
});
afterEach('Destroy API object', () => {
API = null;
cmi = null;
});
/**
@@ -409,11 +381,11 @@ describe('AICC CMI Tests', () => {
describe('Post-Initialize Tests', () => {
beforeEach('Create the API object', () => {
API = new AICC();
API.lmsInitialize();
cmi = new CMI();
cmi.initialize();
});
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 Scorm12API from '../../src/Scorm12API';
import {scorm12_constants} from '../../src/constants/api_constants';
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 = ''}) => {
describe(`Field: ${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}`,
() => {
eval(`API.${fieldName} = 'x'.repeat(${limit})`);
expect(0).to.equal(API.lastErrorCode);
expect(() => eval(`${fieldName} = 'x'.repeat(${limit})`)).
to.not.throw();
});
it(`Should fail to write more than ${limit} characters to ${fieldName}`,
() => {
eval(`API.${fieldName} = 'x'.repeat(${limit + 1})`);
expect(scorm12_error_codes.TYPE_MISMATCH + '').
to.
equal(API.lastErrorCode);
expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)).
to.throw(scorm12_error_codes.TYPE_MISMATCH + '');
});
});
};
@@ -31,14 +29,12 @@ const checkFieldConstraintSize = ({fieldName, limit, expectedValue = ''}) => {
const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${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}`, () => {
eval(`API.${fieldName} = 'xxx'`);
expect(API.lastErrorCode).
to.
equal(scorm12_error_codes.INVALID_SET_VALUE + '');
expect(() => eval(`${fieldName} = 'xxx'`)).
to.throw(scorm12_error_codes.INVALID_SET_VALUE + '');
});
});
};
@@ -46,14 +42,12 @@ const checkInvalidSet = ({fieldName, expectedValue = ''}) => {
const checkReadOnly = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${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}`, () => {
eval(`API.${fieldName} = 'xxx'`);
expect(API.lastErrorCode).
to.
equal(scorm12_error_codes.READ_ONLY_ELEMENT + '');
expect(() => eval(`${fieldName} = 'xxx'`)).
to.throw(scorm12_error_codes.READ_ONLY_ELEMENT + '');
});
});
};
@@ -61,7 +55,7 @@ const checkReadOnly = ({fieldName, expectedValue = ''}) => {
const checkRead = ({fieldName, expectedValue = ''}) => {
describe(`Field: ${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'}) => {
describe(`Field: ${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}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`);
expect(API.lastErrorCode).to.equal(0);
expect(() => eval(`${fieldName} = '${valueToTest}'`)).
to.not.throw();
});
});
};
@@ -82,15 +76,12 @@ const checkReadAndWrite = ({fieldName, expectedValue = '', valueToTest = 'xxx'})
const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should fail to read from ${fieldName}`, () => {
eval(`API.${fieldName}`);
expect(API.lastErrorCode).
to.
equal(scorm12_error_codes.WRITE_ONLY_ELEMENT + '');
expect(() => eval(`${fieldName}`)).
to.throw(scorm12_error_codes.WRITE_ONLY_ELEMENT + '');
});
it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`);
expect(API.lastErrorCode).to.equal(0);
expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
});
});
};
@@ -98,8 +89,7 @@ const checkWriteOnly = ({fieldName, valueToTest = 'xxx'}) => {
const checkWrite = ({fieldName, valueToTest = 'xxx'}) => {
describe(`Field: ${fieldName}`, () => {
it(`Should successfully write to ${fieldName}`, () => {
eval(`API.${fieldName} = '${valueToTest}'`);
expect(API.lastErrorCode).to.equal(0);
expect(() => eval(`${fieldName} = '${valueToTest}'`)).to.not.throw();
});
});
};
@@ -110,8 +100,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
if ({}.hasOwnProperty.call(validValues, idx)) {
it(`Should successfully write '${validValues[idx]}' to ${fieldName}`,
() => {
eval(`API.${fieldName} = '${validValues[idx]}'`);
expect(API.lastErrorCode).to.equal(0);
expect(() => eval(`${fieldName} = '${validValues[idx]}'`)).
to.not.throw();
});
}
}
@@ -120,8 +110,8 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
if ({}.hasOwnProperty.call(invalidValues, idx)) {
it(`Should fail to write '${invalidValues[idx]}' to ${fieldName}`,
() => {
eval(`API.${fieldName} = '${invalidValues[idx]}'`);
expect(API.lastErrorCode).to.equal(expectedError + '');
expect(() => eval(`${fieldName} = '${invalidValues[idx]}'`)).
to.throw(expectedError + '');
});
}
}
@@ -130,30 +120,12 @@ const checkValidValues = ({fieldName, expectedError, validValues, invalidValues}
describe('SCORM 1.2 CMI 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', () => {
beforeEach('Create the API object', () => {
API = new Scorm12API();
cmi = new CMI();
});
afterEach('Destroy API object', () => {
API = null;
cmi = null;
});
/**
@@ -408,11 +380,11 @@ describe('SCORM 1.2 CMI Tests', () => {
describe('Post-Initialize Tests', () => {
beforeEach('Create the API object', () => {
API = new Scorm12API();
API.lmsInitialize();
cmi = new CMI();
cmi.initialize();
});
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',
],
});
});
});
});