Merge branch 'master' into issue/283

This commit is contained in:
Jonathan Putney
2021-05-27 11:02:44 -04:00
committed by GitHub
18 changed files with 1608 additions and 882 deletions

View File

@@ -0,0 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Mocha Unit Tests" type="mocha-javascript-test-runner">
<node-interpreter>project</node-interpreter>
<node-options />
<mocha-package>$PROJECT_DIR$/node_modules/mocha</mocha-package>
<working-directory>$PROJECT_DIR$</working-directory>
<pass-parent-env>true</pass-parent-env>
<ui>bdd</ui>
<extra-mocha-options>--watch --require @babel/register</extra-mocha-options>
<test-kind>PATTERN</test-kind>
<test-pattern>test/**/*.spec.js</test-pattern>
<method v="2" />
</configuration>
</component>

12
.run/compile.run.xml Normal file
View File

@@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="compile" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="compile" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

818
dist/scorm-again.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,46 +1,46 @@
{
"name": "scorm-again",
"version": "1.5.4",
"version": "1.6.0",
"description": "A modern SCORM JavaScript run-time library for AICC, SCORM 1.2, and SCORM 2004",
"main": "dist/scorm-again.min.js",
"directories": {
"test": "test"
},
"devDependencies": {
"@babel/cli": "^7.13.14",
"@babel/core": "^7.13.15",
"@babel/node": "^7.13.13",
"@babel/cli": "^7.14.3",
"@babel/core": "^7.14.3",
"@babel/node": "^7.14.2",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
"@babel/plugin-proposal-optional-chaining": "^7.14.2",
"@babel/plugin-proposal-private-methods": "^7.13.0",
"@babel/preset-env": "^7.13.15",
"@babel/preset-env": "^7.14.2",
"@babel/preset-flow": "^7.13.13",
"@babel/register": "^7.13.14",
"@types/chai": "^4.2.16",
"@babel/register": "^7.13.16",
"@types/chai": "^4.2.18",
"@types/mocha": "^8.2.2",
"babel-eslint": "^11.0.0-beta.2",
"babelify": "^10.0.0",
"browserify": "^17.0.0",
"chai": "^4.3.4",
"eslint": "^7.24.0",
"eslint": "^7.27.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-import": "^2.23.3",
"fetch-pretender": "^1.5.0",
"grunt": "^1.3.0",
"grunt": "^1.4.1",
"grunt-browserify": "^6.0.0",
"grunt-cli": "^1.4.2",
"grunt-cli": "^1.4.3",
"grunt-contrib-watch": "^1.1.0",
"grunt-mocha-test": "^0.13.3",
"jsdoc": "^3.6.6",
"jsdoc": "^3.6.7",
"jsdoc-babel": "^0.5.0",
"lodash.debounce": "^4.0.8",
"minifyify": "^7.3.5",
"minimist": "^1.2.5",
"mocha": "^8.3.2",
"mocha": "^8.4.0",
"mocha-junit-reporter": "^2.0.0",
"mochawesome": "^6.2.1",
"nyc": "^15.1.0",
"sinon": "^10.0.0"
"sinon": "^11.1.1"
},
"scripts": {
"test": "./node_modules/.bin/mocha --require @babel/register --bdd --recursive --reporter list",

View File

@@ -28,6 +28,7 @@ export default class BaseAPI {
logLevel: global_constants.LOG_LEVEL_ERROR,
selfReportSessionTime: false,
alwaysSendTotalTime: false,
strict_errors: true,
responseHandler: function(xhr) {
let result;
if (typeof xhr !== 'undefined') {
@@ -185,7 +186,21 @@ export default class BaseAPI {
this.#error_codes.RETRIEVE_BEFORE_INIT,
this.#error_codes.RETRIEVE_AFTER_TERM)) {
if (checkTerminated) this.lastErrorCode = 0;
returnValue = this.getCMIValue(CMIElement);
try {
returnValue = this.getCMIValue(CMIElement);
} catch (e) {
if (e instanceof ValidationError) {
this.lastErrorCode = e.errorCode;
returnValue = global_constants.SCORM_FALSE;
} else {
if (e.message) {
console.error(e.message);
} else {
console.error(e);
}
this.throwSCORMError(this.#error_codes.GENERAL);
}
}
this.processListeners(callbackName, CMIElement);
}

View File

@@ -202,7 +202,7 @@ export default class Scorm12API extends BaseAPI {
* Returns the message that corresponds to errorNumber.
*
* @param {*} errorNumber
* @param {boolean }detail
* @param {boolean} detail
* @return {string}
*/
getLmsErrorMessageDetails(errorNumber, detail) {
@@ -278,8 +278,7 @@ export default class Scorm12API extends BaseAPI {
if (this.settings.mastery_override &&
this.cmi.student_data.mastery_score !== '' &&
this.cmi.core.score.raw !== '') {
if (parseFloat(this.cmi.core.score.raw) >=
parseFloat(this.cmi.student_data.mastery_score)) {
if (parseFloat(this.cmi.core.score.raw) >= parseFloat(this.cmi.student_data.mastery_score)) {
this.cmi.core.lesson_status = 'passed';
} else {
this.cmi.core.lesson_status = 'failed';
@@ -287,8 +286,7 @@ export default class Scorm12API extends BaseAPI {
}
}
} else if (this.cmi.core.lesson_mode === 'browse') {
if ((this.startingData?.cmi?.core?.lesson_status || '') === '' &&
originalStatus === 'not attempted') {
if ((this.startingData?.cmi?.core?.lesson_status || '') === '' && originalStatus === 'not attempted') {
this.cmi.core.lesson_status = 'browsed';
}
}
@@ -298,13 +296,11 @@ export default class Scorm12API extends BaseAPI {
this.settings.alwaysSendTotalTime);
if (this.apiLogLevel === global_constants.LOG_LEVEL_DEBUG) {
console.debug('Commit (terminated: ' +
(terminateCommit ? 'yes' : 'no') + '): ');
console.debug('Commit (terminated: ' + (terminateCommit ? 'yes' : 'no') + '): ');
console.debug(commitObject);
}
if (this.settings.lmsCommitUrl) {
return this.processHttpRequest(this.settings.lmsCommitUrl, commitObject,
terminateCommit);
return this.processHttpRequest(this.settings.lmsCommitUrl, commitObject, terminateCommit);
} else {
return global_constants.SCORM_TRUE;
}

View File

@@ -1,16 +1,40 @@
import * as Scorm12CMI from './scorm12_cmi';
import {BaseCMI, CMIArray, CMIScore} from './common';
import {BaseCMI, checkValidFormat, CMIArray, CMIScore} from './common';
import APIConstants from '../constants/api_constants';
import Regex from '../constants/regex';
import ErrorCodes from '../constants/error_codes';
import {
check12ValidFormat,
throwReadOnlyError,
} from './scorm12_cmi';
import {AICCValidationError} from '../exceptions';
const aicc_constants = APIConstants.aicc;
const aicc_regex = Regex.aicc;
const scorm12_error_codes = ErrorCodes.scorm12;
const aicc_error_codes = ErrorCodes.scorm12;
/**
* Helper method for throwing Read Only error
*/
function throwReadOnlyError() {
throw new AICCValidationError(aicc_error_codes.READ_ONLY_ELEMENT);
}
/**
* Helper method, no reason to have to pass the same error codes every time
* @param {*} value
* @param {string} regexPattern
* @param {boolean} allowEmptyString
* @return {boolean}
*/
function checkAICCValidFormat(
value: String,
regexPattern: String,
allowEmptyString?: boolean) {
return checkValidFormat(
value,
regexPattern,
aicc_error_codes.TYPE_MISMATCH,
AICCValidationError,
allowEmptyString
);
}
/**
* CMI Class for AICC
@@ -126,8 +150,11 @@ class CMIEvaluationComments extends CMIArray {
* Constructor for AICC Evaluation Comments object
*/
constructor() {
super(aicc_constants.comments_children,
scorm12_error_codes.INVALID_SET_VALUE);
super({
children: aicc_constants.comments_children,
errorCode: aicc_error_codes.INVALID_SET_VALUE,
errorClass: AICCValidationError,
});
}
}
@@ -142,7 +169,8 @@ class AICCStudentPreferences extends Scorm12CMI.CMIStudentPreference {
super(aicc_constants.student_preference_children);
this.windows = new CMIArray({
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
errorCode: aicc_error_codes.INVALID_SET_VALUE,
errorClass: AICCValidationError,
children: '',
});
}
@@ -174,7 +202,7 @@ class AICCStudentPreferences extends Scorm12CMI.CMIStudentPreference {
* @param {string} lesson_type
*/
set lesson_type(lesson_type: string) {
if (check12ValidFormat(lesson_type, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(lesson_type, aicc_regex.CMIString256)) {
this.#lesson_type = lesson_type;
}
}
@@ -192,7 +220,7 @@ class AICCStudentPreferences extends Scorm12CMI.CMIStudentPreference {
* @param {string} text_color
*/
set text_color(text_color: string) {
if (check12ValidFormat(text_color, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(text_color, aicc_regex.CMIString256)) {
this.#text_color = text_color;
}
}
@@ -210,7 +238,7 @@ class AICCStudentPreferences extends Scorm12CMI.CMIStudentPreference {
* @param {string} text_location
*/
set text_location(text_location: string) {
if (check12ValidFormat(text_location, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(text_location, aicc_regex.CMIString256)) {
this.#text_location = text_location;
}
}
@@ -228,7 +256,7 @@ class AICCStudentPreferences extends Scorm12CMI.CMIStudentPreference {
* @param {string} text_size
*/
set text_size(text_size: string) {
if (check12ValidFormat(text_size, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(text_size, aicc_regex.CMIString256)) {
this.#text_size = text_size;
}
}
@@ -246,7 +274,7 @@ class AICCStudentPreferences extends Scorm12CMI.CMIStudentPreference {
* @param {string} video
*/
set video(video: string) {
if (check12ValidFormat(video, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(video, aicc_regex.CMIString256)) {
this.#video = video;
}
}
@@ -374,6 +402,14 @@ export class CMIStudentDemographics extends BaseCMI {
#telephone = '';
#years_experience = '';
/**
* Getter for _children
* @return {string}
*/
get _children() {
return this.#_children;
}
/**
* Getter for city
* @return {string}
@@ -671,7 +707,7 @@ export class CMIPaths extends CMIArray {
* Constructor for inline Paths Array class
*/
constructor() {
super(aicc_constants.paths_children);
super({children: aicc_constants.paths_children});
}
}
@@ -706,7 +742,7 @@ export class CMIPathsObject extends BaseCMI {
* @param {string} location_id
*/
set location_id(location_id) {
if (check12ValidFormat(location_id, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(location_id, aicc_regex.CMIString256)) {
this.#location_id = location_id;
}
}
@@ -724,7 +760,7 @@ export class CMIPathsObject extends BaseCMI {
* @param {string} date
*/
set date(date) {
if (check12ValidFormat(date, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(date, aicc_regex.CMIString256)) {
this.#date = date;
}
}
@@ -742,7 +778,7 @@ export class CMIPathsObject extends BaseCMI {
* @param {string} time
*/
set time(time) {
if (check12ValidFormat(time, aicc_regex.CMITime)) {
if (checkAICCValidFormat(time, aicc_regex.CMITime)) {
this.#time = time;
}
}
@@ -760,7 +796,7 @@ export class CMIPathsObject extends BaseCMI {
* @param {string} status
*/
set status(status) {
if (check12ValidFormat(status, aicc_regex.CMIStatus2)) {
if (checkAICCValidFormat(status, aicc_regex.CMIStatus2)) {
this.#status = status;
}
}
@@ -778,7 +814,7 @@ export class CMIPathsObject extends BaseCMI {
* @param {string} why_left
*/
set why_left(why_left) {
if (check12ValidFormat(why_left, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(why_left, aicc_regex.CMIString256)) {
this.#why_left = why_left;
}
}
@@ -796,7 +832,7 @@ export class CMIPathsObject extends BaseCMI {
* @param {string} time_in_element
*/
set time_in_element(time_in_element) {
if (check12ValidFormat(time_in_element, aicc_regex.CMITime)) {
if (checkAICCValidFormat(time_in_element, aicc_regex.CMITime)) {
this.#time_in_element = time_in_element;
}
}
@@ -837,7 +873,7 @@ export class CMITries extends CMIArray {
* Constructor for inline Tries Array class
*/
constructor() {
super(aicc_constants.tries_children);
super({children: aicc_constants.tries_children});
}
}
@@ -855,9 +891,10 @@ export class CMITriesObject extends BaseCMI {
{
score_children: aicc_constants.score_children,
score_range: aicc_regex.score_range,
invalidErrorCode: scorm12_error_codes.INVALID_SET_VALUE,
invalidTypeCode: scorm12_error_codes.TYPE_MISMATCH,
invalidRangeCode: scorm12_error_codes.VALUE_OUT_OF_RANGE,
invalidErrorCode: aicc_error_codes.INVALID_SET_VALUE,
invalidTypeCode: aicc_error_codes.TYPE_MISMATCH,
invalidRangeCode: aicc_error_codes.VALUE_OUT_OF_RANGE,
errorClass: AICCValidationError,
});
}
@@ -885,7 +922,7 @@ export class CMITriesObject extends BaseCMI {
* @param {string} status
*/
set status(status) {
if (check12ValidFormat(status, aicc_regex.CMIStatus2)) {
if (checkAICCValidFormat(status, aicc_regex.CMIStatus2)) {
this.#status = status;
}
}
@@ -903,7 +940,7 @@ export class CMITriesObject extends BaseCMI {
* @param {string} time
*/
set time(time) {
if (check12ValidFormat(time, aicc_regex.CMITime)) {
if (checkAICCValidFormat(time, aicc_regex.CMITime)) {
this.#time = time;
}
}
@@ -938,7 +975,7 @@ export class CMIAttemptRecords extends CMIArray {
* Constructor for inline Tries Array class
*/
constructor() {
super(aicc_constants.attempt_records_children);
super({children: aicc_constants.attempt_records_children});
}
}
@@ -956,9 +993,10 @@ export class CMIAttemptRecordsObject extends BaseCMI {
{
score_children: aicc_constants.score_children,
score_range: aicc_regex.score_range,
invalidErrorCode: scorm12_error_codes.INVALID_SET_VALUE,
invalidTypeCode: scorm12_error_codes.TYPE_MISMATCH,
invalidRangeCode: scorm12_error_codes.VALUE_OUT_OF_RANGE,
invalidErrorCode: aicc_error_codes.INVALID_SET_VALUE,
invalidTypeCode: aicc_error_codes.TYPE_MISMATCH,
invalidRangeCode: aicc_error_codes.VALUE_OUT_OF_RANGE,
errorClass: AICCValidationError,
});
}
@@ -985,7 +1023,7 @@ export class CMIAttemptRecordsObject extends BaseCMI {
* @param {string} lesson_status
*/
set lesson_status(lesson_status) {
if (check12ValidFormat(lesson_status, aicc_regex.CMIStatus2)) {
if (checkAICCValidFormat(lesson_status, aicc_regex.CMIStatus2)) {
this.#lesson_status = lesson_status;
}
}
@@ -1039,7 +1077,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI {
* @param {string} content
*/
set content(content) {
if (check12ValidFormat(content, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(content, aicc_regex.CMIString256)) {
this.#content = content;
}
}
@@ -1057,7 +1095,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI {
* @param {string} location
*/
set location(location) {
if (check12ValidFormat(location, aicc_regex.CMIString256)) {
if (checkAICCValidFormat(location, aicc_regex.CMIString256)) {
this.#location = location;
}
}
@@ -1075,7 +1113,7 @@ export class CMIEvaluationCommentsObject extends BaseCMI {
* @param {string} time
*/
set time(time) {
if (check12ValidFormat(time, aicc_regex.CMITime)) {
if (checkAICCValidFormat(time, aicc_regex.CMITime)) {
this.#time = time;
}
}

View File

@@ -1,7 +1,6 @@
// @flow
import APIConstants from '../constants/api_constants';
import ErrorCodes from '../constants/error_codes';
import {ValidationError} from '../exceptions';
import Regex from '../constants/regex';
const scorm12_constants = APIConstants.scorm12;
@@ -14,6 +13,7 @@ const scorm12_error_codes = ErrorCodes.scorm12;
* @param {string} value
* @param {string} regexPattern
* @param {number} errorCode
* @param {class} errorClass
* @param {boolean} allowEmptyString
* @return {boolean}
*/
@@ -21,6 +21,7 @@ export function checkValidFormat(
value: String,
regexPattern: String,
errorCode: number,
errorClass: function,
allowEmptyString?: boolean) {
const formatRegex = new RegExp(regexPattern);
const matches = value.match(formatRegex);
@@ -28,7 +29,7 @@ export function checkValidFormat(
return true;
}
if (value === undefined || !matches || matches[0] === '') {
throw new ValidationError(errorCode);
throw new errorClass.prototype.constructor(errorCode);
}
return true;
}
@@ -39,20 +40,24 @@ export function checkValidFormat(
* @param {*} value
* @param {string} rangePattern
* @param {number} errorCode
* @param {class} errorClass
* @return {boolean}
*/
export function checkValidRange(
value: any, rangePattern: String, errorCode: number) {
value: any,
rangePattern: String,
errorCode: number,
errorClass: function) {
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);
throw new errorClass.prototype.constructor(errorCode);
}
} else {
throw new ValidationError(errorCode);
throw new errorClass.prototype.constructor(errorCode);
}
}
@@ -118,6 +123,7 @@ export class CMIScore extends BaseCMI {
* @param {number} invalidTypeCode
* @param {number} invalidRangeCode
* @param {string} decimalRegex
* @param {class} errorClass
*/
constructor(
{
@@ -128,6 +134,7 @@ export class CMIScore extends BaseCMI {
invalidTypeCode,
invalidRangeCode,
decimalRegex,
errorClass,
}) {
super();
@@ -143,6 +150,7 @@ export class CMIScore extends BaseCMI {
scorm12_error_codes.VALUE_OUT_OF_RANGE;
this.#_decimal_regex = decimalRegex ||
scorm12_regex.CMIDecimal;
this.#_error_class = errorClass;
}
#_children;
@@ -151,6 +159,7 @@ export class CMIScore extends BaseCMI {
#_invalid_type_code;
#_invalid_range_code;
#_decimal_regex;
#_error_class;
#raw = '';
#min = '';
#max;
@@ -170,7 +179,7 @@ export class CMIScore extends BaseCMI {
* @private
*/
set _children(_children) {
throw new ValidationError(this.#_invalid_error_code);
throw new this.#_error_class.prototype.constructor(this.#_invalid_error_code);
}
/**
@@ -186,11 +195,9 @@ export class CMIScore extends BaseCMI {
* @param {string} raw
*/
set raw(raw) {
if (checkValidFormat(raw, this.#_decimal_regex,
this.#_invalid_type_code) &&
if (checkValidFormat(raw, this.#_decimal_regex, this.#_invalid_type_code, this.#_error_class) &&
(!this.#_score_range ||
checkValidRange(raw, this.#_score_range,
this.#_invalid_range_code))) {
checkValidRange(raw, this.#_score_range, this.#_invalid_range_code, this.#_error_class))) {
this.#raw = raw;
}
}
@@ -208,11 +215,9 @@ export class CMIScore extends BaseCMI {
* @param {string} min
*/
set min(min) {
if (checkValidFormat(min, this.#_decimal_regex,
this.#_invalid_type_code) &&
if (checkValidFormat(min, this.#_decimal_regex, this.#_invalid_type_code, this.#_error_class) &&
(!this.#_score_range ||
checkValidRange(min, this.#_score_range,
this.#_invalid_range_code))) {
checkValidRange(min, this.#_score_range, this.#_invalid_range_code, this.#_error_class))) {
this.#min = min;
}
}
@@ -230,11 +235,9 @@ export class CMIScore extends BaseCMI {
* @param {string} max
*/
set max(max) {
if (checkValidFormat(max, this.#_decimal_regex,
this.#_invalid_type_code) &&
if (checkValidFormat(max, this.#_decimal_regex, this.#_invalid_type_code, this.#_error_class) &&
(!this.#_score_range ||
checkValidRange(max, this.#_score_range,
this.#_invalid_range_code))) {
checkValidRange(max, this.#_score_range, this.#_invalid_range_code, this.#_error_class))) {
this.#max = max;
}
}
@@ -263,15 +266,18 @@ export class CMIArray extends BaseCMI {
* Constructor cmi *.n arrays
* @param {string} children
* @param {number} errorCode
* @param {class} errorClass
*/
constructor({children, errorCode}) {
constructor({children, errorCode, errorClass}) {
super();
this.#_children = children;
this.#errorCode = errorCode;
this.#errorClass = errorClass;
this.childArray = [];
}
#errorCode;
#errorClass;
#_children;
/**
@@ -287,7 +293,7 @@ export class CMIArray extends BaseCMI {
* @param {string} _children
*/
set _children(_children) {
throw new ValidationError(this.#errorCode);
throw new this.#errorClass.prototype.constructor(this.#errorCode);
}
/**
@@ -303,7 +309,7 @@ export class CMIArray extends BaseCMI {
* @param {number} _count
*/
set _count(_count) {
throw new ValidationError(this.#errorCode);
throw new this.#errorClass.prototype.constructor(this.#errorCode);
}
/**

View File

@@ -9,7 +9,7 @@ import {
import APIConstants from '../constants/api_constants';
import ErrorCodes from '../constants/error_codes';
import Regex from '../constants/regex';
import {ValidationError} from '../exceptions';
import {Scorm12ValidationError} from '../exceptions';
import * as Utilities from '../utilities';
import * as Util from '../utilities';
@@ -21,21 +21,21 @@ const scorm12_error_codes = ErrorCodes.scorm12;
* Helper method for throwing Read Only error
*/
export function throwReadOnlyError() {
throw new ValidationError(scorm12_error_codes.READ_ONLY_ELEMENT);
throw new Scorm12ValidationError(scorm12_error_codes.READ_ONLY_ELEMENT);
}
/**
* Helper method for throwing Write Only error
*/
export function throwWriteOnlyError() {
throw new ValidationError(scorm12_error_codes.WRITE_ONLY_ELEMENT);
throw new Scorm12ValidationError(scorm12_error_codes.WRITE_ONLY_ELEMENT);
}
/**
* Helper method for throwing Invalid Set error
*/
function throwInvalidValueError() {
throw new ValidationError(scorm12_error_codes.INVALID_SET_VALUE);
throw new Scorm12ValidationError(scorm12_error_codes.INVALID_SET_VALUE);
}
/**
@@ -49,8 +49,13 @@ export function check12ValidFormat(
value: String,
regexPattern: String,
allowEmptyString?: boolean) {
return checkValidFormat(value, regexPattern,
scorm12_error_codes.TYPE_MISMATCH, allowEmptyString);
return checkValidFormat(
value,
regexPattern,
scorm12_error_codes.TYPE_MISMATCH,
Scorm12ValidationError,
allowEmptyString
);
}
/**
@@ -64,8 +69,13 @@ export function check12ValidRange(
value: any,
rangePattern: String,
allowEmptyString?: boolean) {
return checkValidRange(value, rangePattern,
scorm12_error_codes.VALUE_OUT_OF_RANGE, allowEmptyString);
return checkValidRange(
value,
rangePattern,
scorm12_error_codes.VALUE_OUT_OF_RANGE,
Scorm12ValidationError,
allowEmptyString
);
}
/**
@@ -277,6 +287,7 @@ class CMICore extends BaseCMI {
invalidErrorCode: scorm12_error_codes.INVALID_SET_VALUE,
invalidTypeCode: scorm12_error_codes.TYPE_MISMATCH,
invalidRangeCode: scorm12_error_codes.VALUE_OUT_OF_RANGE,
errorClass: Scorm12ValidationError,
});
}
@@ -583,6 +594,7 @@ class CMIObjectives extends CMIArray {
super({
children: scorm12_constants.objectives_children,
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
errorClass: Scorm12ValidationError,
});
}
}
@@ -858,6 +870,7 @@ class CMIInteractions extends CMIArray {
super({
children: scorm12_constants.interactions_children,
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
errorClass: Scorm12ValidationError,
});
}
}
@@ -875,10 +888,12 @@ export class CMIInteractionsObject extends BaseCMI {
this.objectives = new CMIArray({
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
errorClass: Scorm12ValidationError,
children: scorm12_constants.objectives_children,
});
this.correct_responses = new CMIArray({
errorCode: scorm12_error_codes.INVALID_SET_VALUE,
errorClass: Scorm12ValidationError,
children: scorm12_constants.correct_responses_children,
});
}
@@ -1082,6 +1097,7 @@ export class CMIObjectivesObject extends BaseCMI {
invalidErrorCode: scorm12_error_codes.INVALID_SET_VALUE,
invalidTypeCode: scorm12_error_codes.TYPE_MISMATCH,
invalidRangeCode: scorm12_error_codes.VALUE_OUT_OF_RANGE,
errorClass: Scorm12ValidationError,
});
}

View File

@@ -10,7 +10,7 @@ import APIConstants from '../constants/api_constants';
import Regex from '../constants/regex';
import ErrorCodes from '../constants/error_codes';
import Responses from '../constants/response_constants';
import {ValidationError} from '../exceptions';
import {Scorm2004ValidationError} from '../exceptions';
import * as Util from '../utilities';
const scorm2004_constants = APIConstants.scorm2004;
@@ -23,21 +23,35 @@ const scorm2004_regex = Regex.scorm2004;
* Helper method for throwing Read Only error
*/
function throwReadOnlyError() {
throw new ValidationError(scorm2004_error_codes.READ_ONLY_ELEMENT);
throw new Scorm2004ValidationError(scorm2004_error_codes.READ_ONLY_ELEMENT);
}
/**
* Helper method for throwing Write Only error
*/
function throwWriteOnlyError() {
throw new ValidationError(scorm2004_error_codes.WRITE_ONLY_ELEMENT);
throw new Scorm2004ValidationError(scorm2004_error_codes.WRITE_ONLY_ELEMENT);
}
/**
* Helper method for throwing Type Mismatch error
*/
function throwTypeMismatchError() {
throw new ValidationError(scorm2004_error_codes.TYPE_MISMATCH);
throw new Scorm2004ValidationError(scorm2004_error_codes.TYPE_MISMATCH);
}
/**
* Helper method for throwing Dependency Not Established error
*/
function throwDependencyNotEstablishedError() {
throw new Scorm2004ValidationError(scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
}
/**
* Helper method for throwing Dependency Not Established error
*/
function throwGeneralSetError() {
throw new Scorm2004ValidationError(scorm2004_error_codes.GENERAL_SET_FAILURE);
}
/**
@@ -51,8 +65,13 @@ function check2004ValidFormat(
value: String,
regexPattern: String,
allowEmptyString?: boolean) {
return checkValidFormat(value, regexPattern,
scorm2004_error_codes.TYPE_MISMATCH, allowEmptyString);
return checkValidFormat(
value,
regexPattern,
scorm2004_error_codes.TYPE_MISMATCH,
Scorm2004ValidationError,
allowEmptyString,
);
}
/**
@@ -62,8 +81,12 @@ function check2004ValidFormat(
* @return {boolean}
*/
function check2004ValidRange(value: any, rangePattern: String) {
return checkValidRange(value, rangePattern,
scorm2004_error_codes.VALUE_OUT_OF_RANGE);
return checkValidRange(
value,
rangePattern,
scorm2004_error_codes.VALUE_OUT_OF_RANGE,
Scorm2004ValidationError,
);
}
/**
@@ -189,8 +212,8 @@ export class CMI extends BaseCMI {
*/
set completion_threshold(completion_threshold) {
!this.initialized ?
this.#completion_threshold = completion_threshold :
throwReadOnlyError();
this.#completion_threshold = completion_threshold :
throwReadOnlyError();
}
/**
@@ -289,8 +312,8 @@ export class CMI extends BaseCMI {
*/
set learner_name(learner_name) {
!this.initialized ?
this.#learner_name = learner_name :
throwReadOnlyError();
this.#learner_name = learner_name :
throwReadOnlyError();
}
/**
@@ -325,8 +348,8 @@ export class CMI extends BaseCMI {
*/
set max_time_allowed(max_time_allowed) {
!this.initialized ?
this.#max_time_allowed = max_time_allowed :
throwReadOnlyError();
this.#max_time_allowed = max_time_allowed :
throwReadOnlyError();
}
/**
@@ -359,7 +382,7 @@ export class CMI extends BaseCMI {
*/
set progress_measure(progress_measure) {
if (check2004ValidFormat(progress_measure, scorm2004_regex.CMIDecimal) &&
check2004ValidRange(progress_measure, scorm2004_regex.progress_range)) {
check2004ValidRange(progress_measure, scorm2004_regex.progress_range)) {
this.#progress_measure = progress_measure;
}
}
@@ -378,8 +401,8 @@ export class CMI extends BaseCMI {
*/
set scaled_passing_score(scaled_passing_score) {
!this.initialized ?
this.#scaled_passing_score = scaled_passing_score :
throwReadOnlyError();
this.#scaled_passing_score = scaled_passing_score :
throwReadOnlyError();
}
/**
@@ -451,8 +474,8 @@ export class CMI extends BaseCMI {
*/
set time_limit_action(time_limit_action) {
!this.initialized ?
this.#time_limit_action = time_limit_action :
throwReadOnlyError();
this.#time_limit_action = time_limit_action :
throwReadOnlyError();
}
/**
@@ -604,7 +627,7 @@ class CMILearnerPreference extends BaseCMI {
*/
set audio_level(audio_level) {
if (check2004ValidFormat(audio_level, scorm2004_regex.CMIDecimal) &&
check2004ValidRange(audio_level, scorm2004_regex.audio_range)) {
check2004ValidRange(audio_level, scorm2004_regex.audio_range)) {
this.#audio_level = audio_level;
}
}
@@ -641,7 +664,7 @@ class CMILearnerPreference extends BaseCMI {
*/
set delivery_speed(delivery_speed) {
if (check2004ValidFormat(delivery_speed, scorm2004_regex.CMIDecimal) &&
check2004ValidRange(delivery_speed, scorm2004_regex.speed_range)) {
check2004ValidRange(delivery_speed, scorm2004_regex.speed_range)) {
this.#delivery_speed = delivery_speed;
}
}
@@ -660,7 +683,7 @@ class CMILearnerPreference extends BaseCMI {
*/
set audio_captioning(audio_captioning) {
if (check2004ValidFormat(audio_captioning, scorm2004_regex.CMISInteger) &&
check2004ValidRange(audio_captioning, scorm2004_regex.text_range)) {
check2004ValidRange(audio_captioning, scorm2004_regex.text_range)) {
this.#audio_captioning = audio_captioning;
}
}
@@ -701,6 +724,7 @@ class CMIInteractions extends CMIArray {
super({
children: scorm2004_constants.interactions_children,
errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT,
errorClass: Scorm2004ValidationError,
});
}
}
@@ -716,6 +740,7 @@ class CMIObjectives extends CMIArray {
super({
children: scorm2004_constants.objectives_children,
errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT,
errorClass: Scorm2004ValidationError,
});
}
}
@@ -731,6 +756,7 @@ class CMICommentsFromLMS extends CMIArray {
super({
children: scorm2004_constants.comments_children,
errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT,
errorClass: Scorm2004ValidationError,
});
}
}
@@ -746,6 +772,7 @@ class CMICommentsFromLearner extends CMIArray {
super({
children: scorm2004_constants.comments_children,
errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT,
errorClass: Scorm2004ValidationError,
});
}
}
@@ -771,10 +798,12 @@ export class CMIInteractionsObject extends BaseCMI {
this.objectives = new CMIArray({
errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT,
errorClass: Scorm2004ValidationError,
children: scorm2004_constants.objectives_children,
});
this.correct_responses = new CMIArray({
errorCode: scorm2004_error_codes.READ_ONLY_ELEMENT,
errorClass: Scorm2004ValidationError,
children: scorm2004_constants.correct_responses_children,
});
}
@@ -820,8 +849,7 @@ export class CMIInteractionsObject extends BaseCMI {
*/
set type(type) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(type, scorm2004_regex.CMIType)) {
this.#type = type;
@@ -843,8 +871,7 @@ export class CMIInteractionsObject extends BaseCMI {
*/
set timestamp(timestamp) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(timestamp, scorm2004_regex.CMITime)) {
this.#timestamp = timestamp;
@@ -866,8 +893,7 @@ export class CMIInteractionsObject extends BaseCMI {
*/
set weighting(weighting) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(weighting, scorm2004_regex.CMIDecimal)) {
this.#weighting = weighting;
@@ -890,8 +916,7 @@ export class CMIInteractionsObject extends BaseCMI {
*/
set learner_response(learner_response) {
if (this.initialized && (this.#type === '' || this.#id === '')) {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
let nodes = [];
const response_type = learner_responses[this.type];
@@ -933,12 +958,12 @@ export class CMIInteractionsObject extends BaseCMI {
}
}
} else {
throw new ValidationError(scorm2004_error_codes.GENERAL_SET_FAILURE);
throwGeneralSetError();
}
this.#learner_response = learner_response;
} else {
throw new ValidationError(scorm2004_error_codes.TYPE_MISMATCH);
throwTypeMismatchError();
}
}
}
@@ -975,8 +1000,7 @@ export class CMIInteractionsObject extends BaseCMI {
*/
set latency(latency) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(latency, scorm2004_regex.CMITimespan)) {
this.#latency = latency;
@@ -998,8 +1022,7 @@ export class CMIInteractionsObject extends BaseCMI {
*/
set description(description) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250,
true)) {
@@ -1104,8 +1127,7 @@ export class CMIObjectivesObject extends BaseCMI {
*/
set success_status(success_status) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(success_status, scorm2004_regex.CMISStatus)) {
this.#success_status = success_status;
@@ -1127,8 +1149,7 @@ export class CMIObjectivesObject extends BaseCMI {
*/
set completion_status(completion_status) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(completion_status, scorm2004_regex.CMICStatus)) {
this.#completion_status = completion_status;
@@ -1150,12 +1171,11 @@ export class CMIObjectivesObject extends BaseCMI {
*/
set progress_measure(progress_measure) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(progress_measure, scorm2004_regex.CMIDecimal) &&
check2004ValidRange(progress_measure,
scorm2004_regex.progress_range)) {
check2004ValidRange(progress_measure,
scorm2004_regex.progress_range)) {
this.#progress_measure = progress_measure;
}
}
@@ -1175,8 +1195,7 @@ export class CMIObjectivesObject extends BaseCMI {
*/
set description(description) {
if (this.initialized && this.#id === '') {
throw new ValidationError(
scorm2004_error_codes.DEPENDENCY_NOT_ESTABLISHED);
throwDependencyNotEstablishedError();
} else {
if (check2004ValidFormat(description, scorm2004_regex.CMILangString250,
true)) {
@@ -1232,6 +1251,7 @@ class Scorm2004CMIScore extends CMIScore {
invalidTypeCode: scorm2004_error_codes.TYPE_MISMATCH,
invalidRangeCode: scorm2004_error_codes.VALUE_OUT_OF_RANGE,
decimalRegex: scorm2004_regex.CMIDecimal,
errorClass: Scorm2004ValidationError,
});
}
@@ -1249,7 +1269,7 @@ class Scorm2004CMIScore extends CMIScore {
*/
set scaled(scaled) {
if (check2004ValidFormat(scaled, scorm2004_regex.CMIDecimal) &&
check2004ValidRange(scaled, scorm2004_regex.scaled_range)) {
check2004ValidRange(scaled, scorm2004_regex.scaled_range)) {
this.#scaled = scaled;
}
}

View File

@@ -66,6 +66,14 @@ const scorm12 = {
basicMessage: 'Incorrect Data Type',
detailMessage: 'LMSSetValue was called with a value that is not consistent with the data format of the supplied data model element.',
},
'407': {
basicMessage: 'Element Value Out Of Range',
detailMessage: 'The numeric value supplied to a LMSSetValue call is outside of the numeric range allowed for the supplied data model element.',
},
'408': {
basicMessage: 'Data Model Dependency Not Established',
detailMessage: 'Some data model elements cannot be set until another data model element was set. This error condition indicates that the prerequisite element was not set before the dependent element.',
},
},
};

View File

@@ -1,19 +1,31 @@
// @flow
import APIConstants from './constants/api_constants';
const scorm12_errors = APIConstants.scorm12.error_descriptions;
const aicc_errors = APIConstants.aicc.error_descriptions;
const scorm2004_errors = APIConstants.scorm2004.error_descriptions;
/**
* Data Validation Exception
* Base Validation Exception
*/
export class ValidationError extends Error {
/**
* Constructor to take in an error message and code
* @param {number} errorCode
* @param {string} errorMessage
* @param {string} detailedMessage
*/
constructor(errorCode: number) {
super(errorCode);
constructor(errorCode: number, errorMessage: String, detailedMessage: String) {
super(errorMessage);
this.#errorCode = errorCode;
this.#errorMessage = errorMessage;
this.#detailedMessage = detailedMessage;
}
#errorCode;
#errorMessage;
#detailedMessage;
/**
* Getter for #errorCode
@@ -24,10 +36,69 @@ export class ValidationError extends Error {
}
/**
* Trying to override the default Error message
* Getter for #errorMessage
* @return {string}
*/
get message() {
return this.#errorCode + '';
get errorMessage() {
return this.#errorMessage;
}
/**
* Getter for #detailedMessage
* @return {string}
*/
get detailedMessage() {
return this.#detailedMessage;
}
}
/**
* SCORM 1.2 Validation Error
*/
export class Scorm12ValidationError extends ValidationError {
/**
* Constructor to take in an error code
* @param {number} errorCode
*/
constructor(errorCode: number) {
if ({}.hasOwnProperty.call(scorm12_errors, String(errorCode))) {
super(errorCode, scorm12_errors[String(errorCode)].basicMessage, scorm12_errors[String(errorCode)].detailMessage);
} else {
super(101, scorm12_errors['101'].basicMessage, scorm12_errors['101'].detailMessage);
}
}
}
/**
* AICC Validation Error
*/
export class AICCValidationError extends ValidationError {
/**
* Constructor to take in an error code
* @param {number} errorCode
*/
constructor(errorCode: number) {
if ({}.hasOwnProperty.call(aicc_errors, String(errorCode))) {
super(errorCode, aicc_errors[String(errorCode)].basicMessage, aicc_errors[String(errorCode)].detailMessage);
} else {
super(101, aicc_errors['101'].basicMessage, aicc_errors['101'].detailMessage);
}
}
}
/**
* SCORM 2004 Validation Error
*/
export class Scorm2004ValidationError extends ValidationError {
/**
* Constructor to take in an error code
* @param {number} errorCode
*/
constructor(errorCode: number) {
if ({}.hasOwnProperty.call(scorm2004_errors, String(errorCode))) {
super(errorCode, scorm2004_errors[String(errorCode)].basicMessage, scorm2004_errors[String(errorCode)].detailMessage);
} else {
super(101, scorm2004_errors['101'].basicMessage, scorm2004_errors['101'].detailMessage);
}
}
}

View File

@@ -45,7 +45,7 @@ export const checkLMSSetValue = (
if (expectedError > 0) {
if (errorThrown) {
expect(() => api.lmsSetValue(fieldName, valueToTest)).
to.throw(String(expectedError));
to.throw().with.property('errorCode', expectedError);
} else {
api.lmsSetValue(fieldName, valueToTest);
expect(String(api.lmsGetLastError())).to.equal(String(expectedError));
@@ -71,7 +71,7 @@ export const checkLMSGetValue = (
initializeFirst = false,
initializationValue = '',
expectedError = 0,
errorThrown = true,
errorThrown = false,
}) => {
describe(`Field: ${fieldName}`, () => {
const status = expectedError > 0 ? 'fail to' : 'successfully';
@@ -84,7 +84,7 @@ export const checkLMSGetValue = (
if (expectedError > 0) {
if (errorThrown) {
expect(() => api.lmsGetValue(fieldName)).
to.throw(String(expectedError));
to.throw().with.property('errorCode', expectedError);
} else {
api.lmsGetValue(fieldName);
expect(String(api.lmsGetLastError())).to.equal(String(expectedError));
@@ -110,7 +110,7 @@ export const checkSetCMIValue = (
if (expectedError > 0) {
if (errorThrown) {
expect(() => api.setCMIValue(fieldName, valueToTest)).
to.throw(String(expectedError));
to.throw().with.property('errorCode', expectedError);
} else {
api.setCMIValue(fieldName, valueToTest);
expect(String(api.lmsGetLastError())).to.equal(String(expectedError));

View File

@@ -23,7 +23,7 @@ export const checkFieldConstraintSize = (
it(`Should fail to write more than ${limit} characters to ${fieldName}`,
() => {
expect(() => eval(`${fieldName} = 'x'.repeat(${limit + 1})`)).
to.throw(expectedError + '');
to.throw().with.property('errorCode', expectedError);
});
});
};
@@ -42,7 +42,7 @@ export const checkReadOnly = (
it(`Should fail to write to ${fieldName}`, () => {
expect(() => eval(`${fieldName} = 'xxx'`)).
to.throw(expectedError + '');
to.throw().with.property('errorCode', expectedError);
});
});
};
@@ -89,7 +89,7 @@ export const checkWriteOnly = (
describe(`Field: ${fieldName}`, () => {
it(`Should fail to read from ${fieldName}`, () => {
expect(() => eval(`${fieldName}`)).
to.throw(expectedError + '');
to.throw().with.property('errorCode', expectedError);
});
it(`Should successfully write to ${fieldName}`, () => {

View File

@@ -1,16 +1,71 @@
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {ValidationError} from '../src/exceptions';
import {ValidationError, AICCValidationError, Scorm12ValidationError, Scorm2004ValidationError} from '../src/exceptions';
import APIConstants from '../src/constants/api_constants';
const scorm12_errors = APIConstants.scorm12.error_descriptions;
const aicc_errors = APIConstants.aicc.error_descriptions;
const scorm2004_errors = APIConstants.scorm2004.error_descriptions;
const checkValidationMessage = (
{
errorClass,
errorCodes,
error_messages,
}
) => {
describe(`ValidationError: ${typeof errorClass}`, () => {
it(`${typeof errorClass} should return general errorCode number when not recognized`, () => {
expect(
new errorClass.prototype.constructor(53).errorCode,
).to.equal(101);
});
it(`${typeof errorClass} should return general message when not recognized`, () => {
expect(
new errorClass.prototype.constructor(53).message,
).to.equal(error_messages['101'].basicMessage);
});
for (let i = 0; i < errorCodes.length; i++) {
const errorCode = errorCodes[i];
it(`${typeof errorClass} should return proper errorCode number when recognized`, () => {
expect(
new errorClass.prototype.constructor(errorCode).errorCode,
).to.equal(errorCode);
});
it(`${typeof errorClass} should return proper ${errorCode} message`, () => {
expect(
new errorClass.prototype.constructor(errorCode).message,
).to.equal(error_messages[String(errorCode)].basicMessage);
});
}
});
};
describe('Exception Tests', () => {
it('ValidationException should return message string', () => {
expect(
new ValidationError(0).message,
).to.equal('0');
new ValidationError(0, 'Error Message').message,
).to.equal('Error Message');
});
it('ValidationException should return errorCode number', () => {
expect(
new ValidationError(0).errorCode,
new ValidationError(0, 'Error Message').errorCode,
).to.equal(0);
});
checkValidationMessage({
errorClass: AICCValidationError,
errorCodes: [101, 201, 202, 203, 301, 401, 402, 403, 404, 405, 407, 408],
error_messages: aicc_errors,
});
checkValidationMessage({
errorClass: Scorm12ValidationError,
errorCodes: [101, 201, 202, 203, 301, 401, 402, 403, 404, 405, 407, 408],
error_messages: scorm12_errors,
});
checkValidationMessage({
errorClass: Scorm2004ValidationError,
errorCodes: [0, 101, 102, 103, 104, 111, 112, 113, 122, 123, 132, 133, 142, 143, 201, 301, 351, 391, 401, 402, 403, 404, 405, 406, 407, 408],
error_messages: scorm2004_errors,
});
});

1087
yarn.lock

File diff suppressed because it is too large Load Diff