first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
import { expect } from 'chai'
import { render, screen, fireEvent } from '@testing-library/react'
import fetchMock from 'fetch-mock'
import sinon from 'sinon'
import RegisterForm from '../../../../frontend/js/components/register-form'
describe('RegisterForm', function () {
beforeEach(function () {
fetchMock.removeRoutes().clearHistory()
})
afterEach(function () {
fetchMock.removeRoutes().clearHistory()
})
it('should render the register form', async function () {
const setRegistrationSuccessStub = sinon.stub()
const setEmailsStub = sinon.stub()
const setRegisterErrorStub = sinon.stub()
const setFailedEmailsStub = sinon.stub()
render(
<RegisterForm
setRegistrationSuccess={setRegistrationSuccessStub}
setEmails={setEmailsStub}
setRegisterError={setRegisterErrorStub}
setFailedEmails={setFailedEmailsStub}
/>
)
await screen.findByLabelText('emails to register')
screen.getByRole('button', { name: /register/i })
})
it('should call the fetch request when register button is pressed', async function () {
const email = 'abc@gmail.com'
const setRegistrationSuccessStub = sinon.stub()
const setEmailsStub = sinon.stub()
const setRegisterErrorStub = sinon.stub()
const setFailedEmailsStub = sinon.stub()
const endPointResponse = {
status: 200,
body: {
email,
setNewPasswordUrl: 'SetNewPasswordURL',
},
}
const registerMock = fetchMock.post('/admin/register', endPointResponse)
render(
<RegisterForm
setRegistrationSuccess={setRegistrationSuccessStub}
setEmails={setEmailsStub}
setRegisterError={setRegisterErrorStub}
setFailedEmails={setFailedEmailsStub}
/>
)
const registerInput = screen.getByLabelText('emails to register')
const registerButton = screen.getByRole('button', { name: /register/i })
fireEvent.change(registerInput, { target: { value: email } })
fireEvent.click(registerButton)
expect(registerMock.callHistory.called()).to.be.true
})
})

View File

@@ -0,0 +1,140 @@
import { expect } from 'chai'
import { render, screen, fireEvent } from '@testing-library/react'
import fetchMock from 'fetch-mock'
import UserActivateRegister from '../../../../frontend/js/components/user-activate-register'
describe('UserActivateRegister', function () {
beforeEach(function () {
fetchMock.removeRoutes().clearHistory()
})
afterEach(function () {
fetchMock.removeRoutes().clearHistory()
})
it('should display the error message', async function () {
const email = 'abc@gmail.com'
render(<UserActivateRegister />)
const endPointResponse = {
status: 500,
}
const registerMock = fetchMock.post('/admin/register', endPointResponse)
const registerInput = screen.getByLabelText('emails to register')
const registerButton = screen.getByRole('button', { name: /register/i })
fireEvent.change(registerInput, { target: { value: email } })
fireEvent.click(registerButton)
expect(registerMock.callHistory.called()).to.be.true
await screen.findByText('Sorry, an error occured', { exact: false })
})
it('should display the success message', async function () {
const email = 'abc@gmail.com'
render(<UserActivateRegister />)
const endPointResponse = {
status: 200,
body: {
email,
setNewPasswordUrl: 'SetNewPasswordURL',
},
}
const registerMock = fetchMock.post('/admin/register', endPointResponse)
const registerInput = screen.getByLabelText('emails to register')
const registerButton = screen.getByRole('button', { name: /register/i })
fireEvent.change(registerInput, { target: { value: email } })
fireEvent.click(registerButton)
expect(registerMock.callHistory.called()).to.be.true
await screen.findByText(
"We've sent out welcome emails to the registered users."
)
})
it('should display the registered emails', async function () {
const email = 'abc@gmail.com, def@gmail.com'
render(<UserActivateRegister />)
const endPointResponse1 = {
status: 200,
body: {
email: 'abc@gmail.com',
setNewPasswordUrl: 'SetNewPasswordURL',
},
}
const endPointResponse2 = {
status: 200,
body: {
email: 'def@gmail.com',
setNewPasswordUrl: 'SetNewPasswordURL',
},
}
const registerMock = fetchMock.post('/admin/register', (path, req) => {
const body = JSON.parse(req.body)
if (body.email === 'abc@gmail.com') return endPointResponse1
else if (body.email === 'def@gmail.com') return endPointResponse2
})
const registerInput = screen.getByLabelText('emails to register')
const registerButton = screen.getByRole('button', { name: /register/i })
fireEvent.change(registerInput, { target: { value: email } })
fireEvent.click(registerButton)
expect(registerMock.callHistory.called()).to.be.true
await screen.findByText('abc@gmail.com')
await screen.findByText('def@gmail.com')
})
it('should display the failed emails', async function () {
const email = 'abc@, def@'
render(<UserActivateRegister />)
const endPointResponse1 = {
status: 500,
}
const endPointResponse2 = {
status: 500,
}
const registerMock = fetchMock.post('/admin/register', (path, req) => {
const body = JSON.parse(req.body)
if (body.email === 'abc@') return endPointResponse1
else if (body.email === 'def@') return endPointResponse2
})
const registerInput = screen.getByLabelText('emails to register')
const registerButton = screen.getByRole('button', { name: /register/i })
fireEvent.change(registerInput, { target: { value: email } })
fireEvent.click(registerButton)
expect(registerMock.callHistory.called()).to.be.true
await screen.findByText('abc@')
await screen.findByText('def@')
})
it('should display the registered and failed emails together', async function () {
const email = 'abc@gmail.com, def@'
render(<UserActivateRegister />)
const endPointResponse1 = {
status: 200,
body: {
email: 'abc@gmail.com',
setNewPasswordUrl: 'SetNewPasswordURL',
},
}
const endPointResponse2 = {
status: 500,
}
const registerMock = fetchMock.post('/admin/register', (path, req) => {
const body = JSON.parse(req.body)
if (body.email === 'abc@gmail.com') return endPointResponse1
else if (body.email === 'def@gmail.com') return endPointResponse2
else return 500
})
const registerInput = screen.getByLabelText('emails to register')
const registerButton = screen.getByRole('button', { name: /register/i })
fireEvent.change(registerInput, { target: { value: email } })
fireEvent.click(registerButton)
expect(registerMock.callHistory.called()).to.be.true
await screen.findByText('abc@gmail.com')
await screen.findByText('def@')
})
})

View File

@@ -0,0 +1,134 @@
import Path from 'node:path'
import { fileURLToPath } from 'node:url'
import { strict as esmock } from 'esmock'
import sinon from 'sinon'
const __dirname = Path.dirname(fileURLToPath(import.meta.url))
const MODULE_PATH = '../../../app/src/UserActivateController.mjs'
const VIEW_PATH = Path.join(__dirname, '../../../app/views/user/activate')
describe('UserActivateController', function () {
beforeEach(async function () {
this.user = {
_id: (this.user_id = 'kwjewkl'),
features: {},
email: 'joe@example.com',
}
this.UserGetter = {
promises: {
getUser: sinon.stub(),
},
}
this.UserRegistrationHandler = { promises: {} }
this.ErrorController = { notFound: sinon.stub() }
this.SplitTestHandler = {
promises: {
getAssignment: sinon.stub().resolves({ variant: 'default' }),
},
}
this.UserActivateController = await esmock(MODULE_PATH, {
'../../../../../app/src/Features/User/UserGetter.js': this.UserGetter,
'../../../../../app/src/Features/User/UserRegistrationHandler.js':
this.UserRegistrationHandler,
'../../../../../app/src/Features/Errors/ErrorController.js':
this.ErrorController,
'../../../../../app/src/Features/SplitTests/SplitTestHandler':
this.SplitTestHandler,
})
this.req = {
body: {},
query: {},
session: {
user: this.user,
},
}
this.res = {
json: sinon.stub(),
}
})
describe('activateAccountPage', function () {
beforeEach(function () {
this.UserGetter.promises.getUser = sinon.stub().resolves(this.user)
this.req.query.user_id = this.user_id
this.req.query.token = this.token = 'mock-token-123'
})
it('should 404 without a user_id', async function (done) {
delete this.req.query.user_id
this.ErrorController.notFound = () => done()
this.UserActivateController.activateAccountPage(this.req, this.res)
})
it('should 404 without a token', function (done) {
delete this.req.query.token
this.ErrorController.notFound = () => done()
this.UserActivateController.activateAccountPage(this.req, this.res)
})
it('should 404 without a valid user_id', function (done) {
this.UserGetter.promises.getUser = sinon.stub().resolves(null)
this.ErrorController.notFound = () => done()
this.UserActivateController.activateAccountPage(this.req, this.res)
})
it('should 403 for complex user_id', function (done) {
this.ErrorController.forbidden = () => done()
this.req.query.user_id = { first_name: 'X' }
this.UserActivateController.activateAccountPage(this.req, this.res)
})
it('should redirect activated users to login', function (done) {
this.user.loginCount = 1
this.res.redirect = url => {
sinon.assert.calledWith(this.UserGetter.promises.getUser, this.user_id)
url.should.equal('/login')
return done()
}
this.UserActivateController.activateAccountPage(this.req, this.res)
})
it('render the activation page if the user has not logged in before', function (done) {
this.user.loginCount = 0
this.res.render = (page, opts) => {
page.should.equal(VIEW_PATH)
opts.email.should.equal(this.user.email)
opts.token.should.equal(this.token)
return done()
}
this.UserActivateController.activateAccountPage(this.req, this.res)
})
})
describe('register', function () {
beforeEach(async function () {
this.UserRegistrationHandler.promises.registerNewUserAndSendActivationEmail =
sinon.stub().resolves({
user: this.user,
setNewPasswordUrl: (this.url = 'mock/url'),
})
this.req.body.email = this.user.email = this.email = 'email@example.com'
await this.UserActivateController.register(this.req, this.res)
})
it('should register the user and send them an email', function () {
sinon.assert.calledWith(
this.UserRegistrationHandler.promises
.registerNewUserAndSendActivationEmail,
this.email
)
})
it('should return the user and activation url', function () {
this.res.json
.calledWith({
email: this.email,
setNewPasswordUrl: this.url,
})
.should.equal(true)
})
})
})

View File

@@ -0,0 +1 @@
{ "extends": "../../../../tsconfig.backend.json" }