Files
overleaf-cep/services/web/test/unit/src/User/UserCreator.test.mjs
Antoine Clausse d7e1ad5588 Merge pull request #28981 from overleaf/ac-some-web-esm-migration-6
[web] Convert some Features/User files to ES modules

GitOrigin-RevId: c0d487082fa4822c68130e1e98c043297d4bedeb
2025-10-17 08:05:53 +00:00

363 lines
12 KiB
JavaScript

import { vi, assert } from 'vitest'
import sinon from 'sinon'
const modulePath = '../../../../app/src/Features/User/UserCreator.mjs'
describe('UserCreator', function () {
beforeEach(async function (ctx) {
const self = ctx
ctx.user = { _id: '12390i', ace: {} }
ctx.user.save = sinon.stub().resolves(self.user)
ctx.UserModel = class Project {
constructor() {
return self.user
}
}
ctx.logger = {
error: sinon.stub(),
}
vi.doMock('@overleaf/logger', () => ({
default: ctx.logger,
}))
vi.doMock('../../../../app/src/models/User', () => ({
User: ctx.UserModel,
}))
vi.doMock('../../../../app/src/infrastructure/Features', () => ({
default: (ctx.Features = {
hasFeature: sinon.stub().returns(false),
}),
}))
vi.doMock('../../../../app/src/Features/User/UserDeleter', () => ({
default: (ctx.UserDeleter = {
promises: {
deleteNewUser: sinon.stub().resolves(),
},
}),
}))
vi.doMock('../../../../app/src/Features/User/UserGetter', () => ({
default: (ctx.UserGetter = {
promises: {
getUser: sinon.stub().resolves(ctx.user),
},
}),
}))
vi.doMock('../../../../app/src/Features/User/UserUpdater', () => ({
default: (ctx.UserUpdater = {
promises: {
addAffiliationForNewUser: sinon.stub().resolves({
matchedCount: 1,
modifiedCount: 1,
acknowledged: true,
}),
updateUser: sinon.stub().resolves(),
},
}),
}))
vi.doMock(
'../../../../app/src/Features/Analytics/AnalyticsManager',
() => ({
default: (ctx.Analytics = {
recordEventForUserInBackground: sinon.stub(),
setUserPropertyForUser: sinon.stub(),
}),
})
)
vi.doMock(
'../../../../app/src/Features/SplitTests/SplitTestHandler',
() => ({
default: (ctx.SplitTestHandler = {
promises: {
getAssignmentForUser: sinon.stub().resolves({ variant: 'active' }),
},
}),
})
)
vi.doMock(
'../../../../app/src/Features/User/UserOnboardingEmailManager',
() => ({
default: (ctx.UserOnboardingEmailManager = {
scheduleOnboardingEmail: sinon.stub(),
}),
})
)
vi.doMock(
'../../../../app/src/Features/User/UserPostRegistrationAnalyticsManager',
() => ({
default: (ctx.UserPostRegistrationAnalyticsManager = {
schedulePostRegistrationAnalytics: sinon.stub(),
}),
})
)
ctx.UserCreator = (await import(modulePath)).default
ctx.email = 'bob.oswald@gmail.com'
})
describe('createNewUser', function () {
describe('with callbacks', function () {
it('should take the opts and put them in the model', async function (ctx) {
const user = await ctx.UserCreator.promises.createNewUser({
email: ctx.email,
holdingAccount: true,
})
assert.equal(user.email, ctx.email)
assert.equal(user.holdingAccount, true)
assert.equal(user.first_name, 'bob.oswald')
})
it('should use the start of the email if the first name is empty string', async function (ctx) {
const user = await ctx.UserCreator.promises.createNewUser({
email: ctx.email,
holdingAccount: true,
first_name: '',
})
assert.equal(user.email, ctx.email)
assert.equal(user.holdingAccount, true)
assert.equal(user.first_name, 'bob.oswald')
})
it('should use the first name if passed', async function (ctx) {
const user = await ctx.UserCreator.promises.createNewUser({
email: ctx.email,
holdingAccount: true,
first_name: 'fiiirstname',
})
assert.equal(user.email, ctx.email)
assert.equal(user.holdingAccount, true)
assert.equal(user.first_name, 'fiiirstname')
})
it('should use the last name if passed', async function (ctx) {
const user = await ctx.UserCreator.promises.createNewUser({
email: ctx.email,
holdingAccount: true,
last_name: 'lastNammmmeee',
})
assert.equal(user.email, ctx.email)
assert.equal(user.holdingAccount, true)
assert.equal(user.last_name, 'lastNammmmeee')
})
it('should set emails attribute', async function (ctx) {
const user = await ctx.UserCreator.promises.createNewUser({
email: ctx.email,
})
user.email.should.equal(ctx.email)
user.emails.length.should.equal(1)
user.emails[0].email.should.equal(ctx.email)
user.emails[0].createdAt.should.be.a('date')
user.emails[0].reversedHostname.should.equal('moc.liamg')
})
describe('with affiliations feature', function () {
let attributes, user
beforeEach(function (ctx) {
attributes = { email: ctx.email }
ctx.Features.hasFeature = sinon
.stub()
.withArgs('affiliations')
.returns(true)
})
describe('when v1 affiliations API does not return an error', function () {
beforeEach(async function (ctx) {
user = await ctx.UserCreator.promises.createNewUser(attributes)
})
it('should flag that affiliation is unchecked', function () {
user.emails[0].affiliationUnchecked.should.equal(true)
})
it('should try to add affiliation to v1', function (ctx) {
sinon.assert.calledOnce(
ctx.UserUpdater.promises.addAffiliationForNewUser
)
sinon.assert.calledWithMatch(
ctx.UserUpdater.promises.addAffiliationForNewUser,
user._id,
ctx.email
)
})
it('should query for updated user data', function (ctx) {
sinon.assert.calledOnce(ctx.UserGetter.promises.getUser)
})
})
describe('when v1 affiliations API does return an error', function () {
beforeEach(async function (ctx) {
ctx.UserUpdater.promises.addAffiliationForNewUser.rejects()
user = await ctx.UserCreator.promises.createNewUser(attributes)
})
it('should flag that affiliation is unchecked', function () {
user.emails[0].affiliationUnchecked.should.equal(true)
})
it('should try to add affiliation to v1', function (ctx) {
sinon.assert.calledOnce(
ctx.UserUpdater.promises.addAffiliationForNewUser
)
sinon.assert.calledWithMatch(
ctx.UserUpdater.promises.addAffiliationForNewUser,
user._id,
ctx.email
)
})
it('should not query for updated user data', function (ctx) {
sinon.assert.notCalled(ctx.UserGetter.promises.getUser)
})
it('should log error', function (ctx) {
sinon.assert.calledOnce(ctx.logger.error)
})
})
describe('when v1 affiliations API returns an error and requireAffiliation=true', function () {
beforeEach(async function (ctx) {
ctx.UserUpdater.promises.addAffiliationForNewUser.rejects()
user = await ctx.UserCreator.promises.createNewUser(attributes)
})
it('should flag that affiliation is unchecked', function () {
user.emails[0].affiliationUnchecked.should.equal(true)
})
it('should try to add affiliation to v1', function (ctx) {
sinon.assert.calledOnce(
ctx.UserUpdater.promises.addAffiliationForNewUser
)
sinon.assert.calledWithMatch(
ctx.UserUpdater.promises.addAffiliationForNewUser,
user._id,
ctx.email
)
})
it('should not query for updated user data', function (ctx) {
sinon.assert.notCalled(ctx.UserGetter.promises.getUser)
})
it('should log error', function (ctx) {
sinon.assert.calledOnce(ctx.logger.error)
})
})
})
it('should not add affiliation when without affiliation feature', async function (ctx) {
const attributes = { email: ctx.email }
await ctx.UserCreator.promises.createNewUser(attributes)
sinon.assert.notCalled(
ctx.UserUpdater.promises.addAffiliationForNewUser
)
})
})
describe('with promises', function () {
it('should take the opts and put them in the model', async function (ctx) {
const opts = {
email: ctx.email,
holdingAccount: true,
}
const user = await ctx.UserCreator.promises.createNewUser(opts)
assert.equal(user.email, ctx.email)
assert.equal(user.holdingAccount, true)
assert.equal(user.first_name, 'bob.oswald')
})
it('should add affiliation when with affiliation feature', async function (ctx) {
ctx.Features.hasFeature = sinon
.stub()
.withArgs('affiliations')
.returns(true)
const attributes = { email: ctx.email }
const user = await ctx.UserCreator.promises.createNewUser(attributes)
sinon.assert.calledOnce(
ctx.UserUpdater.promises.addAffiliationForNewUser
)
sinon.assert.calledWithMatch(
ctx.UserUpdater.promises.addAffiliationForNewUser,
user._id,
ctx.email
)
})
it('should not add affiliation when without affiliation feature', async function (ctx) {
ctx.Features.hasFeature = sinon.stub().returns(false)
const attributes = { email: ctx.email }
await ctx.UserCreator.promises.createNewUser(attributes)
sinon.assert.notCalled(
ctx.UserUpdater.promises.addAffiliationForNewUser
)
})
it('should include SAML provider ID with email', async function (ctx) {
const attributes = {
email: ctx.email,
samlIdentifiers: [{ email: ctx.email, providerId: '1' }],
}
const user = await ctx.UserCreator.promises.createNewUser(attributes)
assert.equal(user.emails[0].samlProviderId, '1')
})
it('should fire an analytics event and user property on registration', async function (ctx) {
const user = await ctx.UserCreator.promises.createNewUser({
email: ctx.email,
})
assert.equal(user.email, ctx.email)
sinon.assert.calledWith(
ctx.Analytics.recordEventForUserInBackground,
user._id,
'user-registered'
)
sinon.assert.calledWith(
ctx.Analytics.setUserPropertyForUser,
user._id,
'created-at'
)
})
it('should schedule post registration jobs on registration with saas feature', async function (ctx) {
ctx.Features.hasFeature = sinon.stub().withArgs('saas').returns(true)
const user = await ctx.UserCreator.promises.createNewUser({
email: ctx.email,
})
assert.equal(user.email, ctx.email)
sinon.assert.calledWith(
ctx.UserOnboardingEmailManager.scheduleOnboardingEmail,
user
)
sinon.assert.calledWith(
ctx.UserPostRegistrationAnalyticsManager
.schedulePostRegistrationAnalytics,
user
)
})
it('should not schedule post registration checks when without saas feature', async function (ctx) {
const attributes = { email: ctx.email }
await ctx.UserCreator.promises.createNewUser(attributes)
sinon.assert.notCalled(
ctx.UserOnboardingEmailManager.scheduleOnboardingEmail
)
sinon.assert.notCalled(
ctx.UserPostRegistrationAnalyticsManager
.schedulePostRegistrationAnalytics
)
})
})
})
})