Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/app/obojobo-repository/client/css/_defaults.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ $color-reward-text: #947d00;
$color-obojobo-blue: #0d4fa7;
$color-preview: #af1b5c;

$color-nav-highlight: $color-obojobo-blue;
$color-nav-hover: darken($color-highlight, 25%);
$color-nav-bg-selected: lighten($color-obojobo-blue, 62%);
$color-nav-bg-hover: lighten($color-obojobo-blue, 58%);

$size-spacing-vertical-big: 40px;
$size-spacing-vertical-half: $size-spacing-vertical-big / 2;
$size-spacing-vertical-internal: $size-spacing-vertical-half / 2;
Expand Down
14 changes: 14 additions & 0 deletions packages/app/obojobo-repository/server/routes/stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,18 @@ router
res.render('pages/page-stats-server.jsx', props)
})

router
.route('/statsCourses')
.get([requireCurrentUser, requireCanViewStatsPage])
.get((req, res) => {
const props = {
title: 'StatsCourses',
currentUser: req.currentUser,
// must use webpackAssetPath for all webpack assets to work in dev and production!
appCSSUrl: webpackAssetPath('stats.css'),
appJsUrl: webpackAssetPath('stats.js')
}
res.render('pages/page-stats-server.jsx', props)
})

module.exports = router
29 changes: 29 additions & 0 deletions packages/app/obojobo-repository/server/routes/stats.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,33 @@ describe('repository stats route', () => {
expect(response.statusCode).toBe(200)
})
})

test('get /statsCourses returns a "not authorized" if the viewer does not have canViewStatsPage', () => {
// always return false - a.k.a. the user does not have the right perms to use this
mockCurrentUser.hasPermission = () => false

expect.hasAssertions()

return request(app)
.get('/statsCourses')
.then(response => {
expect(mockStatsComponent).toHaveBeenCalledTimes(0)
expect(response.statusCode).toBe(401)
})
})

test('get /statsCourses sends the correct props to the Stats component when the user has canViewStatsPage', () => {
expect.hasAssertions()

return request(app)
.get('/statsCourses')
.then(response => {
expect(mockStatsComponent).toHaveBeenCalledTimes(1)
expect(mockStatsComponentConstructor).toHaveBeenCalledWith({
title: 'StatsCourses',
currentUser: mockCurrentUser
})
expect(response.statusCode).toBe(200)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ const { MODE_RECENT, MODE_ALL, MODE_COLLECTION } = require('../repository-consta
const debouncePromise = require('debounce-promise')
const dayjs = require('dayjs')
const advancedFormat = require('dayjs/plugin/advancedFormat')
const { apiGetAssessmentDetailsForDraft } = require('./shared-api-methods')
const {
apiGetAssessmentDetailsForDraft,
apiGetCoursesForDraft,
apiGetAssessmentDetailsForCourse
} = require('./shared-api-methods')

dayjs.extend(advancedFormat)
// =================== API =======================
Expand Down Expand Up @@ -232,6 +236,20 @@ const showAssessmentScoreData = module => ({
promise: apiGetAssessmentDetailsForDraft(module.draftId)
})

const SHOW_COURSES_BY_DRAFT = 'SHOW_COURSES_BY_DRAFT'
const showCoursesByDraft = module => ({
type: SHOW_COURSES_BY_DRAFT,
meta: { module },
promise: apiGetCoursesForDraft(module.draftId)
})

const SHOW_ASSESSMENTS_BY_COURSE = 'SHOW_ASSESSMENTS_BY_COURSE'
const showCourseAssessmentData = module => ({
type: SHOW_ASSESSMENTS_BY_COURSE,
meta: { module },
promise: apiGetAssessmentDetailsForCourse(module.draftId, module.courseId)
})

const RESTORE_VERSION = 'RESTORE_VERSION'
const restoreVersion = (draftId, versionId) => ({
type: RESTORE_VERSION,
Expand Down Expand Up @@ -664,6 +682,8 @@ module.exports = {
IMPORT_MODULE_FILE,
CHECK_MODULE_LOCK,
SHOW_ASSESSMENT_SCORE_DATA,
SHOW_COURSES_BY_DRAFT,
SHOW_ASSESSMENTS_BY_COURSE,
GET_DELETED_MODULES,
GET_MODULES,
BULK_RESTORE_MODULES,
Expand Down Expand Up @@ -705,6 +725,8 @@ module.exports = {
importModuleFile,
checkModuleLock,
showAssessmentScoreData,
showCoursesByDraft,
showCourseAssessmentData,
getDeletedModules,
getModules,
bulkRestoreModules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ const dayjs = require('dayjs')
const advancedFormat = require('dayjs/plugin/advancedFormat')

jest.mock('./shared-api-methods', () => ({
apiGetAssessmentDetailsForDraft: () => Promise.resolve()
apiGetAssessmentDetailsForDraft: () => Promise.resolve(),
apiGetCoursesForDraft: () => Promise.resolve(),
apiGetAssessmentDetailsForCourse: () => Promise.resolve()
}))

dayjs.extend(advancedFormat)
Expand Down Expand Up @@ -1767,6 +1769,30 @@ describe('Dashboard Actions', () => {
})
})

test('showCourseByDraft returns expected object', async () => {
const result = await DashboardActions.showCoursesByDraft(['draft-id-1', 'draft-id-2'])

expect(result).toEqual({
type: 'SHOW_COURSES_BY_DRAFT',
meta: {
module: ['draft-id-1', 'draft-id-2']
},
promise: expect.any(Promise)
})
})

test('showCourseAssessmentData returns expected object', async () => {
const result = await DashboardActions.showCourseAssessmentData(['draft-id-1', 'draft-id-2'])

expect(result).toEqual({
type: 'SHOW_ASSESSMENTS_BY_COURSE',
meta: {
module: ['draft-id-1', 'draft-id-2']
},
promise: expect.any(Promise)
})
})

test('restoreVersion makes all other sub-calls - draft json as object, restoration errors', () => {
// numerous API calls are made in sequence
const mockFetchJSON = jest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,24 @@ const apiGetAssessmentDetailsForDraft = draftId => {
const apiGetAssessmentDetailsForMultipleDrafts = draftIds =>
Promise.all(draftIds.map(id => apiGetAssessmentDetailsForDraft(id))).then(result => result.flat())

const apiGetCoursesForDraft = draftId => {
return fetch(`/api/courses/${draftId}`, defaultOptions())
.then(res => res.json())
.then(res => res.value)
}

const apiGetAssessmentDetailsForCourse = params => {
return fetch(
`/api/assessments/${params.draftId}/course/${params.contextId}/details`,
defaultOptions()
)
.then(res => res.json())
.then(res => res.value)
}

module.exports = {
apiGetAssessmentDetailsForMultipleDrafts,
apiGetAssessmentDetailsForDraft
apiGetAssessmentDetailsForDraft,
apiGetCoursesForDraft,
apiGetAssessmentDetailsForCourse
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const {
apiGetAssessmentDetailsForMultipleDrafts,
apiGetAssessmentDetailsForDraft
apiGetAssessmentDetailsForDraft,
apiGetCoursesForDraft,
apiGetAssessmentDetailsForCourse
} = require('./shared-api-methods')

describe('sharedAPIMethods', () => {
Expand Down Expand Up @@ -123,4 +125,43 @@ describe('sharedAPIMethods', () => {
}
])
})

test('apiGetCoursesForDraft returns expected object', async () => {
const result = await apiGetCoursesForDraft('draft-id-1')

expect(global.fetch).toHaveBeenCalledTimes(1)
expect(result).toEqual([
{
attemptId: 'mock-attempt-1',
draftId: 'draft-id-1',
userRoles: ['A']
},
{
attemptId: 'mock-attempt-2',
draftId: 'draft-id-1',
userRoles: ['A']
}
])
})

test('apiGetAssessmentDetailsForCourse returns expected object', async () => {
const result = await apiGetAssessmentDetailsForCourse({
draftId: 'draft-id-1',
contextId: 'context-id-1'
})

expect(global.fetch).toHaveBeenCalledTimes(1)
expect(result).toEqual([
{
attemptId: 'mock-attempt-1',
draftId: 'draft-id-1',
userRoles: ['A']
},
{
attemptId: 'mock-attempt-2',
draftId: 'draft-id-1',
userRoles: ['A']
}
])
})
})
15 changes: 13 additions & 2 deletions packages/app/obojobo-repository/shared/actions/stats-actions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// =================== API =======================

const { apiGetAssessmentDetailsForMultipleDrafts } = require('./shared-api-methods')
const {
apiGetAssessmentDetailsForMultipleDrafts,
apiGetAssessmentDetailsForCourse
} = require('./shared-api-methods')

// ================== ACTIONS ===================

Expand Down Expand Up @@ -29,9 +32,17 @@ const loadModuleAssessmentDetails = draftIds => ({
promise: apiGetAssessmentDetailsForMultipleDrafts(draftIds)
})

const LOAD_COURSE_ASSESSMENT_DATA = 'LOAD_COURSE_ASSESSMENT_DATA'
const loadCourseAssessmentData = params => ({
type: LOAD_COURSE_ASSESSMENT_DATA,
promise: apiGetAssessmentDetailsForCourse(params)
})

module.exports = {
LOAD_STATS_PAGE_MODULES_FOR_USER,
LOAD_MODULE_ASSESSMENT_DETAILS,
LOAD_COURSE_ASSESSMENT_DATA,
loadUserModuleList,
loadModuleAssessmentDetails
loadModuleAssessmentDetails,
loadCourseAssessmentData
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
const { loadUserModuleList, loadModuleAssessmentDetails } = require('./stats-actions')
const {
loadUserModuleList,
loadModuleAssessmentDetails,
loadCourseAssessmentData
} = require('./stats-actions')

jest.mock('./shared-api-methods', () => ({
apiGetAssessmentDetailsForMultipleDrafts: () => Promise.resolve()
apiGetAssessmentDetailsForMultipleDrafts: () => Promise.resolve(),
apiGetAssessmentDetailsForCourse: () => Promise.resolve()
}))

describe('statsActions', () => {
Expand Down Expand Up @@ -34,4 +39,16 @@ describe('statsActions', () => {
promise: expect.any(Promise)
})
})

test('loadCourseAssessmentData returns expected object', async () => {
const result = await loadCourseAssessmentData({
draftId: 'draft-id-1',
contextId: 'context-id-2'
})

expect(result).toEqual({
type: 'LOAD_COURSE_ASSESSMENT_DATA',
promise: expect.any(Promise)
})
})
})
Loading