From 5bdfa604f102a5e51b5152457cd1a16d79177e26 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 19 Jan 2023 09:30:05 +0100 Subject: Add E2E client tests for signup approval --- client/e2e/src/po/admin-config.po.ts | 27 +- client/e2e/src/po/admin-registration.po.ts | 35 +++ client/e2e/src/po/login.po.ts | 36 ++- client/e2e/src/po/signup.po.ts | 51 ++-- client/e2e/src/suites-local/signup.e2e-spec.ts | 403 ++++++++++++++++++++++--- client/e2e/src/utils/elements.ts | 17 +- client/e2e/src/utils/email.ts | 31 ++ client/e2e/src/utils/hooks.ts | 24 +- client/e2e/src/utils/index.ts | 2 + client/e2e/src/utils/mock-smtp.ts | 58 ++++ client/e2e/src/utils/server.ts | 4 +- 11 files changed, 598 insertions(+), 90 deletions(-) create mode 100644 client/e2e/src/po/admin-registration.po.ts create mode 100644 client/e2e/src/utils/email.ts create mode 100644 client/e2e/src/utils/mock-smtp.ts (limited to 'client/e2e/src') diff --git a/client/e2e/src/po/admin-config.po.ts b/client/e2e/src/po/admin-config.po.ts index 27957a71f..510037ddd 100644 --- a/client/e2e/src/po/admin-config.po.ts +++ b/client/e2e/src/po/admin-config.po.ts @@ -1,4 +1,4 @@ -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) } } diff --git a/client/e2e/src/po/admin-registration.po.ts b/client/e2e/src/po/admin-registration.po.ts new file mode 100644 index 000000000..85234654d --- /dev/null +++ b/client/e2e/src/po/admin-registration.po.ts @@ -0,0 +1,35 @@ +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) + } + +} diff --git a/client/e2e/src/po/login.po.ts b/client/e2e/src/po/login.po.ts index bc1854dbc..f1d13a2b0 100644 --- a/client/e2e/src/po/login.po.ts +++ b/client/e2e/src/po/login.po.ts @@ -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') diff --git a/client/e2e/src/po/signup.po.ts b/client/e2e/src/po/signup.po.ts index cc2ed7c5f..7917cdda7 100644 --- a/client/e2e/src/po/signup.po.ts +++ b/client/e2e/src/po/signup.po.ts @@ -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) } } diff --git a/client/e2e/src/suites-local/signup.e2e-spec.ts b/client/e2e/src/suites-local/signup.e2e-spec.ts index 4eed3eefe..b6f7ad1a7 100644 --- a/client/e2e/src/suites-local/signup.e2e-spec.ts +++ b/client/e2e/src/suites-local/signup.e2e-spec.ts @@ -1,12 +1,89 @@ 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() + }) + + 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 2') + await signupPage.validateStep() + }) + + it('Should validate the third step (account)', async function () { + await signupPage.fillAccountStep({ + username: 'user_4', + displayName: 'user_4 display name', + email: 'user_4@example.com', + password: 'password' + }) + await signupPage.validateStep() + }) + + it('Should validate the third step (channel)', async function () { + await signupPage.fillChannelStep({ name: 'user_4_channel' }) + await signupPage.validateStep() + }) + + it('Should have a valid end message', async function () { + const message = await signupPage.getEndMessage() + + checkEndMessage({ + message, + requiresEmailVerification: true, + requiresApproval: true, + afterEmailVerification: false + }) + + await browser.saveScreenshot('./screenshots/request-with-email.png') + }) + + it('Should display a message when trying to login with this account', async function () { + const error = await loginPage.getLoginError('user_4', 'password') + + expect(error).toContain('awaiting approval') + }) + + it('Should accept the registration', async function () { + await loginPage.loginAsRootUser() + + await adminRegistrationPage.navigateToRegistratonsList() + await adminRegistrationPage.accept('user_4', 'moderation response 2') + + await loginPage.logout() + }) + + it('Should validate the email', async function () { + let email: { text: string } + + while (!(email = findEmailTo(emails, 'user_4@example.com'))) { + await browserSleep(100) + } + + await go(getVerificationLink(email)) + + const message = await signupPage.getEndMessage() + + checkEndMessage({ + message, + requiresEmailVerification: false, + requiresApproval: true, + afterEmailVerification: true + }) + + await browser.saveScreenshot('./screenshots/request-after-email.png') + }) + }) + + before(() => { + MockSMTPServer.Instance.kill() + }) }) }) diff --git a/client/e2e/src/utils/elements.ts b/client/e2e/src/utils/elements.ts index b0ddd5a65..d9435e520 100644 --- a/client/e2e/src/utils/elements.ts +++ b/client/e2e/src/utils/elements.ts @@ -5,6 +5,10 @@ async function getCheckbox (name: string) { return input.parentElement() } +function isCheckboxSelected (name: string) { + return $(`input[id=${name}]`).isSelected() +} + async function selectCustomSelect (id: string, valueLabel: string) { const wrapper = $(`[formcontrolname=${id}] .ng-arrow-wrapper`) @@ -22,7 +26,18 @@ async function selectCustomSelect (id: string, valueLabel: string) { return option.click() } +async function findParentElement ( + el: WebdriverIO.Element, + finder: (el: WebdriverIO.Element) => Promise +) { + if (await finder(el) === true) return el + + return findParentElement(await el.parentElement(), finder) +} + export { getCheckbox, - selectCustomSelect + isCheckboxSelected, + selectCustomSelect, + findParentElement } diff --git a/client/e2e/src/utils/email.ts b/client/e2e/src/utils/email.ts new file mode 100644 index 000000000..2ad120333 --- /dev/null +++ b/client/e2e/src/utils/email.ts @@ -0,0 +1,31 @@ +function getVerificationLink (email: { text: string }) { + const { text } = email + + const regexp = /\[(?http:\/\/[^\]]+)\]/g + const matched = text.matchAll(regexp) + + if (!matched) throw new Error('Could not find verification link in email') + + for (const match of matched) { + const link = match.groups.link + + if (link.includes('/verify-account/')) return link + } + + throw new Error('Could not find /verify-account/ link') +} + +function findEmailTo (emails: { text: string, to: { address: string }[] }[], to: string) { + for (const email of emails) { + for (const { address } of email.to) { + if (address === to) return email + } + } + + return undefined +} + +export { + getVerificationLink, + findEmailTo +} diff --git a/client/e2e/src/utils/hooks.ts b/client/e2e/src/utils/hooks.ts index 889cf1d86..7fe247681 100644 --- a/client/e2e/src/utils/hooks.ts +++ b/client/e2e/src/utils/hooks.ts @@ -1,10 +1,13 @@ import { ChildProcessWithoutNullStreams } from 'child_process' import { basename } from 'path' import { runCommand, runServer } from './server' +import { setValue } from '@wdio/shared-store-service' -let appInstance: string +let appInstance: number let app: ChildProcessWithoutNullStreams +let emailPort: number + async function beforeLocalSuite (suite: any) { const config = buildConfig(suite.file) @@ -17,13 +20,20 @@ function afterLocalSuite () { app = undefined } -function beforeLocalSession (config: { baseUrl: string }, capabilities: { browserName: string }) { - appInstance = capabilities['browserName'] === 'chrome' ? '1' : '2' +async function beforeLocalSession (config: { baseUrl: string }, capabilities: { browserName: string }) { + appInstance = capabilities['browserName'] === 'chrome' + ? 1 + : 2 + + emailPort = 1025 + appInstance + config.baseUrl = 'http://localhost:900' + appInstance + + await setValue('emailPort', emailPort) } async function onBrowserStackPrepare () { - const appInstance = '1' + const appInstance = 1 await runCommand('npm run clean:server:test -- ' + appInstance) app = runServer(appInstance) @@ -71,7 +81,11 @@ function buildConfig (suiteFile: string = undefined) { if (filename === 'signup.e2e-spec.ts') { return { signup: { - enabled: true + limit: -1 + }, + smtp: { + hostname: '127.0.0.1', + port: emailPort } } } diff --git a/client/e2e/src/utils/index.ts b/client/e2e/src/utils/index.ts index 354352ee2..420fd239e 100644 --- a/client/e2e/src/utils/index.ts +++ b/client/e2e/src/utils/index.ts @@ -1,5 +1,7 @@ export * from './common' export * from './elements' +export * from './email' export * from './hooks' +export * from './mock-smtp' export * from './server' export * from './urls' diff --git a/client/e2e/src/utils/mock-smtp.ts b/client/e2e/src/utils/mock-smtp.ts new file mode 100644 index 000000000..614477d7d --- /dev/null +++ b/client/e2e/src/utils/mock-smtp.ts @@ -0,0 +1,58 @@ +import { ChildProcess } from 'child_process' +import MailDev from '@peertube/maildev' + +class MockSMTPServer { + + private static instance: MockSMTPServer + private started = false + private emailChildProcess: ChildProcess + private emails: object[] + + collectEmails (port: number, emailsCollection: object[]) { + return new Promise((res, rej) => { + this.emails = emailsCollection + + if (this.started) { + return res(undefined) + } + + const maildev = new MailDev({ + ip: '127.0.0.1', + smtp: port, + disableWeb: true, + silent: true + }) + + maildev.on('new', email => { + this.emails.push(email) + }) + + maildev.listen(err => { + if (err) return rej(err) + + this.started = true + + return res(port) + }) + }) + } + + kill () { + if (!this.emailChildProcess) return + + process.kill(this.emailChildProcess.pid) + + this.emailChildProcess = null + MockSMTPServer.instance = null + } + + static get Instance () { + return this.instance || (this.instance = new this()) + } +} + +// --------------------------------------------------------------------------- + +export { + MockSMTPServer +} diff --git a/client/e2e/src/utils/server.ts b/client/e2e/src/utils/server.ts index 140054794..227f4aea6 100644 --- a/client/e2e/src/utils/server.ts +++ b/client/e2e/src/utils/server.ts @@ -1,10 +1,10 @@ import { exec, spawn } from 'child_process' import { join, resolve } from 'path' -function runServer (appInstance: string, config: any = {}) { +function runServer (appInstance: number, config: any = {}) { const env = Object.create(process.env) env['NODE_ENV'] = 'test' - env['NODE_APP_INSTANCE'] = appInstance + env['NODE_APP_INSTANCE'] = appInstance + '' env['NODE_CONFIG'] = JSON.stringify({ rates_limit: { -- cgit v1.2.3