diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index e97f280d..45f7dfda 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -34,7 +34,7 @@ jobs: run: npm run coverage:clover - name: Report coverage if: always() - uses: slavcodev/coverage-monitor-action@1.1.0 + uses: slavcodev/coverage-monitor-action@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} clover_file: coverage/clover.xml diff --git a/.github/workflows/release-package.yml b/.github/workflows/release-package.yml new file mode 100644 index 00000000..1bc1af0f --- /dev/null +++ b/.github/workflows/release-package.yml @@ -0,0 +1,39 @@ +name: Release NPM package + +on: + pull_request: + branches: + - develop + types: [closed] + +jobs: + auto-release: + if: github.event.pull_request.merged == true + name: Automated package release + runs-on: ubuntu-latest + + steps: + - name: Clone the repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 #avoid unrelated history error + token: ${{ secrets.SEMVER_GH_TOKEN }} #bypass branch protection rule + + - name: Configure git user + #configuring git for runner + run: | + git config --global user.name "oat-github-bot" + git config --global user.email "oat-github-bot@taotesting.com" + + - name: Install and apply the release tool + env: + GITHUB_TOKEN: ${{ secrets.SEMVER_GH_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.CI_NPM_RELEASE_TOKEN }} + run: | + # setup the place + npm config set //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN} + npm i -g @oat-sa/tao-extension-release + # install the package + npm ci + #create tag and release a new version + taoRelease npmRelease --release-branch master --no-interactive diff --git a/package-lock.json b/package-lock.json index fe515d37..ebfa392c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,13 +16,13 @@ "@oat-sa/eslint-config-tao": "^2.0.0", "@oat-sa/prettier-config": "^0.1.1", "@oat-sa/rollup-plugin-wildcard-external": "^1.0.0", - "@oat-sa/tao-core-libs": "^0.5.3", - "@oat-sa/tao-core-sdk": "^2.0.1", - "@oat-sa/tao-core-ui": "^2.0.0", - "@oat-sa/tao-item-runner": "^0.8.2", - "@oat-sa/tao-item-runner-qti": "^1.11.1", + "@oat-sa/tao-core-libs": "^1.0.0", + "@oat-sa/tao-core-sdk": "^3.0.0", + "@oat-sa/tao-core-ui": "^3.0.1", + "@oat-sa/tao-item-runner": "^1.0.0", + "@oat-sa/tao-item-runner-qti": "^2.0.1", "@oat-sa/tao-qunit-testrunner": "^2.0.0", - "@oat-sa/tao-test-runner": "^0.9.1", + "@oat-sa/tao-test-runner": "^1.0.0", "async": "0.2.10", "autoprefixer": "^10.4.14", "dompurify": "^2.4.0", @@ -35,7 +35,7 @@ "jquery": "1.9.1", "jquery-mockjax": "^2.5.0", "jquery-simulate": "^1.0.2", - "lodash": "2.4.1", + "lodash": "^4.17.21", "moment": "^2.29.4", "moment-timezone": "^0.5.43", "nyc": "^15.1.0", @@ -2671,9 +2671,9 @@ } }, "node_modules/@oat-sa/tao-core-libs": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@oat-sa/tao-core-libs/-/tao-core-libs-0.5.3.tgz", - "integrity": "sha512-0zRfr1xOrLvbPG6Mqra07qfoG5A46hcz4/2sFmz+b+hNl6YfCIgElSUkniXyQo4dcC651kS61VfGPedkuue3cQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oat-sa/tao-core-libs/-/tao-core-libs-1.0.0.tgz", + "integrity": "sha512-XYoJfq2IRmt9Bx3ACe3RsZDkUICn6IhGLn23WD3WFWMDTU+xuQBL7X3Af98iEcs+xAfZpoldCfVhhAeQx3O9qQ==", "dev": true, "peerDependencies": { "async": "0.2.10", @@ -2682,7 +2682,7 @@ "handlebars": "1.3.0", "interactjs": "1.3.4", "jquery": ">= 1.9.1 < 3.0.0", - "lodash": "2.4.1", + "lodash": "^4.17.21", "moment": "^2.29.4", "moment-timezone": "^0.5.43", "popper.js": "1.16.1", @@ -2692,9 +2692,9 @@ } }, "node_modules/@oat-sa/tao-core-sdk": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@oat-sa/tao-core-sdk/-/tao-core-sdk-2.0.1.tgz", - "integrity": "sha512-1kiqbHOk5+uVqVBRKiiJgfLJWQc0Iqmw4d0vrtsgQk/foiiEGy8tW0lHRRS0cO1nnmBftov7eINuPgEsVYwiJw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@oat-sa/tao-core-sdk/-/tao-core-sdk-3.0.0.tgz", + "integrity": "sha512-YfTY2tFNNjgVcy0SalWkGRRfuTnfIxQaW9rAF7O/8Dff5vWzsJ+80AubFJa4fviJVB7QUzl+Etr24mjDoAZO+g==", "dev": true, "dependencies": { "fastestsmallesttextencoderdecoder": "1.0.14", @@ -2703,21 +2703,21 @@ } }, "node_modules/@oat-sa/tao-core-ui": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@oat-sa/tao-core-ui/-/tao-core-ui-2.0.0.tgz", - "integrity": "sha512-0PivM4VDtzYPWagVaZjULr8ehNcN8VrN4CwzZ8v8mMbqLvYHlQh8bEI+YhSoXp1fdBiCb+Foq8x0aiL9uBuGcA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@oat-sa/tao-core-ui/-/tao-core-ui-3.0.1.tgz", + "integrity": "sha512-wfISAGkAil45HUUq3Nq0xFesBSRkPvr8vPrPEzqQBNjD+Nb4UeybzxXD+pRl1bNPzlg76zcfuDBLElElNv+6dw==", "dev": true }, "node_modules/@oat-sa/tao-item-runner": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@oat-sa/tao-item-runner/-/tao-item-runner-0.8.2.tgz", - "integrity": "sha512-JSP/NRUjn6dImcfLGgpguItB5Q6t+Gf1lWj8ibx7VlImBKQ305/aHxM+M1H1A5p/nSlIxdtD+t5HHwICi9RUQQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oat-sa/tao-item-runner/-/tao-item-runner-1.0.0.tgz", + "integrity": "sha512-wSDlxrzIceNdss6STX0e9Lpz095NuoqaluqOQ/r/CpNLo+6K9HgvWSDNLgjFsgb96FdyCiGDeUz7OaMgg3gakQ==", "dev": true }, "node_modules/@oat-sa/tao-item-runner-qti": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@oat-sa/tao-item-runner-qti/-/tao-item-runner-qti-1.11.1.tgz", - "integrity": "sha512-CEEzGsEVRGquFpG9patw1qARpxywGshzDo57h07vw3e/WyosxsKiw4e6XvxkoVSqx+PVq9O1y6Sue7/C0yF9HQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@oat-sa/tao-item-runner-qti/-/tao-item-runner-qti-2.0.1.tgz", + "integrity": "sha512-P7JFmf48EuahXrnMmeK0k24+GMilJeo4MyglUnMAc+hjkoILyfTmCnNDWRCX+kOKuxn3B6bQT2ZwSzR0hxossA==", "dev": true }, "node_modules/@oat-sa/tao-qunit-testrunner": { @@ -2862,9 +2862,9 @@ } }, "node_modules/@oat-sa/tao-test-runner": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@oat-sa/tao-test-runner/-/tao-test-runner-0.9.1.tgz", - "integrity": "sha512-VFl9M/cO1OuGznSdqf30uqgrXHRoDO56YSD62ZRqeKr+Z3k+syCCp5m789JWp5prGA/VB1S/b1SsTmqd6cfzpw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@oat-sa/tao-test-runner/-/tao-test-runner-1.0.0.tgz", + "integrity": "sha512-ehZcyU+rQS/rN+RO7/qd5Yw3E3u25xTHv7tD+lBJDgnD+AbfOIe1Fcz/K+tFWc97cHzzYo1eUTKsaTsVbbPOSQ==", "dev": true, "engines": { "node": ">=14.0.0" @@ -6265,14 +6265,10 @@ "dev": true }, "node_modules/lodash": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz", - "integrity": "sha512-qa6QqjA9jJB4AYw+NpD2GI4dzHL6Mv0hL+By6iIul4Ce0C1refrjZJmcGvWdnLUwl4LIPtvzje3UQfGH+nCEsQ==", - "dev": true, - "engines": [ - "node", - "rhino" - ] + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.debounce": { "version": "4.0.8", diff --git a/package.json b/package.json index 7bb4fd9c..cfe228cd 100644 --- a/package.json +++ b/package.json @@ -57,13 +57,13 @@ "@oat-sa/eslint-config-tao": "^2.0.0", "@oat-sa/prettier-config": "^0.1.1", "@oat-sa/rollup-plugin-wildcard-external": "^1.0.0", - "@oat-sa/tao-core-libs": "^0.5.3", - "@oat-sa/tao-core-sdk": "^2.0.1", - "@oat-sa/tao-core-ui": "^2.0.0", - "@oat-sa/tao-item-runner": "^0.8.2", - "@oat-sa/tao-item-runner-qti": "^1.11.1", + "@oat-sa/tao-core-libs": "^1.0.0", + "@oat-sa/tao-core-sdk": "^3.0.0", + "@oat-sa/tao-core-ui": "^3.0.1", + "@oat-sa/tao-item-runner": "^1.0.0", + "@oat-sa/tao-item-runner-qti": "^2.0.1", "@oat-sa/tao-qunit-testrunner": "^2.0.0", - "@oat-sa/tao-test-runner": "^0.9.1", + "@oat-sa/tao-test-runner": "^1.0.0", "async": "0.2.10", "autoprefixer": "^10.4.14", "dompurify": "^2.4.0", @@ -76,7 +76,7 @@ "jquery": "1.9.1", "jquery-mockjax": "^2.5.0", "jquery-simulate": "^1.0.2", - "lodash": "2.4.1", + "lodash": "^4.17.21", "moment": "^2.29.4", "moment-timezone": "^0.5.43", "nyc": "^15.1.0", diff --git a/src/branchRule/types/match.js b/src/branchRule/types/match.js index 5f44575f..805b04fd 100644 --- a/src/branchRule/types/match.js +++ b/src/branchRule/types/match.js @@ -18,7 +18,6 @@ /** * @author Péter Halász */ -import _ from 'lodash'; /** * MATCH branching rule @@ -48,8 +47,8 @@ export default function matchBranchRuleFactory( return Promise.all([ responseStore.getCorrectResponse(correctIdentifier), responseStore.getResponse(variableIdentifier) - ]).then(function(result) { - return _.contains(result[0], result[1]); + ]).then(function (result) { + return result[0].includes(result[1]); }); } }; diff --git a/src/helpers/currentItem.js b/src/helpers/currentItem.js index d76d0b8f..a9ba4ba5 100644 --- a/src/helpers/currentItem.js +++ b/src/helpers/currentItem.js @@ -71,7 +71,7 @@ var currentItemHelper = { */ getResponseDeclaration: function getResponseDeclaration(runner, identifier) { var found = null; - _.forEach(currentItemHelper.getDeclarations(runner), function(declaration) { + _.forEach(currentItemHelper.getDeclarations(runner), function (declaration) { var attributes = declaration.attributes || {}; if (attributes.identifier === identifier) { found = declaration; @@ -100,9 +100,9 @@ var currentItemHelper = { if (baseType === 'boolean') { transform = v => v === true || v === 'true'; } else if (baseType === 'integer') { - transform = v => typeof v === 'number' ? v : parseInt(v); + transform = v => (typeof v === 'number' ? v : parseInt(v)); } else if (baseType === 'float') { - transform = v => typeof v === 'number' ? v : parseFloat(v); + transform = v => (typeof v === 'number' ? v : parseFloat(v)); } else if (baseType === 'directedPair' || baseType === 'pair') { transform = v => { if (_.isString(v)) { @@ -155,11 +155,7 @@ var currentItemHelper = { const stringyValue = 'string' === baseType || 'integer' === baseType || 'float' === baseType; - return ( - null === value || - (stringyValue && value === '') || - (cardinality !== 'single' && _.isEmpty(value)) - ); + return null === value || (stringyValue && value === '') || (cardinality !== 'single' && _.isEmpty(value)); }, /** @@ -200,7 +196,7 @@ var currentItemHelper = { var constraintValues = {}; - _.forEach(interactions, function(interaction) { + _.forEach(interactions, function (interaction) { var attributes = interaction.attributes || {}; var qtiClass = interaction.__proto__.qtiClass; var constraintProperty; @@ -233,7 +229,7 @@ var currentItemHelper = { declarations = currentItemHelper.getDeclarations(runner); constraintValues = currentItemHelper.guessInteractionConstraintValues(runner); - _.forEach(declarations, function(declaration) { + _.forEach(declarations, function (declaration) { var attributes = declaration.attributes || {}; var response = responses[attributes.identifier]; var baseType = attributes.baseType; @@ -272,12 +268,11 @@ var currentItemHelper = { return _(interactions) .values() - .filter(function(element) { + .filter(function (element) { return element.qtiClass === 'include'; }) - .pluck('attributes') - .pluck('href') - .value(); + .value() + .map(val => (val.attributes ? val.attributes.href : null)); }, /** @@ -291,9 +286,9 @@ var currentItemHelper = { var textStimuli; if (stimuli.length > 0) { // Filter the ones containing text: - textStimuli = stimuli.filter(function(stimulusHref) { + textStimuli = stimuli.filter(function (stimulusHref) { var domNode = document.querySelector(`.qti-include[data-href="${stimulusHref}"]`); - return _(domNode.childNodes).some(function(child) { + return _(domNode.childNodes).some(function (child) { return child.nodeType === child.TEXT_NODE; }); }); diff --git a/src/navigator/navigator.js b/src/navigator/navigator.js index d0116ebe..3e0400eb 100644 --- a/src/navigator/navigator.js +++ b/src/navigator/navigator.js @@ -38,7 +38,7 @@ import testContextBuilder from 'taoQtiTest/runner/helpers/testContextBuilder'; * @throws {TypeError} if the given parameters aren't objects */ var navigatorFactory = function navigatorFactory(testContext, testMap) { - if (!_.all([testContext, testMap], _.isPlainObject)) { + if (![testContext, testMap].every(_.isPlainObject)) { throw new TypeError('The navigator must be built with a testData, a testContext and a testMap'); } @@ -64,11 +64,7 @@ var navigatorFactory = function navigatorFactory(testContext, testMap) { * @returns {Object} the new test context */ nextItem: function nextItem() { - return testContextBuilder.buildTestContextFromPosition( - testContext, - testMap, - testContext.itemPosition + 1 - ); + return testContextBuilder.buildTestContextFromPosition(testContext, testMap, testContext.itemPosition + 1); }, /** @@ -76,11 +72,7 @@ var navigatorFactory = function navigatorFactory(testContext, testMap) { * @returns {Object} the new test context */ previousItem: function previsousItem() { - return testContextBuilder.buildTestContextFromPosition( - testContext, - testMap, - testContext.itemPosition - 1 - ); + return testContextBuilder.buildTestContextFromPosition(testContext, testMap, testContext.itemPosition - 1); }, /** diff --git a/src/plugins/controls/timer/strategy/strategyHandler.js b/src/plugins/controls/timer/strategy/strategyHandler.js index f3f1af08..190b08ea 100644 --- a/src/plugins/controls/timer/strategy/strategyHandler.js +++ b/src/plugins/controls/timer/strategy/strategyHandler.js @@ -63,7 +63,7 @@ export default function getStrategyHandler(testRunner, strategies) { */ var applyToStrategies = function applyToStrategies(timerId, action) { var api = _.keys(strategyHandler); - if (_.isEmpty(timerId) || _.isEmpty(action) || !_.contains(api, action)) { + if (_.isEmpty(timerId) || _.isEmpty(action) || !api.includes(action)) { throw new TypeError('Invalid timer id or unauthorized action'); } @@ -71,7 +71,7 @@ export default function getStrategyHandler(testRunner, strategies) { return Promise.resolve(); } return Promise.all( - _.map(actives[timerId], function(strategy) { + _.map(actives[timerId], function (strategy) { if (_.isFunction(strategy[action])) { return strategy[action](); } @@ -96,7 +96,7 @@ export default function getStrategyHandler(testRunner, strategies) { * @returns {Promise} resolves once the set up is done */ setUp: function setUp(timer) { - _.forEach(availableStrategies, function(availableStrategy) { + _.forEach(availableStrategies, function (availableStrategy) { var strategy = availableStrategy(testRunner, timer); if (strategy !== false) { actives[timer.id] = actives[timer.id] || []; @@ -152,7 +152,7 @@ export default function getStrategyHandler(testRunner, strategies) { * @returns {Promise} */ tearDown: function tearDown(timer) { - return applyToStrategies(timer.id, 'tearDown').then(function() { + return applyToStrategies(timer.id, 'tearDown').then(function () { actives = _.omit(actives, timer.id); }); } diff --git a/src/plugins/controls/timer/timers.js b/src/plugins/controls/timer/timers.js index ce095fe6..a9e703f6 100644 --- a/src/plugins/controls/timer/timers.js +++ b/src/plugins/controls/timer/timers.js @@ -57,7 +57,7 @@ var getScope = function getScope(value) { if (scopeMapping[value]) { return scopeMapping[value]; } - if (_.contains(scopes, value)) { + if (scopes.includes(value)) { return value; } return null; @@ -101,9 +101,9 @@ export default function getTimers(timeConstraints, isLinear, config) { */ var constraintsWarnings = _.reduce( config.warnings, - function(acc, warnings, qtiScope) { + function (acc, warnings, qtiScope) { var scope = getScope(qtiScope); - acc[scope] = _.map(warnings, function(value, key) { + acc[scope] = _.map(warnings, function (value, key) { return { threshold: parseInt(key, 10) * precision, message: function applyMessage(remainingTime) { @@ -120,7 +120,6 @@ export default function getTimers(timeConstraints, isLinear, config) { {} ); - /** * The warnings comes in a weird format (ie. {scope:[threshold, ...]}) , so we reformat them */ @@ -129,18 +128,12 @@ export default function getTimers(timeConstraints, isLinear, config) { (acc, warnings, qtiScope) => { const scope = getScope(qtiScope); - acc[scope] = _.map(warnings, (value) => ({ + acc[scope] = _.map(warnings, value => ({ threshold: parseInt(value, 10) * precision, message: function applyMessage(remainingTime, unansweredQuestions) { - const displayRemaining = moment - .duration(remainingTime / precision, 'seconds') - .humanize(); - - return format( - warningMessagesForScreenraeder[scope], - displayRemaining, - unansweredQuestions - ); + const displayRemaining = moment.duration(remainingTime / precision, 'seconds').humanize(); + + return format(warningMessagesForScreenraeder[scope], displayRemaining, unansweredQuestions); }, scope, shown: false @@ -178,7 +171,7 @@ export default function getTimers(timeConstraints, isLinear, config) { timer.allowLateSubmission = constraintData.allowLateSubmission; if (type === 'min') { - timer.id = `${type }-${ constraintData.scope }-${ constraintData.source}`; + timer.id = `${type}-${constraintData.scope}-${constraintData.source}`; timer.originalTime = constraintData.minTime * precision; timer.remainingTime = constraintData.minTimeRemaining * precision; } else { @@ -211,7 +204,7 @@ export default function getTimers(timeConstraints, isLinear, config) { return timer; }; - _.forEach(timeConstraints, function(timeConstraint) { + _.forEach(timeConstraints, function (timeConstraint) { var constraintData = _.clone(timeConstraint); var newTimer; diff --git a/src/plugins/tools/areaMasking/areaMasking.js b/src/plugins/tools/areaMasking/areaMasking.js index f4453833..ac0250fe 100644 --- a/src/plugins/tools/areaMasking/areaMasking.js +++ b/src/plugins/tools/areaMasking/areaMasking.js @@ -65,17 +65,14 @@ export default pluginFactory({ const self = this; const testRunner = this.getTestRunner(); - const $container = testRunner - .getAreaBroker() - .getContentArea() - .parent(); + const $container = testRunner.getAreaBroker().getContentArea().parent(); const testRunnerOptions = testRunner.getOptions(); const config = Object.assign({}, defaultConfig, this.getConfig()); const pluginShortcuts = (testRunnerOptions.shortcuts || {})[pluginName] || {}; function addMask() { maskComponent() - .on('render', function() { + .on('render', function () { self.masks.push(this); self.button.turnOn(); @@ -84,7 +81,7 @@ export default pluginFactory({ */ self.trigger('maskadd'); }) - .on('destroy', function() { + .on('destroy', function () { self.masks = _.without(self.masks, this); if (self.masks.length < config.max) { self.enable(); @@ -116,17 +113,17 @@ export default pluginFactory({ }); //add a new mask each time the button is pressed - this.button.on('click', function(e) { + this.button.on('click', function (e) { e.preventDefault(); testRunner.trigger(`${actionPrefix}toggle`); }); // handle the plugin's shortcuts if (testRunnerOptions.allowShortcuts) { - _.forEach(pluginShortcuts, function(command, key) { + _.forEach(pluginShortcuts, function (command, key) { shortcut.add( namespaceHelper.namespaceAll(command, pluginName, true), - function() { + function () { // just fire the action using the event loop testRunner.trigger(actionPrefix + key); }, @@ -167,16 +164,16 @@ export default pluginFactory({ //update plugin state based on changes testRunner .on('loaditem', togglePlugin) - .on('enabletools renderitem', function() { + .on('enabletools renderitem', function () { self.enable(); }) - .on('disabletools unloaditem', function() { + .on('disabletools unloaditem', function () { self.disable(); //remove all masks - _.invoke(self.masks, 'destroy'); + self.masks.forEach(mask => mask.destroy()); }) // commands that controls the plugin - .on(`${actionPrefix}toggle`, function() { + .on(`${actionPrefix}toggle`, function () { if (isEnabled()) { if (self.masks.length === 0) { self.trigger('open'); @@ -184,7 +181,7 @@ export default pluginFactory({ if (self.masks.length < config.max) { addMask(); } else if (config.max === 1) { - _.invoke(self.masks, 'destroy'); + self.masks.forEach(mask => mask.destroy()); } } }); diff --git a/src/provider/toolStateBridge.js b/src/provider/toolStateBridge.js index 89880fc1..ab241628 100644 --- a/src/provider/toolStateBridge.js +++ b/src/provider/toolStateBridge.js @@ -36,7 +36,7 @@ import _ from 'lodash'; var mergeCollection = function mergeCollection(collection) { return _.reduce( collection, - function(acc, value) { + function (acc, value) { if (value) { return _.merge(acc, value); } @@ -83,10 +83,10 @@ export default function toolStateBridgeFactory(testStore, activePlugins) { */ setTools: function setTools(toolNames) { tools = _(toolNames) - .filter(function(toolName) { - return _.contains(activePlugins, toolName); + .filter(function (toolName) { + return activePlugins.includes(toolName); }) - .map(function(toolName) { + .map(function (toolName) { testStore.startChangeTracking(toolName); return toolName; }) @@ -109,22 +109,22 @@ export default function toolStateBridgeFactory(testStore, activePlugins) { * @returns {Promise} resolves with true if restored */ restoreState: function restoreState(toolName, toolState) { - if (_.contains(tools, toolName) && _.isPlainObject(toolState)) { + if (tools.includes(toolName) && _.isPlainObject(toolState)) { return testStore .getStore(toolName) - .then(function(toolStore) { - return toolStore.clear().then(function() { + .then(function (toolStore) { + return toolStore.clear().then(function () { return toolStore; }); }) - .then(function(toolStore) { + .then(function (toolStore) { return Promise.all( - _.map(toolState, function(value, key) { + _.map(toolState, function (value, key) { return toolStore.setItem(key, value); }) ); }) - .then(function() { + .then(function () { testStore.resetChanges(toolName); return true; }); @@ -140,8 +140,8 @@ export default function toolStateBridgeFactory(testStore, activePlugins) { restoreStates: function restoreStates(states) { var self = this; return Promise.all( - _.map(states, function(toolState, toolName) { - return self.restoreState(toolName, toolState).then(function(result) { + _.map(states, function (toolState, toolName) { + return self.restoreState(toolName, toolState).then(function (result) { var formattedResult = {}; formattedResult[toolName] = result; return formattedResult; @@ -157,8 +157,8 @@ export default function toolStateBridgeFactory(testStore, activePlugins) { * @returns {Promise} resolves with the state */ getState: function getState(toolName, reset) { - if (_.contains(tools, toolName) && testStore.hasChanges(toolName)) { - return testStore.getStore(toolName).then(function(toolStore) { + if (tools.includes(toolName) && testStore.hasChanges(toolName)) { + return testStore.getStore(toolName).then(function (toolStore) { if (reset !== false) { testStore.resetChanges(toolName); } @@ -175,8 +175,8 @@ export default function toolStateBridgeFactory(testStore, activePlugins) { getStates: function getStates() { var self = this; return Promise.all( - _.map(tools, function(toolName) { - return self.getState(toolName).then(function(toolState) { + _.map(tools, function (toolName) { + return self.getState(toolName).then(function (toolState) { //format the state to keep the tool identifier var formattedState = {}; if (toolState) { diff --git a/src/proxy/cache/itemPreloader.js b/src/proxy/cache/itemPreloader.js index c47a23a4..c2296b19 100644 --- a/src/proxy/cache/itemPreloader.js +++ b/src/proxy/cache/itemPreloader.js @@ -175,13 +175,22 @@ function itemPreloaderFactory(options) { * @private */ const interactionLoad = item => { - return Promise.all(getItemInteractions(item.itemData).map(interaction => { - if (interactionPreloader.has(interaction.qtiClass)) { - logger.debug(`Loading interaction ${interaction.serial}(${interaction.qtiClass}) for item ${item.itemIdentifier}`); - return interactionPreloader.load(interaction.qtiClass, interaction, item.itemData, item.itemIdentifier); - } - return Promise.resolve(); - })); + return Promise.all( + getItemInteractions(item.itemData).map(interaction => { + if (interactionPreloader.has(interaction.qtiClass)) { + logger.debug( + `Loading interaction ${interaction.serial}(${interaction.qtiClass}) for item ${item.itemIdentifier}` + ); + return interactionPreloader.load( + interaction.qtiClass, + interaction, + item.itemData, + item.itemIdentifier + ); + } + return Promise.resolve(); + }) + ); }; /** @@ -193,13 +202,22 @@ function itemPreloaderFactory(options) { * @private */ const interactionUnload = item => { - return Promise.all(getItemInteractions(item.itemData).map(interaction => { - if (interactionPreloader.has(interaction.qtiClass)) { - logger.debug(`Unloading interaction ${interaction.serial}(${interaction.qtiClass}) for item ${item.itemIdentifier}`); - return interactionPreloader.unload(interaction.qtiClass, interaction, item.itemData, item.itemIdentifier); - } - return Promise.resolve(); - })); + return Promise.all( + getItemInteractions(item.itemData).map(interaction => { + if (interactionPreloader.has(interaction.qtiClass)) { + logger.debug( + `Unloading interaction ${interaction.serial}(${interaction.qtiClass}) for item ${item.itemIdentifier}` + ); + return interactionPreloader.unload( + interaction.qtiClass, + interaction, + item.itemData, + item.itemIdentifier + ); + } + return Promise.resolve(); + }) + ); }; /** @@ -288,7 +306,7 @@ function itemPreloaderFactory(options) { loading.push(assetLoad(item)); } } - return Promise.all(loading).then(results => results.length > 0 && _.all(results, _.isTrue)); + return Promise.all(loading).then(results => results.length > 0 && results.every(Boolean)); }, /** @@ -313,7 +331,7 @@ function itemPreloaderFactory(options) { } } - return Promise.all(loading).then(results => results.length > 0 && _.all(results, _.isTrue)); + return Promise.all(loading).then(results => results.length > 0 && results.every(Boolean)); } }; } diff --git a/src/proxy/cache/proxy.js b/src/proxy/cache/proxy.js index c0d19a3b..3b799eb0 100644 --- a/src/proxy/cache/proxy.js +++ b/src/proxy/cache/proxy.js @@ -174,7 +174,7 @@ export default _.defaults( //we just block those actions and the end of the test if ( - _.contains(blockingActions, action) || + blockingActions.includes(action) || (actionParams.direction === 'next' && navigationHelper.isLast(testMap, testContext.itemIdentifier)) ) { throw offlineErrorHelper.buildErrorFromContext(offlineErrorHelper.getOfflineExitError()); diff --git a/src/proxy/offline/proxy.js b/src/proxy/offline/proxy.js index e8bb01e4..3bd406a1 100644 --- a/src/proxy/offline/proxy.js +++ b/src/proxy/offline/proxy.js @@ -116,7 +116,7 @@ export default _.defaults( * @returns {Object} action result */ this.offlineAction = function offlineAction(action, actionParams) { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { var result = { success: true }; var blockingActions = ['exitTest', 'timeout', 'pause']; var dataHolder = self.getDataHolder(); @@ -125,7 +125,7 @@ export default _.defaults( var isLast = testContext && testMap ? navigationHelper.isLast(testMap, testContext.itemIdentifier) : false; var isOffline = self.isOffline(); - var isBlocked = _.contains(blockingActions, action); + var isBlocked = blockingActions.includes(action); var isNavigationAction = actionParams.direction === 'next' || action === 'skip'; var isDirectionDefined; var isMeaningfullScope = !!actionParams.scope; @@ -138,14 +138,14 @@ export default _.defaults( * @param {Object} options.testContext - current test testContext dataset * @param {Object} results - navigtion result output object */ - var navigate = function(navigator, options, results) { + var navigate = function (navigator, options, results) { var newTestContext; navigator .setTestContext(options.testContext) .setTestMap(options.testMap) .navigate(actionParams.direction, actionParams.scope, actionParams.ref, actionParams) - .then(function(res) { + .then(function (res) { newTestContext = res; if ( @@ -161,7 +161,7 @@ export default _.defaults( results.testContext = newTestContext; resolve(results); }) - .catch(function(err) { + .catch(function (err) { reject(err); }); }; @@ -177,22 +177,22 @@ export default _.defaults( result.testContext = { state: states.testSession.closed }; - const offlineSync = function() { + const offlineSync = function () { offlineSyncModal(self) - .on('proceed', function() { + .on('proceed', function () { self.syncData() - .then(function() { + .then(function () { // if is online resolve promise if (self.isOnline()) { return resolve(result); } }) - .catch(function() { + .catch(function () { return resolve({ success: false }); }); }) - .on('secondaryaction', function() { - self.initiateDownload().catch(function() { + .on('secondaryaction', function () { + self.initiateDownload().catch(function () { return resolve({ success: false }); }); }); @@ -202,7 +202,7 @@ export default _.defaults( } else { return self .syncData() - .then(function() { + .then(function () { if (self.isOffline()) { // in case last request was failed and connection lost // show offlineWaitingDialog @@ -210,7 +210,7 @@ export default _.defaults( } return resolve(result); }) - .catch(function() { + .catch(function () { return resolve({ success: false }); }); } @@ -228,7 +228,7 @@ export default _.defaults( } else { return self .syncData() - .then(function() { + .then(function () { navigate( self.offlineNavigator, { @@ -238,7 +238,7 @@ export default _.defaults( result ); }) - .catch(function() { + .catch(function () { return resolve({ success: false }); }); } @@ -264,7 +264,7 @@ export default _.defaults( return self.actionStore .push(action, self.prepareParams(_.defaults(actionParams || {}, self.requestConfig))) - .then(function() { + .then(function () { return { action: action, params: actionParams @@ -280,21 +280,23 @@ export default _.defaults( * @returns {Promise} resolves with the action result */ this.sendSyncData = function sendSyncData(data, attempt = 1) { - return new Promise((resolve, reject) => self.send('sync', data) - .then(resolve) - .catch((err) => { - if (self.isConnectivityError(err) && attempt < maxSyncAttempts) { - return self.sendSyncData(data, attempt + 1) - .then(resolve) - .catch(reject); - } + return new Promise((resolve, reject) => + self + .send('sync', data) + .then(resolve) + .catch(err => { + if (self.isConnectivityError(err) && attempt < maxSyncAttempts) { + return self + .sendSyncData(data, attempt + 1) + .then(resolve) + .catch(reject); + } - return reject(err); - }) + return reject(err); + }) ); }; - /** * Flush and synchronize actions collected while offline * @@ -305,19 +307,19 @@ export default _.defaults( this.syncInProgress = true; - return this.queue.serie(function() { + return this.queue.serie(function () { return self.actionStore .flush() - .then(function(data) { + .then(function (data) { actions = data; if (data && data.length) { return self.sendSyncData(data); } }) - .catch(function(err) { + .catch(function (err) { if (self.isConnectivityError(err)) { self.setOffline('communicator'); - _.forEach(actions, function(action) { + _.forEach(actions, function (action) { self.actionStore.push(action.action, action.parameters, action.timestamp); }); return; @@ -327,7 +329,8 @@ export default _.defaults( self.trigger('error', err); throw err; - }).then(data => { + }) + .then(data => { self.syncInProgress = false; return data; }); @@ -357,18 +360,18 @@ export default _.defaults( }; this.initiateDownload = function initiateDownload() { - return this.queue.serie(function() { + return this.queue.serie(function () { return self.actionStore .flush() - .then(function(actions) { - _.forEach(actions, function(action) { + .then(function (actions) { + _.forEach(actions, function (action) { self.actionStore.push(action.action, action.parameters, action.timestamp); }); return actions; }) .then(self.prepareDownload) - .then(function(data) { + .then(function (data) { return download(data.filename, data.content); }); }); @@ -411,7 +414,7 @@ export default _.defaults( return true; }); - return InitCallPromise.then(function(response) { + return InitCallPromise.then(function (response) { var promises = []; if (!response.items) { @@ -420,7 +423,7 @@ export default _.defaults( self.itemStore.setCacheSize(_.size(response.items)); - _.forEach(response.items, function(item, itemIdentifier) { + _.forEach(response.items, function (item, itemIdentifier) { promises.push(self.itemStore.set(itemIdentifier, item)); }); @@ -444,7 +447,7 @@ export default _.defaults( destroy: function destroy() { var self = this; - return this.itemStore.clear().then(function() { + return this.itemStore.clear().then(function () { return qtiServiceProxy.destroy.call(self); }); }, @@ -473,7 +476,7 @@ export default _.defaults( submitItem: function submitItem(itemIdentifier, state, response, params) { var self = this; - return this.itemStore.update(itemIdentifier, 'itemState', state).then(function() { + return this.itemStore.update(itemIdentifier, 'itemState', state).then(function () { return qtiServiceProxy.submitItem.call(self, itemIdentifier, state, response, params); }); }, @@ -493,10 +496,10 @@ export default _.defaults( return self .scheduleAction(action, actionParams) - .then(function() { + .then(function () { return self.offlineAction(action, actionParams); }) - .catch(function(err) { + .catch(function (err) { return Promise.reject(err); }); }, @@ -514,10 +517,10 @@ export default _.defaults( return self .scheduleAction(action, params) - .then(function() { + .then(function () { return self.offlineAction(action, params); }) - .catch(function(err) { + .catch(function (err) { return Promise.reject(err); }); }, @@ -550,19 +553,19 @@ export default _.defaults( } return updateStatePromise - .then(function() { + .then(function () { params = _.assign({ itemDefinition: itemIdentifier }, params); return self .scheduleAction(action, params) - .then(function() { + .then(function () { return self.offlineAction(action, params); }) - .catch(function(err) { + .catch(function (err) { return Promise.reject(err); }); }) - .catch(function(err) { + .catch(function (err) { return Promise.reject(err); }); } diff --git a/src/proxy/qtiServiceProxy.js b/src/proxy/qtiServiceProxy.js index 19467849..693b91bc 100644 --- a/src/proxy/qtiServiceProxy.js +++ b/src/proxy/qtiServiceProxy.js @@ -55,8 +55,8 @@ var qtiServiceProxy = { var stringifyParams = ['itemState', 'itemResponse', 'toolStates']; if (_.isPlainObject(actionParams)) { - return _.mapValues(actionParams, function(value, key) { - if (_.contains(stringifyParams, key)) { + return _.mapValues(actionParams, function (value, key) { + if (stringifyParams.includes(key)) { return JSON.stringify(value); } return value; @@ -86,7 +86,7 @@ var qtiServiceProxy = { sequential: true, timeout: self.configStorage.getTimeout() }) - .then(function(response) { + .then(function (response) { self.setOnline(); if (response && response.success) { @@ -95,7 +95,7 @@ var qtiServiceProxy = { return Promise.reject(response); } }) - .catch(function(error) { + .catch(function (error) { if (self.isConnectivityError(error)) { self.setOffline('request'); } diff --git a/test/mocks/areaBrokerMock.js b/test/mocks/areaBrokerMock.js index a286c19b..ef365648 100644 --- a/test/mocks/areaBrokerMock.js +++ b/test/mocks/areaBrokerMock.js @@ -19,7 +19,7 @@ * @author Jean-Sébastien Conan * @author Christophe Noël */ -define(['jquery', 'lodash', 'ui/areaBroker', 'taoQtiTest/runner/ui/toolbox/toolbox'], function( +define(['jquery', 'lodash', 'ui/areaBroker', 'taoQtiTest/runner/ui/toolbox/toolbox'], function ( $, _, areaBrokerFactory, @@ -74,14 +74,11 @@ define(['jquery', 'lodash', 'ui/areaBroker', 'taoQtiTest/runner/ui/toolbox/toolb if (!config.areas) { config.areas = config.defaultAreas; } else { - config.areas = _.keys(_.merge(_.object(config.areas), _.object(config.defaultAreas))); + config.areas = [...new Set([...config.areas, ...config.defaultAreas])]; } _.forEach(config.areas, areaId => { - config.mapping[areaId] = $('
') - .addClass('test-area') - .addClass(areaId) - .appendTo($areaBrokerDom); + config.mapping[areaId] = $('
').addClass('test-area').addClass(areaId).appendTo($areaBrokerDom); }); // Create only missing areas diff --git a/test/ui/toolbox/toolbox/test.js b/test/ui/toolbox/toolbox/test.js index aca7e57c..d3468ebf 100644 --- a/test/ui/toolbox/toolbox/test.js +++ b/test/ui/toolbox/toolbox/test.js @@ -18,14 +18,19 @@ /** * @author Christophe Noël */ -define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], function($, _, hider, toolboxFactory) { +define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], function ( + $, + _, + hider, + toolboxFactory +) { 'use strict'; var fixtureContainer = '#qunit-fixture'; QUnit.module('plugin'); - QUnit.test('module', function(assert) { + QUnit.test('module', function (assert) { assert.expect(1); assert.ok(typeof toolboxFactory === 'function', 'The module expose a function'); @@ -33,7 +38,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], QUnit.module('Toolbox rendering'); - QUnit.test('render / destroy', function(assert) { + QUnit.test('render / destroy', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer); @@ -62,7 +67,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], QUnit.module('Item creation and rendering'); - QUnit.test('.createText()', function(assert) { + QUnit.test('.createText()', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), $result; @@ -84,7 +89,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], assert.ok($result.hasClass('sample-class'), 'text item has the correct class'); }); - QUnit.test('.createEntry()', function(assert) { + QUnit.test('.createEntry()', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), $result; @@ -113,7 +118,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], assert.equal($result.length, 1, 'entry has an icon'); }); - QUnit.test('.createMenu()', function(assert) { + QUnit.test('.createMenu()', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), items = {}, @@ -167,7 +172,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], QUnit.module('Default renderer'); - QUnit.test('render entries in registration order', function(assert) { + QUnit.test('render entries in registration order', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), items = {}, @@ -195,7 +200,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], assert.equal($result.eq(4).data('control'), 'entry5', 'entry5 has been rendered in 5th position'); }); - QUnit.test('handle menu entries and orphan menu entries', function(assert) { + QUnit.test('handle menu entries and orphan menu entries', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), items = {}, @@ -275,7 +280,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], QUnit.module('Menus interactions'); - QUnit.test('Interacting with a pre-rendered menu does not cause errors', function(assert) { + QUnit.test('Interacting with a pre-rendered menu does not cause errors', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), menu; @@ -285,7 +290,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], toolbox.init(); menu = toolbox.createMenu(); - _.each(Object.getOwnPropertyNames(menu), function(prop) { + _.each(Object.getOwnPropertyNames(menu), function (prop) { if (typeof menu[prop] === 'function') { menu[prop](); } @@ -303,7 +308,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], menu.enable(); }); - QUnit.test('Opening a menu close opened menus', function(assert) { + QUnit.test('Opening a menu close opened menus', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), items = {}, @@ -345,7 +350,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], toolbox.render($container); toolbox.enable(); - _.invoke(items, 'enable'); // Enable all items. This is usually the plugin responsability + Object.keys(items).forEach(key => items[key].enable()); // Enable all items. This is usually the plugin responsability $toggle1 = $container.find('[data-control="menu1"]'); $toggle2 = $container.find('[data-control="menu2"]'); @@ -384,7 +389,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], assert.ok(hider.isHidden($menu3), 'menu3 is hidden'); }); - QUnit.test('Clicking outside of an opened menu closes it', function(assert) { + QUnit.test('Clicking outside of an opened menu closes it', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), items = {}, @@ -406,7 +411,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], toolbox.render($container); toolbox.enable(); - _.invoke(items, 'enable'); // Enable all items. This is usually the plugin responsability + Object.keys(items).forEach(key => items[key].enable()); // Enable all items. This is usually the plugin responsability $toggle = $container.find('[data-control="menu"]'); $menu = $container.find('[data-control="menu-menu"]'); @@ -420,7 +425,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], assert.ok(hider.isHidden($menu), 'menu is hidden'); }); - QUnit.test('Clicking a menu entry closes the menu', function(assert) { + QUnit.test('Clicking a menu entry closes the menu', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), items = {}, @@ -443,7 +448,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], toolbox.render($container); toolbox.enable(); - _.invoke(items, 'enable'); // Enable all items. This is usually the plugin responsability + Object.keys(items).forEach(key => items[key].enable()); // Enable all items. This is usually the plugin responsability $toggle = $container.find('[data-control="menu"]'); $menu = $container.find('[data-control="menu-menu"]'); @@ -480,7 +485,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], QUnit.module('Menu button'); - QUnit.test('button opened/closed state', function(assert) { + QUnit.test('button opened/closed state', function (assert) { var toolbox = toolboxFactory(), $container = $(fixtureContainer), items = {}, @@ -500,7 +505,7 @@ define(['jquery', 'lodash', 'ui/hider', 'taoQtiTest/runner/ui/toolbox/toolbox'], toolbox.render($container); - _.invoke(items, 'enable'); // Enable all items. This is usually the plugin responsability + Object.keys(items).forEach(key => items[key].enable()); // Enable all items. This is usually the plugin responsability // Check button $result = $container.find('[data-control="sample-menu"]');