From 1e8fc5337cdc11f88fc9f577f5d1ac3d04cc6c96 Mon Sep 17 00:00:00 2001 From: Jessica Lawshe Date: Mon, 16 Dec 2019 10:35:08 -0600 Subject: [PATCH] Merge pull request #2449 from overleaf/ew-user-helper-docs-and-tweaks add docs on UserHelper and tweak a few things GitOrigin-RevId: 410be6b3cdf32cc72e14e26569f56369ce30c0a2 --- .../test/acceptance/src/UserHelperTests.js | 102 +++++++++++------- .../test/acceptance/src/helpers/UserHelper.js | 97 ++++++++++++++--- 2 files changed, 147 insertions(+), 52 deletions(-) diff --git a/services/web/test/acceptance/src/UserHelperTests.js b/services/web/test/acceptance/src/UserHelperTests.js index e1d1b409d8..ab5563186c 100644 --- a/services/web/test/acceptance/src/UserHelperTests.js +++ b/services/web/test/acceptance/src/UserHelperTests.js @@ -9,6 +9,13 @@ chai.should() chai.use(chaiAsPromised) describe('UserHelper', function() { + // Disable all tests unless the public-registration feature is enabled + beforeEach(function() { + if (!Features.hasFeature('public-registration')) { + this.skip() + } + }) + describe('UserHelper.createUser', function() { describe('with no args', function() { it('should create new user with default username and password', async function() { @@ -51,7 +58,7 @@ describe('UserHelper', function() { }) }) - describe('fetching existing user', function() { + describe('UserHelper.getUser', function() { let user beforeEach(async function() { @@ -73,34 +80,50 @@ describe('UserHelper', function() { }) }) - describe('getCsrfToken', function() { - describe('when the csrfToken is not cached', function() { - it('should fetch csrfToken', async function() { - const userHelper = new UserHelper() - await userHelper.getCsrfToken() - expect(userHelper.csrfToken).to.be.a.string + describe('UserHelper.loginUser', function() { + let userHelper + + beforeEach(async function() { + userHelper = await UserHelper.createUser() + }) + + describe('with email and password', function() { + it('should login user', async function() { + const newUserHelper = await UserHelper.loginUser({ + email: userHelper.getDefaultEmail(), + password: userHelper.getDefaultPassword() + }) + newUserHelper.user.email.should.equal(userHelper.user.email) }) }) - describe('when the csrfToken is cached', function() { - it('should fetch csrfToken', async function() { - let userHelper = new UserHelper() - await userHelper.getCsrfToken() - const csrfToken = userHelper._csrfToken - await userHelper.getCsrfToken() - expect(csrfToken).to.equal(userHelper._csrfToken) + describe('without email', function() { + it('should throw error', async function() { + await UserHelper.loginUser({ + password: userHelper.getDefaultPassword() + }).should.be.rejectedWith('email and password required') + }) + }) + + describe('without password', function() { + it('should throw error', async function() { + await UserHelper.loginUser({ + email: userHelper.getDefaultEmail() + }).should.be.rejectedWith('email and password required') + }) + }) + + describe('without email and password', function() { + it('should throw error', async function() { + await UserHelper.loginUser().should.be.rejectedWith( + 'email and password required' + ) }) }) }) - describe('registerUser', function() { + describe('UserHelper.registerUser', function() { describe('with no args', function() { - before(function() { - if (!Features.hasFeature('public-registration')) { - this.skip() - } - }) - it('should create new user with default username and password', async function() { const userHelper = await UserHelper.registerUser() userHelper.user.email.should.equal(userHelper.getDefaultEmail()) @@ -113,12 +136,6 @@ describe('UserHelper', function() { }) describe('with email', function() { - before(function() { - if (!Features.hasFeature('public-registration')) { - this.skip() - } - }) - it('should create new user with provided email and default password', async function() { const userHelper = await UserHelper.registerUser({ email: 'foo2@test.com' @@ -133,12 +150,6 @@ describe('UserHelper', function() { }) describe('with password', function() { - before(function() { - if (!Features.hasFeature('public-registration')) { - this.skip() - } - }) - it('should create new user with provided password and default email', async function() { const userHelper = await UserHelper.registerUser({ password: 'foofoofoo' @@ -153,13 +164,28 @@ describe('UserHelper', function() { }) }) + describe('getCsrfToken', function() { + describe('when the csrfToken is not cached', function() { + it('should fetch csrfToken', async function() { + const userHelper = new UserHelper() + await userHelper.getCsrfToken() + expect(userHelper.csrfToken).to.be.a.string + }) + }) + + describe('when the csrfToken is cached', function() { + it('should return cached csrfToken', async function() { + let userHelper = new UserHelper() + await userHelper.getCsrfToken() + const csrfToken = userHelper._csrfToken + await userHelper.getCsrfToken() + expect(csrfToken).to.equal(userHelper._csrfToken) + }) + }) + }) + describe('after logout', function() { let userHelper, oldCsrfToken - before(function() { - if (!Features.hasFeature('public-registration')) { - this.skip() - } - }) beforeEach(async function() { userHelper = await UserHelper.registerUser() diff --git a/services/web/test/acceptance/src/helpers/UserHelper.js b/services/web/test/acceptance/src/helpers/UserHelper.js index 822b93efcf..2df1c92ab7 100644 --- a/services/web/test/acceptance/src/helpers/UserHelper.js +++ b/services/web/test/acceptance/src/helpers/UserHelper.js @@ -6,7 +6,11 @@ const request = require('request-promise-native') let globalUserNum = 1 -module.exports = class UserHelper { +class UserHelper { + /** + * Create UserHelper + * @param {object} [user] - Mongo User object + */ constructor(user = null) { // used for constructing default emails, etc this.userNum = globalUserNum++ @@ -18,22 +22,41 @@ module.exports = class UserHelper { /* sync functions */ + /** + * Generate default email from unique (per instantiation) user number + * @returns {string} email + */ getDefaultEmail() { return `test.user.${this.userNum}@example.com` } + /** + * Generate email, password args object. Default values will be used if + * email and password are not passed in args. + * @param {object} [userData] + * @param {string} [userData.email] email to use + * @param {string} [userData.password] password to use + * @returns {object} email, password object + */ getDefaultEmailPassword(userData = {}) { return { email: this.getDefaultEmail(), - password: this.getDefaultPassword(this.userNum), + password: this.getDefaultPassword(), ...userData } } + /** + * Generate default password from unique (per instantiation) user number + * @returns {string} password + */ getDefaultPassword() { - return `new-password-${this.userNum}!` + return `New-Password-${this.userNum}!` } + /** + * (Re)set internal state of UserHelper object. + */ reset() { // cached csrf token this._csrfToken = '' @@ -44,22 +67,27 @@ module.exports = class UserHelper { // create new request instance this.request = request.defaults({}) // initialize request instance with default options - this.setRequestDefaults() - } - - setRequestDefaults(defaults = {}) { - // request-promise instance for making requests - this.request = this.request.defaults({ + this.setRequestDefaults({ baseUrl: UserHelper.baseUrl(), followRedirect: false, jar: this.jar, - resolveWithFullResponse: true, - ...defaults + resolveWithFullResponse: true }) } + /* Set defaults for request object. Applied over existing defaults. + * @param {object} [defaults] + */ + setRequestDefaults(defaults = {}) { + // request-promise instance for making requests + this.request = this.request.defaults(defaults) + } + /* async http api call methods */ + /** + * Requests csrf token unless already cached in internal state + */ async getCsrfToken() { if (this._csrfToken) { return @@ -73,6 +101,11 @@ module.exports = class UserHelper { }) } + /** + * Make request to POST /logout + * @param {object} [options] options to pass to request + * @returns {object} http response + */ async logout(options = {}) { // do not throw exception on 302 options.simple = false @@ -84,20 +117,30 @@ module.exports = class UserHelper { ) { throw new Error('logout failed') } - // clear out internal state - this.reset() + // after logout CSRF token becomes invalid + this._csrfToken = '' // resolve with http request response return response } /* static sync methods */ + /** + * Generates base URL from env options + * @returns {string} baseUrl + */ static baseUrl() { return `http://${process.env['HTTP_TEST_HOST'] || 'localhost'}:3000` } /* static async instantiation methods */ + /** + * Create a new user via UserCreator and return UserHelper instance + * @param {object} attributes user data for UserCreator + * @param {object} options options for UserCreator + * @returns {UserHelper} + */ static async createUser(attributes = {}, options = {}) { const userHelper = new UserHelper() attributes = userHelper.getDefaultEmailPassword(attributes) @@ -122,14 +165,30 @@ module.exports = class UserHelper { return userHelper } + /** + * Get existing user via UserGetter and return UserHelper instance. + * All args passed to UserGetter.getUser. + * @returns {UserHelper} + */ static async getUser(...args) { const user = await UserGetter.promises.getUser(...args) + if (!user) { + throw new Error(`no user found for args: ${JSON.stringify([...args])}`) + } + return new UserHelper(user) } + /** + * Login to existing account via request and return UserHelper instance + * @param {object} userData + * @param {string} userData.email + * @param {string} userData.password + * @returns {UserHelper} + */ static async loginUser(userData) { - if (!userData.email || !userData.password) { + if (!userData || !userData.email || !userData.password) { throw new Error('email and password required') } const userHelper = new UserHelper() @@ -153,6 +212,14 @@ module.exports = class UserHelper { return userHelper } + /** + * Register new account via request and return UserHelper instance. + * If userData is not provided the default email and password will be used. + * @param {object} [userData] + * @param {string} [userData.email] + * @param {string} [userData.password] + * @returns {UserHelper} + */ static async registerUser(userData, options = {}) { const userHelper = new UserHelper() await userHelper.getCsrfToken() @@ -177,3 +244,5 @@ module.exports = class UserHelper { return userHelper } } + +module.exports = UserHelper