mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #19719 from overleaf/jpa-e2e-register
[server-pro] add e2e tests for registering users via GUI/script/email GitOrigin-RevId: 25243532038c8df72f1360c433af215b3a551f3a
This commit is contained in:
parent
fa8161d4ce
commit
05ecfa2e23
6 changed files with 129 additions and 18 deletions
|
@ -1,10 +1,105 @@
|
||||||
import { isExcludedBySharding, startWith } from './helpers/config'
|
import { isExcludedBySharding, startWith } from './helpers/config'
|
||||||
import { activateUser, ensureUserExists, login } from './helpers/login'
|
import {
|
||||||
|
activateUser,
|
||||||
|
createMongoUser,
|
||||||
|
ensureUserExists,
|
||||||
|
login,
|
||||||
|
} from './helpers/login'
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import { createProject } from './helpers/project'
|
import { createProject } from './helpers/project'
|
||||||
import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
|
import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
|
||||||
|
import { openEmail } from './helpers/email'
|
||||||
|
|
||||||
describe('admin panel', function () {
|
describe('admin panel', function () {
|
||||||
|
function registrationTests() {
|
||||||
|
it('via GUI and opening URL manually', () => {
|
||||||
|
const user = `${uuid()}@example.com`
|
||||||
|
cy.get('input[name="email"]').type(user + '{enter}')
|
||||||
|
|
||||||
|
cy.get('td')
|
||||||
|
.contains(/\/user\/activate/)
|
||||||
|
.then($td => {
|
||||||
|
const url = $td.text().trim()
|
||||||
|
activateUser(url)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('via GUI and email', () => {
|
||||||
|
const user = `${uuid()}@example.com`
|
||||||
|
cy.get('input[name="email"]').type(user + '{enter}')
|
||||||
|
|
||||||
|
let url: string
|
||||||
|
cy.get('td')
|
||||||
|
.contains(/\/user\/activate/)
|
||||||
|
.then($td => {
|
||||||
|
url = $td.text().trim()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.then(() => {
|
||||||
|
openEmail(
|
||||||
|
'Activate your Overleaf Community Edition Account',
|
||||||
|
(frame, { url }) => {
|
||||||
|
frame.contains('Set password').then(el => {
|
||||||
|
expect(el.attr('href')!).to.equal(url)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ url }
|
||||||
|
)
|
||||||
|
// Run activateUser in the main origin instead of inside openEmail. See docs on openEmail.
|
||||||
|
activateUser(url)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('via script and opening URL manually', () => {
|
||||||
|
const user = `${uuid()}@example.com`
|
||||||
|
let url: string
|
||||||
|
cy.then(async () => {
|
||||||
|
;({ url } = await createMongoUser({ email: user }))
|
||||||
|
})
|
||||||
|
cy.then(() => {
|
||||||
|
activateUser(url)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('via script and email', () => {
|
||||||
|
const user = `${uuid()}@example.com`
|
||||||
|
let url: string
|
||||||
|
cy.then(async () => {
|
||||||
|
;({ url } = await createMongoUser({ email: user }))
|
||||||
|
})
|
||||||
|
cy.then(() => {
|
||||||
|
openEmail(
|
||||||
|
'Activate your Overleaf Community Edition Account',
|
||||||
|
(frame, { url }) => {
|
||||||
|
frame.contains('Set password').then(el => {
|
||||||
|
expect(el.attr('href')!).to.equal(url)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ url }
|
||||||
|
)
|
||||||
|
// Run activateUser in the main origin instead of inside openEmail. See docs on openEmail.
|
||||||
|
activateUser(url)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('in CE', () => {
|
||||||
|
if (isExcludedBySharding('CE_DEFAULT')) return
|
||||||
|
startWith({ pro: false, version: 'latest' })
|
||||||
|
const admin = 'admin@example.com'
|
||||||
|
const user = `user+${uuid()}@example.com`
|
||||||
|
ensureUserExists({ email: admin, isAdmin: true })
|
||||||
|
ensureUserExists({ email: user })
|
||||||
|
|
||||||
|
describe('create users', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
login(admin)
|
||||||
|
cy.visit('/project')
|
||||||
|
cy.get('nav').findByText('Admin').click()
|
||||||
|
cy.get('nav').findByText('Manage Users').click()
|
||||||
|
})
|
||||||
|
registrationTests()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('in server pro', () => {
|
describe('in server pro', () => {
|
||||||
const admin = 'admin@example.com'
|
const admin = 'admin@example.com'
|
||||||
const user1 = 'user@example.com'
|
const user1 = 'user@example.com'
|
||||||
|
@ -83,18 +178,11 @@ describe('admin panel', function () {
|
||||||
cy.get('nav').findByText('Manage Users').click()
|
cy.get('nav').findByText('Manage Users').click()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('create and login user', () => {
|
describe('create users', () => {
|
||||||
const user = `${uuid()}@example.com`
|
beforeEach(() => {
|
||||||
|
|
||||||
cy.get('a').contains('New User').click()
|
cy.get('a').contains('New User').click()
|
||||||
cy.get('input[name="email"]').type(user + '{enter}')
|
|
||||||
|
|
||||||
cy.get('td')
|
|
||||||
.contains(/\/user\/activate/)
|
|
||||||
.then($td => {
|
|
||||||
const url = $td.text().trim()
|
|
||||||
activateUser(url)
|
|
||||||
})
|
})
|
||||||
|
registrationTests()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('user list RegExp search', () => {
|
it('user list RegExp search', () => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { reconfigure } from './hostAdminClient'
|
import { reconfigure } from './hostAdminClient'
|
||||||
import { resetCreatedUsersCache } from './login'
|
import { resetActivateUserRateLimit, resetCreatedUsersCache } from './login'
|
||||||
|
|
||||||
export const STARTUP_TIMEOUT =
|
export const STARTUP_TIMEOUT =
|
||||||
parseInt(Cypress.env('STARTUP_TIMEOUT'), 10) || 120_000
|
parseInt(Cypress.env('STARTUP_TIMEOUT'), 10) || 120_000
|
||||||
|
@ -39,6 +39,7 @@ export function startWith({
|
||||||
})
|
})
|
||||||
if (resetData) {
|
if (resetData) {
|
||||||
resetCreatedUsersCache()
|
resetCreatedUsersCache()
|
||||||
|
resetActivateUserRateLimit()
|
||||||
// no return here, always reconfigure when resetting data
|
// no return here, always reconfigure when resetting data
|
||||||
} else if (lastConfig === cfg) {
|
} else if (lastConfig === cfg) {
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
* Helper function for opening an email in Roundcube based mailtrap.
|
* Helper function for opening an email in Roundcube based mailtrap.
|
||||||
* We need to cross an origin boundary, which complicates the use of variables.
|
* We need to cross an origin boundary, which complicates the use of variables.
|
||||||
* Any variables need to be explicitly defined and the "runner" may only reference these and none from its scope.
|
* Any variables need to be explicitly defined and the "runner" may only reference these and none from its scope.
|
||||||
* It is not possible to use Cypress helper functions, e.g. from the testing library inside the "runner".
|
* It is not possible to use Cypress helper functions, e.g. from the testing library or other functions like "activateUser", inside the "runner".
|
||||||
* REF: https://github.com/testing-library/cypress-testing-library/issues/221
|
* REF: https://github.com/testing-library/cypress-testing-library/issues/221
|
||||||
*/
|
*/
|
||||||
export function openEmail<T>(
|
export function openEmail<T>(
|
||||||
subject: string | RegExp,
|
subject: string | RegExp,
|
||||||
runner: (frame: Cypress.Chainable<JQuery<any>>, args?: T) => void,
|
runner: (frame: Cypress.Chainable<JQuery<any>>, args: T) => void,
|
||||||
args?: T
|
args?: T
|
||||||
) {
|
) {
|
||||||
const runnerS = runner.toString()
|
const runnerS = runner.toString()
|
||||||
|
|
|
@ -21,7 +21,7 @@ export async function createMongoUser({
|
||||||
script: 'modules/server-ce-scripts/scripts/create-user.js',
|
script: 'modules/server-ce-scripts/scripts/create-user.js',
|
||||||
args: [`--email=${email}`, `--admin=${isAdmin}`],
|
args: [`--email=${email}`, `--admin=${isAdmin}`],
|
||||||
})
|
})
|
||||||
const [url] = stdout.match(/\/user\/activate\?token=\S+/)!
|
const [url] = stdout.match(/http:\/\/.+\/user\/activate\?token=\S+/)!
|
||||||
const userId = new URL(url, location.origin).searchParams.get('user_id')!
|
const userId = new URL(url, location.origin).searchParams.get('user_id')!
|
||||||
const signupDate = parseInt(userId.slice(0, 8), 16)
|
const signupDate = parseInt(userId.slice(0, 8), 16)
|
||||||
if (signupDate < t0) {
|
if (signupDate < t0) {
|
||||||
|
@ -70,7 +70,27 @@ export function login(username: string, password = DEFAULT_PASSWORD) {
|
||||||
return startOrResumeSession
|
return startOrResumeSession
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let activateRateLimitState = { count: 0, reset: 0 }
|
||||||
|
export function resetActivateUserRateLimit() {
|
||||||
|
activateRateLimitState = { count: 0, reset: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleActivateUserRateLimit() {
|
||||||
|
cy.then(() => {
|
||||||
|
activateRateLimitState.count++
|
||||||
|
if (activateRateLimitState.reset < Date.now()) {
|
||||||
|
activateRateLimitState.reset = Date.now() + 65_000
|
||||||
|
activateRateLimitState.count = 1
|
||||||
|
} else if (activateRateLimitState.count >= 6) {
|
||||||
|
cy.wait(activateRateLimitState.reset - Date.now())
|
||||||
|
activateRateLimitState.count = 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function activateUser(url: string, password = DEFAULT_PASSWORD) {
|
export function activateUser(url: string, password = DEFAULT_PASSWORD) {
|
||||||
|
handleActivateUserRateLimit()
|
||||||
|
|
||||||
cy.session(url, () => {
|
cy.session(url, () => {
|
||||||
cy.visit(url)
|
cy.visit(url)
|
||||||
cy.url().then(url => {
|
cy.url().then(url => {
|
||||||
|
|
|
@ -159,6 +159,7 @@ const allowedVars = Joi.object(
|
||||||
'OVERLEAF_LDAP_LAST_NAME_ATT',
|
'OVERLEAF_LDAP_LAST_NAME_ATT',
|
||||||
'OVERLEAF_LDAP_UPDATE_USER_DETAILS_ON_LOGIN',
|
'OVERLEAF_LDAP_UPDATE_USER_DETAILS_ON_LOGIN',
|
||||||
// Old branding, used for upgrade tests
|
// Old branding, used for upgrade tests
|
||||||
|
'SHARELATEX_SITE_URL',
|
||||||
'SHARELATEX_MONGO_URL',
|
'SHARELATEX_MONGO_URL',
|
||||||
'SHARELATEX_REDIS_HOST',
|
'SHARELATEX_REDIS_HOST',
|
||||||
].map(name => [name, Joi.string()])
|
].map(name => [name, Joi.string()])
|
||||||
|
|
|
@ -145,7 +145,8 @@ describe('Upgrading', function () {
|
||||||
const optionsFourDotTwo = {
|
const optionsFourDotTwo = {
|
||||||
version: '4.2',
|
version: '4.2',
|
||||||
vars: {
|
vars: {
|
||||||
// Add database vars with old branding
|
// Add core vars with old branding
|
||||||
|
SHARELATEX_SITE_URL: 'http://sharelatex',
|
||||||
SHARELATEX_MONGO_URL: 'mongodb://mongo/sharelatex',
|
SHARELATEX_MONGO_URL: 'mongodb://mongo/sharelatex',
|
||||||
SHARELATEX_REDIS_HOST: 'redis',
|
SHARELATEX_REDIS_HOST: 'redis',
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue