Browse Source

Merge pull request #19719 from overleaf/jpa-e2e-register

[server-pro] add e2e tests for registering users via GUI/script/email

GitOrigin-RevId: 25243532038c8df72f1360c433af215b3a551f3a
Jakob Ackermann 2 năm trước cách đây
mục cha
commit
05ecfa2e23

+ 101 - 13
server-ce/test/admin.spec.ts

@@ -1,10 +1,105 @@
 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 { createProject } from './helpers/project'
 import { beforeWithReRunOnTestRetry } from './helpers/beforeWithReRunOnTestRetry'
+import { openEmail } from './helpers/email'
 
 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', () => {
     const admin = 'admin@example.com'
     const user1 = 'user@example.com'
@@ -83,18 +178,11 @@ describe('admin panel', function () {
         cy.get('nav').findByText('Manage Users').click()
       })
 
-      it('create and login user', () => {
-        const user = `${uuid()}@example.com`
-
-        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)
-          })
+      describe('create users', () => {
+        beforeEach(() => {
+          cy.get('a').contains('New User').click()
+        })
+        registrationTests()
       })
 
       it('user list RegExp search', () => {

+ 2 - 1
server-ce/test/helpers/config.ts

@@ -1,5 +1,5 @@
 import { reconfigure } from './hostAdminClient'
-import { resetCreatedUsersCache } from './login'
+import { resetActivateUserRateLimit, resetCreatedUsersCache } from './login'
 
 export const STARTUP_TIMEOUT =
   parseInt(Cypress.env('STARTUP_TIMEOUT'), 10) || 120_000
@@ -39,6 +39,7 @@ export function startWith({
     })
     if (resetData) {
       resetCreatedUsersCache()
+      resetActivateUserRateLimit()
       // no return here, always reconfigure when resetting data
     } else if (lastConfig === cfg) {
       return

+ 2 - 2
server-ce/test/helpers/email.ts

@@ -2,12 +2,12 @@
  * Helper function for opening an email in Roundcube based mailtrap.
  * 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.
- * 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
  */
 export function openEmail<T>(
   subject: string | RegExp,
-  runner: (frame: Cypress.Chainable<JQuery<any>>, args?: T) => void,
+  runner: (frame: Cypress.Chainable<JQuery<any>>, args: T) => void,
   args?: T
 ) {
   const runnerS = runner.toString()

+ 21 - 1
server-ce/test/helpers/login.ts

@@ -21,7 +21,7 @@ export async function createMongoUser({
     script: 'modules/server-ce-scripts/scripts/create-user.js',
     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 signupDate = parseInt(userId.slice(0, 8), 16)
   if (signupDate < t0) {
@@ -70,7 +70,27 @@ export function login(username: string, password = DEFAULT_PASSWORD) {
   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) {
+  handleActivateUserRateLimit()
+
   cy.session(url, () => {
     cy.visit(url)
     cy.url().then(url => {

+ 1 - 0
server-ce/test/host-admin.js

@@ -159,6 +159,7 @@ const allowedVars = Joi.object(
       'OVERLEAF_LDAP_LAST_NAME_ATT',
       'OVERLEAF_LDAP_UPDATE_USER_DETAILS_ON_LOGIN',
       // Old branding, used for upgrade tests
+      'SHARELATEX_SITE_URL',
       'SHARELATEX_MONGO_URL',
       'SHARELATEX_REDIS_HOST',
     ].map(name => [name, Joi.string()])

+ 2 - 1
server-ce/test/upgrading.spec.ts

@@ -145,7 +145,8 @@ describe('Upgrading', function () {
   const optionsFourDotTwo = {
     version: '4.2',
     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_REDIS_HOST: 'redis',
     },