Merge pull request #9956 from overleaf/em-node-fetch-web

Replace request-promise with fetch in web acceptance tests

GitOrigin-RevId: f50357cdea2d1353d7a82c5346b149018f91823f
This commit is contained in:
Eric Mc Sween 2022-10-17 09:53:55 -04:00 committed by Copybot
parent 00b051e2d7
commit fe963ba692
9 changed files with 368 additions and 453 deletions

89
package-lock.json generated
View file

@ -25893,49 +25893,6 @@
"throttleit": "^1.0.0"
}
},
"node_modules/request-promise-core": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
"dependencies": {
"lodash": "^4.17.19"
},
"engines": {
"node": ">=0.10.0"
},
"peerDependencies": {
"request": "^2.34"
}
},
"node_modules/request-promise-native": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
"integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
"deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142",
"dependencies": {
"request-promise-core": "1.1.4",
"stealthy-require": "^1.1.1",
"tough-cookie": "^2.3.3"
},
"engines": {
"node": ">=0.12.0"
},
"peerDependencies": {
"request": "^2.34"
}
},
"node_modules/request-promise-native/node_modules/tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"dependencies": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/request/node_modules/form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
@ -28212,14 +28169,6 @@
"node": ">= 0.6"
}
},
"node_modules/stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/stream-buffers": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz",
@ -35066,7 +35015,6 @@
"recurly": "^4.0.0",
"referer-parser": "0.0.3",
"request": "^2.88.2",
"request-promise-native": "^1.0.8",
"requestretry": "^6.0.0",
"rimraf": "2.2.6",
"rolling-rate-limiter": "^0.2.10",
@ -35169,6 +35117,7 @@
"terser-webpack-plugin": "^5.3.1",
"timekeeper": "^2.2.0",
"to-string-loader": "^1.2.0",
"tough-cookie": "^4.0.0",
"typescript": "^4.5.5",
"val-loader": "^4.0.0",
"webpack": "^5.71.0",
@ -42853,7 +42802,6 @@
"recurly": "^4.0.0",
"referer-parser": "0.0.3",
"request": "^2.88.2",
"request-promise-native": "^1.0.8",
"requestretry": "^6.0.0",
"requirejs": "^2.3.6",
"rimraf": "2.2.6",
@ -42870,6 +42818,7 @@
"terser-webpack-plugin": "^5.3.1",
"timekeeper": "^2.2.0",
"to-string-loader": "^1.2.0",
"tough-cookie": "^4.0.0",
"tsscmp": "^1.0.6",
"typescript": "^4.5.5",
"underscore": "^1.13.1",
@ -61749,35 +61698,6 @@
"throttleit": "^1.0.0"
}
},
"request-promise-core": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
"integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
"requires": {
"lodash": "^4.17.19"
}
},
"request-promise-native": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
"integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
"requires": {
"request-promise-core": "1.1.4",
"stealthy-require": "^1.1.1",
"tough-cookie": "^2.3.3"
},
"dependencies": {
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"requires": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
}
}
},
"requestretry": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/requestretry/-/requestretry-4.1.2.tgz",
@ -63594,11 +63514,6 @@
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
},
"stream-buffers": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.2.tgz",

View file

@ -9,9 +9,10 @@ describe('Launchpad', function () {
const user = new UserHelper()
it('should show the launchpad page', async function () {
const response = await user.request.get('/launchpad')
expect(response.statusCode).to.equal(200)
const $ = cheerio.load(response.body)
const response = await user.fetch('/launchpad')
expect(response.status).to.equal(200)
const body = await response.text()
const $ = cheerio.load(body)
expect($('h2').first().text()).to.equal('Create the first Admin account')
expect($('form[name="email"]').first()).to.exist
expect($('form[name="password"]').first()).to.exist
@ -19,45 +20,54 @@ describe('Launchpad', function () {
it('should allow for creation of the first admin user', async function () {
// Load the launchpad page
const initialPageResponse = await user.request.get('/launchpad')
expect(initialPageResponse.statusCode).to.equal(200)
const $ = cheerio.load(initialPageResponse.body)
const initialPageResponse = await user.fetch('/launchpad')
expect(initialPageResponse.status).to.equal(200)
const initialPageBody = await initialPageResponse.text()
const $ = cheerio.load(initialPageBody)
expect($('h2').first().text()).to.equal('Create the first Admin account')
expect($('form[name="email"]').first()).to.exist
expect($('form[name="password"]').first()).to.exist
// Submit the form
let csrfToken = await user.getCsrfToken()
const postResponse = await user.request.post({
url: '/launchpad/register_admin',
json: {
const postResponse = await user.fetch('/launchpad/register_admin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
_csrf: csrfToken,
email: adminEmail,
password: adminPassword,
},
}),
})
expect(postResponse.statusCode).to.equal(200)
expect(postResponse.body).to.deep.equal({ redir: '/launchpad' })
expect(postResponse.status).to.equal(200)
const postBody = await postResponse.json()
expect(postBody).to.deep.equal({ redir: '/launchpad' })
// Try to load the page again
const secondPageResponse = await user.request.get('/launchpad', {
simple: false,
})
expect(secondPageResponse.statusCode).to.equal(302)
expect(secondPageResponse.headers.location).to.equal('/login')
const secondPageResponse = await user.fetch('/launchpad')
expect(secondPageResponse.status).to.equal(302)
expect(secondPageResponse.headers.get('location')).to.equal(
UserHelper.url('/login').toString()
)
// Forbid submitting the form again
csrfToken = await user.getCsrfToken()
const badPostResponse = await user.request.post({
url: '/launchpad/register_admin',
json: {
const badPostResponse = await user.fetch('/launchpad/register_admin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
_csrf: csrfToken,
email: adminEmail + '1',
password: adminPassword + '1',
},
simple: false,
}),
})
expect(badPostResponse.statusCode).to.equal(403)
expect(badPostResponse.status).to.equal(403)
// Log in as this new admin user
const adminUser = await UserHelper.loginUser({

View file

@ -206,7 +206,6 @@
"recurly": "^4.0.0",
"referer-parser": "0.0.3",
"request": "^2.88.2",
"request-promise-native": "^1.0.8",
"requestretry": "^6.0.0",
"rimraf": "2.2.6",
"rolling-rate-limiter": "^0.2.10",
@ -309,6 +308,7 @@
"terser-webpack-plugin": "^5.3.1",
"timekeeper": "^2.2.0",
"to-string-loader": "^1.2.0",
"tough-cookie": "^4.0.0",
"typescript": "^4.5.5",
"val-loader": "^4.0.0",
"webpack": "^5.71.0",

View file

@ -13,31 +13,21 @@ describe('BetaProgram', function () {
})
})
it('should opt in', async function () {
const response = await userHelper.request.post('/beta/opt-in', {
simple: false,
})
expect(response.statusCode).to.equal(302)
response.statusCode.should.equal(302)
expect(response.headers.location).to.equal('/beta/participate')
const user = (
await UserHelper.getUser({
email,
})
).user
const response = await userHelper.fetch('/beta/opt-in', { method: 'POST' })
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/beta/participate').toString()
)
const user = (await UserHelper.getUser({ email })).user
expect(user.betaProgram).to.equal(true)
})
it('should opt out', async function () {
const response = await userHelper.request.post('/beta/opt-out', {
simple: false,
})
expect(response.statusCode).to.equal(302)
response.statusCode.should.equal(302)
expect(response.headers.location).to.equal('/beta/participate')
const user = (
await UserHelper.getUser({
email,
})
).user
const response = await userHelper.fetch('/beta/opt-out', { method: 'POST' })
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/beta/participate').toString()
)
const user = (await UserHelper.getUser({ email })).user
expect(user.betaProgram).to.equal(false)
})
})

View file

@ -13,10 +13,9 @@ describe('PasswordReset', function () {
// generate the token
await userHelper.getCsrfToken()
response = await userHelper.request.post('/user/password/reset', {
form: {
email,
},
response = await userHelper.fetch('/user/password/reset', {
method: 'POST',
body: new URLSearchParams({ email }),
})
token = (
@ -32,20 +31,20 @@ describe('PasswordReset', function () {
email,
password: userHelper.getDefaultPassword(),
})
response = await userHelper.request.get(
`/user/password/set?passwordResetToken=${token}&email=${email}`,
{ simple: false }
response = await userHelper.fetch(
`/user/password/set?passwordResetToken=${token}&email=${email}`
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal(
`/user/password/set${emailQuery}`
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url(`/user/password/set${emailQuery}`).toString()
)
// send reset request
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
password: 'a-password',
},
}),
})
userHelper = await UserHelper.getUser({ email })
user = userHelper.user
@ -75,20 +74,20 @@ describe('PasswordReset', function () {
email: otherUserEmail,
password: userHelper.getDefaultPassword(),
})
response = await userHelper.request.get(
`/user/password/set?passwordResetToken=${token}&email=${email}`,
{ simple: false }
response = await userHelper.fetch(
`/user/password/set?passwordResetToken=${token}&email=${email}`
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal(
`/user/password/set${emailQuery}`
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url(`/user/password/set${emailQuery}`).toString()
)
// send reset request
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
password: 'a-password',
},
}),
})
userHelper = await UserHelper.getUser({ email })
user = userHelper.user
@ -110,20 +109,20 @@ describe('PasswordReset', function () {
})
describe('when not logged in', function () {
beforeEach(async function () {
response = await userHelper.request.get(
`/user/password/set?passwordResetToken=${token}&email=${email}`,
{ simple: false }
response = await userHelper.fetch(
`/user/password/set?passwordResetToken=${token}&email=${email}`
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal(
`/user/password/set${emailQuery}`
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url(`/user/password/set${emailQuery}`).toString()
)
// send reset request
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
password: 'a-password',
},
}),
})
userHelper = await UserHelper.getUser({ email })
user = userHelper.user
@ -144,24 +143,23 @@ describe('PasswordReset', function () {
})
describe('password checks', function () {
beforeEach(async function () {
response = await userHelper.request.get(
`/user/password/set?passwordResetToken=${token}&email=${email}`,
{ simple: false }
response = await userHelper.fetch(
`/user/password/set?passwordResetToken=${token}&email=${email}`
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal(
`/user/password/set${emailQuery}`
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url(`/user/password/set${emailQuery}`).toString()
)
})
it('without a password should return 400 and not log the change', async function () {
// send reset request
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
},
simple: false,
}),
})
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
userHelper = await UserHelper.getUser({ email })
const auditLog = userHelper.getAuditLogWithoutNoise()
@ -170,14 +168,14 @@ describe('PasswordReset', function () {
it('without a valid password should return 400 and not log the change', async function () {
// send reset request
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
password: 'short',
},
simple: false,
}),
})
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
userHelper = await UserHelper.getUser({ email })
const auditLog = userHelper.getAuditLogWithoutNoise()
@ -187,41 +185,45 @@ describe('PasswordReset', function () {
it('should flag email in password', async function () {
const localPart = email.split('@').shift()
// send bad password
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
passwordResetToken: token,
password: localPart,
email,
},
json: true,
simple: false,
}),
})
expect(response.statusCode).to.equal(400)
expect(response.body).to.deep.equal({
expect(response.status).to.equal(400)
const body = await response.json()
expect(body).to.deep.equal({
message: { text: 'password contains part of email address' },
})
})
it('should be able to retry after providing an invalid password', async function () {
// send bad password
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
password: 'short',
},
simple: false,
}),
})
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
// send good password
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
password: 'SomeThingVeryStrong!11',
},
simple: false,
}),
})
expect(response.statusCode).to.equal(200)
expect(response.status).to.equal(200)
userHelper = await UserHelper.getUser({ email })
const auditLog = userHelper.getAuditLogWithoutNoise()
@ -230,17 +232,16 @@ describe('PasswordReset', function () {
it('when the password is the same as current, should return 400 and log the change', async function () {
// send reset request
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
password: userHelper.getDefaultPassword(),
},
simple: false,
}),
})
expect(response.statusCode).to.equal(400)
expect(JSON.parse(response.body).message.key).to.equal(
'password-must-be-different'
)
expect(response.status).to.equal(400)
const body = await response.json()
expect(body.message.key).to.equal('password-must-be-different')
userHelper = await UserHelper.getUser({ email })
const auditLog = userHelper.getAuditLogWithoutNoise()
@ -251,80 +252,81 @@ describe('PasswordReset', function () {
describe('multiple attempts to set the password, reaching attempt limit', async function () {
beforeEach(async function () {
response = await userHelper.request.get(
`/user/password/set?passwordResetToken=${token}&email=${email}`,
{ simple: false }
response = await userHelper.fetch(
`/user/password/set?passwordResetToken=${token}&email=${email}`
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal(
`/user/password/set${emailQuery}`
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url(`/user/password/set${emailQuery}`).toString()
)
})
it('should allow multiple attempts with same-password error, then deny further attempts', async function () {
const sendSamePasswordRequest = async function () {
return userHelper.request.post('/user/password/set', {
form: {
return userHelper.fetch('/user/password/set', {
method: 'POST',
headers: {
Accept: 'application/json',
},
body: new URLSearchParams({
passwordResetToken: token,
password: userHelper.getDefaultPassword(),
},
simple: false,
}),
})
}
// Three attempts at setting the password, all rejected for being the same as
// the current password
const response1 = await sendSamePasswordRequest()
expect(response1.statusCode).to.equal(400)
expect(JSON.parse(response1.body).message.key).to.equal(
'password-must-be-different'
)
expect(response1.status).to.equal(400)
const body1 = await response1.json()
expect(body1.message.key).to.equal('password-must-be-different')
const response2 = await sendSamePasswordRequest()
expect(response2.statusCode).to.equal(400)
expect(JSON.parse(response2.body).message.key).to.equal(
'password-must-be-different'
)
expect(response2.status).to.equal(400)
const body2 = await response2.json()
expect(body2.message.key).to.equal('password-must-be-different')
const response3 = await sendSamePasswordRequest()
expect(response3.statusCode).to.equal(400)
expect(JSON.parse(response3.body).message.key).to.equal(
'password-must-be-different'
)
expect(response3.status).to.equal(400)
const body3 = await response3.json()
expect(body3.message.key).to.equal('password-must-be-different')
// Fourth attempt is rejected because the token has been used too many times
const response4 = await sendSamePasswordRequest()
expect(response4.statusCode).to.equal(404)
expect(JSON.parse(response4.body).message.key).to.equal('token-expired')
expect(response4.status).to.equal(404)
const body4 = await response4.json()
expect(body4.message.key).to.equal('token-expired')
})
it('should allow multiple attempts with same-password error, then set the password', async function () {
const sendSamePasswordRequest = async function () {
return userHelper.request.post('/user/password/set', {
form: {
return userHelper.fetch('/user/password/set', {
method: 'POST',
headers: {
Accept: 'application/json',
},
body: new URLSearchParams({
passwordResetToken: token,
password: userHelper.getDefaultPassword(),
},
simple: false,
}),
})
}
// Two attempts at setting the password, all rejected for being the same as
// the current password
const response1 = await sendSamePasswordRequest()
expect(response1.statusCode).to.equal(400)
expect(JSON.parse(response1.body).message.key).to.equal(
'password-must-be-different'
)
expect(response1.status).to.equal(400)
const body1 = await response1.json()
expect(body1.message.key).to.equal('password-must-be-different')
const response2 = await sendSamePasswordRequest()
expect(response2.statusCode).to.equal(400)
expect(JSON.parse(response2.body).message.key).to.equal(
'password-must-be-different'
)
expect(response2.status).to.equal(400)
const body2 = await response2.json()
expect(body2.message.key).to.equal('password-must-be-different')
// Third attempt is succeeds
const response3 = await userHelper.request.post('/user/password/set', {
form: {
const response3 = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
password: 'some-new-password',
},
simple: false,
}),
})
expect(response3.statusCode).to.equal(200)
expect(response3.status).to.equal(200)
// Check the user and audit log
userHelper = await UserHelper.getUser({ email })
user = userHelper.user
@ -342,83 +344,79 @@ describe('PasswordReset', function () {
describe('without a valid token', function () {
it('no token should redirect to page to re-request reset token', async function () {
response = await userHelper.request.get(
`/user/password/set?&email=${email}`,
{ simple: false }
response = await userHelper.fetch(`/user/password/set?&email=${email}`)
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/user/password/reset').toString()
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal('/user/password/reset')
})
it('should show error for invalid tokens and return 404 if used', async function () {
const invalidToken = 'not-real-token'
response = await userHelper.request.get(
`/user/password/set?&passwordResetToken=${invalidToken}&email=${email}`,
{ simple: false }
response = await userHelper.fetch(
`/user/password/set?&passwordResetToken=${invalidToken}&email=${email}`
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal(
`/user/password/reset?error=token_expired`
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/user/password/reset?error=token_expired').toString()
)
// send reset request
response = await userHelper.request.post('/user/password/set', {
form: {
response = await userHelper.fetch('/user/password/set', {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: invalidToken,
password: 'a-password',
},
simple: false,
}),
})
expect(response.statusCode).to.equal(404)
expect(response.status).to.equal(404)
})
})
describe('password reset', function () {
it('should return 200 if email field is valid', async function () {
response = await userHelper.request.post(`/user/password/reset`, {
form: {
email,
},
response = await userHelper.fetch(`/user/password/reset`, {
method: 'POST',
body: new URLSearchParams({ email }),
})
expect(response.statusCode).to.equal(200)
expect(response.status).to.equal(200)
})
it('should return 400 if email field is missing', async function () {
response = await userHelper.request.post(`/user/password/reset`, {
form: {
mail: email,
},
simple: false,
response = await userHelper.fetch(`/user/password/reset`, {
method: 'POST',
body: new URLSearchParams({ mail: email }),
})
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
})
})
describe('password set', function () {
it('should return 200 if password and passwordResetToken fields are valid', async function () {
response = await userHelper.request.post(`/user/password/set`, {
form: {
response = await userHelper.fetch(`/user/password/set`, {
method: 'POST',
body: new URLSearchParams({
password: 'new-password',
passwordResetToken: token,
},
}),
})
expect(response.statusCode).to.equal(200)
expect(response.status).to.equal(200)
})
it('should return 400 if password field is missing', async function () {
response = await userHelper.request.post(`/user/password/set`, {
form: {
response = await userHelper.fetch(`/user/password/set`, {
method: 'POST',
body: new URLSearchParams({
passwordResetToken: token,
},
simple: false,
}),
})
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
})
it('should return 400 if passwordResetToken field is missing', async function () {
response = await userHelper.request.post(`/user/password/set`, {
form: {
response = await userHelper.fetch(`/user/password/set`, {
method: 'POST',
body: new URLSearchParams({
password: 'new-password',
},
simple: false,
}),
})
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
})
})
})

View file

@ -23,19 +23,19 @@ describe('PasswordUpdate', function () {
})
describe('success', function () {
beforeEach(async function () {
response = await userHelper.request.post('/user/password/update', {
form: {
response = await userHelper.fetch('/user/password/update', {
method: 'POST',
body: new URLSearchParams({
currentPassword: password,
newPassword1: 'new-password',
newPassword2: 'new-password',
},
simple: false,
}),
})
userHelper = await UserHelper.getUser({ email })
user = userHelper.user
})
it('should return 200', async function () {
expect(response.statusCode).to.equal(200)
expect(response.status).to.equal(200)
})
it('should update the audit log', function () {
const auditLog = userHelper.getAuditLogWithoutNoise()
@ -50,17 +50,17 @@ describe('PasswordUpdate', function () {
describe('errors', function () {
describe('missing current password', function () {
beforeEach(async function () {
response = await userHelper.request.post('/user/password/update', {
form: {
response = await userHelper.fetch('/user/password/update', {
method: 'POST',
body: new URLSearchParams({
newPassword1: 'new-password',
newPassword2: 'new-password',
},
simple: false,
}),
})
userHelper = await UserHelper.getUser({ email })
})
it('should return 500', async function () {
expect(response.statusCode).to.equal(500)
expect(response.status).to.equal(500)
})
it('should not update audit log', async function () {
const auditLog = userHelper.getAuditLogWithoutNoise()
@ -69,18 +69,18 @@ describe('PasswordUpdate', function () {
})
describe('wrong current password', function () {
beforeEach(async function () {
response = await userHelper.request.post('/user/password/update', {
form: {
response = await userHelper.fetch('/user/password/update', {
method: 'POST',
body: new URLSearchParams({
currentPassword: 'wrong-password',
newPassword1: 'new-password',
newPassword2: 'new-password',
},
simple: false,
}),
})
userHelper = await UserHelper.getUser({ email })
})
it('should return 400', async function () {
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
})
it('should not update audit log', async function () {
const auditLog = userHelper.getAuditLogWithoutNoise()
@ -89,22 +89,26 @@ describe('PasswordUpdate', function () {
})
describe('newPassword1 does not match newPassword2', function () {
beforeEach(async function () {
response = await userHelper.request.post('/user/password/update', {
form: {
response = await userHelper.fetch('/user/password/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
currentPassword: password,
newPassword1: 'new-password',
newPassword2: 'oops-password',
},
json: true,
simple: false,
}),
})
userHelper = await UserHelper.getUser({ email })
})
it('should return 400', async function () {
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
})
it('should return error message', async function () {
expect(response.body.message).to.equal('Passwords do not match')
const body = await response.json()
expect(body.message).to.equal('Passwords do not match')
})
it('should not update audit log', async function () {
const auditLog = userHelper.getAuditLogWithoutNoise()
@ -113,22 +117,26 @@ describe('PasswordUpdate', function () {
})
describe('new password is not valid', function () {
beforeEach(async function () {
response = await userHelper.request.post('/user/password/update', {
form: {
response = await userHelper.fetch('/user/password/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
currentPassword: password,
newPassword1: 'short',
newPassword2: 'short',
},
json: true,
simple: false,
}),
})
userHelper = await UserHelper.getUser({ email })
})
it('should return 400', async function () {
expect(response.statusCode).to.equal(400)
expect(response.status).to.equal(400)
})
it('should return error message', async function () {
expect(response.body.message).to.equal('password is too short')
const body = await response.json()
expect(body.message).to.equal('password is too short')
})
it('should not update audit log', async function () {
const auditLog = userHelper.getAuditLogWithoutNoise()

View file

@ -43,19 +43,18 @@ describe('PrimaryEmailCheck', function () {
describe('redirections', function () {
describe('when the user has signed up recently', function () {
it("shouldn't be redirected from project list to the primary email check page", async function () {
const response = await userHelper.request.get(
'/project' + SPLIT_TEST_QUERY
)
expect(response.statusCode).to.equal(200)
const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
expect(response.status).to.equal(200)
})
it('should be redirected from the primary email check page to the project list', async function () {
const response = await userHelper.request.get(
'/user/emails/primary-email-check' + SPLIT_TEST_QUERY,
{ simple: false }
const response = await userHelper.fetch(
'/user/emails/primary-email-check' + SPLIT_TEST_QUERY
)
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/project').toString()
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal('/project')
})
})
@ -68,19 +67,18 @@ describe('PrimaryEmailCheck', function () {
})
it("shouldn't be redirected from project list to the primary email check page", async function () {
const response = await userHelper.request.get(
'/project' + SPLIT_TEST_QUERY
)
expect(response.statusCode).to.equal(200)
const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
expect(response.status).to.equal(200)
})
it('should be redirected from the primary email check page to the project list', async function () {
const response = await userHelper.request.get(
'/user/emails/primary-email-check' + SPLIT_TEST_QUERY,
{ simple: false }
const response = await userHelper.fetch(
'/user/emails/primary-email-check' + SPLIT_TEST_QUERY
)
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/project').toString()
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal('/project')
})
})
@ -100,19 +98,18 @@ describe('PrimaryEmailCheck', function () {
})
it("shouldn't be redirected from project list to the primary email check page", async function () {
const response = await userHelper.request.get(
'/project' + SPLIT_TEST_QUERY
)
expect(response.statusCode).to.equal(200)
const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
expect(response.status).to.equal(200)
})
it('should be redirected from the primary email check page to the project list', async function () {
const response = await userHelper.request.get(
'/user/emails/primary-email-check' + SPLIT_TEST_QUERY,
{ simple: false }
const response = await userHelper.fetch(
'/user/emails/primary-email-check' + SPLIT_TEST_QUERY
)
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/project').toString()
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal('/project')
})
})
@ -125,21 +122,18 @@ describe('PrimaryEmailCheck', function () {
})
it('should be redirected from project list to the primary email check page', async function () {
const response = await userHelper.request.get(
'/project' + SPLIT_TEST_QUERY,
{ simple: false }
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal(
'/user/emails/primary-email-check'
const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/user/emails/primary-email-check').toString()
)
})
it('can visit the primary email check page', async function () {
const response = await userHelper.request.get(
const response = await userHelper.fetch(
'/user/emails/primary-email-check'
)
expect(response.statusCode).to.equal(200)
expect(response.status).to.equal(200)
})
})
})
@ -154,34 +148,32 @@ describe('PrimaryEmailCheck', function () {
$set: { lastPrimaryEmailCheck: new Date(time) },
})
checkResponse = await userHelper.request.post(
checkResponse = await userHelper.fetch(
'/user/emails/primary-email-check' + SPLIT_TEST_QUERY,
{
form: {},
simple: false,
}
{ method: 'POST' }
)
})
it('should be redirected to the project list page', function () {
expect(checkResponse.statusCode).to.equal(302)
expect(checkResponse.headers.location).to.equal('/project')
expect(checkResponse.status).to.equal(302)
expect(checkResponse.headers.get('location')).to.equal(
UserHelper.url('/project').toString()
)
})
it("shouldn't be redirected from project list to the primary email check page any longer", async function () {
const response = await userHelper.request.get(
'/project' + SPLIT_TEST_QUERY
)
expect(response.statusCode).to.equal(200)
const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
expect(response.status).to.equal(200)
})
it('visiting the primary email check page should redirect to the project list page', async function () {
const response = await userHelper.request.get(
'/user/emails/primary-email-check',
{ simple: false }
const response = await userHelper.fetch(
'/user/emails/primary-email-check'
)
expect(response.status).to.equal(302)
expect(response.headers.get('location')).to.equal(
UserHelper.url('/project').toString()
)
expect(response.statusCode).to.equal(302)
expect(response.headers.location).to.equal('/project')
})
})
})

View file

@ -668,15 +668,14 @@ describe('ProjectDuplicateNames', function () {
})
it('should handle characters that would cause an invalid regular expression', async function () {
const projectName = 'Example (test'
response = await userHelper.request.post('/project/new', {
simple: false,
form: { projectName },
response = await userHelper.fetch('/project/new', {
method: 'POST',
body: new URLSearchParams([['projectName', projectName]]),
})
expect(response.statusCode).to.equal(200) // can create project
response = await userHelper.request.get(
`/project/${JSON.parse(response.body).project_id}`
)
expect(response.statusCode).to.equal(200) // can open project
const body = await response.json()
expect(response.status).to.equal(200) // can create project
response = await userHelper.fetch(`/project/${body.project_id}`)
expect(response.status).to.equal(200) // can open project
})
})
})

View file

@ -1,4 +1,5 @@
const { expect } = require('chai')
const { CookieJar } = require('tough-cookie')
const AuthenticationManager = require('../../../../app/src/Features/Authentication/AuthenticationManager')
const Settings = require('@overleaf/settings')
const InstitutionsAPI = require('../../../../app/src/Features/Institutions/InstitutionsAPI')
@ -6,7 +7,7 @@ const UserCreator = require('../../../../app/src/Features/User/UserCreator')
const UserGetter = require('../../../../app/src/Features/User/UserGetter')
const UserUpdater = require('../../../../app/src/Features/User/UserUpdater')
const moment = require('moment')
const request = require('request-promise-native')
const fetch = require('node-fetch')
const { db } = require('../../../../app/src/infrastructure/mongodb')
const { ObjectId } = require('mongodb')
const {
@ -82,43 +83,33 @@ class UserHelper {
// used to store mongo user object once created/loaded
this.user = null
// cookie jar
this.jar = request.jar()
// create new request instance
this.request = request.defaults({})
// initialize request instance with default options
this.setRequestDefaults({
baseUrl: UserHelper.baseUrl(),
followRedirect: false,
jar: this.jar,
resolveWithFullResponse: true,
this.jar = new CookieJar()
}
async fetch(url, opts = {}) {
url = UserHelper.url(url)
const headers = {}
const cookieString = this.jar.getCookieStringSync(url)
if (cookieString) {
headers.Cookie = cookieString
}
if (this._csrfToken) {
headers['x-csrf-token'] = this._csrfToken
}
const response = await fetch(url, {
redirect: 'manual',
...opts,
headers: { ...headers, ...opts.headers },
})
}
/* 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)
// From https://www.npmjs.com/package/node-fetch#extract-set-cookie-header
const cookies = response.headers.raw()['set-cookie']
if (cookies != null) {
for (const cookie of cookies) {
this.jar.setCookieSync(cookie, url)
}
/**
* Make a request for the user and run expectations on the response.
* @param {object} [requestOptions] options to pass to request
* @param {object} [responseExpectations] expectations:
* - {Int} statusCode the expected status code
* - {RegEx} message a matcher for the message
*/
async expectErrorOnRequest(requestOptions, responseExpectations) {
let error
try {
await this.request(requestOptions)
} catch (e) {
error = e
}
expect(error).to.exist
expect(error.statusCode).to.equal(responseExpectations.statusCode)
expect(error.message).to.match(responseExpectations.message)
return response
}
/* async http api call methods */
@ -128,12 +119,8 @@ class UserHelper {
*/
async getCsrfToken() {
// get csrf token from api and store
const response = await this.request.get('/dev/csrf')
this._csrfToken = response.body
// use csrf token for requests
this.setRequestDefaults({
headers: { 'x-csrf-token': this._csrfToken },
})
const response = await this.fetch('/dev/csrf')
this._csrfToken = await response.text()
}
/**
@ -142,13 +129,11 @@ class UserHelper {
* @returns {object} http response
*/
async logout(options = {}) {
// do not throw exception on 302
options.simple = false
// post logout
const response = await this.request.post('/logout', options)
const response = await this.fetch('/logout', { method: 'POST', ...options })
if (
response.statusCode !== 302 ||
!response.headers.location.includes('/login')
response.status !== 302 ||
!response.headers.get('location').includes('/login')
) {
throw new Error('logout failed')
}
@ -168,6 +153,13 @@ class UserHelper {
return `http://${process.env.HTTP_TEST_HOST || 'localhost'}:23000`
}
/**
* Generates a full URL given a path
*/
static url(path) {
return new URL(path, UserHelper.baseUrl())
}
/* static async instantiation methods */
/**
@ -247,17 +239,30 @@ class UserHelper {
const userHelper = new UserHelper()
const loginPath = Settings.enableLegacyLogin ? '/login/legacy' : '/login'
await userHelper.getCsrfToken()
const response = await userHelper.request.post(loginPath, {
json: {
const response = await userHelper.fetch(loginPath, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
'g-recaptcha-response': 'valid',
...userData,
},
}),
})
if (response.statusCode !== 200 || response.body.redir !== '/project') {
if (!response.ok) {
const error = new Error('login failed')
error.response = response
throw error
}
const body = await response.json()
if (body.redir !== '/project') {
const error = new Error('login failed')
error.response = response
throw error
}
userHelper.user = await UserGetter.promises.getUser({
email: userData.email,
})
@ -274,10 +279,10 @@ class UserHelper {
* @returns {Boolean}
*/
async isLoggedIn() {
const response = await this.request.get('/user/sessions', {
followRedirect: true,
const response = await this.fetch('/user/sessions', {
redirect: 'follow',
})
return response.request.path === '/user/sessions'
return !response.redirected
}
/**
@ -292,8 +297,16 @@ class UserHelper {
const userHelper = new UserHelper()
await userHelper.getCsrfToken()
userData = userHelper.getDefaultEmailPassword(userData)
options.json = userData
const { body } = await userHelper.request.post('/register', options)
const response = await userHelper.fetch('/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify(userData),
...options,
})
const body = await response.json()
if (body.message && body.message.type === 'error') {
throw new Error(`register api error: ${body.message.text}`)
}
@ -321,14 +334,11 @@ class UserHelper {
}
async addEmail(email) {
const response = await this.request.post({
form: {
email,
},
simple: false,
uri: '/user/emails',
const response = await this.fetch('/user/emails', {
method: 'POST',
body: new URLSearchParams([['email', email]]),
})
expect(response.statusCode).to.equal(204)
expect(response.status).to.equal(204)
}
async addEmailAndConfirm(userId, email) {
@ -393,16 +403,12 @@ class UserHelper {
}
async confirmEmail(userId, email) {
let response
// UserHelper.createUser does not create a confirmation token
response = await this.request.post({
form: {
email,
},
simple: false,
uri: '/user/emails/resend_confirmation',
let response = await this.fetch('/user/emails/resend_confirmation', {
method: 'POST',
body: new URLSearchParams([['email', email]]),
})
expect(response.statusCode).to.equal(200)
expect(response.status).to.equal(200)
const tokenData = await db.tokens
.find({
use: 'email_confirmation',
@ -411,14 +417,11 @@ class UserHelper {
usedAt: { $exists: false },
})
.next()
response = await this.request.post({
form: {
token: tokenData.token,
},
simple: false,
uri: '/user/emails/confirm',
response = await this.fetch('/user/emails/confirm', {
method: 'POST',
body: new URLSearchParams([['token', tokenData.token]]),
})
expect(response.statusCode).to.equal(200)
expect(response.status).to.equal(200)
}
}