mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-08 14:21:54 +00:00
Merge branch 'master' into ns-remove-wufoo-log-links
This commit is contained in:
commit
c034b0654b
21 changed files with 290 additions and 63 deletions
services/web
app
coffee/Features
Analytics
Institutions
User
views
_mixins
mixins
project/editor
public
coffee/ide
history
pdf/controllers
stylesheets
test
unit/coffee
Analytics
Institutions
User
unit_frontend/coffee/ide/history
|
@ -1,6 +1,7 @@
|
|||
AnalyticsManager = require "./AnalyticsManager"
|
||||
Errors = require "../Errors/Errors"
|
||||
AuthenticationController = require("../Authentication/AuthenticationController")
|
||||
InstitutionsAPI = require("../Institutions/InstitutionsAPI")
|
||||
GeoIpLookup = require '../../infrastructure/GeoIpLookup'
|
||||
|
||||
module.exports = AnalyticsController =
|
||||
|
@ -23,6 +24,14 @@ module.exports = AnalyticsController =
|
|||
AnalyticsManager.recordEvent user_id, req.params.event, req.body, (error) ->
|
||||
respondWith(error, res, next)
|
||||
|
||||
licences: (req, res, next) ->
|
||||
{resource_id, start_date, end_date, lag} = req.query
|
||||
InstitutionsAPI.getInstitutionLicences resource_id, start_date, end_date, lag, (error, licences) ->
|
||||
if error?
|
||||
next(error)
|
||||
else
|
||||
res.send licences
|
||||
|
||||
respondWith = (error, res, next) ->
|
||||
if error instanceof Errors.ServiceNotConfiguredError
|
||||
# ignore, no-op
|
||||
|
|
|
@ -23,4 +23,4 @@ module.exports =
|
|||
|
||||
publicApiRouter.use '/analytics/uniExternalCollaboration',
|
||||
AuthenticationController.httpAuth,
|
||||
AnalyticsProxy.call('/uniExternalCollaboration')
|
||||
AnalyticsProxy.call('/uniExternalCollaboration')
|
||||
|
|
|
@ -9,15 +9,22 @@ module.exports = InstitutionsAPI =
|
|||
method: 'GET'
|
||||
path: "/api/v2/institutions/#{institutionId.toString()}/affiliations"
|
||||
defaultErrorMessage: "Couldn't get institution affiliations"
|
||||
}, callback
|
||||
}, (error, body) -> callback(error, body or [])
|
||||
|
||||
getInstitutionLicences: (institutionId, startDate, endDate, lag, callback = (error, body) ->) ->
|
||||
makeAffiliationRequest {
|
||||
method: 'GET'
|
||||
path: "/api/v2/institutions/#{institutionId.toString()}/institution_licences"
|
||||
body: {start_date: startDate, end_date: endDate, lag}
|
||||
defaultErrorMessage: "Couldn't get institution licences"
|
||||
}, callback
|
||||
|
||||
getUserAffiliations: (userId, callback = (error, body) ->) ->
|
||||
makeAffiliationRequest {
|
||||
method: 'GET'
|
||||
path: "/api/v2/users/#{userId.toString()}/affiliations"
|
||||
defaultErrorMessage: "Couldn't get user affiliations"
|
||||
}, callback
|
||||
}, (error, body) -> callback(error, body or [])
|
||||
|
||||
|
||||
addAffiliation: (userId, email, affiliationOptions, callback) ->
|
||||
|
@ -25,11 +32,11 @@ module.exports = InstitutionsAPI =
|
|||
callback = affiliationOptions
|
||||
affiliationOptions = {}
|
||||
|
||||
{ university, department, role } = affiliationOptions
|
||||
{ university, department, role, confirmedAt } = affiliationOptions
|
||||
makeAffiliationRequest {
|
||||
method: 'POST'
|
||||
path: "/api/v2/users/#{userId.toString()}/affiliations"
|
||||
body: { email, university, department, role }
|
||||
body: { email, university, department, role, confirmedAt }
|
||||
defaultErrorMessage: "Couldn't create affiliation"
|
||||
}, callback
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ module.exports = UserUpdater =
|
|||
email = EmailHelper.parseEmail(email)
|
||||
return callback(new Error('invalid email')) if !email?
|
||||
logger.log {userId, email}, 'confirming user email'
|
||||
addAffiliation userId, email, (error) =>
|
||||
addAffiliation userId, email, {confirmedAt: confirmedAt}, (error) =>
|
||||
if error?
|
||||
logger.err error: error, 'problem adding affiliation while confirming email'
|
||||
return callback(error)
|
||||
|
|
21
services/web/app/views/_mixins/faq_search.pug
Normal file
21
services/web/app/views/_mixins/faq_search.pug
Normal file
|
@ -0,0 +1,21 @@
|
|||
mixin faq_search(headerText, headerClass)
|
||||
- if(typeof(settings.algolia) != "undefined" && typeof(settings.algolia.indexes) != "undefined" && typeof(settings.algolia.indexes.wiki) != "undefined")
|
||||
if headerText
|
||||
div(class=headerClass) #{headerText}
|
||||
.wiki(ng-controller="SearchWikiController")
|
||||
form.project-search.form-horizontal(role="form")
|
||||
.form-group.has-feedback.has-feedback-left
|
||||
.col-sm-12
|
||||
input.form-control(type='text', ng-model='searchQueryText', ng-keyup='search()', placeholder="Search help library....")
|
||||
i.fa.fa-search.form-control-feedback-left
|
||||
i.fa.fa-times.form-control-feedback(
|
||||
ng-click="clearSearchText()",
|
||||
style="cursor: pointer;",
|
||||
ng-show="searchQueryText.length > 0"
|
||||
)
|
||||
|
||||
.row
|
||||
.col-md-12(ng-cloak)
|
||||
a(ng-href='{{hit.url}}',ng-repeat='hit in hits').search-result.card.card-thin
|
||||
span(ng-bind-html='hit.name')
|
||||
div.search-result-content(ng-show="hit.content != ''", ng-bind-html='hit.content')
|
74
services/web/app/views/mixins/_pagination.pug
Normal file
74
services/web/app/views/mixins/_pagination.pug
Normal file
|
@ -0,0 +1,74 @@
|
|||
mixin paginate(pages, page_path, max_btns)
|
||||
//- @param pages.current_page the current page viewed
|
||||
//- @param pages.total_pages previously calculated,
|
||||
//- based on total entries and entries per page
|
||||
//- @param page_path the relative path, minus a trailing slash and page param
|
||||
//- @param max_btns max number of buttons on either side of the current page
|
||||
//- button and excludes first, prev, next, last
|
||||
|
||||
if pages && pages.current_page && pages.total_pages
|
||||
- var max_btns = max_btns || 4
|
||||
- var prev_page = Math.max(parseInt(pages.current_page, 10) - max_btns, 1)
|
||||
- var next_page = parseInt(pages.current_page, 10) + 1
|
||||
- var next_index = 0;
|
||||
- var full_page_path = page_path + "/page/"
|
||||
|
||||
nav(role="navigation" aria-label="Pagination Navigation")
|
||||
ul.pagination
|
||||
if pages.current_page > 1
|
||||
li
|
||||
a(
|
||||
aria-label="Go to first page"
|
||||
href=page_path
|
||||
) « First
|
||||
li
|
||||
a(
|
||||
aria-label="Go to previous page"
|
||||
href=full_page_path + (parseInt(pages.current_page, 10) - 1)
|
||||
rel="prev"
|
||||
) ‹ Prev
|
||||
|
||||
if pages.current_page - max_btns > 1
|
||||
li
|
||||
span …
|
||||
|
||||
while prev_page < pages.current_page
|
||||
li
|
||||
a(
|
||||
aria-label="Go to page " + prev_page
|
||||
href=full_page_path + prev_page
|
||||
) #{prev_page}
|
||||
- prev_page++
|
||||
|
||||
li(class="active")
|
||||
span(
|
||||
aria-label="Current Page, Page " + pages.current_page
|
||||
aria-current="true"
|
||||
) #{pages.current_page}
|
||||
|
||||
if pages.current_page < pages.total_pages
|
||||
while next_page <= pages.total_pages && next_index < max_btns
|
||||
li
|
||||
a(
|
||||
aria-label="Go to page " + next_page
|
||||
href=full_page_path + next_page
|
||||
) #{next_page}
|
||||
- next_page++
|
||||
- next_index++
|
||||
|
||||
if next_page <= pages.total_pages
|
||||
li
|
||||
span …
|
||||
|
||||
li
|
||||
a(
|
||||
aria-label="Go to next page"
|
||||
href=full_page_path + (parseInt(pages.current_page, 10) + 1)
|
||||
rel="next"
|
||||
) Next ›
|
||||
|
||||
li
|
||||
a(
|
||||
aria-label="Go to last page"
|
||||
href=full_page_path + pages.total_pages
|
||||
) Last »
|
|
@ -1,45 +1,4 @@
|
|||
div#history(ng-show="ui.view == 'history'")
|
||||
span
|
||||
.upgrade-prompt(ng-if="project.features.versioning === false && ui.view === 'history'")
|
||||
.message(ng-if="project.owner._id == user.id")
|
||||
p.text-center: strong #{translate("upgrade_to_get_feature", {feature:"full Project History"})}
|
||||
p.text-center.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
|
||||
ul.list-unstyled
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("unlimited_projects")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("collabs_per_proj", {collabcount:'Multiple'})}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("full_doc_history")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("sync_to_dropbox")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("sync_to_github")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
|#{translate("compile_larger_projects")}
|
||||
p.text-center(ng-controller="FreeTrialModalController")
|
||||
a.btn.btn-success(
|
||||
href
|
||||
ng-class="buttonClass"
|
||||
ng-click="startFreeTrial('history')"
|
||||
) #{translate("start_free_trial")}
|
||||
|
||||
.message(ng-show="project.owner._id != user.id")
|
||||
p #{translate("ask_proj_owner_to_upgrade_for_history")}
|
||||
p
|
||||
a.small(href, ng-click="toggleHistory()") #{translate("cancel")}
|
||||
|
||||
include ./history/entriesListV1
|
||||
include ./history/entriesListV2
|
||||
|
||||
|
|
|
@ -6,11 +6,13 @@ aside.change-list(
|
|||
ng-if="!history.showOnlyLabels && !history.error"
|
||||
entries="history.updates"
|
||||
current-user="user"
|
||||
current-user-is-owner="project.owner._id === user.id"
|
||||
users="projectUsers"
|
||||
load-entries="loadMore()"
|
||||
load-disabled="history.loading || history.atEnd"
|
||||
load-initialize="ui.view == 'history'"
|
||||
is-loading="history.loading"
|
||||
free-history-limit-hit="history.freeHistoryLimitHit"
|
||||
on-entry-select="handleEntrySelect(selectedEntry)"
|
||||
on-label-delete="handleLabelDelete(label)"
|
||||
)
|
||||
|
@ -134,6 +136,48 @@ script(type="text/ng-template", id="historyEntriesListTpl")
|
|||
.loading(ng-show="$ctrl.isLoading")
|
||||
i.fa.fa-spin.fa-refresh
|
||||
| #{translate("loading")}...
|
||||
.history-entries-list-upgrade-prompt(
|
||||
ng-if="$ctrl.freeHistoryLimitHit && $ctrl.currentUserIsOwner"
|
||||
ng-controller="FreeTrialModalController"
|
||||
)
|
||||
p #{translate("currently_seeing_only_24_hrs_history")}
|
||||
p: strong #{translate("upgrade_to_get_feature", {feature:"full Project History"})}
|
||||
ul.list-unstyled
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("unlimited_projects")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("collabs_per_proj", {collabcount:'Multiple'})}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("full_doc_history")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("sync_to_dropbox")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
| #{translate("sync_to_github")}
|
||||
|
||||
li
|
||||
i.fa.fa-check
|
||||
|#{translate("compile_larger_projects")}
|
||||
p.text-center
|
||||
a.btn.btn-success(
|
||||
href
|
||||
ng-class="buttonClass"
|
||||
ng-click="startFreeTrial('history')"
|
||||
) #{translate("start_free_trial")}
|
||||
p.small(ng-show="startedFreeTrial") #{translate("refresh_page_after_starting_free_trial")}
|
||||
.history-entries-list-upgrade-prompt(
|
||||
ng-if="$ctrl.freeHistoryLimitHit && !$ctrl.currentUserIsOwner"
|
||||
)
|
||||
p #{translate("currently_seeing_only_24_hrs_history")}
|
||||
strong #{translate("ask_proj_owner_to_upgrade_for_full_history")}
|
||||
|
||||
script(type="text/ng-template", id="historyEntryTpl")
|
||||
.history-entry(
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
i.fa.fa-spin.fa-refresh
|
||||
| #{translate("loading")}...
|
||||
span.history-toolbar-selected-version(
|
||||
ng-show="!history.loadingFileTree && !history.showOnlyLabels && !history.error"
|
||||
ng-show="!history.loadingFileTree && !history.showOnlyLabels && history.selection.updates.length && !history.error"
|
||||
) #{translate("browsing_project_as_of")}
|
||||
time.history-toolbar-time {{ history.selection.updates[0].meta.end_ts | formatDate:'Do MMM YYYY, h:mm a' }}
|
||||
span.history-toolbar-selected-version(
|
||||
|
@ -19,13 +19,13 @@
|
|||
button.history-toolbar-btn(
|
||||
ng-click="showAddLabelDialog();"
|
||||
ng-if="!history.showOnlyLabels"
|
||||
ng-disabled="history.loadingFileTree"
|
||||
ng-disabled="history.loadingFileTree || history.selection.updates.length == 0"
|
||||
)
|
||||
i.fa.fa-tag
|
||||
| #{translate("history_label_this_version")}
|
||||
button.history-toolbar-btn(
|
||||
ng-click="toggleHistoryViewMode();"
|
||||
ng-disabled="history.loadingFileTree"
|
||||
ng-disabled="history.loadingFileTree || history.selection.updates.length == 0"
|
||||
)
|
||||
i.fa.fa-exchange
|
||||
| #{translate("compare_to_another_version")}
|
||||
|
|
|
@ -243,7 +243,7 @@ div.full-size.pdf(ng-controller="PdfController")
|
|||
dbl-click-callback="syncToCode"
|
||||
)
|
||||
iframe(
|
||||
ng-src="{{ pdf.url }}"
|
||||
ng-src="{{ pdf.url | trusted }}"
|
||||
ng-if="settings.pdfViewer == 'native'"
|
||||
)
|
||||
|
||||
|
|
|
@ -82,6 +82,8 @@ define [
|
|||
viewMode: null
|
||||
nextBeforeTimestamp: null
|
||||
atEnd: false
|
||||
userHasFullFeature: @$scope.project?.features?.versioning or false
|
||||
freeHistoryLimitHit: false
|
||||
selection: {
|
||||
label: null
|
||||
updates: []
|
||||
|
@ -232,9 +234,11 @@ define [
|
|||
@$scope.history.labels = @_sortLabelsByVersionAndDate response.labels.data
|
||||
@_loadUpdates(updatesData.updates)
|
||||
@$scope.history.nextBeforeTimestamp = updatesData.nextBeforeTimestamp
|
||||
if !updatesData.nextBeforeTimestamp?
|
||||
if !updatesData.nextBeforeTimestamp? or @$scope.history.freeHistoryLimitHit
|
||||
@$scope.history.atEnd = true
|
||||
@$scope.history.loading = false
|
||||
if @$scope.history.updates.length == 0
|
||||
@$scope.history.loadingFileTree = false
|
||||
.catch (error) =>
|
||||
{ status, statusText } = error
|
||||
@$scope.history.error = { status, statusText }
|
||||
|
@ -387,23 +391,34 @@ define [
|
|||
|
||||
_loadUpdates: (updates = []) ->
|
||||
previousUpdate = @$scope.history.updates[@$scope.history.updates.length - 1]
|
||||
|
||||
for update in updates or []
|
||||
dateTimeNow = new Date()
|
||||
timestamp24hoursAgo = dateTimeNow.setDate(dateTimeNow.getDate() - 1)
|
||||
cutOffIndex = null
|
||||
|
||||
for update, i in updates or []
|
||||
for user in update.meta.users or []
|
||||
if user?
|
||||
user.hue = ColorManager.getHueForUserId(user.id)
|
||||
|
||||
if !previousUpdate? or !moment(previousUpdate.meta.end_ts).isSame(update.meta.end_ts, "day")
|
||||
update.meta.first_in_day = true
|
||||
|
||||
|
||||
update.selectedFrom = false
|
||||
update.selectedTo = false
|
||||
update.inSelection = false
|
||||
|
||||
previousUpdate = update
|
||||
|
||||
if !@$scope.history.userHasFullFeature and update.meta.end_ts < timestamp24hoursAgo
|
||||
cutOffIndex = i or 1 # Make sure that we show at least one entry (to allow labelling).
|
||||
@$scope.history.freeHistoryLimitHit = true
|
||||
break
|
||||
|
||||
firstLoad = @$scope.history.updates.length == 0
|
||||
|
||||
if !@$scope.history.userHasFullFeature and cutOffIndex?
|
||||
updates = updates.slice 0, cutOffIndex
|
||||
|
||||
@$scope.history.updates =
|
||||
@$scope.history.updates.concat(updates)
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ define [
|
|||
loadInitialize: "<"
|
||||
isLoading: "<"
|
||||
currentUser: "<"
|
||||
freeHistoryLimitHit: "<"
|
||||
currentUserIsOwner: "<"
|
||||
onEntrySelect: "&"
|
||||
onLabelDelete: "&"
|
||||
controller: historyEntriesListController
|
||||
|
|
|
@ -11,6 +11,10 @@ define [
|
|||
# and then again on ack.
|
||||
AUTO_COMPILE_DEBOUNCE = 2000
|
||||
|
||||
App.filter('trusted', ['$sce', ($sce)->
|
||||
return (url)-> return $sce.trustAsResourceUrl(url);
|
||||
])
|
||||
|
||||
App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, localStorage) ->
|
||||
# enable per-user containers by default
|
||||
perUserCompile = true
|
||||
|
@ -212,7 +216,7 @@ define [
|
|||
|
||||
buildPdfDownloadUrl = (pdfDownloadDomain, path)->
|
||||
#we only download builds from compiles server for security reasons
|
||||
if pdfDownloadDomain? and path.indexOf("build") != -1
|
||||
if pdfDownloadDomain? and path? and path.indexOf("build") != -1
|
||||
return "#{pdfDownloadDomain}#{path}"
|
||||
else
|
||||
return path
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: (@line-height-computed / 2);
|
||||
}
|
||||
.history-toolbar-time,
|
||||
.history-toolbar-selected-label {
|
||||
|
@ -33,7 +34,7 @@
|
|||
.btn-xs;
|
||||
padding-left: @padding-small-horizontal;
|
||||
padding-right: @padding-small-horizontal;
|
||||
margin-left: (@line-height-computed / 2);
|
||||
margin-right: (@line-height-computed / 2);
|
||||
}
|
||||
.history-toolbar-entries-list {
|
||||
flex: 0 0 @changesListWidth;
|
||||
|
@ -173,6 +174,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.history-entries-list-upgrade-prompt {
|
||||
background-color: #FFF;
|
||||
margin-bottom: 2px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.history-labels-list {
|
||||
.history-entries;
|
||||
overflow-y: auto;
|
||||
|
|
|
@ -277,6 +277,21 @@
|
|||
@chat-new-message-textarea-bg : @ol-blue-gray-1;
|
||||
@chat-new-message-textarea-color : @ol-blue-gray-6;
|
||||
|
||||
// Pagination
|
||||
@pagination-active-bg : @ol-dark-green;
|
||||
@pagination-active-border : @gray-lighter;
|
||||
@pagination-active-color : #FFF;
|
||||
@pagination-bg : #FFF;
|
||||
@pagination-border : @gray-lighter;
|
||||
@pagination-color : @ol-dark-green;
|
||||
@pagination-disabled-color : @gray-dark;
|
||||
@pagination-disabled-bg : @gray-lightest;
|
||||
@pagination-disabled-border : @gray-lighter;
|
||||
@pagination-hover-color : @ol-dark-green;
|
||||
@pagination-hover-bg : @gray-lightest;
|
||||
@pagination-hover-border : @gray-lighter;
|
||||
|
||||
|
||||
// PDF
|
||||
@pdf-top-offset : @toolbar-small-height;
|
||||
@pdf-bg : @ol-blue-gray-1;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
@import "_ol_style_includes.less";
|
||||
@import "components/embed-responsive.less";
|
||||
@import "components/icons.less";
|
||||
@import "components/pagination.less";
|
||||
|
||||
// Pages
|
||||
@import "app/about.less";
|
||||
|
|
|
@ -17,9 +17,13 @@ describe 'AnalyticsController', ->
|
|||
updateEditingSession: sinon.stub().callsArgWith(3)
|
||||
recordEvent: sinon.stub().callsArgWith(3)
|
||||
|
||||
@InstitutionsAPI =
|
||||
getInstitutionLicences: sinon.stub().callsArgWith(4)
|
||||
|
||||
@controller = SandboxedModule.require modulePath, requires:
|
||||
"./AnalyticsManager":@AnalyticsManager
|
||||
"../Authentication/AuthenticationController":@AuthenticationController
|
||||
"../Institutions/InstitutionsAPI":@InstitutionsAPI
|
||||
"logger-sharelatex":
|
||||
log:->
|
||||
'../../infrastructure/GeoIpLookup': @GeoIpLookup =
|
||||
|
@ -66,3 +70,19 @@ describe 'AnalyticsController', ->
|
|||
@controller.recordEvent @req, @res
|
||||
@AnalyticsManager.recordEvent.calledWith(@req.sessionID, @req.params["event"], @req.body).should.equal true
|
||||
done()
|
||||
|
||||
describe "licences", ->
|
||||
beforeEach ->
|
||||
@req =
|
||||
query:
|
||||
resource_id:1
|
||||
start_date:'1514764800'
|
||||
end_date:'1530662400'
|
||||
resource_type:'institution'
|
||||
sessionID: "sessionIDHere"
|
||||
session: {}
|
||||
|
||||
it "should trigger institutions api to fetch licences graph data", (done)->
|
||||
@controller.licences @req, @res
|
||||
@InstitutionsAPI.getInstitutionLicences.calledWith(@req.query["resource_id"], @req.query["start_date"], @req.query["end_date"], @req.query["lag"]).should.equal true
|
||||
done()
|
||||
|
|
|
@ -11,12 +11,12 @@ describe "InstitutionsAPI", ->
|
|||
|
||||
beforeEach ->
|
||||
@logger = err: sinon.stub(), log: ->
|
||||
settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } }
|
||||
@settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } }
|
||||
@request = sinon.stub()
|
||||
@InstitutionsAPI = SandboxedModule.require modulePath, requires:
|
||||
"logger-sharelatex": @logger
|
||||
"metrics-sharelatex": timeAsyncMethod: sinon.stub()
|
||||
'settings-sharelatex': settings
|
||||
'settings-sharelatex': @settings
|
||||
'request': @request
|
||||
|
||||
@stubbedUser =
|
||||
|
@ -40,6 +40,34 @@ describe "InstitutionsAPI", ->
|
|||
should.not.exist(requestOptions.body)
|
||||
body.should.equal responseBody
|
||||
done()
|
||||
|
||||
it 'handle empty response', (done)->
|
||||
@settings.apis = null
|
||||
@InstitutionsAPI.getInstitutionAffiliations @institutionId, (err, body) =>
|
||||
should.not.exist(err)
|
||||
expect(body).to.be.a 'Array'
|
||||
body.length.should.equal 0
|
||||
done()
|
||||
|
||||
describe 'getInstitutionLicences', ->
|
||||
it 'get licences', (done)->
|
||||
@institutionId = 123
|
||||
responseBody = {"lag":"monthly","data":[{"key":"users","values":[{"x":"2018-01-01","y":1}]}]}
|
||||
@request.yields(null, { statusCode: 200 }, responseBody)
|
||||
startDate = '1417392000'
|
||||
endDate = '1420848000'
|
||||
@InstitutionsAPI.getInstitutionLicences @institutionId, startDate, endDate, 'monthly', (err, body) =>
|
||||
should.not.exist(err)
|
||||
@request.calledOnce.should.equal true
|
||||
requestOptions = @request.lastCall.args[0]
|
||||
expectedUrl = "v1.url/api/v2/institutions/#{@institutionId}/institution_licences"
|
||||
requestOptions.url.should.equal expectedUrl
|
||||
requestOptions.method.should.equal 'GET'
|
||||
requestOptions.body['start_date'].should.equal startDate
|
||||
requestOptions.body['end_date'].should.equal endDate
|
||||
requestOptions.body.lag.should.equal 'monthly'
|
||||
body.should.equal responseBody
|
||||
done()
|
||||
|
||||
describe 'getUserAffiliations', ->
|
||||
it 'get affiliations', (done)->
|
||||
|
@ -65,6 +93,14 @@ describe "InstitutionsAPI", ->
|
|||
err.message.should.have.string body.errors
|
||||
done()
|
||||
|
||||
it 'handle empty response', (done)->
|
||||
@settings.apis = null
|
||||
@InstitutionsAPI.getUserAffiliations @stubbedUser._id, (err, body) =>
|
||||
should.not.exist(err)
|
||||
expect(body).to.be.a 'Array'
|
||||
body.length.should.equal 0
|
||||
done()
|
||||
|
||||
describe 'addAffiliation', ->
|
||||
beforeEach ->
|
||||
@request.callsArgWith(1, null, { statusCode: 201 })
|
||||
|
@ -74,6 +110,7 @@ describe "InstitutionsAPI", ->
|
|||
university: { id: 1 }
|
||||
role: 'Prof'
|
||||
department: 'Math'
|
||||
confirmedAt: new Date()
|
||||
@InstitutionsAPI.addAffiliation @stubbedUser._id, @newEmail, affiliationOptions, (err)=>
|
||||
should.not.exist(err)
|
||||
@request.calledOnce.should.equal true
|
||||
|
@ -83,11 +120,12 @@ describe "InstitutionsAPI", ->
|
|||
requestOptions.method.should.equal 'POST'
|
||||
|
||||
body = requestOptions.body
|
||||
Object.keys(body).length.should.equal 4
|
||||
Object.keys(body).length.should.equal 5
|
||||
body.email.should.equal @newEmail
|
||||
body.university.should.equal affiliationOptions.university
|
||||
body.department.should.equal affiliationOptions.department
|
||||
body.role.should.equal affiliationOptions.role
|
||||
body.confirmedAt.should.equal affiliationOptions.confirmedAt
|
||||
done()
|
||||
|
||||
it 'handle error', (done)->
|
||||
|
|
|
@ -241,7 +241,7 @@ describe "UserUpdater", ->
|
|||
@UserUpdater.confirmEmail @stubbedUser._id, @newEmail, (err)=>
|
||||
should.not.exist(err)
|
||||
@addAffiliation.calledOnce.should.equal true
|
||||
sinon.assert.calledWith(@addAffiliation, @stubbedUser._id, @newEmail)
|
||||
sinon.assert.calledWith(@addAffiliation, @stubbedUser._id, @newEmail, { confirmedAt: new Date() } )
|
||||
done()
|
||||
|
||||
it 'handle error', (done)->
|
||||
|
@ -264,7 +264,7 @@ describe "UserUpdater", ->
|
|||
done()
|
||||
|
||||
it 'handle affiliation error', (done)->
|
||||
@addAffiliation.callsArgWith(2, new Error('nope'))
|
||||
@addAffiliation.callsArgWith(3, new Error('nope'))
|
||||
@UserUpdater.confirmEmail @stubbedUser._id, @newEmail, (err)=>
|
||||
should.exist(err)
|
||||
@UserUpdater.updateUser.called.should.equal false
|
||||
|
|
|
@ -4,16 +4,21 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) ->
|
|||
@scope =
|
||||
$watch: sinon.stub()
|
||||
$on: sinon.stub()
|
||||
project:
|
||||
features:
|
||||
versioning: true
|
||||
@ide = {}
|
||||
@historyManager = new HistoryV2Manager(@ide, @scope)
|
||||
|
||||
it "should setup the history scope on intialization", ->
|
||||
it "should setup the history scope on initialization", ->
|
||||
expect(@scope.history).to.deep.equal({
|
||||
isV2: true
|
||||
updates: []
|
||||
viewMode: null
|
||||
nextBeforeTimestamp: null
|
||||
atEnd: false
|
||||
userHasFullFeature: true
|
||||
freeHistoryLimitHit: false
|
||||
selection: {
|
||||
label: null
|
||||
updates: []
|
||||
|
@ -32,6 +37,12 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) ->
|
|||
selectedFile: null
|
||||
})
|
||||
|
||||
|
||||
it "should setup history without full access to the feature if the project does not have versioning", ->
|
||||
@scope.project.features.versioning = false
|
||||
@historyManager = new HistoryV2Manager(@ide, @scope)
|
||||
expect(@scope.history.userHasFullFeature).to.equal false
|
||||
|
||||
describe "_perDocSummaryOfUpdates", ->
|
||||
it "should return the range of updates for the docs", ->
|
||||
result = @historyManager._perDocSummaryOfUpdates([{
|
||||
|
|
Loading…
Add table
Reference in a new issue