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'
})