diff --git a/.github/workflows/test_suite.yml b/.github/workflows/test_suite.yml index 9d3c9b8b..7809ed64 100644 --- a/.github/workflows/test_suite.yml +++ b/.github/workflows/test_suite.yml @@ -13,20 +13,20 @@ jobs: runs-on: ubuntu-latest steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: setup node - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: - node-version: '12.x' + node-version: 14 - name: cache dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-node-14-${{ hashFiles('**/package-lock.json') }} restore-keys: | - ${{ runner.os }}-node- + ${{ runner.os }}-node-14- - run: npm install - run: npm run lint:code @@ -37,20 +37,20 @@ jobs: needs: [lintcode] steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: setup node - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: - node-version: '12.x' + node-version: 14 - name: cache dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-node-14-${{ hashFiles('**/package-lock.json') }} restore-keys: | - ${{ runner.os }}-node- + ${{ runner.os }}-node-14- - run: npm install - run: npm run lint:style @@ -60,20 +60,20 @@ jobs: needs: [lintcode,lintstyle] steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: setup node - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: - node-version: '12.x' + node-version: 14 - name: cache dependencies - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + key: ${{ runner.os }}-node-14-${{ hashFiles('**/package-lock.json') }} restore-keys: | - ${{ runner.os }}-node- + ${{ runner.os }}-node-14- - run: npm install - run: npm run lint:markdown @@ -85,62 +85,69 @@ jobs: # CHECKOUTS - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Checkout leaonline:testing repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: leaonline/testing path: github/testing - name: Checkout leaonline:utils repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: leaonline/utils path: github/utils - name: Checkout leaonline:corelib repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: leaonline/corelib path: github/corelib - name: Checkout leaonline:ui repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: leaonline/ui path: github/ui - name: Checkout leaonline:service-registry repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: leaonline/service-registry path: github/service-registry - name: Checkout leaonline:theme repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: leaonline/theme path: github/theme + - name: Checkout Collection2 + uses: actions/checkout@v3 + with: + repository: Meteor-Community-Packages/meteor-collection2 + path: github/meteor-collection2 + ref: release/v4 + # CACHING - name: Install Meteor id: cache-meteor-install uses: actions/cache@v2 with: path: ~/.meteor - key: v1-meteor-${{ hashFiles('.meteor/versions') }} + key: v3-meteor-${{ hashFiles('.meteor/versions') }} restore-keys: | - v1-meteor- + v3-meteor- - name: Cache NPM dependencies id: cache-meteor-npm uses: actions/cache@v2 with: path: ~/.npm - key: v1-npm-${{ hashFiles('package-lock.json') }} + key: v3-npm-${{ hashFiles('package-lock.json') }} restore-keys: | - v1-npm- + v3-npm- - name: Cache Meteor build id: cache-meteor-build @@ -151,14 +158,14 @@ jobs: .meteor/local/plugin-cache .meteor/local/isopacks .meteor/local/bundler-cache/scanner - key: v1-meteor_build_cache-${{ github.ref }}-${{ github.sha }} + key: v3-meteor_build_cache-${{ github.ref }}-${{ github.sha }} restore-key: | - v1-meteor_build_cache- + v3-meteor_build_cache- - name: Setup meteor uses: meteorengineer/setup-meteor@v1 with: - meteor-release: '2.2' + meteor-release: '2.7.3' - name: Install NPM Dependencies run: meteor npm ci @@ -178,7 +185,7 @@ jobs: needs: [tests] steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Download coverage uses: actions/download-artifact@v2 diff --git a/.gitignore b/.gitignore index 4d1966fb..2f9b61a6 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ public/fonts/ public/logos/ .deploy +.production # Logs logs diff --git a/.meteor/packages b/.meteor/packages index 5f0c49a8..b33b9bca 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -6,15 +6,15 @@ meteor-base@1.5.1 # Packages every Meteor app needs to have mobile-experience@1.1.0 # Packages for a great mobile UX -mongo@1.13.0 # The database Meteor supports right now -blaze-html-templates # Compile .html files into Meteor Blaze views +mongo@1.15.0 # The database Meteor supports right now +blaze-html-templates@2.0.0 # Compile .html files into Meteor Blaze views reactive-var@1.0.11 # Reactive variable for tracker tracker@1.2.0 # Meteor's client-side reactive programming library # standard-minifier-css@1.5.3 # CSS minifier run for production mode -standard-minifier-js@2.7.1 # JS minifier run for production mode +standard-minifier-js@2.8.0 # JS minifier run for production mode es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers -ecmascript@0.16.0 # Enable ECMAScript2015+ syntax in app code +ecmascript@0.16.2 # Enable ECMAScript2015+ syntax in app code # shell-server@0.5.0 # Server-side component of the `meteor shell` command @@ -44,7 +44,7 @@ leaonline:publication-factory leaonline:ratelimit-factory # Collections -aldeed:collection2 +aldeed:collection2@4.0.0! dburles:mongo-collection-instances@0.3.6 # files @@ -55,15 +55,15 @@ dburles:mongo-collection-instances@0.3.6 mdg:validated-method # Accounts -accounts-base@2.2.0 -accounts-password@2.2.0 +accounts-base@2.2.3 +accounts-password@2.3.1 leaonline:ddp-login leaonline:ddp-login-handler service-configuration@1.3.0 # UI reactive-dict@1.3.0 -hot-module-replacement@0.4.0 +hot-module-replacement@0.5.1 blaze-hot fourseven:scss@4.15.0 @@ -82,7 +82,7 @@ jkuester:template-loader ccorcos:subs-cache # reporting -email@2.2.0 +email@2.2.1 #======================================== # SECURITY diff --git a/.meteor/release b/.meteor/release index 55995295..66dd7b66 100644 --- a/.meteor/release +++ b/.meteor/release @@ -1 +1 @@ -METEOR@2.5 +METEOR@2.7.3 diff --git a/.meteor/versions b/.meteor/versions index b3ab0f71..87954276 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -1,18 +1,18 @@ -accounts-base@2.2.0 -accounts-password@2.2.0 +accounts-base@2.2.4 +accounts-password@2.3.1 aldeed:autoform@7.0.0 -aldeed:collection2@3.4.0 -allow-deny@1.1.0 +aldeed:collection2@4.0.0 +allow-deny@1.1.1 audit-argument-checks@1.0.7 autoupdate@1.8.0 -babel-compiler@7.7.0 -babel-runtime@1.5.0 +babel-compiler@7.9.2 +babel-runtime@1.5.1 base64@1.0.12 binary-heap@1.0.11 -blaze@2.5.0 -blaze-hot@1.1.0 -blaze-html-templates@1.2.1 -blaze-tools@1.1.2 +blaze@2.6.1 +blaze-hot@1.1.1 +blaze-html-templates@2.0.0 +blaze-tools@1.1.3 boilerplate-generator@1.7.1 caching-compiler@1.2.2 caching-html-compiler@1.2.1 @@ -28,12 +28,12 @@ ddp-server@2.5.0 deps@1.0.12 diff-sequence@1.1.1 dynamic-import@0.7.2 -ecmascript@0.16.0 +ecmascript@0.16.2 ecmascript-runtime@0.8.0 ecmascript-runtime-client@0.12.1 ecmascript-runtime-server@0.11.0 -ejson@1.1.1 -email@2.2.0 +ejson@1.1.2 +email@2.2.1 es5-shim@4.8.0 fetch@0.1.1 force-ssl@1.1.0 @@ -41,8 +41,8 @@ force-ssl-common@1.1.0 fourseven:scss@4.15.0 geojson-utils@1.0.10 hot-code-push@1.0.4 -hot-module-replacement@0.4.0 -html-tools@1.1.2 +hot-module-replacement@0.5.1 +html-tools@1.1.3 htmljs@1.1.1 http@2.0.0 id-map@1.1.1 @@ -58,10 +58,10 @@ leaonline:corelib@1.0.0 leaonline:ddp-login@2.0.2 leaonline:ddp-login-handler@2.0.0 leaonline:method-factory@1.1.0 -leaonline:publication-factory@1.0.0 +leaonline:publication-factory@1.1.1 leaonline:ratelimit-factory@1.0.0 leaonline:service-registry@1.0.0 -leaonline:theme@1.0.0 +leaonline:theme@1.1.0 leaonline:ui@1.0.0 livedata@1.0.18 lmieulet:meteor-coverage@3.2.0 @@ -70,34 +70,34 @@ logging@1.3.1 mdg:validated-method@1.2.0 meteor@1.10.0 meteor-base@1.5.1 -meteortesting:browser-tests@1.3.4 +meteortesting:browser-tests@1.3.5 meteortesting:mocha@2.0.3 meteortesting:mocha-core@8.0.1 -minifier-css@1.6.0 -minifier-js@2.7.1 -minimongo@1.7.0 +minifier-css@1.6.1 +minifier-js@2.7.5 +minimongo@1.8.0 mobile-experience@1.1.0 mobile-status-bar@1.1.0 -modern-browsers@0.1.7 -modules@0.17.0 -modules-runtime@0.12.0 +modern-browsers@0.1.8 +modules@0.18.0 +modules-runtime@0.13.0 modules-runtime-hot@0.14.0 -momentjs:moment@2.29.1 -mongo@1.13.0 -mongo-decimal@0.1.2 +momentjs:moment@2.29.3 +mongo@1.15.0 +mongo-decimal@0.1.3 mongo-dev-server@1.1.0 mongo-id@1.0.8 -npm-mongo@3.9.1 -observe-sequence@1.0.19 +npm-mongo@4.3.1 +observe-sequence@1.0.20 ordered-dict@1.1.0 -ostrio:cstorage@2.2.2 -ostrio:flow-router-extra@3.7.5 -ostrio:i18n@3.1.0 +ostrio:cstorage@4.0.1 +ostrio:flow-router-extra@3.9.0 +ostrio:i18n@3.2.0 promise@0.12.0 raix:eventemitter@1.0.0 random@1.2.0 rate-limit@1.0.9 -react-fast-refresh@0.2.0 +react-fast-refresh@0.2.3 reactive-dict@1.3.0 reactive-var@1.0.11 reload@1.3.1 @@ -106,19 +106,20 @@ routepolicy@1.1.1 seba:minifiers-autoprefixer@2.0.1 service-configuration@1.3.0 sha@1.0.9 -socket-stream-client@0.4.0 -spacebars@1.2.0 -spacebars-compiler@1.3.0 -standard-minifier-js@2.7.1 -templating@1.4.1 +socket-stream-client@0.5.0 +spacebars@1.3.0 +spacebars-compiler@1.3.1 +standard-minifier-js@2.8.1 +templating@1.4.2 templating-compiler@1.4.1 -templating-runtime@1.5.0 -templating-tools@1.2.1 +templating-runtime@1.6.1 +templating-tools@1.2.2 tmeasday:check-npm-versions@1.0.2 tracker@1.2.0 -typescript@4.4.0 +typescript@4.5.4 ui@1.0.13 underscore@1.0.10 url@1.3.2 -webapp@1.13.0 +webapp@1.13.1 webapp-hashing@1.1.0 +zodern:types@1.0.9 diff --git a/client/main.html b/client/main.html index 5fe3c31c..a92bd58c 100644 --- a/client/main.html +++ b/client/main.html @@ -28,21 +28,17 @@ - - + diff --git a/client/main.js b/client/main.js index 03a72773..e950d37c 100644 --- a/client/main.js +++ b/client/main.js @@ -1,11 +1,10 @@ import '../imports/startup/client/serviceWorker' +import '../imports/startup/client/errors' import '../imports/startup/client/testDebug' import '../imports/startup/client/templates' import '../imports/startup/client/bootstrap' import '../imports/startup/client/fontawesome' import '../imports/startup/client/routes' import '../imports/startup/client/routeHelpers' -// import '../imports/startup/client/videos' // TODO import '../imports/ui/layout/footer/footer' -import './main.scss' import './main.html' diff --git a/client/main.scss b/client/main.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/imports/api/i18n/initLanguage.js b/imports/api/i18n/initLanguage.js index a448551d..c984548a 100644 --- a/imports/api/i18n/initLanguage.js +++ b/imports/api/i18n/initLanguage.js @@ -13,7 +13,9 @@ export const initLanguage = async (debug = () => {}) => { i18n: { settings: { defaultLocale, [defaultLocale]: localeSettings }, [defaultLocale]: language - } + }, + helperName: '___i18n___', + helperSettingsName: '___i18n___' }) i18n.load({ diff --git a/imports/api/notify/notifyUsersAboutError.js b/imports/api/notify/notifyUsersAboutError.js index c58e02b7..1ff3546d 100644 --- a/imports/api/notify/notifyUsersAboutError.js +++ b/imports/api/notify/notifyUsersAboutError.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor' import { Email } from 'meteor/email' -const appName = Meteor.settings.public.app.label +const appName = Meteor.settings.public.app.name const { notify, replyTo, from } = Meteor.settings.email export const notifyUsersAboutError = error => { @@ -10,7 +10,7 @@ export const notifyUsersAboutError = error => { notify.forEach(address => { Email.send({ to: address, - subject: `${appName} [error]: ${error.type} - ${error.name}`, + subject: `${appName} [error]: ${error.message}`, replyTo: replyTo, from: from, text: JSON.stringify(error, null, 2) diff --git a/imports/api/notify/tests/notifyUsersAboutError.tests.js b/imports/api/notify/tests/notifyUsersAboutError.tests.js index 746e386a..b45cb516 100644 --- a/imports/api/notify/tests/notifyUsersAboutError.tests.js +++ b/imports/api/notify/tests/notifyUsersAboutError.tests.js @@ -20,7 +20,7 @@ describe(notifyUsersAboutError.name, function () { expect(to).to.equal('admin@example.com') expect(from).to.equal('system@example.com') expect(replyTo).to.equal('noreply@example.com') - expect(subject).to.equal('apps.otulea.title [error]: testError - Error') + expect(subject).to.equal('otu.lea [error]: foobar') expect(text).to.equal(JSON.stringify(err, null, 2).trim()) }) diff --git a/imports/api/tts/initializeTTS.js b/imports/api/tts/initializeTTS.js index f7de10ec..644f5a9c 100644 --- a/imports/api/tts/initializeTTS.js +++ b/imports/api/tts/initializeTTS.js @@ -34,6 +34,7 @@ export const initializeTTS = async () => { }, onComplete () { console.debug('[initializeTTS]: configure complete') + TTSEngine.defaults({ rate: 0.8 }) resolve(TTSEngine) } }) diff --git a/imports/patches/alphaUsers.js b/imports/patches/alphaUsers.js new file mode 100644 index 00000000..53b70b0a --- /dev/null +++ b/imports/patches/alphaUsers.js @@ -0,0 +1,222 @@ +import { Meteor } from 'meteor/meteor' +import { Session } from '../contexts/session/Session' +import { TestCycle } from '../contexts/testcycle/TestCycle' +import { Feedback } from '../contexts/feedback/Feedback' +import { Dimension } from '../contexts/Dimension' +import { Level } from '../contexts/Level' +import { getCompetencies } from '../contexts/feedback/api/getCompetencies' +import { getAlphaLevels } from '../contexts/feedback/api/getAlphaLevels' +import fs from 'fs' + +const fields = { + userId: 1, + code: 1, + startedAt: 1, + completedAt: 1, + duration: 1, + cancelledAt: 1, + dimension: 1, + level: 1 +} + +const fieldNames = Object.keys(fields) + +const createCachedGetter = (collection) => { + const map = new Map() + return (_id) => { + if (!map.has(_id)) { + map.set(_id, collection.findOne({ _id })) + } + return map.get(_id) + } +} + +const getDimension = createCachedGetter(Dimension.collection()) +const getLevel = createCachedGetter(Level.collection()) +const getCompetency = _id => { + const map = getCompetencies([_id]) + return map.get(_id) +} +const getAlphaLevel = _id => { + const map = getAlphaLevels([_id]) + return map.get(_id) +} +const toDate = value => value ? new Date(value).toLocaleDateString() : '' + +class Row { + constructor ({ user }) { + this.user = user + this.userId = user._id + this.code = user.username + this.dimension = '' + this.level = '' + this.startedAt = '' + this.completedAt = '' + this.cancelledAt = '' + this.duration = '' + this.competencies = {} + this.alphaLevels = {} + } + + addSession (sessionDoc, testCycleDoc = {}) { + this.dimension = getDimension(testCycleDoc.dimension).title ?? 'missing' + this.level = getLevel(testCycleDoc.level).title ?? 'missing' + this.startedAt = toDate(sessionDoc.startedAt) + this.completedAt = toDate(sessionDoc.completedAt) + this.cancelledAt = toDate(sessionDoc.cancelledAt) + this.isCancelled = this.cancelledAt || !this.completedAt + this.progress = sessionDoc.progress || 0 + this.maxProgress = sessionDoc.maxProgress || -999 + this.isComplete = this.progress === this.maxProgress + + if (!this.cancelledAt && sessionDoc.startedAt && sessionDoc.completedAt) { + this.duration = new Date(sessionDoc.completedAt).getTime() - new Date(sessionDoc.startedAt).getTime() + } + else { + this.duration = -999 // no time computable + } + } + + addRecord (doc = {}) { + const competencies = doc.competencies || [] + const alphaLevels = doc.alphaLevels || [] + + alphaLevels.forEach(({ alphaLevelId, count, scored, undef }) => { + const alphaLevelDoc = getAlphaLevel(alphaLevelId) + const shortCode = alphaLevelDoc?.shortCode ?? `missing-${alphaLevelId}` + const existingComp = this.alphaLevels[shortCode] || { count: 0, scored: 0, perc: 0, undef: 0, graded: 0 } + existingComp.count += (count || 0) + existingComp.scored += (scored || 0) + existingComp.undef += (undef || 0) + existingComp.perc += existingComp.scored / (existingComp.count || 1) + this.alphaLevels[shortCode] = existingComp + }) + + competencies.forEach(({ competencyId, count, scored, undef }) => { + const competencyDoc = getCompetency(competencyId) + const shortCode = competencyDoc?.shortCode ?? `missing-${competencyId}` + const existingComp = this.competencies[shortCode] || { count: 0, scored: 0, perc: 0, undef: 0, graded: 0 } + existingComp.count += (count || 0) + existingComp.scored += (scored || 0) + existingComp.undef += (undef || 0) + existingComp.perc += existingComp.scored / (existingComp.count || 1) + this.competencies[shortCode] = existingComp + }) + } +} + +export const alphaUsers = ({ dryRun = true }) => { + const rows = [] + let missing = '' + + Meteor.users.find({}).forEach((user) => { + if (!user || !user.username || !user._id || !user.createdAt || !user.updatedAt) { + return + } + + const sessionCursor = Session.collection().find({ userId: user._id }) + if (sessionCursor.count() === 0) { return } + + const row = new Row({ user }) + sessionCursor.forEach(sessionDoc => { + if (!sessionDoc.testCycle) { + missing += `[Missing] test cycle for ${user.username} and session ${sessionDoc._id}\n` + return + } + + const testCycleDoc = TestCycle.collection().findOne(sessionDoc.testCycle) + + if (!testCycleDoc) { + missing += `[Missing] test cycle for ${user.username} and session ${sessionDoc._id}\n` + return + } + + row.addSession(sessionDoc, testCycleDoc) + + // if we have an existing feedback it's all fine + if (sessionDoc.completedAt) { + Feedback.collection().find({ sessionId: sessionDoc._id }).forEach(doc => row.addRecord(doc)) + } + + // otherwise we need to generate it first! + else { + missing += `[Missing] report for ${user.username} and session ${sessionDoc._id}\n` + } + }) + + rows.push(row) + }) + + // get all alpha levels and competencies to build our header + const allAlphaLevels = {} + const allCompetencies = {} + + rows.forEach(row => { + const ckeys = Object.keys(row.competencies) + const akeys = Object.keys(row.alphaLevels) + // print(row.code, ckeys.length, akeys.length) + ckeys.forEach(key => { + allCompetencies[`${key}#c`] = 1 + allCompetencies[`${key}#s`] = 1 + allCompetencies[`${key}#u`] = 1 + allCompetencies[`${key}#p`] = 1 + }) + akeys.forEach(key => { + allAlphaLevels[`${key}#c`] = 1 + allAlphaLevels[`${key}#s`] = 1 + allAlphaLevels[`${key}#u`] = 1 + allAlphaLevels[`${key}#p`] = 1 + }) + }) + + const header = [] + const addToHeader = name => header.push(name) + fieldNames.forEach(addToHeader) + const alphaKeys = Object.keys(allAlphaLevels) + alphaKeys.sort().forEach(addToHeader) + + const competencyKeys = Object.keys(allCompetencies) + competencyKeys.sort().forEach(addToHeader) + + let out = header.join(';') + '\n' + + rows.forEach(row => { + let line = '' + const addToLine = value => { + if (value === null || value === undefined) { + value = '' + } + line += `${value};` + } + const addComplex = target => keyWithHash => { + const [key, type] = keyWithHash.split('#') + const value = target[key] || {} + + switch (type) { + case 'c': + return addToLine(value.count) + case 's': + return addToLine(value.scored) + case 'u': + return addToLine(value.undef) + case 'p': + return addToLine(value.perc) + } + } + + fieldNames.forEach(key => addToLine(row[key])) + alphaKeys.forEach(addComplex(row.alphaLevels)) + competencyKeys.forEach(addComplex(row.competencies)) + + out += line + '\n' + }) + + if (!dryRun) { + fs.writeFile(`${process.cwd()}/missing.txt`, missing, (err) => { + if (err) console.log(err) + }) + fs.writeFile(`${process.cwd()}/alphaUsers.csv`, out, (err) => { + if (err) console.log(err) + }) + } +} diff --git a/imports/startup/client/errors.js b/imports/startup/client/errors.js new file mode 100644 index 00000000..ff9a0247 --- /dev/null +++ b/imports/startup/client/errors.js @@ -0,0 +1,77 @@ +import { Meteor } from 'meteor/meteor' +import { Blaze } from 'meteor/blaze' +import { Template } from 'meteor/templating' +import { sendError } from '../../contexts/errors/api/sendError' + +const parseAndSendError = ({ error }) => { + const instance = Template.instance() + const templateName = instance + ? instance.viewName ?? instance.view?.name ?? 'unknown' + : 'unknown' + sendError({ + userId: Meteor.userId(), + template: templateName, + error + }) +} + +// to log internal blaze errors we setup this exception handler +// and transform their internal error into something we can use +if (Blaze.setExceptionHandler) { + console.debug('[Blaze]: set exception handler') + Blaze.setExceptionHandler(function (message, blazeRuntimeError) { + blazeRuntimeError.details = Object.assign({}, blazeRuntimeError.details, { blazeMessage: message }) + parseAndSendError({ error: blazeRuntimeError }) + }) +} + +// we also want to log any error that occurs on the window level +window.onerror = function (event, source, lineno, colno, error) { + if (!event.isTrusted) { + return console.error('untrusted event raised', event) + } + const message = typeof event === 'object' + ? event.message + : event + const details = { message, source, lineno, colno } + error.details = Object.assign({}, error.details, details) + parseAndSendError({ error }) +} + +// CSP errors need to be logged as well +Template.body.onCreated(function () { + document.addEventListener('securitypolicyviolation', (securityViolationEvent) => { + if (!securityViolationEvent.isTrusted) { + return console.error('untrusted event raised', securityViolationEvent) + } + + const error = new Error('securitypolicyviolation') + error.details = { + blockedURI: securityViolationEvent.blockedURI, + violatedDirective: securityViolationEvent.violatedDirective, + columnNumber: securityViolationEvent.columnNumber, + documentURI: securityViolationEvent.documentURI, + effectiveDirective: securityViolationEvent.effectiveDirective, + lineNumber: securityViolationEvent.lineNumber, + referrer: securityViolationEvent.referrer, + sample: securityViolationEvent.sample, + sourceFile: securityViolationEvent.sourceFile, + statusCode: securityViolationEvent.statusCode + } + parseAndSendError({ error }) + }) + + document.fonts.addEventListener('loadingerror', (event) => { + if (!event.isTrusted) { + return console.error('untrusted event raised', event) + } + const error = new Error('loadingerror') + error.details = { + fonts: event.fontfaces.map(font => ({ + family: font.family, + reason: font.loaded?.reason?.message + })) + } + parseAndSendError({ error }) + }) +}) diff --git a/imports/startup/server/collection2.js b/imports/startup/server/collection2.js index 77d7ba6d..585d56ad 100644 --- a/imports/startup/server/collection2.js +++ b/imports/startup/server/collection2.js @@ -4,3 +4,6 @@ import Collection2 from 'meteor/aldeed:collection2' if (Collection2 && typeof Collection2.load === 'function') { Collection2.load() } +else { + console.warn('Skip Collection2.load as it\'s not present!') +} diff --git a/imports/startup/server/patches.js b/imports/startup/server/patches.js index 7f1c29f5..8217a6aa 100644 --- a/imports/startup/server/patches.js +++ b/imports/startup/server/patches.js @@ -3,6 +3,7 @@ import { Email } from 'meteor/email' import { addDimensionToFeedback } from '../../patches/addDimensionToFeedback' import { generateAccounts } from '../../patches/generateAccounts' import { removeDeadAccounts } from '../../patches/removeDeadAccounts' +import { alphaUsers } from '../../patches/alphaUsers' const appName = Meteor.settings.public.app.label const { notify: defaultNotify, replyTo, from } = Meteor.settings.email @@ -63,3 +64,10 @@ function notifyUsers ({ notify = [], patchName, result, dryRun }) { }) }) } + +if (patches.alphaUsers?.active) { + console.debug('[patches]: run alphaUsers') + Meteor.defer(function () { + alphaUsers(patches.alphaUsers) + }) +} diff --git a/imports/ui/pages/complete/complete.html b/imports/ui/pages/complete/complete.html index d656ea13..a70c9c99 100644 --- a/imports/ui/pages/complete/complete.html +++ b/imports/ui/pages/complete/complete.html @@ -157,10 +157,7 @@
{{/if}} {{! > textGroup text=(i18n alphaLevel.gradeLabel) type=currentType}} - {{! WE ONLY DISPLAY THE PERCENT VALUE IF IT IS GREATER THAN 0 }} - {{#with truncate alphaLevel.perc}} - {{> textGroup text=(i18n "pages.complete.percent" this) type=currentType}} - {{/with}} + {{> textGroup text=(getPercent alphaLevel) type=currentType}} {{else}} @@ -274,12 +271,9 @@
{{competency.scored}} {{competency.perc}} {{/if}} - + {{! > textGroup text=(i18n competency.gradeLabel) type=currentType}} - {{! WE ONLY DISPLAY THE PERCENT VALUE IF IT IS GREATER THAN 0 }} - {{#with truncate competency.perc}} - {{> textGroup text=(i18n "pages.complete.percent" this) type=currentType}} - {{/with}} + {{> textGroup text=(getPercent competency) type=currentType}} {{else}} diff --git a/imports/ui/pages/complete/complete.js b/imports/ui/pages/complete/complete.js index 0836e979..3c070ae6 100644 --- a/imports/ui/pages/complete/complete.js +++ b/imports/ui/pages/complete/complete.js @@ -9,6 +9,8 @@ import { sessionIsComplete } from '../../../contexts/session/utils/sessionIsComp import { AlphaLevel } from '../../../contexts/AlphaLevel' import { Response } from '../../../contexts/response/Response' import { Unit } from '../../../contexts/Unit' +import { truncatePercent } from './helpers/truncatePercent' +import { translate } from '../../../api/i18n/translate' import '../../components/container/container' import '../../layout/navbar/navbar' import './complete.scss' @@ -102,7 +104,9 @@ Template.complete.onCreated(function () { resultDoc.shortCode = competencyDoc.shortCode resultDoc.description = competencyDoc.descriptionSimple resultDoc.gradeLabel = `thresholds.${resultDoc.gradeName}` - resultDoc.perc = resultDoc.perc * 100 + + const percentValue = Number(resultDoc.perc ?? 0) * 100 + resultDoc.perc = truncatePercent(percentValue) console.debug({ resultDoc }) return resultDoc @@ -148,7 +152,10 @@ Template.complete.onCreated(function () { alpha.shortCode = alphaLevelDoc.shortCode alpha.description = alphaLevelDoc.description alpha.gradeLabel = `thresholds.${alpha.gradeName}` - alpha.perc = alpha.perc * 100 + + const percentValue = Number(alpha.perc ?? 0) * 100 + alpha.perc = truncatePercent(percentValue) + return alpha }) .sort((a, b) => a.shortCode.localeCompare(b.shortCode)) @@ -340,11 +347,9 @@ Template.complete.helpers({ currentType () { return Template.instance().state.get('color') }, - truncate (percent = 0) { - console.debug(percent) - const int = Math.trunc(percent) - console.debug(int) - return Number.isNaN(int) ? 0 : int + getPercent (doc = {}) { + const percent = String(doc.perc ?? 0) + return translate('pages.complete.percent', { percent }) }, // /////////////////////////////////////////////////////////////////////////// // DEBUG-USER-ONLY! diff --git a/imports/ui/pages/complete/helpers/trunactePercent.tests.js b/imports/ui/pages/complete/helpers/trunactePercent.tests.js new file mode 100644 index 00000000..c859001f --- /dev/null +++ b/imports/ui/pages/complete/helpers/trunactePercent.tests.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +import { expect } from 'chai' +import { truncatePercent } from './truncatePercent' +import { restoreAll, stub } from '../../../../../tests/helpers.tests' + +describe(truncatePercent.name, () => { + afterEach(function () { + restoreAll() + }) + it('throws if the input is not a number', function () { + ['1', '1.1232131', true, false, () => {}, {}, [], null, undefined].forEach(input => { + expect(() => truncatePercent(input)) + .to.throw(`Expected a valid number, got ${input} (${typeof input}), in ${truncatePercent.name}.`) + }) + }) + it('throws if the truncated input is not a safe integer', function () { + stub(Math, 'trunc', () => NaN) + expect(() => truncatePercent(1.111111)) + .to.throw(`Expected truncated safe integer, got ${NaN} (${typeof NaN}), in ${truncatePercent.name}.`) + }) + it('returns the integer base of the given input', function () { + const toInt = n => Number(n.toString(10).split('.')[0]) + + for (let i = 0; i < 100; i++) { + const value = Math.random() * 100 + const expected = toInt(value) + + expect(truncatePercent(value)).to.equal(expected) + } + }) +}) diff --git a/imports/ui/pages/complete/helpers/truncatePercent.js b/imports/ui/pages/complete/helpers/truncatePercent.js new file mode 100644 index 00000000..efaf7871 --- /dev/null +++ b/imports/ui/pages/complete/helpers/truncatePercent.js @@ -0,0 +1,28 @@ +/** + * Truncates the decimal part from a float and returns the + * integer base. + * @param percent {number} + * @returns {number} + * @throws if input is not a number or truncated result is not a valid integer + */ +export const truncatePercent = (percent) => { + if (!isValidNumber(percent)) { + throw new Error(`Expected a valid number, got ${percent} (${typeof percent}), in ${truncatePercent.name}.`) + } + + const integer = Math.trunc(percent) + + if (!isValidNumber(integer) || !Number.isSafeInteger(integer)) { + throw new Error(`Expected truncated safe integer, got ${integer} (${typeof integer}), in ${truncatePercent.name}.`) + } + + return integer +} + +/** + * Checks whether we deal with a computable number + * @private + * @param n + * @return {boolean} + */ +const isValidNumber = n => typeof n === 'number' && !Number.isNaN(n) && Number.isFinite(n) diff --git a/imports/ui/pages/complete/tests/index.js b/imports/ui/pages/complete/tests/index.js new file mode 100644 index 00000000..e7387864 --- /dev/null +++ b/imports/ui/pages/complete/tests/index.js @@ -0,0 +1,4 @@ +/* eslint-env mocha */ +describe('complete', function () { + import '../helpers/trunactePercent.tests' +}) diff --git a/imports/ui/pages/tests/index.js b/imports/ui/pages/tests/index.js new file mode 100644 index 00000000..0fcbfc59 --- /dev/null +++ b/imports/ui/pages/tests/index.js @@ -0,0 +1,4 @@ +/* eslint-env mocha */ +describe('pages', function () { + import '../complete/tests' +}) diff --git a/imports/ui/routing/Routes.js b/imports/ui/routing/Routes.js index 941d4c95..0730408e 100644 --- a/imports/ui/routing/Routes.js +++ b/imports/ui/routing/Routes.js @@ -10,7 +10,7 @@ const go = (...args) => { const settings = () => { // TODO load dynamically using i18n locale - import settingsFile from '../../../resources/i18n/de/routes' + import settingsFile from '../../../resources/i18n/de/routes.json' return settingsFile } diff --git a/imports/utils/documents/docsToMap.js b/imports/utils/documents/docsToMap.js index 6863b7cb..10ad74d9 100644 --- a/imports/utils/documents/docsToMap.js +++ b/imports/utils/documents/docsToMap.js @@ -8,7 +8,7 @@ * If not `weak` is explicilty set to `true` a `Map` will be used, * otherwise a `WeakMap` is used. * - * @param {Array} docs + * @param {Array|Mongo.Cursor} docs * @param {string} key * @param {boolean} weak * @return {Map|WeakMap} diff --git a/package-lock.json b/package-lock.json index 4d02cbcb..40224de9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -647,9 +647,9 @@ "dev": true }, "@types/node": { - "version": "17.0.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.18.tgz", - "integrity": "sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true, "optional": true }, @@ -672,9 +672,9 @@ "dev": true }, "@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, "requires": { @@ -947,7 +947,7 @@ "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, "buffer-from": { @@ -1234,6 +1234,15 @@ } } }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dev": true, + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1329,9 +1338,9 @@ "integrity": "sha512-dnlZbDqWoHzo+T720Hq/Q4E3kZjOe9+esGuO3bCYDCbvtfVtq9EADk4cy93ntmEtjlAehqzzn8wduphYqsNodA==" }, "devtools-protocol": { - "version": "0.0.901419", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.901419.tgz", - "integrity": "sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ==", + "version": "0.0.1056733", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1056733.tgz", + "integrity": "sha512-CmTu6SQx2g3TbZzDCAV58+LTxVdKplS7xip0g5oDXpZ+isr0rv5dDP8ToyVRywzPHkCCPKgKgScEcwz4uPWDIA==", "dev": true }, "diff": { @@ -2035,7 +2044,7 @@ "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "requires": { "pend": "~1.2.0" @@ -4195,14 +4204,11 @@ "yallist": "^4.0.0" } }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true }, "mongo-object": { "version": "0.1.4", @@ -4523,7 +4529,7 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true }, "picocolors": { @@ -4811,103 +4817,67 @@ "dev": true }, "puppeteer": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-10.4.0.tgz", - "integrity": "sha512-2cP8mBoqnu5gzAVpbZ0fRaobBWZM8GEUF4I1F6WbgHrKV/rz7SX8PG2wMymZgD0wo0UBlg2FBPNxlF/xlqW6+w==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.3.0.tgz", + "integrity": "sha512-WJbi/ULaeuFOz7cfMgJlJCBAZiyqIFeQ6os4h5ex3PVTt2qosXgwI9eruFZqFAwJRv8x5pOuMhWR0aSRgyDqEg==", + "dev": true, + "requires": { + "cosmiconfig": "7.0.1", + "devtools-protocol": "0.0.1056733", + "https-proxy-agent": "5.0.1", + "progress": "2.0.3", + "proxy-from-env": "1.1.0", + "puppeteer-core": "19.3.0" + }, + "dependencies": { + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + } + } + }, + "puppeteer-core": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.3.0.tgz", + "integrity": "sha512-P8VAAOBnBJo/7DKJnj1b0K9kZBF2D8lkdL94CjJ+DZKCp182LQqYemPI9omUSZkh4bgykzXjZhaVR1qtddTTQg==", "dev": true, "requires": { - "debug": "4.3.1", - "devtools-protocol": "0.0.901419", + "cross-fetch": "3.1.5", + "debug": "4.3.4", + "devtools-protocol": "0.0.1056733", "extract-zip": "2.0.1", - "https-proxy-agent": "5.0.0", - "node-fetch": "2.6.1", - "pkg-dir": "4.2.0", - "progress": "2.0.1", + "https-proxy-agent": "5.0.1", "proxy-from-env": "1.1.0", "rimraf": "3.0.2", - "tar-fs": "2.0.0", - "unbzip2-stream": "1.3.3", - "ws": "7.4.6" + "tar-fs": "2.1.1", + "unbzip2-stream": "1.4.3", + "ws": "8.10.0" }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "requires": { - "find-up": "^4.0.0" + "agent-base": "6", + "debug": "4" } - }, - "progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", - "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", - "dev": true } } }, @@ -5797,15 +5767,15 @@ } }, "tar-fs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", - "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", "dev": true, "requires": { "chownr": "^1.1.1", - "mkdirp": "^0.5.1", + "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", - "tar-stream": "^2.0.0" + "tar-stream": "^2.1.4" }, "dependencies": { "chownr": { @@ -5862,7 +5832,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "to-fast-properties": { @@ -5969,9 +5939,9 @@ } }, "unbzip2-stream": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", - "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", "dev": true, "requires": { "buffer": "^5.2.1", @@ -6197,9 +6167,9 @@ } }, "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz", + "integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==", "dev": true }, "xdg-basedir": { @@ -6282,7 +6252,7 @@ "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "requires": { "buffer-crc32": "~0.2.3", diff --git a/package.json b/package.json index 2971afed..8f535b2c 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "chai": "^4.3.4", "eslint-plugin-security": "^1.4.0", "markdownlint-cli": "^0.31.1", - "puppeteer": "^10.4.0", + "puppeteer": "19.3.0", "sinon": "^11.1.2", "snazzy": "^9.0.0", "standardx": "^7.0.0", diff --git a/run.sh b/run.sh index ab153011..d676d7c5 100755 --- a/run.sh +++ b/run.sh @@ -19,7 +19,9 @@ while getopts "pr" opt; do esac done -PACKAGE_DIRS="../lib:../liboauth:../libext:../meteor-collection2/package" +PACKAGE_DIRS="../blaze/packages:../lib:../liboauth:../libext:../meteor-collection2/package" + +# final command to run DEBUG="app" METEOR_PACKAGE_DIRS=${PACKAGE_DIRS} meteor \ --exclude-archs=web.cordova \ --settings=settings.json \ diff --git a/test.sh b/test.sh index f90261df..1730ca64 100755 --- a/test.sh +++ b/test.sh @@ -80,7 +80,7 @@ done # build paths: PROJECT_PATH=$(pwd) -T_PACKAGE_DIRS="../lib:../libnpm:../liboauth:../libext:../meteor-collection2/package:./github" +T_PACKAGE_DIRS="../lib:../libnpm:../liboauth:../libext:../meteor-collection2/package:./github:./github/meteor-collection2/package" PORT=3099 diff --git a/tests/client/index.js b/tests/client/index.js index a89f713e..4dc3aeda 100644 --- a/tests/client/index.js +++ b/tests/client/index.js @@ -11,4 +11,5 @@ describe('routing', function () { describe('ui', function () { import '../../imports/ui/components/tests' import '../../imports/ui/loading/tests' + import '../../imports/ui/pages/tests' })