Commit 5bdfa604 authored by Chocobozzz's avatar Chocobozzz Committed by Chocobozzz
Browse files

Add E2E client tests for signup approval

parent 9589907c
......@@ -11,5 +11,6 @@
/src/locale/target/server_*.xml
/e2e/local.log
/e2e/browserstack.err
/e2e/screenshots
/src/standalone/player/build
/src/standalone/player/dist
import { getCheckbox, go } from '../utils'
import { browserSleep, getCheckbox, go, isCheckboxSelected } from '../utils'
export class AdminConfigPage {
......@@ -8,7 +8,6 @@ export class AdminConfigPage {
'basic-configuration': 'APPEARANCE',
'instance-information': 'INSTANCE'
}
await go('/admin/config/edit-custom#' + tab)
await $('.inner-form-title=' + waitTitles[tab]).waitForDisplayed()
......@@ -28,17 +27,39 @@ export class AdminConfigPage {
return $('#instanceCustomHomepageContent').setValue(newValue)
}
async toggleSignup () {
async toggleSignup (enabled: boolean) {
if (await isCheckboxSelected('signupEnabled') === enabled) return
const checkbox = await getCheckbox('signupEnabled')
await checkbox.waitForClickable()
await checkbox.click()
}
async toggleSignupApproval (required: boolean) {
if (await isCheckboxSelected('signupRequiresApproval') === required) return
const checkbox = await getCheckbox('signupRequiresApproval')
await checkbox.waitForClickable()
await checkbox.click()
}
async toggleSignupEmailVerification (required: boolean) {
if (await isCheckboxSelected('signupRequiresEmailVerification') === required) return
const checkbox = await getCheckbox('signupRequiresEmailVerification')
await checkbox.waitForClickable()
await checkbox.click()
}
async save () {
const button = $('input[type=submit]')
await button.waitForClickable()
await button.click()
await browserSleep(1000)
}
}
import { browserSleep, findParentElement, go } from '../utils'
export class AdminRegistrationPage {
async navigateToRegistratonsList () {
await go('/admin/moderation/registrations/list')
await $('my-registration-list').waitForDisplayed()
}
async accept (username: string, moderationResponse: string) {
const usernameEl = await $('*=' + username)
await usernameEl.waitForDisplayed()
const tr = await findParentElement(usernameEl, async el => await el.getTagName() === 'tr')
await tr.$('.action-cell .dropdown-root').click()
const accept = await $('span*=Accept this registration')
await accept.waitForClickable()
await accept.click()
const moderationResponseTextarea = await $('#moderationResponse')
await moderationResponseTextarea.waitForDisplayed()
await moderationResponseTextarea.setValue(moderationResponse)
const submitButton = $('.modal-footer input[type=submit]')
await submitButton.waitForClickable()
await submitButton.click()
await browserSleep(1000)
}
}
......@@ -6,7 +6,14 @@ export class LoginPage {
}
async login (username: string, password: string, url = '/login') {
async login (options: {
username: string
password: string
displayName?: string
url?: string
}) {
const { username, password, url = '/login', displayName = username } = options
await go(url)
await browser.execute(`window.localStorage.setItem('no_account_setup_warning_modal', 'true')`)
......@@ -27,27 +34,40 @@ export class LoginPage {
await menuToggle.click()
await this.ensureIsLoggedInAs(username)
await this.ensureIsLoggedInAs(displayName)
await menuToggle.click()
} else {
await this.ensureIsLoggedInAs(username)
await this.ensureIsLoggedInAs(displayName)
}
}
async getLoginError (username: string, password: string) {
await go('/login')
await $('input#username').setValue(username)
await $('input#password').setValue(password)
await browser.pause(1000)
await $('form input[type=submit]').click()
return $('.alert-danger').getText()
}
async loginAsRootUser () {
return this.login('root', 'test' + this.getSuffix())
return this.login({ username: 'root', password: 'test' + this.getSuffix() })
}
loginOnPeerTube2 () {
return this.login('e2e', process.env.PEERTUBE2_E2E_PASSWORD, 'https://peertube2.cpy.re/login')
return this.login({ username: 'e2e', password: process.env.PEERTUBE2_E2E_PASSWORD, url: 'https://peertube2.cpy.re/login' })
}
async logout () {
const loggedInMore = $('.logged-in-more')
const loggedInDropdown = $('.logged-in-more .logged-in-info')
await loggedInMore.waitForClickable()
await loggedInMore.click()
await loggedInDropdown.waitForClickable()
await loggedInDropdown.click()
const logout = $('.dropdown-item*=Log out')
......
......@@ -27,42 +27,39 @@ export class SignupPage {
return terms.click()
}
async getEndMessage () {
const alert = $('.pt-alert-primary')
await alert.waitForDisplayed()
return alert.getText()
}
async fillRegistrationReason (reason: string) {
await $('#registrationReason').setValue(reason)
}
async fillAccountStep (options: {
displayName: string
username: string
email: string
password: string
password?: string
displayName?: string
email?: string
}) {
if (options.displayName) {
await $('#displayName').setValue(options.displayName)
}
if (options.username) {
await $('#username').setValue(options.username)
}
await $('#displayName').setValue(options.displayName || `${options.username} display name`)
if (options.email) {
// Fix weird bug on firefox that "cannot scroll into view" when using just `setValue`
await $('#email').scrollIntoView(false)
await $('#email').waitForClickable()
await $('#email').setValue(options.email)
}
await $('#username').setValue(options.username)
await $('#password').setValue(options.password || 'password')
if (options.password) {
await $('#password').setValue(options.password)
}
// Fix weird bug on firefox that "cannot scroll into view" when using just `setValue`
await $('#email').scrollIntoView(false)
await $('#email').waitForClickable()
await $('#email').setValue(options.email || `${options.username}@example.com`)
}
async fillChannelStep (options: {
displayName: string
name: string
displayName?: string
}) {
if (options.displayName) {
await $('#displayName').setValue(options.displayName)
}
if (options.name) {
await $('#name').setValue(options.name)
}
await $('#displayName').setValue(options.displayName || `${options.name} channel display name`)
await $('#name').setValue(options.name)
}
}
import { AdminConfigPage } from '../po/admin-config.po'
import { AdminRegistrationPage } from '../po/admin-registration.po'
import { LoginPage } from '../po/login.po'
import { SignupPage } from '../po/signup.po'
import { isMobileDevice, waitServerUp } from '../utils'
import { browserSleep, getVerificationLink, go, findEmailTo, isMobileDevice, MockSMTPServer, waitServerUp } from '../utils'
function checkEndMessage (options: {
message: string
requiresEmailVerification: boolean
requiresApproval: boolean
afterEmailVerification: boolean
}) {
const { message, requiresApproval, requiresEmailVerification, afterEmailVerification } = options
{
const created = 'account has been created'
const request = 'account request has been sent'
if (requiresApproval) {
expect(message).toContain(request)
expect(message).not.toContain(created)
} else {
expect(message).not.toContain(request)
expect(message).toContain(created)
}
}
{
const checkEmail = 'Check your emails'
if (requiresEmailVerification) {
expect(message).toContain(checkEmail)
} else {
expect(message).not.toContain(checkEmail)
const moderatorsApproval = 'moderator will check your registration request'
if (requiresApproval) {
expect(message).toContain(moderatorsApproval)
} else {
expect(message).not.toContain(moderatorsApproval)
}
}
}
{
const emailVerified = 'email has been verified'
if (afterEmailVerification) {
expect(message).toContain(emailVerified)
} else {
expect(message).not.toContain(emailVerified)
}
}
}
describe('Signup', () => {
let loginPage: LoginPage
let adminConfigPage: AdminConfigPage
let signupPage: SignupPage
let adminRegistrationPage: AdminRegistrationPage
async function prepareSignup (options: {
enabled: boolean
requiresApproval?: boolean
requiresEmailVerification?: boolean
}) {
await loginPage.loginAsRootUser()
await adminConfigPage.navigateTo('basic-configuration')
await adminConfigPage.toggleSignup(options.enabled)
if (options.enabled) {
if (options.requiresApproval !== undefined) {
await adminConfigPage.toggleSignupApproval(options.requiresApproval)
}
if (options.requiresEmailVerification !== undefined) {
await adminConfigPage.toggleSignupEmailVerification(options.requiresEmailVerification)
}
}
await adminConfigPage.save()
await loginPage.logout()
await browser.refresh()
}
before(async () => {
await waitServerUp()
......@@ -16,72 +93,310 @@ describe('Signup', () => {
loginPage = new LoginPage(isMobileDevice())
adminConfigPage = new AdminConfigPage()
signupPage = new SignupPage()
adminRegistrationPage = new AdminRegistrationPage()
await browser.maximizeWindow()
})
it('Should disable signup', async () => {
await loginPage.loginAsRootUser()
describe('Signup disabled', function () {
it('Should disable signup', async () => {
await prepareSignup({ enabled: false })
await adminConfigPage.navigateTo('basic-configuration')
await adminConfigPage.toggleSignup()
await expect(signupPage.getRegisterMenuButton()).not.toBeDisplayed()
})
})
await adminConfigPage.save()
describe('Email verification disabled', function () {
await loginPage.logout()
await browser.refresh()
describe('Direct registration', function () {
expect(signupPage.getRegisterMenuButton()).not.toBeDisplayed()
})
it('Should enable signup without approval', async () => {
await prepareSignup({ enabled: true, requiresApproval: false, requiresEmailVerification: false })
it('Should enable signup', async () => {
await loginPage.loginAsRootUser()
await signupPage.getRegisterMenuButton().waitForDisplayed()
})
await adminConfigPage.navigateTo('basic-configuration')
await adminConfigPage.toggleSignup()
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})
await adminConfigPage.save()
it('Should validate the first step (about page)', async function () {
await signupPage.validateStep()
})
await loginPage.logout()
await browser.refresh()
it('Should validate the second step (terms)', async function () {
await signupPage.checkTerms()
await signupPage.validateStep()
})
expect(signupPage.getRegisterMenuButton()).toBeDisplayed()
})
it('Should validate the third step (account)', async function () {
await signupPage.fillAccountStep({ username: 'user_1', displayName: 'user_1_dn' })
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})
await signupPage.validateStep()
})
it('Should validate the first step (about page)', async function () {
await signupPage.validateStep()
})
it('Should validate the third step (channel)', async function () {
await signupPage.fillChannelStep({ name: 'user_1_channel' })
it('Should validate the second step (terms)', async function () {
await signupPage.checkTerms()
await signupPage.validateStep()
})
await signupPage.validateStep()
})
it('Should be logged in', async function () {
await loginPage.ensureIsLoggedInAs('user_1_dn')
})
it('Should have a valid end message', async function () {
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: false,
requiresApproval: false,
afterEmailVerification: false
})
it('Should validate the third step (account)', async function () {
await signupPage.fillAccountStep({
displayName: 'user 1',
username: 'user_1',
email: 'user_1@example.com',
password: 'my_super_password'
await browser.saveScreenshot('./screenshots/direct-without-email.png')
await loginPage.logout()
})
})
await signupPage.validateStep()
describe('Registration with approval', function () {
it('Should enable signup with approval', async () => {
await prepareSignup({ enabled: true, requiresApproval: true, requiresEmailVerification: false })
await signupPage.getRegisterMenuButton().waitForDisplayed()
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})
it('Should validate the first step (about page)', async function () {
await signupPage.validateStep()
})
it('Should validate the second step (terms)', async function () {
await signupPage.checkTerms()
await signupPage.fillRegistrationReason('my super reason')
await signupPage.validateStep()
})
it('Should validate the third step (account)', async function () {
await signupPage.fillAccountStep({ username: 'user_2', displayName: 'user_2 display name', password: 'password' })
await signupPage.validateStep()
})
it('Should validate the third step (channel)', async function () {
await signupPage.fillChannelStep({ name: 'user_2_channel' })
await signupPage.validateStep()
})
it('Should have a valid end message', async function () {
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: false,
requiresApproval: true,
afterEmailVerification: false
})
await browser.saveScreenshot('./screenshots/request-without-email.png')
})
it('Should display a message when trying to login with this account', async function () {
const error = await loginPage.getLoginError('user_2', 'password')
expect(error).toContain('awaiting approval')
})
it('Should accept the registration', async function () {
await loginPage.loginAsRootUser()
await adminRegistrationPage.navigateToRegistratonsList()
await adminRegistrationPage.accept('user_2', 'moderation response')
await loginPage.logout()
})
it('Should be able to login with this new account', async function () {
await loginPage.login({ username: 'user_2', password: 'password', displayName: 'user_2 display name' })
await loginPage.logout()
})
})
})
it('Should validate the third step (channel)', async function () {
await signupPage.fillChannelStep({
displayName: 'user 1 channel',
name: 'user_1_channel'
describe('Email verification enabled', function () {
const emails: any[] = []
let emailPort: number
before(async () => {
// FIXME: typings are wrong, get returns a promise
emailPort = await browser.sharedStore.get('emailPort') as unknown as number
MockSMTPServer.Instance.collectEmails(emailPort, emails)
})
await signupPage.validateStep()
})
describe('Direct registration', function () {
it('Should enable signup without approval', async () => {
await prepareSignup({ enabled: true, requiresApproval: false, requiresEmailVerification: true })
await signupPage.getRegisterMenuButton().waitForDisplayed()
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})
it('Should validate the first step (about page)', async function () {
await signupPage.validateStep()
})
it('Should validate the second step (terms)', async function () {
await signupPage.checkTerms()
await signupPage.validateStep()
})
it('Should validate the third step (account)', async function () {
await signupPage.fillAccountStep({ username: 'user_3', displayName: 'user_3 display name', email: 'user_3@example.com' })
await signupPage.validateStep()
})
it('Should validate the third step (channel)', async function () {
await signupPage.fillChannelStep({ name: 'user_3_channel' })
await signupPage.validateStep()
})
it('Should have a valid end message', async function () {
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: true,
requiresApproval: false,
afterEmailVerification: false
})
await browser.saveScreenshot('./screenshots/direct-with-email.png')
})
it('Should validate the email', async function () {
let email: { text: string }
while (!(email = findEmailTo(emails, 'user_3@example.com'))) {
await browserSleep(100)
}
await go(getVerificationLink(email))
const message = await signupPage.getEndMessage()
checkEndMessage({
message,
requiresEmailVerification: false,
requiresApproval: false,
afterEmailVerification: true
})
it('Should be logged in', async function () {
await loginPage.ensureIsLoggedInAs('user 1')
await browser.saveScreenshot('./screenshots/direct-after-email.png')
})
})
describe('Registration with approval', function () {
it('Should enable signup without approval', async () => {
await prepareSignup({ enabled: true, requiresApproval: true, requiresEmailVerification: true })
await signupPage.getRegisterMenuButton().waitForDisplayed()
})
it('Should go on signup page', async function () {
await signupPage.clickOnRegisterInMenu()
})