mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-31 21:01:33 +02:00
Merge pull request #31827 from overleaf/ii-project-sharing-access-denied
[web] Project sharing access denied redesign GitOrigin-RevId: b1e3016eb7ef9e2a502e0b67abc3b10c08531fe9
This commit is contained in:
@@ -263,10 +263,18 @@ async function viewInvite(req, res) {
|
||||
const projectId = req.params.Project_id
|
||||
const { token } = req.params
|
||||
|
||||
const { variant: sharingUpdates } =
|
||||
await SplitTestHandler.promises.getAssignment(req, res, 'sharing-updates')
|
||||
|
||||
const _renderInvalidPage = function () {
|
||||
res.status(404)
|
||||
logger.debug({ projectId }, 'invite not valid, rendering not-valid page')
|
||||
res.render('project/invite/not-valid', { title: 'Invalid Invite' })
|
||||
|
||||
if (sharingUpdates === 'enabled') {
|
||||
res.render('project/invite/not-valid', { title: 'Invalid Invite' })
|
||||
} else {
|
||||
res.render('project/invite/not-valid-legacy', { title: 'Invalid Invite' })
|
||||
}
|
||||
}
|
||||
|
||||
// check if the user is already a member of the project
|
||||
@@ -329,9 +337,6 @@ async function viewInvite(req, res) {
|
||||
// cleanup if set for register page
|
||||
delete req.session.sharedProjectData
|
||||
|
||||
const { variant: sharingUpdates } =
|
||||
await SplitTestHandler.promises.getAssignment(req, res, 'sharing-updates')
|
||||
|
||||
// finally render the invite
|
||||
if (sharingUpdates === 'enabled') {
|
||||
res.render('project/invite/show', {
|
||||
|
||||
18
services/web/app/views/project/invite/not-valid-legacy.pug
Normal file
18
services/web/app/views/project/invite/not-valid-legacy.pug
Normal file
@@ -0,0 +1,18 @@
|
||||
extends ../../layout-marketing
|
||||
|
||||
block content
|
||||
main#main-content.content.content-alt
|
||||
.container
|
||||
.row
|
||||
.col-md-8.col-md-offset-2.offset-md-2
|
||||
.card.project-invite-invalid
|
||||
.card-body
|
||||
.page-header.text-center
|
||||
h1 #{translate("invite_not_valid")}
|
||||
.row.text-center
|
||||
.col-12.col-md-12
|
||||
p
|
||||
| #{translate("invite_not_valid_description")}.
|
||||
.row.text-center.actions
|
||||
.col-12.col-md-12
|
||||
a.btn.btn-secondary-info.btn-secondary(href='/project') #{translate("back_to_your_projects")}
|
||||
@@ -1,18 +1,14 @@
|
||||
extends ../../layout-marketing
|
||||
extends ../../layout-website-redesign
|
||||
|
||||
block vars
|
||||
- isWebsiteRedesign = true
|
||||
|
||||
block entrypointVar
|
||||
- entrypoint = 'pages/project-invite-not-valid'
|
||||
|
||||
block append meta
|
||||
meta(name='ol-user' data-type='json' content=user)
|
||||
|
||||
block content
|
||||
main#main-content.content.content-alt
|
||||
.container
|
||||
.row
|
||||
.col-md-8.col-md-offset-2.offset-md-2
|
||||
.card.project-invite-invalid
|
||||
.card-body
|
||||
.page-header.text-center
|
||||
h1 #{translate("invite_not_valid")}
|
||||
.row.text-center
|
||||
.col-12.col-md-12
|
||||
p
|
||||
| #{translate("invite_not_valid_description")}.
|
||||
.row.text-center.actions
|
||||
.col-12.col-md-12
|
||||
a.btn.btn-secondary-info.btn-secondary(href='/project') #{translate("back_to_your_projects")}
|
||||
#project-invite-not-valid-page
|
||||
|
||||
@@ -13,4 +13,4 @@ block append meta
|
||||
meta(name='ol-user' data-type='json' content=user)
|
||||
|
||||
block content
|
||||
#token-access-page
|
||||
#token-access-page.token-access-legacy-page
|
||||
|
||||
@@ -188,6 +188,7 @@
|
||||
"back_to_configuration": "",
|
||||
"back_to_editing": "",
|
||||
"back_to_editor": "",
|
||||
"back_to_my_projects": "",
|
||||
"back_to_subscription": "",
|
||||
"back_to_your_projects": "",
|
||||
"basic_compile_time": "",
|
||||
@@ -1725,6 +1726,7 @@
|
||||
"sorry_there_are_no_experiments": "",
|
||||
"sorry_there_was_an_issue_adding_x_users_to_your_subscription": "",
|
||||
"sorry_there_was_an_issue_upgrading_your_subscription": "",
|
||||
"sorry_this_project_is_not_available": "",
|
||||
"sorry_you_can_only_change_to_group_from_trial_via_support": "",
|
||||
"sorry_you_can_only_change_to_group_via_support": "",
|
||||
"sorry_your_table_cant_be_displayed_at_the_moment": "",
|
||||
@@ -1901,6 +1903,7 @@
|
||||
"the_following_folder_already_exists_in_this_project_plural": "",
|
||||
"the_home_of_research_writing": "",
|
||||
"the_latex_engine_used_for_compiling": "",
|
||||
"the_link_may_be_broken_or_you_may_not_have_access_rights": "",
|
||||
"the_new_and_improved_overleaf_editor_design": "",
|
||||
"the_next_payment_will_be_collected_on": "",
|
||||
"the_original_text_has_changed": "",
|
||||
@@ -2282,6 +2285,7 @@
|
||||
"you_are_a_manager_of_x_plan_as_member_of_group_subscription_y_administered_by_z": "",
|
||||
"you_are_a_manager_of_x_plan_as_member_of_group_subscription_y_administered_by_z_you": "",
|
||||
"you_are_currently_logged_in_as": "",
|
||||
"you_are_currently_logged_in_as_x_you_might_need_to_log_in_with_different_email": "",
|
||||
"you_are_on_a_paid_plan_contact_support_to_find_out_more": "",
|
||||
"you_are_on_x_plan_as_a_confirmed_member_of_institution_y": "",
|
||||
"you_are_on_x_plan_as_member_of_group_subscription_y_administered_by_z": "",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
|
||||
import getMeta from '@/utils/meta'
|
||||
import InviteNotValid from '@/features/share-project/invite-not-valid'
|
||||
import { User } from '@ol-types/user'
|
||||
|
||||
export default function InviteNotValidRoot() {
|
||||
const user = getMeta('ol-user') as User | undefined
|
||||
const { isReady } = useWaitForI18n()
|
||||
|
||||
if (!isReady) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <InviteNotValid email={user?.email} />
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { useTranslation, Trans } from 'react-i18next'
|
||||
import OLRow from '@/shared/components/ol/ol-row'
|
||||
import OLCol from '@/shared/components/ol/ol-col'
|
||||
import OLButton from '@/shared/components/ol/ol-button'
|
||||
import overleafLogo from '@/shared/svgs/overleaf-logo.svg'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
type InviteNotValidProps = {
|
||||
email?: string
|
||||
}
|
||||
|
||||
function InviteNotValid({ email }: InviteNotValidProps) {
|
||||
const { t } = useTranslation()
|
||||
const { appName } = getMeta('ol-ExposedSettings')
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<OLRow>
|
||||
<OLCol lg={{ span: 6, offset: 3 }}>
|
||||
<div className="project-join-container">
|
||||
<img src={overleafLogo} alt={appName} />
|
||||
<h1 className="h4 mb-2">
|
||||
{t('sorry_this_project_is_not_available')}
|
||||
</h1>
|
||||
<div className="mb-4">
|
||||
{t('the_link_may_be_broken_or_you_may_not_have_access_rights')}
|
||||
</div>
|
||||
{email && (
|
||||
<>
|
||||
<OLButton
|
||||
variant="primary"
|
||||
size="lg"
|
||||
href="/project"
|
||||
className="mb-4"
|
||||
>
|
||||
{t('back_to_my_projects')}
|
||||
</OLButton>
|
||||
<div>
|
||||
<small>
|
||||
<Trans
|
||||
i18nKey="you_are_currently_logged_in_as_x_you_might_need_to_log_in_with_different_email"
|
||||
components={[<b />]} // eslint-disable-line react/jsx-key
|
||||
values={{ email }}
|
||||
shouldUnescape
|
||||
tOptions={{ interpolation: { escapeValue: true } }}
|
||||
/>
|
||||
</small>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</OLCol>
|
||||
</OLRow>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InviteNotValid
|
||||
@@ -1,5 +1,8 @@
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import InviteNotValid from '@/features/share-project/invite-not-valid'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
|
||||
export const AccessAttemptScreen: FC<{
|
||||
loadingScreenBrandHeight: string
|
||||
@@ -7,6 +10,32 @@ export const AccessAttemptScreen: FC<{
|
||||
accessError: string | boolean
|
||||
}> = ({ loadingScreenBrandHeight, inflight, accessError }) => {
|
||||
const { t } = useTranslation()
|
||||
const user = getMeta('ol-user')
|
||||
const isSharingUpdatesEnabled = useFeatureFlag('sharing-updates')
|
||||
|
||||
if (isSharingUpdatesEnabled) {
|
||||
if (accessError) {
|
||||
return <InviteNotValid email={user?.email} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="vertically-centered-content">
|
||||
<div className="loading-screen">
|
||||
<div className="loading-screen-brand-container">
|
||||
<div
|
||||
className="loading-screen-brand"
|
||||
style={{ height: loadingScreenBrandHeight }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3 className="loading-screen-label text-center">
|
||||
{t('join_project')}
|
||||
{inflight && <LoadingScreenEllipses />}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="loading-screen">
|
||||
|
||||
12
services/web/frontend/js/pages/project-invite-not-valid.tsx
Normal file
12
services/web/frontend/js/pages/project-invite-not-valid.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import './../utils/meta'
|
||||
import '../utils/webpack-public-path'
|
||||
import './../infrastructure/error-reporter'
|
||||
import '@/i18n'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import InviteNotValidRoot from '@/features/share-project/invite-not-valid-root'
|
||||
|
||||
const element = document.getElementById('project-invite-not-valid-page')
|
||||
if (element) {
|
||||
const root = createRoot(element)
|
||||
root.render(<InviteNotValidRoot />)
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
#token-access-page {
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
&.token-access-legacy-page {
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.vertically-centered-content {
|
||||
height: $thin-footer-content-height;
|
||||
}
|
||||
}
|
||||
|
||||
@include theme('default') {
|
||||
|
||||
@@ -236,6 +236,7 @@
|
||||
"back_to_editing": "Back to editing",
|
||||
"back_to_editor": "Back to editor",
|
||||
"back_to_log_in": "Back to log in",
|
||||
"back_to_my_projects": "Back to my projects",
|
||||
"back_to_subscription": "Back to subscription",
|
||||
"back_to_your_projects": "Back to your projects",
|
||||
"basic": "Basic",
|
||||
@@ -2211,6 +2212,7 @@
|
||||
"sorry_there_was_an_issue_adding_x_users_to_your_subscription": "Sorry, there was an issue adding __count__ users to your subscription. Please <0>contact our Support team</0> for help.",
|
||||
"sorry_there_was_an_issue_upgrading_your_subscription": "Sorry, there was an issue upgrading your subscription. Please <0>contact our Support team</0> for help.",
|
||||
"sorry_this_account_has_been_suspended": "Sorry, this account has been suspended.",
|
||||
"sorry_this_project_is_not_available": "Sorry, this project isn’t available",
|
||||
"sorry_you_can_only_change_to_group_from_trial_via_support": "Sorry, you can only change to a group plan during a free trial by contacting support.",
|
||||
"sorry_you_can_only_change_to_group_via_support": "Sorry, you can only change to a group plan by contacting support.",
|
||||
"sorry_your_table_cant_be_displayed_at_the_moment": "Sorry, your table can’t be displayed at the moment.",
|
||||
@@ -2426,6 +2428,7 @@
|
||||
"the_following_folder_already_exists_in_this_project_plural": "The following folders already exist in this project:",
|
||||
"the_home_of_research_writing": "The home of research writing.",
|
||||
"the_latex_engine_used_for_compiling": "The LaTeX engine used for compiling",
|
||||
"the_link_may_be_broken_or_you_may_not_have_access_rights": "The link may be broken or you may not have access rights.",
|
||||
"the_new_and_improved_overleaf_editor_design": "The new and improved __appName__ editor design brings you a cleaner, less cluttered interface to help you focus on what matters—your work.",
|
||||
"the_next_payment_will_be_collected_on": "The next payment will be collected on <strong>__date__</strong>.",
|
||||
"the_original_text_has_changed": "The original text has changed, so this suggestion can’t be applied",
|
||||
@@ -2851,6 +2854,7 @@
|
||||
"you_are_a_manager_of_x_plan_as_member_of_group_subscription_y_administered_by_z": "You are a <1>manager</1> of the <0>__planName__</0> group subscription <1>__groupName__</1> administered by <1>__adminEmail__</1>.",
|
||||
"you_are_a_manager_of_x_plan_as_member_of_group_subscription_y_administered_by_z_you": "You are a <1>manager</1> of the <0>__planName__</0> group subscription <1>__groupName__</1> administered by <1>you (__adminEmail__</1>).",
|
||||
"you_are_currently_logged_in_as": "You are currently logged in as <b>__email__</b>.",
|
||||
"you_are_currently_logged_in_as_x_you_might_need_to_log_in_with_different_email": "You are currently logged in as <0>__email__</0>. You might need to log in with a different email address.",
|
||||
"you_are_on_a_paid_plan_contact_support_to_find_out_more": "You’re on an __appName__ Paid plan. <0>Contact Support</0> to find out more.",
|
||||
"you_are_on_x_plan_as_a_confirmed_member_of_institution_y": "You are on our <0>__planName__</0> plan as a <1>confirmed member</1> of <1>__institutionName__</1>",
|
||||
"you_are_on_x_plan_as_member_of_group_subscription_y_administered_by_z": "You are on our <0>__planName__</0> plan as a <1>member</1> of the group subscription <1>__groupName__</1> administered by <1>__adminEmail__</1>",
|
||||
|
||||
@@ -79,10 +79,16 @@ describe('<TokenAccessPage/>', function () {
|
||||
|
||||
cy.wait('@grantRequest')
|
||||
|
||||
cy.get('h3').should('have.text', 'Join Project')
|
||||
cy.get('h4').should('have.text', 'Project not found')
|
||||
|
||||
cy.findByRole('heading', { name: /sorry, this project isn’t available/i })
|
||||
cy.findByText(/the link may be broken or you may not have access rights/i)
|
||||
cy.findByRole('button', { name: 'Join Project' }).should('not.exist')
|
||||
cy.contains(
|
||||
new RegExp(
|
||||
'you are currently logged in as test@example.com. ' +
|
||||
'you might need to log in with a different email address',
|
||||
'i'
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it('handles a redirect response', function () {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import InviteNotValid from '@/features/share-project/invite-not-valid'
|
||||
|
||||
describe('<InviteNotValid />', function () {
|
||||
const email = 'test@example.com'
|
||||
|
||||
it('renders the sorry message', function () {
|
||||
cy.mount(<InviteNotValid email={email} />)
|
||||
|
||||
cy.findByRole('heading', {
|
||||
name: /sorry, this project isn’t available/i,
|
||||
})
|
||||
})
|
||||
|
||||
it('renders the broken link message', function () {
|
||||
cy.mount(<InviteNotValid email={email} />)
|
||||
|
||||
cy.findByText(/the link may be broken or you may not have access rights/i)
|
||||
})
|
||||
|
||||
it('renders a back to projects button linking to /project', function () {
|
||||
cy.mount(<InviteNotValid email={email} />)
|
||||
|
||||
cy.findByRole('link', { name: /back to my projects/i }).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/project'
|
||||
)
|
||||
})
|
||||
|
||||
it('renders the logged-in email', function () {
|
||||
cy.mount(<InviteNotValid email={email} />)
|
||||
|
||||
cy.contains(
|
||||
new RegExp(
|
||||
`you are currently logged in as ${email}. you might need to log in with a different email address`,
|
||||
'i'
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it('does not render the CTA and email when email not provided', function () {
|
||||
cy.mount(<InviteNotValid />)
|
||||
|
||||
cy.findByRole('link', { name: /back to my projects/i }).should('not.exist')
|
||||
cy.contains(
|
||||
new RegExp(
|
||||
`you are currently logged in as ${email}. you might need to log in with a different email address`,
|
||||
'i'
|
||||
)
|
||||
).should('not.exist')
|
||||
})
|
||||
})
|
||||
@@ -1035,54 +1035,108 @@ describe('CollaboratorsInviteController', function () {
|
||||
})
|
||||
|
||||
describe('when the getInviteByToken does not produce an invite', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
describe('when the sharing-updates variant is "enabled"', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.SplitTestHandler.promises.getAssignment.resolves({
|
||||
variant: 'enabled',
|
||||
})
|
||||
await new Promise(resolve => {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.resolves(
|
||||
null
|
||||
)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid',
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid',
|
||||
expect.anything()
|
||||
)
|
||||
describe('when the sharing-updates variant is "default"', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.SplitTestHandler.promises.getAssignment.resolves({
|
||||
variant: 'default',
|
||||
})
|
||||
await new Promise(resolve => {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.resolves(
|
||||
null
|
||||
)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid-legacy view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid-legacy',
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
describe('common behaviour', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.resolves(
|
||||
null
|
||||
)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should call CollaboratorsGetter.isUserInvitedMemberOfProject', function (ctx) {
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject
|
||||
.calledWith(ctx.currentUser._id, ctx.projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
it('should call CollaboratorsGetter.isUserInvitedMemberOfProject', function (ctx) {
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject
|
||||
.calledWith(ctx.currentUser._id, ctx.projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call getInviteByToken', function (ctx) {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject
|
||||
.calledWith(ctx.currentUser._id, ctx.projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
it('should call getInviteByToken', function (ctx) {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject
|
||||
.calledWith(ctx.currentUser._id, ctx.projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should not call User.getUser', function (ctx) {
|
||||
ctx.UserGetter.promises.getUser.callCount.should.equal(0)
|
||||
})
|
||||
it('should not call User.getUser', function (ctx) {
|
||||
ctx.UserGetter.promises.getUser.callCount.should.equal(0)
|
||||
})
|
||||
|
||||
it('should not call ProjectGetter.getProject', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.callCount.should.equal(0)
|
||||
it('should not call ProjectGetter.getProject', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.callCount.should.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1132,54 +1186,102 @@ describe('CollaboratorsInviteController', function () {
|
||||
})
|
||||
|
||||
describe('when User.getUser does not find a user', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.UserGetter.promises.getUser.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
describe('when the sharing-updates variant is "enabled"', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.SplitTestHandler.promises.getAssignment.resolves({
|
||||
variant: 'enabled',
|
||||
})
|
||||
await new Promise(resolve => {
|
||||
ctx.UserGetter.promises.getUser.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid',
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid',
|
||||
expect.anything()
|
||||
)
|
||||
describe('when the sharing-updates variant is "default"', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.SplitTestHandler.promises.getAssignment.resolves({
|
||||
variant: 'default',
|
||||
})
|
||||
await new Promise(resolve => {
|
||||
ctx.UserGetter.promises.getUser.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid-legacy view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid-legacy',
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
describe('common behaviour', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.UserGetter.promises.getUser.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should call CollaboratorsGetter.isUserInvitedMemberOfProject', function (ctx) {
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject
|
||||
.calledWith(ctx.currentUser._id, ctx.projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
it('should call CollaboratorsGetter.isUserInvitedMemberOfProject', function (ctx) {
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject
|
||||
.calledWith(ctx.currentUser._id, ctx.projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call getInviteByToken', function (ctx) {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
})
|
||||
it('should call getInviteByToken', function (ctx) {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
})
|
||||
|
||||
it('should call User.getUser', function (ctx) {
|
||||
ctx.UserGetter.promises.getUser.callCount.should.equal(1)
|
||||
ctx.UserGetter.promises.getUser
|
||||
.calledWith({ _id: ctx.fakeProject.owner_ref })
|
||||
.should.equal(true)
|
||||
})
|
||||
it('should call User.getUser', function (ctx) {
|
||||
ctx.UserGetter.promises.getUser.callCount.should.equal(1)
|
||||
ctx.UserGetter.promises.getUser
|
||||
.calledWith({ _id: ctx.fakeProject.owner_ref })
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should not call ProjectGetter.getProject', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.callCount.should.equal(0)
|
||||
it('should not call ProjectGetter.getProject', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.callCount.should.equal(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1229,54 +1331,102 @@ describe('CollaboratorsInviteController', function () {
|
||||
})
|
||||
|
||||
describe('when Project.getUser does not find a user', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.ProjectGetter.promises.getProject.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
describe('when the sharing-updates variant is "enabled"', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.SplitTestHandler.promises.getAssignment.resolves({
|
||||
variant: 'enabled',
|
||||
})
|
||||
await new Promise(resolve => {
|
||||
ctx.ProjectGetter.promises.getProject.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid',
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid',
|
||||
expect.anything()
|
||||
)
|
||||
describe('when the sharing-updates variant is "default"', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
ctx.SplitTestHandler.promises.getAssignment.resolves({
|
||||
variant: 'default',
|
||||
})
|
||||
await new Promise(resolve => {
|
||||
ctx.ProjectGetter.promises.getProject.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should render the not-valid-legacy view template', function (ctx) {
|
||||
expect(ctx.res.render).toHaveBeenCalledTimes(1)
|
||||
expect(ctx.res.render).toHaveBeenCalledWith(
|
||||
'project/invite/not-valid-legacy',
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not call next', function (ctx) {
|
||||
ctx.next.callCount.should.equal(0)
|
||||
})
|
||||
describe('common behaviour', function () {
|
||||
beforeEach(async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
ctx.ProjectGetter.promises.getProject.resolves(null)
|
||||
ctx.res.callback = () => resolve()
|
||||
ctx.CollaboratorsInviteController.viewInvite(
|
||||
ctx.req,
|
||||
ctx.res,
|
||||
ctx.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should call CollaboratorsGetter.isUserInvitedMemberOfProject', function (ctx) {
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject
|
||||
.calledWith(ctx.currentUser._id, ctx.projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
it('should call CollaboratorsGetter.isUserInvitedMemberOfProject', function (ctx) {
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
ctx.CollaboratorsGetter.promises.isUserInvitedMemberOfProject
|
||||
.calledWith(ctx.currentUser._id, ctx.projectId)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call getInviteByToken', function (ctx) {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
})
|
||||
it('should call getInviteByToken', function (ctx) {
|
||||
ctx.CollaboratorsInviteGetter.promises.getInviteByToken.callCount.should.equal(
|
||||
1
|
||||
)
|
||||
})
|
||||
|
||||
it('should call getUser', function (ctx) {
|
||||
ctx.UserGetter.promises.getUser.callCount.should.equal(1)
|
||||
ctx.UserGetter.promises.getUser
|
||||
.calledWith({ _id: ctx.fakeProject.owner_ref })
|
||||
.should.equal(true)
|
||||
})
|
||||
it('should call getUser', function (ctx) {
|
||||
ctx.UserGetter.promises.getUser.callCount.should.equal(1)
|
||||
ctx.UserGetter.promises.getUser
|
||||
.calledWith({ _id: ctx.fakeProject.owner_ref })
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
it('should call ProjectGetter.getProject', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.callCount.should.equal(1)
|
||||
it('should call ProjectGetter.getProject', function (ctx) {
|
||||
ctx.ProjectGetter.promises.getProject.callCount.should.equal(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user