mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-04 12:16:51 +00:00
Merge branch 'master' into ja-turn-off-registration
This commit is contained in:
commit
310aa1d49d
18 changed files with 210 additions and 73 deletions
|
@ -24,6 +24,7 @@ AnalyticsManager = require "../Analytics/AnalyticsManager"
|
|||
Sources = require "../Authorization/Sources"
|
||||
TokenAccessHandler = require '../TokenAccess/TokenAccessHandler'
|
||||
CollaboratorsHandler = require '../Collaborators/CollaboratorsHandler'
|
||||
Modules = require '../../infrastructure/Modules'
|
||||
crypto = require 'crypto'
|
||||
|
||||
module.exports = ProjectController =
|
||||
|
@ -148,6 +149,11 @@ module.exports = ProjectController =
|
|||
NotificationsHandler.getUserNotifications user_id, cb
|
||||
projects: (cb)->
|
||||
ProjectGetter.findAllUsersProjects user_id, 'name lastUpdated publicAccesLevel archived owner_ref tokens', cb
|
||||
v1Projects: (cb) ->
|
||||
Modules.hooks.fire "findAllV1Projects", user_id, (error, projects = []) ->
|
||||
if error? and error.message == 'No V1 connection'
|
||||
return cb(null, projects: [], tags: [], noConnection: true)
|
||||
return cb(error, projects[0]) # hooks.fire returns an array of results, only need first
|
||||
hasSubscription: (cb)->
|
||||
LimitationsManager.userHasSubscriptionOrIsGroupMember currentUser, cb
|
||||
user: (cb) ->
|
||||
|
@ -157,11 +163,12 @@ module.exports = ProjectController =
|
|||
logger.err err:err, "error getting data for project list page"
|
||||
return next(err)
|
||||
logger.log results:results, user_id:user_id, "rendering project list"
|
||||
tags = results.tags[0]
|
||||
v1Tags = results.v1Projects?.tags or []
|
||||
tags = results.tags[0].concat(v1Tags)
|
||||
notifications = require("underscore").map results.notifications, (notification)->
|
||||
notification.html = req.i18n.translate(notification.templateKey, notification.messageOpts)
|
||||
return notification
|
||||
projects = ProjectController._buildProjectList results.projects
|
||||
projects = ProjectController._buildProjectList results.projects, results.v1Projects?.projects
|
||||
user = results.user
|
||||
ProjectController._injectProjectOwners projects, (error, projects) ->
|
||||
return next(error) if error?
|
||||
|
@ -173,6 +180,8 @@ module.exports = ProjectController =
|
|||
notifications: notifications or []
|
||||
user: user
|
||||
hasSubscription: results.hasSubscription[0]
|
||||
isShowingV1Projects: results.v1Projects?
|
||||
noV1Connection: results.v1Projects?.noConnection
|
||||
}
|
||||
|
||||
if Settings?.algolia?.app_id? and Settings?.algolia?.read_only_api_key?
|
||||
|
@ -280,7 +289,7 @@ module.exports = ProjectController =
|
|||
# Extract data from user's ObjectId
|
||||
timestamp = parseInt(user_id.toString().substring(0, 8), 16)
|
||||
|
||||
rolloutPercentage = 5 # Percentage of users to roll out to
|
||||
rolloutPercentage = 10 # Percentage of users to roll out to
|
||||
if !ProjectController._isInPercentageRollout('autocompile', user_id, rolloutPercentage)
|
||||
# Don't show if user is not part of roll out
|
||||
return cb(null, { enabled: false, showOnboarding: false })
|
||||
|
@ -333,7 +342,7 @@ module.exports = ProjectController =
|
|||
enableTokenAccessUI = ProjectController._isInPercentageRollout(
|
||||
'linksharing',
|
||||
project.owner_ref,
|
||||
40
|
||||
100
|
||||
)
|
||||
showLinkSharingOnboarding = enableTokenAccessUI && results.couldShowLinkSharingOnboarding
|
||||
AuthorizationManager.getPrivilegeLevelForProject user_id, project_id, token, (error, privilegeLevel)->
|
||||
|
@ -390,7 +399,7 @@ module.exports = ProjectController =
|
|||
showLinkSharingOnboarding: showLinkSharingOnboarding
|
||||
timer.done()
|
||||
|
||||
_buildProjectList: (allProjects)->
|
||||
_buildProjectList: (allProjects, v1Projects = [])->
|
||||
{owned, readAndWrite, readOnly, tokenReadAndWrite, tokenReadOnly} = allProjects
|
||||
projects = []
|
||||
for project in owned
|
||||
|
@ -400,6 +409,8 @@ module.exports = ProjectController =
|
|||
projects.push ProjectController._buildProjectViewModel(project, "readWrite", Sources.INVITE)
|
||||
for project in readOnly
|
||||
projects.push ProjectController._buildProjectViewModel(project, "readOnly", Sources.INVITE)
|
||||
for project in v1Projects
|
||||
projects.push ProjectController._buildV1ProjectViewModel(project)
|
||||
# Token-access
|
||||
# Only add these projects if they're not already present, this gives us cascading access
|
||||
# from 'owner' => 'token-read-only'
|
||||
|
@ -424,9 +435,20 @@ module.exports = ProjectController =
|
|||
archived: !!project.archived
|
||||
owner_ref: project.owner_ref
|
||||
tokens: project.tokens
|
||||
isV1Project: false
|
||||
}
|
||||
return model
|
||||
|
||||
_buildV1ProjectViewModel: (project) ->
|
||||
{
|
||||
id: project.id
|
||||
name: project.title
|
||||
lastUpdated: new Date(project.updated_at * 1000) # Convert from epoch
|
||||
accessLevel: "readOnly",
|
||||
archived: project.removed || project.archived
|
||||
isV1Project: true
|
||||
}
|
||||
|
||||
_injectProjectOwners: (projects, callback = (error, projects) ->) ->
|
||||
users = {}
|
||||
for project in projects
|
||||
|
|
|
@ -51,7 +51,6 @@ module.exports = UserController =
|
|||
|
||||
updateUserSettings : (req, res)->
|
||||
user_id = AuthenticationController.getLoggedInUserId(req)
|
||||
usingExternalAuth = settings.ldap? or settings.saml?
|
||||
logger.log user_id: user_id, "updating account settings"
|
||||
User.findById user_id, (err, user)->
|
||||
if err? or !user?
|
||||
|
@ -84,7 +83,7 @@ module.exports = UserController =
|
|||
user.ace.syntaxValidation = req.body.syntaxValidation
|
||||
user.save (err)->
|
||||
newEmail = req.body.email?.trim().toLowerCase()
|
||||
if !newEmail? or newEmail == user.email or usingExternalAuth
|
||||
if !newEmail? or newEmail == user.email or req.externalAuthenticationSystemUsed()
|
||||
# end here, don't update email
|
||||
AuthenticationController.setInSessionUser(req, {first_name: user.first_name, last_name: user.last_name})
|
||||
return res.sendStatus 200
|
||||
|
|
40
services/web/app/views/project/list/item.pug
Normal file
40
services/web/app/views/project/list/item.pug
Normal file
|
@ -0,0 +1,40 @@
|
|||
.col-xs-6
|
||||
input.select-item(
|
||||
select-individual,
|
||||
type="checkbox",
|
||||
ng-model="project.selected"
|
||||
stop-propagation="click"
|
||||
aria-label=translate('select_project') + " '{{ project.name }}'"
|
||||
)
|
||||
span
|
||||
a.projectName(
|
||||
ng-href="{{projectLink(project)}}"
|
||||
stop-propagation="click"
|
||||
) {{project.name}}
|
||||
span(
|
||||
ng-controller="TagListController"
|
||||
)
|
||||
.tag-label(
|
||||
ng-repeat='tag in project.tags'
|
||||
stop-propagation="click"
|
||||
)
|
||||
a.label.label-default.tag-label-name(
|
||||
href,
|
||||
ng-click="selectTag(tag)"
|
||||
) {{tag.name}}
|
||||
a.label.label-default.tag-label-remove(
|
||||
href
|
||||
ng-click="removeProjectFromTag(project, tag)"
|
||||
) ×
|
||||
|
||||
.col-xs-2
|
||||
span.owner {{ownerName()}}
|
||||
span(ng-if="isLinkSharingProject(project)")
|
||||
|
|
||||
i.fa.fa-link.small(
|
||||
tooltip=translate("link_sharing")
|
||||
tooltip-placement="right"
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
.col-xs-4
|
||||
span.last-modified {{project.lastUpdated | formatDate}}
|
|
@ -114,6 +114,10 @@
|
|||
) #{translate("delete_forever")}
|
||||
|
||||
.row.row-spaced
|
||||
if noV1Connection
|
||||
.col-xs-12
|
||||
.alert.alert-warning No V1 Connection
|
||||
|
||||
.col-xs-12
|
||||
.card.card-thin.project-list-card
|
||||
ul.list-unstyled.project-list.structured-list(
|
||||
|
@ -142,47 +146,15 @@
|
|||
ng-repeat="project in visibleProjects | orderBy:predicate:reverse",
|
||||
ng-controller="ProjectListItemController"
|
||||
)
|
||||
.row(select-row)
|
||||
.col-xs-6
|
||||
input.select-item(
|
||||
select-individual,
|
||||
type="checkbox",
|
||||
ng-model="project.selected"
|
||||
stop-propagation="click"
|
||||
aria-label=translate('select_project') + " '{{ project.name }}'"
|
||||
)
|
||||
span
|
||||
a.projectName(
|
||||
ng-href="{{projectLink(project)}}"
|
||||
stop-propagation="click"
|
||||
) {{project.name}}
|
||||
span(
|
||||
ng-controller="TagListController"
|
||||
)
|
||||
.tag-label(
|
||||
ng-repeat='tag in project.tags'
|
||||
stop-propagation="click"
|
||||
)
|
||||
a.label.label-default.tag-label-name(
|
||||
href,
|
||||
ng-click="selectTag(tag)"
|
||||
) {{tag.name}}
|
||||
a.label.label-default.tag-label-remove(
|
||||
href
|
||||
ng-click="removeProjectFromTag(project, tag)"
|
||||
) ×
|
||||
|
||||
.col-xs-2
|
||||
span.owner {{ownerName()}}
|
||||
span(ng-if="isLinkSharingProject(project)")
|
||||
|
|
||||
i.fa.fa-link.small(
|
||||
tooltip=translate("link_sharing")
|
||||
tooltip-placement="right"
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
.col-xs-4
|
||||
span.last-modified {{project.lastUpdated | formatDate}}
|
||||
.row(
|
||||
ng-if="!project.isV1Project"
|
||||
select-row
|
||||
)
|
||||
include ./item
|
||||
.row(
|
||||
ng-if="project.isV1Project"
|
||||
)
|
||||
include ./v1-item
|
||||
li(
|
||||
ng-if="visibleProjects.length == 0",
|
||||
ng-cloak
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
a(href) #{translate("shared_with_you")}
|
||||
li(ng-class="{active: (filter == 'archived')}", ng-click="filterProjects('archived')")
|
||||
a(href) #{translate("deleted_projects")}
|
||||
if isShowingV1Projects
|
||||
li(ng-class="{active: (filter == 'v1')}", ng-click="filterProjects('v1')")
|
||||
a(href) #{translate("v1_projects")}
|
||||
li.separator
|
||||
h2 #{translate("folders")}
|
||||
li.tag(
|
||||
|
@ -62,6 +65,11 @@
|
|||
)
|
||||
span.name {{tag.name}}
|
||||
span.subdued ({{tag.project_ids.length}})
|
||||
span.v1-badge(
|
||||
ng-if="tag.isV1",
|
||||
ng-cloak,
|
||||
aria-label=translate("v1_badge")
|
||||
)
|
||||
span.dropdown.tag-menu(dropdown)
|
||||
a.dropdown-toggle(
|
||||
href="#",
|
||||
|
|
11
services/web/app/views/project/list/v1-item.pug
Normal file
11
services/web/app/views/project/list/v1-item.pug
Normal file
|
@ -0,0 +1,11 @@
|
|||
.col-xs-8
|
||||
span.v1-badge(aria-label=translate("v1_badge"))
|
||||
span
|
||||
if settings.overleaf && settings.overleaf.host
|
||||
a.projectName(
|
||||
href=settings.overleaf.host + "/{{project.id}}"
|
||||
stop-propagation="click"
|
||||
) {{project.name}}
|
||||
|
||||
.col-xs-4
|
||||
span.last-modified {{project.lastUpdated | formatDate}}
|
|
@ -35,7 +35,7 @@ define [
|
|||
url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}"
|
||||
return url
|
||||
|
||||
App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble, $http) ->
|
||||
App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble, $http, $q) ->
|
||||
monkeyPatchSearch($rootScope, $compile)
|
||||
|
||||
return {
|
||||
|
@ -97,7 +97,7 @@ define [
|
|||
|
||||
if scope.spellCheck # only enable spellcheck when explicitly required
|
||||
spellCheckCache = $cacheFactory("spellCheck-#{scope.name}", {capacity: 1000})
|
||||
spellCheckManager = new SpellCheckManager(scope, editor, element, spellCheckCache, $http)
|
||||
spellCheckManager = new SpellCheckManager(scope, editor, element, spellCheckCache, $http, $q)
|
||||
undoManager = new UndoManager(scope, editor, element)
|
||||
highlightsManager = new HighlightsManager(scope, editor, element)
|
||||
cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage)
|
||||
|
@ -356,6 +356,7 @@ define [
|
|||
session.setOption("useWorker", scope.syntaxValidation);
|
||||
|
||||
# now attach session to editor
|
||||
editor.setReadOnly(true) # set to readonly until document change handlers are attached
|
||||
editor.setSession(session)
|
||||
|
||||
doc = session.getDocument()
|
||||
|
@ -364,6 +365,8 @@ define [
|
|||
editor.initing = true
|
||||
sharejs_doc.attachToAce(editor)
|
||||
editor.initing = false
|
||||
# now ready to edit document
|
||||
editor.setReadOnly(scope.readOnly) # respect the readOnly setting, normally false
|
||||
|
||||
resetScrollMargins()
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ define [
|
|||
lastCharIsBackslash = lineUpToCursor.slice(-1) == "\\"
|
||||
lastTwoChars = lineUpToCursor.slice(-2)
|
||||
# Don't offer autocomplete on double-backslash, backslash-colon, etc
|
||||
if lastTwoChars.match(/^\\[^a-z]$/)
|
||||
if lastTwoChars.match(/^\\[^a-zA-Z]$/)
|
||||
@editor?.completer?.detach?()
|
||||
return
|
||||
if commandName in ['begin', 'end']
|
||||
|
@ -229,8 +229,9 @@ define [
|
|||
99999
|
||||
)
|
||||
)
|
||||
|
||||
if lineBeyondCursor
|
||||
if partialCommandMatch = lineBeyondCursor.match(/^([a-z0-9]+)\{/)
|
||||
if partialCommandMatch = lineBeyondCursor.match(/^([a-zA-Z0-9]+)\{/)
|
||||
# We've got a partial command after the cursor
|
||||
commandTail = partialCommandMatch[1]
|
||||
# remove rest of the partial command, right of cursor
|
||||
|
|
|
@ -17,8 +17,8 @@ define [
|
|||
# \includegraphics[width=\textwidth]{..
|
||||
# should not match the \textwidth.
|
||||
blankArguments = lineUpToCursor.replace /\[([^\]]*)\]/g, (args) ->
|
||||
Array(args.length+1).join('.')
|
||||
if m = blankArguments.match(/(\\[^\\]+)$/)
|
||||
Array(args.length + 1).join('.')
|
||||
if m = blankArguments.match(/(\\[^\\]*)$/)
|
||||
return m.index
|
||||
else
|
||||
return -1
|
||||
|
|
|
@ -5,9 +5,9 @@ define [
|
|||
Range = ace.require("ace/range").Range
|
||||
|
||||
class SpellCheckManager
|
||||
constructor: (@$scope, @editor, @element, @cache, @$http) ->
|
||||
constructor: (@$scope, @editor, @element, @cache, @$http, @$q) ->
|
||||
$(document.body).append @element.find(".spell-check-menu")
|
||||
|
||||
@inProgressRequest = null
|
||||
@updatedLines = []
|
||||
@highlightedWordManager = new HighlightedWordManager(@editor)
|
||||
|
||||
|
@ -23,8 +23,8 @@ define [
|
|||
|
||||
@editor.on "changeSession", (e) =>
|
||||
@highlightedWordManager.reset()
|
||||
# if @inProgressRequest?
|
||||
# @inProgressRequest.abort()
|
||||
if @inProgressRequest?
|
||||
@inProgressRequest.abort()
|
||||
|
||||
if @$scope.spellCheckEnabled and @$scope.spellCheckLanguage and @$scope.spellCheckLanguage != ""
|
||||
@runSpellCheckSoon(200)
|
||||
|
@ -235,11 +235,18 @@ define [
|
|||
apiRequest: (endpoint, data, callback = (error, result) ->)->
|
||||
data.token = window.user.id
|
||||
data._csrf = window.csrfToken
|
||||
@$http.post("/spelling" + endpoint, data)
|
||||
# use angular timeout option to cancel request if doc is changed
|
||||
requestHandler = @$q.defer()
|
||||
options = {timeout: requestHandler.promise}
|
||||
httpRequest = @$http.post("/spelling" + endpoint, data, options)
|
||||
.then (response) =>
|
||||
callback(null, response.data)
|
||||
.catch (response) =>
|
||||
callback(new Error('api failure'))
|
||||
# provide a method to cancel the request
|
||||
abortRequest = () ->
|
||||
requestHandler.resolve()
|
||||
return { abort: abortRequest }
|
||||
|
||||
blacklistedCommandRegex: ///
|
||||
\\ # initial backslash
|
||||
|
|
|
@ -85,7 +85,11 @@ define [
|
|||
isTimeNonMonotonic = timeSinceLastCompile < 0
|
||||
|
||||
if isTimeNonMonotonic || timeSinceLastCompile >= AUTO_COMPILE_TIMEOUT
|
||||
if (!ide.$scope.hasLintingError)
|
||||
# If user has code check disabled, it is likely because they have
|
||||
# linting errors that they are ignoring. Therefore it doesn't make sense
|
||||
# to block auto compiles. It also causes problems where server-provided
|
||||
# linting errors aren't cleared after typing
|
||||
if (ide.$scope.settings.syntaxValidation and !ide.$scope.hasLintingError)
|
||||
$scope.recompile(isAutoCompileOnChange: true)
|
||||
else
|
||||
# Extend remainder of timeout
|
||||
|
|
|
@ -116,6 +116,10 @@ define [
|
|||
if $scope.filter == "shared" and project.accessLevel == "owner"
|
||||
visible = false
|
||||
|
||||
# Hide projects from V1 if we only want to see shared projects
|
||||
if $scope.filter == "shared" and project.isV1Project
|
||||
visible = false
|
||||
|
||||
# Hide projects we don't own if we only want to see owned projects
|
||||
if $scope.filter == "owned" and project.accessLevel != "owner"
|
||||
visible = false
|
||||
|
@ -129,6 +133,9 @@ define [
|
|||
if project.archived
|
||||
visible = false
|
||||
|
||||
if $scope.filter == "v1" and !project.isV1Project
|
||||
visible = false
|
||||
|
||||
if visible
|
||||
$scope.visibleProjects.push project
|
||||
else
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
@import "app/invite.less";
|
||||
@import "app/review-features-page.less";
|
||||
@import "app/error-pages.less";
|
||||
@import "app/v1-badge.less";
|
||||
|
||||
@import "../js/libs/pdfListView/TextLayer.css";
|
||||
@import "../js/libs/pdfListView/AnnotationsLayer.css";
|
||||
|
|
|
@ -366,6 +366,11 @@ ul.project-list {
|
|||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.v1-badge {
|
||||
margin-right: 9px;
|
||||
margin-left: 7px;
|
||||
}
|
||||
}
|
||||
i.tablesort {
|
||||
padding-left: 8px;
|
||||
|
|
10
services/web/public/stylesheets/app/v1-badge.less
Normal file
10
services/web/public/stylesheets/app/v1-badge.less
Normal file
|
@ -0,0 +1,10 @@
|
|||
.v1-badge {
|
||||
&:extend(.label);
|
||||
&:extend(.label-default);
|
||||
vertical-align: 11%;
|
||||
padding: 1px 3px;
|
||||
margin: 0 6px;
|
||||
&:before {
|
||||
content: "V1";
|
||||
}
|
||||
}
|
|
@ -66,6 +66,9 @@ describe "ProjectController", ->
|
|||
protectTokens: sinon.stub()
|
||||
@CollaboratorsHandler =
|
||||
userIsTokenMember: sinon.stub().callsArgWith(2, null, false)
|
||||
@Modules =
|
||||
hooks:
|
||||
fire: sinon.stub()
|
||||
@ProjectController = SandboxedModule.require modulePath, requires:
|
||||
"settings-sharelatex":@settings
|
||||
"logger-sharelatex":
|
||||
|
@ -93,6 +96,7 @@ describe "ProjectController", ->
|
|||
"../Analytics/AnalyticsManager": @AnalyticsManager
|
||||
"../TokenAccess/TokenAccessHandler": @TokenAccessHandler
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler
|
||||
"../../infrastructure/Modules": @Modules
|
||||
|
||||
@projectName = "£12321jkj9ujkljds"
|
||||
@req =
|
||||
|
@ -263,6 +267,7 @@ describe "ProjectController", ->
|
|||
@TagsHandler.getAllTags.callsArgWith(1, null, @tags, {})
|
||||
@NotificationsHandler.getUserNotifications = sinon.stub().callsArgWith(1, null, @notifications, {})
|
||||
@ProjectGetter.findAllUsersProjects.callsArgWith(2, null, @allProjects)
|
||||
@Modules.hooks.fire.withArgs('findAllV1Projects', @user._id).yields(undefined) # Without integration module hook, cb returns undefined
|
||||
|
||||
it "should render the project/list page", (done)->
|
||||
@res.render = (pageName, opts)=>
|
||||
|
@ -295,6 +300,53 @@ describe "ProjectController", ->
|
|||
done()
|
||||
@ProjectController.projectListPage @req, @res
|
||||
|
||||
describe 'with overleaf-integration-web-module hook', ->
|
||||
beforeEach ->
|
||||
@V1Response =
|
||||
projects: [
|
||||
{ id: '123mockV1Id', title: 'mock title', updated_at: 1509616411, removed: false, archived: false }
|
||||
{ id: '456mockV1Id', title: 'mock title 2', updated_at: 1509616411, removed: true, archived: false }
|
||||
],
|
||||
tags: [
|
||||
{ name: 'mock tag', project_ids: ['123mockV1Id'] }
|
||||
]
|
||||
@Modules.hooks.fire.withArgs('findAllV1Projects', @user._id).yields(null, [@V1Response]) # Need to wrap response in array, as multiple hooks could fire
|
||||
|
||||
it 'should include V1 projects', (done) ->
|
||||
@res.render = (pageName, opts) =>
|
||||
opts.projects.length.should.equal (
|
||||
@projects.length +
|
||||
@collabertions.length +
|
||||
@readOnly.length +
|
||||
@tokenReadAndWrite.length +
|
||||
@tokenReadOnly.length +
|
||||
@V1Response.projects.length
|
||||
)
|
||||
opts.projects.forEach (p) ->
|
||||
# Check properties correctly mapped from V1
|
||||
expect(p).to.have.property 'id'
|
||||
expect(p).to.have.property 'name'
|
||||
expect(p).to.have.property 'lastUpdated'
|
||||
expect(p).to.have.property 'accessLevel'
|
||||
expect(p).to.have.property 'archived'
|
||||
done()
|
||||
@ProjectController.projectListPage @req, @res
|
||||
|
||||
it 'should include V1 tags', (done) ->
|
||||
@res.render = (pageName, opts) =>
|
||||
opts.tags.length.should.equal (@tags.length + @V1Response.tags.length)
|
||||
opts.tags.forEach (t) ->
|
||||
expect(t).to.have.property 'name'
|
||||
expect(t).to.have.property 'project_ids'
|
||||
done()
|
||||
@ProjectController.projectListPage @req, @res
|
||||
|
||||
it 'should have isShowingV1Projects flag', (done) ->
|
||||
@res.render = (pageName, opts) =>
|
||||
opts.isShowingV1Projects.should.equal true
|
||||
done()
|
||||
@ProjectController.projectListPage @req, @res
|
||||
|
||||
describe "projectListPage with duplicate projects", ->
|
||||
|
||||
beforeEach ->
|
||||
|
@ -338,6 +390,7 @@ describe "ProjectController", ->
|
|||
@TagsHandler.getAllTags.callsArgWith(1, null, @tags, {})
|
||||
@NotificationsHandler.getUserNotifications = sinon.stub().callsArgWith(1, null, @notifications, {})
|
||||
@ProjectGetter.findAllUsersProjects.callsArgWith(2, null, @allProjects)
|
||||
@Modules.hooks.fire.withArgs('findAllV1Projects', @user._id).yields(undefined) # Without integration module hook, cb returns undefined
|
||||
|
||||
it "should render the project/list page", (done)->
|
||||
@res.render = (pageName, opts)=>
|
||||
|
|
|
@ -187,6 +187,7 @@ describe "UserController", ->
|
|||
describe "updateUserSettings", ->
|
||||
beforeEach ->
|
||||
@newEmail = "hello@world.com"
|
||||
@req.externalAuthenticationSystemUsed = sinon.stub().returns(false)
|
||||
|
||||
it "should call save", (done)->
|
||||
@req.body = {}
|
||||
|
@ -280,10 +281,7 @@ describe "UserController", ->
|
|||
beforeEach ->
|
||||
@UserUpdater.changeEmailAddress.callsArgWith(2)
|
||||
@newEmail = 'someone23@example.com'
|
||||
@settings.ldap = {active: true}
|
||||
|
||||
afterEach ->
|
||||
delete @settings.ldap
|
||||
@req.externalAuthenticationSystemUsed = sinon.stub().returns(true)
|
||||
|
||||
it 'should not set a new email', (done) ->
|
||||
@req.body.email = @newEmail
|
||||
|
|
|
@ -33,23 +33,19 @@ describe "Opening", ->
|
|||
return done(err)
|
||||
logger.log "smoke test: clearing rate limit "
|
||||
require("../../../app/js/infrastructure/RateLimiter.js").clearRateLimit "open-project", "#{Settings.smokeTest.projectId}:#{Settings.smokeTest.userId}", ->
|
||||
logger.log "smoke test: hitting /register"
|
||||
logger.log "smoke test: hitting dev/csrf"
|
||||
command = """
|
||||
curl -H "X-Forwarded-Proto: https" -c #{cookeFilePath} #{buildUrl('register')}
|
||||
curl -H "X-Forwarded-Proto: https" -c #{cookeFilePath} #{buildUrl('dev/csrf')}
|
||||
"""
|
||||
child.exec command, (err, stdout, stderr)->
|
||||
if err? then done(err)
|
||||
csrfMatches = stdout.match("<input name=\"_csrf\" type=\"hidden\" value=\"(.*?)\">")
|
||||
if !csrfMatches?
|
||||
logger.err stdout:stdout, "smoke test: does not have csrf token"
|
||||
return done("smoke test: does not have csrf token")
|
||||
csrf = csrfMatches[1]
|
||||
csrf = stdout
|
||||
logger.log "smoke test: converting cookie file 1"
|
||||
convertCookieFile (err) ->
|
||||
return done(err) if err?
|
||||
logger.log "smoke test: hitting /register with csrf"
|
||||
logger.log "smoke test: hitting /login with csrf"
|
||||
command = """
|
||||
curl -c #{cookeFilePath} -H "Content-Type: application/json" -H "X-Forwarded-Proto: https" -d '{"_csrf":"#{csrf}", "email":"#{Settings.smokeTest.user}", "password":"#{Settings.smokeTest.password}"}' #{buildUrl('register')}
|
||||
curl -c #{cookeFilePath} -H "Content-Type: application/json" -H "X-Forwarded-Proto: https" -d '{"_csrf":"#{csrf}", "email":"#{Settings.smokeTest.user}", "password":"#{Settings.smokeTest.password}"}' #{buildUrl('login')}
|
||||
"""
|
||||
child.exec command, (err) ->
|
||||
return done(err) if err?
|
||||
|
|
Loading…
Reference in a new issue