Migrate more tests to mocha.

This commit is contained in:
Clement Michaud 2019-02-13 23:04:57 +01:00
parent 7c2fd91271
commit c487ed0a37
23 changed files with 213 additions and 99 deletions

View File

@ -110,6 +110,7 @@
"should": "^13.2.1", "should": "^13.2.1",
"sinon": "^5.0.7", "sinon": "^5.0.7",
"tmp": "0.0.33", "tmp": "0.0.33",
"tree-kill": "^1.2.1",
"ts-node": "^6.0.1", "ts-node": "^6.0.1",
"tslint": "^5.2.0", "tslint": "^5.2.0",
"typescript": "^2.9.2", "typescript": "^2.9.2",

View File

@ -4,6 +4,7 @@ var program = require('commander');
var spawn = require('child_process').spawn; var spawn = require('child_process').spawn;
var chokidar = require('chokidar'); var chokidar = require('chokidar');
var fs = require('fs'); var fs = require('fs');
var kill = require('tree-kill');
program program
.option('-s, --suite <suite>', 'The suite to run Authelia for. This suite represents a configuration for Authelia and a set of tests for that configuration.') .option('-s, --suite <suite>', 'The suite to run Authelia for. This suite represents a configuration for Authelia and a set of tests for that configuration.')
@ -14,17 +15,18 @@ if (!program.suite) {
} }
const ENVIRONMENT_FILENAME = '.suite'; const ENVIRONMENT_FILENAME = '.suite';
const AUTHELIA_INTERRUPT_FILENAME = '.authelia-interrupt';
var tsWatcher = chokidar.watch(['server', 'shared/**/*.ts', 'node_modules'], { var tsWatcher = chokidar.watch(['server', 'shared/**/*.ts', 'node_modules', AUTHELIA_INTERRUPT_FILENAME], {
persistent: true, persistent: true,
ignoreInitial: true, ignoreInitial: true,
}); });
// Properly cleanup server and client if ctrl-c is hit // Properly cleanup server and client if ctrl-c is hit
process.on('SIGINT', function() { process.on('SIGINT', function() {
killServer(() => {}); killServer();
killClient(() => {}); killClient();
fs.unlinkSync(ENVIRONMENT_FILENAME); fs.unlinkSync(ENVIRONMENT_FILENAME);
process.exit(); process.exit();
}); });
@ -38,7 +40,11 @@ function reloadServer() {
} }
function startServer() { function startServer() {
serverProcess = spawn('./scripts/run-dev-server.sh', [`test/suites/${program.suite}/config.yml`], {detached: true}); if (fs.existsSync(AUTHELIA_INTERRUPT_FILENAME)) {
console.log('Authelia is interrupted. Consider removing ' + AUTHELIA_INTERRUPT_FILENAME + ' if it\'s not expected.');
return;
}
serverProcess = spawn('./scripts/run-dev-server.sh', [`test/suites/${program.suite}/config.yml`]);
serverProcess.stdout.pipe(process.stdout); serverProcess.stdout.pipe(process.stdout);
serverProcess.stderr.pipe(process.stderr); serverProcess.stderr.pipe(process.stderr);
} }
@ -46,7 +52,6 @@ function startServer() {
let clientProcess; let clientProcess;
function startClient() { function startClient() {
clientProcess = spawn('npm', ['run', 'start'], { clientProcess = spawn('npm', ['run', 'start'], {
detached: true,
cwd: './client', cwd: './client',
env: { env: {
...process.env, ...process.env,
@ -61,14 +66,16 @@ function killServer(onExit) {
if (serverProcess) { if (serverProcess) {
serverProcess.on('exit', () => { serverProcess.on('exit', () => {
serverProcess = undefined; serverProcess = undefined;
onExit(); if (onExit) onExit();
}); });
try { try {
process.kill(-serverProcess.pid); kill(serverProcess.pid, 'SIGKILL');
} catch (e) { } catch (e) {
console.error(e); console.error(e);
onExit(); if (onExit) onExit();
} }
} else {
if (onExit) onExit();
} }
} }
@ -76,14 +83,16 @@ function killClient(onExit) {
if (clientProcess) { if (clientProcess) {
clientProcess.on('exit', () => { clientProcess.on('exit', () => {
clientProcess = undefined; clientProcess = undefined;
onExit(); if (onExit) onExit();
}); });
try { try {
process.kill(-clientProcess.pid); kill(clientProcess.pid, 'SIGKILL');
} catch (e) { } catch (e) {
console.error(e); console.error(e);
onExit(); if (onExit) onExit();
} }
} else {
if (onExit) onExit();
} }
} }
@ -97,6 +106,16 @@ function reload(path) {
console.log('Schema needs to be regenerated.'); console.log('Schema needs to be regenerated.');
generateConfigurationSchema(); generateConfigurationSchema();
} }
else if (path === AUTHELIA_INTERRUPT_FILENAME) {
if (fs.existsSync(path)) {
console.log('Authelia is being interrupted.');
killServer();
} else {
console.log('Authelia is restarting.');
startServer();
}
return;
}
reloadServer(); reloadServer();
} }
@ -128,7 +147,7 @@ async function main() {
console.log('Start watching...'); console.log('Start watching...');
tsWatcher.on('add', reload); tsWatcher.on('add', reload);
tsWatcher.on('remove', reload); tsWatcher.on('unlink', reload);
tsWatcher.on('change', reload); tsWatcher.on('change', reload);
startServer(); startServer();

View File

@ -0,0 +1,11 @@
// Error thrown when the authentication failed when checking
// user/password.
class AuthenticationError extends Error {
constructor(msg: string) {
super(msg);
}
}
export default AuthenticationError

View File

@ -8,6 +8,7 @@ import { GroupsAndEmails } from "../GroupsAndEmails";
import { IUsersDatabase } from "../IUsersDatabase"; import { IUsersDatabase } from "../IUsersDatabase";
import { HashGenerator } from "../../../utils/HashGenerator"; import { HashGenerator } from "../../../utils/HashGenerator";
import { ReadWriteQueue } from "./ReadWriteQueue"; import { ReadWriteQueue } from "./ReadWriteQueue";
import AuthenticationError from "../../AuthenticationError";
const loadAsync = Bluebird.promisify(Yaml.load); const loadAsync = Bluebird.promisify(Yaml.load);
@ -80,7 +81,7 @@ export class FileUsersDatabase implements IUsersDatabase {
return HashGenerator.ssha512(password, rounds, salt) return HashGenerator.ssha512(password, rounds, salt)
.then((hash: string) => { .then((hash: string) => {
if (hash !== storedHash) { if (hash !== storedHash) {
return Bluebird.reject(new Error("Wrong username/password.")); return Bluebird.reject(new AuthenticationError("Wrong username/password."));
} }
return Bluebird.resolve(); return Bluebird.resolve();
}); });

View File

@ -5,6 +5,7 @@ import { LdapConfiguration } from "../../../configuration/schema/LdapConfigurati
import { ISession } from "./ISession"; import { ISession } from "./ISession";
import { GroupsAndEmails } from "../GroupsAndEmails"; import { GroupsAndEmails } from "../GroupsAndEmails";
import Exceptions = require("../../../Exceptions"); import Exceptions = require("../../../Exceptions");
import AuthenticationError from "../../AuthenticationError";
type SessionCallback<T> = (session: ISession) => Bluebird<T>; type SessionCallback<T> = (session: ISession) => Bluebird<T>;
@ -58,7 +59,7 @@ export class LdapUsersDatabase implements IUsersDatabase {
.then(() => getInfo(session)); .then(() => getInfo(session));
}) })
.catch((err) => .catch((err) =>
Bluebird.reject(new Exceptions.LdapError(err.message))); Bluebird.reject(new AuthenticationError(err.message)));
} }
getEmails(username: string): Bluebird<string[]> { getEmails(username: string): Bluebird<string[]> {

View File

@ -15,6 +15,7 @@ import { BelongToDomain } from "../../../../../shared/BelongToDomain";
import { URLDecomposer } from "../..//utils/URLDecomposer"; import { URLDecomposer } from "../..//utils/URLDecomposer";
import { Object } from "../../../lib/authorization/Object"; import { Object } from "../../../lib/authorization/Object";
import { Subject } from "../../../lib/authorization/Subject"; import { Subject } from "../../../lib/authorization/Subject";
import AuthenticationError from "../../../lib/authentication/AuthenticationError";
export default function (vars: ServerVariables) { export default function (vars: ServerVariables) {
return function (req: express.Request, res: express.Response) return function (req: express.Request, res: express.Response)
@ -95,7 +96,7 @@ export default function (vars: ServerVariables) {
res.send(); res.send();
return BluebirdPromise.resolve(); return BluebirdPromise.resolve();
}) })
.catch(Exceptions.LdapBindError, function (err: Error) { .catch(AuthenticationError, function (err: Error) {
vars.regulator.mark(username, false); vars.regulator.mark(username, false);
return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.AUTHENTICATION_FAILED)(err); return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.AUTHENTICATION_FAILED)(err);
}) })

View File

@ -1,39 +0,0 @@
@needs-regulation-config
Feature: Authelia regulates authentication to avoid brute force
@need-registered-user-blackhat
Scenario: Attacker tries too many authentication in a short period of time and get banned
Given I visit "https://login.example.com:8080/"
And I set field "username" to "blackhat"
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
When I set field "password" to "password"
And I click on "Sign in"
Then I get a notification of type "error" with message "Authentication failed. Please check your credentials."
@need-registered-user-blackhat
Scenario: User is unbanned after a configured amount of time
Given I visit "https://login.example.com:8080/?rd=https://public.example.com:8080/secret.html"
And I set field "username" to "blackhat"
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
When I wait 6 seconds
And I set field "password" to "password"
And I click on "Sign in"
And I use "REGISTERED" as TOTP token handle
And I click on "Sign in"
Then I'm redirected to "https://public.example.com:8080/secret.html"

View File

@ -1,15 +0,0 @@
Feature: Authelia keeps user sessions despite the application restart
@need-authenticated-user-john
Scenario: Session is still valid after Authelia restarts
When the application restarts
Then I have access to "https://admin.example.com:8080/secret.html"
@need-registered-user-john
Scenario: Secrets are stored even when Authelia restarts
When the application restarts
And I visit "https://admin.example.com:8080/secret.html" and get redirected "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"
And I login with user "john" and password "password"
And I use "REGISTERED" as TOTP token handle
And I click on "Sign in"
Then I'm redirected to "https://admin.example.com:8080/secret.html"

View File

@ -1,16 +0,0 @@
Feature: User can access certain subdomains with single factor
Scenario: User is redirected to service after first factor if allowed
When I visit "https://login.example.com:8080/?rd=https://single_factor.example.com:8080/secret.html"
And I login with user "john" and password "password"
Then I'm redirected to "https://single_factor.example.com:8080/secret.html"
Scenario: Redirection after first factor fails if single_factor not allowed. It redirects user to first factor.
When I visit "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"
And I login with user "john" and password "password"
Then I'm redirected to "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"
Scenario: User can login using basic authentication
When I request "https://single_factor.example.com:8080/secret.html" with username "john" and password "password" using basic authentication
Then I receive the secret page

View File

@ -1,7 +1,7 @@
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver"; import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
import Assert = require("assert"); import Assert = require("assert");
export default async function(driver: WebDriver, type: string, message: string) { export default async function(driver: WebDriver, message: string) {
await driver.wait(SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.className("notification")), 5000) await driver.wait(SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.className("notification")), 5000)
const notificationEl = driver.findElement(SeleniumWebdriver.By.className("notification")); const notificationEl = driver.findElement(SeleniumWebdriver.By.className("notification"));
const txt = await notificationEl.getText(); const txt = await notificationEl.getText();

View File

@ -0,0 +1,9 @@
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
import Assert = require("assert");
export default async function(driver: WebDriver, message: string) {
await driver.wait(SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.className("notification")), 5000)
const notificationEl = driver.findElement(SeleniumWebdriver.By.className("notification"));
const txt = await notificationEl.getText();
Assert.equal(message, txt);
}

View File

@ -5,8 +5,7 @@ import { StatusCodeError } from 'request-promise/errors';
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
// Sent a GET request to the url and expect a 401 export async function GET_ExpectError(url: string, statusCode: number) {
export async function GET_Expect401(url: string) {
try { try {
await Request.get(url, { await Request.get(url, {
json: true, json: true,
@ -15,13 +14,22 @@ export async function GET_Expect401(url: string) {
throw new Error('No response'); throw new Error('No response');
} catch (e) { } catch (e) {
if (e instanceof StatusCodeError) { if (e instanceof StatusCodeError) {
Assert.equal(e.statusCode, 401); Assert.equal(e.statusCode, statusCode);
return; return;
} }
} }
return; return;
} }
// Sent a GET request to the url and expect a 401
export async function GET_Expect401(url: string) {
return await GET_ExpectError(url, 401);
}
export async function GET_Expect502(url: string) {
return await GET_ExpectError(url, 502);
}
export async function POST_Expect401(url: string, body?: any) { export async function POST_Expect401(url: string, body?: any) {
try { try {
await Request.post(url, { await Request.post(url, {

View File

@ -213,10 +213,10 @@ regulation:
# The time range during which the user can attempt login before being banned. # The time range during which the user can attempt login before being banned.
# The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window.
find_time: 120 find_time: 15
# The length of time before a banned user can login again. # The length of time before a banned user can login again.
ban_time: 300 ban_time: 5
# Configuration of the storage backend used to store data and secrets. # Configuration of the storage backend used to store data and secrets.
# #

View File

@ -5,6 +5,8 @@ import AccessControl from "./scenarii/AccessControl";
import CustomHeadersForwarded from "./scenarii/CustomHeadersForwarded"; import CustomHeadersForwarded from "./scenarii/CustomHeadersForwarded";
import SingleFactorAuthentication from "./scenarii/SingleFactorAuthentication"; import SingleFactorAuthentication from "./scenarii/SingleFactorAuthentication";
import BasicAuthentication from "./scenarii/BasicAuthentication"; import BasicAuthentication from "./scenarii/BasicAuthentication";
import AutheliaRestart from "./scenarii/AutheliaRestart";
import AuthenticationRegulation from "./scenarii/AuthenticationRegulation";
AutheliaSuite('Complete configuration', __dirname + '/config.yml', function() { AutheliaSuite('Complete configuration', __dirname + '/config.yml', function() {
this.timeout(10000); this.timeout(10000);
@ -16,4 +18,6 @@ AutheliaSuite('Complete configuration', __dirname + '/config.yml', function() {
describe('Enforce internal redirections only', EnforceInternalRedirectionsOnly); describe('Enforce internal redirections only', EnforceInternalRedirectionsOnly);
describe('Single factor authentication', SingleFactorAuthentication); describe('Single factor authentication', SingleFactorAuthentication);
describe('Basic authentication', BasicAuthentication); describe('Basic authentication', BasicAuthentication);
describe('Authelia restart', AutheliaRestart);
describe('Authentication regulation', AuthenticationRegulation);
}); });

View File

@ -0,0 +1,72 @@
import Logout from "../../../helpers/Logout";
import ChildProcess from 'child_process';
import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver";
import VerifySecretObserved from "../../../helpers/assertions/VerifySecretObserved";
import RegisterAndLoginTwoFactor from "../../../helpers/behaviors/RegisterAndLoginTwoFactor";
import VisitPageAndWaitUrlIs from "../../../helpers/behaviors/VisitPageAndWaitUrlIs";
import { GET_Expect502 } from "../../../helpers/utils/Requests";
import LoginAndRegisterTotp from "../../../helpers/LoginAndRegisterTotp";
import FullLogin from "../../../helpers/FullLogin";
export default function() {
describe('Session is still valid after Authelia restarts', function() {
before(async function() {
// Be sure to start fresh
ChildProcess.execSync('rm -f .authelia-interrupt');
this.driver = await StartDriver();
await RegisterAndLoginTwoFactor(this.driver, 'john', true, 'https://admin.example.com:8080/secret.html');
await VisitPageAndWaitUrlIs(this.driver, 'https://home.example.com:8080/');
});
after(async function() {
await Logout(this.driver);
await StopDriver(this.driver);
// Be sure to cleanup
ChildProcess.execSync('rm -f .authelia-interrupt');
});
it("should still access the secret after Authelia restarted", async function() {
ChildProcess.execSync('touch .authelia-interrupt');
await GET_Expect502('https://login.example.com:8080/api/state');
await this.driver.sleep(1000);
ChildProcess.execSync('rm .authelia-interrupt');
await this.driver.sleep(1000);
await VisitPageAndWaitUrlIs(this.driver, 'https://admin.example.com:8080/secret.html');
await VerifySecretObserved(this.driver);
});
});
describe('Secrets are persisted even if Authelia restarts', function() {
before(async function() {
// Be sure to start fresh
ChildProcess.execSync('rm -f .authelia-interrupt');
this.driver = await StartDriver();
this.secret = await LoginAndRegisterTotp(this.driver, 'john', true);
await Logout(this.driver);
});
after(async function() {
await Logout(this.driver);
await StopDriver(this.driver);
// Be sure to cleanup
ChildProcess.execSync('rm -f .authelia-interrupt');
});
it("should still access the secret after Authelia restarted", async function() {
ChildProcess.execSync('touch .authelia-interrupt');
await GET_Expect502('https://login.example.com:8080/api/state');
await this.driver.sleep(1000);
ChildProcess.execSync('rm .authelia-interrupt');
await this.driver.sleep(1000);
// The user can re-authenticate with the secret.
await FullLogin(this.driver, 'john', this.secret, 'https://admin.example.com:8080/secret.html')
});
});
}

View File

@ -0,0 +1,53 @@
import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver";
import LoginAs from "../../../helpers/LoginAs";
import VerifyNotificationDisplayed from "../../../helpers/assertions/VerifyNotificationDisplayed";
import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage";
/*
Given I visit "https://login.example.com:8080/"
And I set field "username" to "blackhat"
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
And I set field "password" to "bad-password"
And I click on "Sign in"
And I get a notification of type "error" with message "Authentication failed. Please check your credentials."
When I set field "password" to "password"
And I click on "Sign in"
Then I get a notification of type "error" with message "Authentication failed. Please check your credentials."
*/
export default function() {
describe('Authelia regulates authentications when a hacker is brute forcing', function() {
this.timeout(15000);
before(async function() {
this.driver = await StartDriver();
});
after(async function() {
await StopDriver(this.driver);
});
it("should return an error message when providing correct credentials the 4th time.", async function() {
await LoginAs(this.driver, "blackhat", "bad-password");
await VerifyNotificationDisplayed(this.driver, "Authentication failed. Please check your credentials.");
await LoginAs(this.driver, "blackhat", "bad-password");
await VerifyNotificationDisplayed(this.driver, "Authentication failed. Please check your credentials.");
await LoginAs(this.driver, "blackhat", "bad-password");
await VerifyNotificationDisplayed(this.driver, "Authentication failed. Please check your credentials.");
// when providing good credentials, the hacker is regulated and see same message as previously.
await LoginAs(this.driver, "blackhat", "password");
await VerifyNotificationDisplayed(this.driver, "Authentication failed. Please check your credentials.");
// Wait the regulation ban time before retrying with correct credentials.
// It should authenticate normally.
await this.driver.sleep(6000);
await LoginAs(this.driver, "blackhat", "password");
await VerifyIsSecondFactorStage(this.driver);
});
});
}

View File

@ -6,6 +6,9 @@ import VisitPage from "../../../helpers/VisitPage";
import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs"; import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs";
import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage";
/*
* Those tests are related to single factor protected resources.
*/
export default function() { export default function() {
beforeEach(async function() { beforeEach(async function() {
this.driver = await StartDriver(); this.driver = await StartDriver();

View File

@ -73,10 +73,10 @@ regulation:
max_retries: 3 max_retries: 3
# The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window. # The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window.
find_time: 120 find_time: 10
# The length of time before a banned user can login again. # The length of time before a banned user can login again.
ban_time: 300 ban_time: 5
# Default redirection URL # Default redirection URL
# #

View File

@ -17,7 +17,7 @@ export default function() {
it('should get a notification message', async function () { it('should get a notification message', async function () {
this.timeout(10000); this.timeout(10000);
await SeeNotification(this.driver, "error", AUTHENTICATION_FAILED); await SeeNotification(this.driver, AUTHENTICATION_FAILED);
}); });
}); });
} }

View File

@ -4,6 +4,8 @@ import ValidateTotp from "../../../helpers/ValidateTotp";
import WaitRedirected from "../../../helpers/WaitRedirected"; import WaitRedirected from "../../../helpers/WaitRedirected";
import { WebDriver } from "selenium-webdriver"; import { WebDriver } from "selenium-webdriver";
import VisitPageAndWaitUrlIs from "../../../helpers/behaviors/VisitPageAndWaitUrlIs"; import VisitPageAndWaitUrlIs from "../../../helpers/behaviors/VisitPageAndWaitUrlIs";
import VisitPage from "../../../helpers/VisitPage";
import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs";
export default function(this: Mocha.ISuiteCallbackContext) { export default function(this: Mocha.ISuiteCallbackContext) {
this.timeout(20000); this.timeout(20000);
@ -52,8 +54,8 @@ export default function(this: Mocha.ISuiteCallbackContext) {
await WaitRedirected(driver, "https://admin.example.com:8080/secret.html"); await WaitRedirected(driver, "https://admin.example.com:8080/secret.html");
await VisitPageAndWaitUrlIs(driver, "https://home.example.com:8080/"); await VisitPageAndWaitUrlIs(driver, "https://home.example.com:8080/");
await driver.sleep(6000); await driver.sleep(6000);
await driver.get("https://admin.example.com:8080/secret.html"); await VisitPage(driver, "https://admin.example.com:8080/secret.html");
await WaitRedirected(driver, "https://admin.example.com:8080/secret.html"); await VerifyUrlIs(driver, "https://admin.example.com:8080/secret.html");
}); });
}); });
} }

View File

@ -29,7 +29,6 @@ export default function() {
}); });
it("should have user and issuer in otp url", async function() { it("should have user and issuer in otp url", async function() {
// this.timeout(100000);
const el = await (this.driver as WebDriver).wait( const el = await (this.driver as WebDriver).wait(
SeleniumWebdriver.until.elementLocated( SeleniumWebdriver.until.elementLocated(
SeleniumWebdriver.By.className('otpauth-secret')), 5000); SeleniumWebdriver.By.className('otpauth-secret')), 5000);

View File

@ -57,6 +57,6 @@ export default function() {
await FillField(this.driver, "password1", "newpass"); await FillField(this.driver, "password1", "newpass");
await FillField(this.driver, "password2", "badpass"); await FillField(this.driver, "password2", "badpass");
await ClickOn(this.driver, SeleniumWebDriver.By.id('reset-button')); await ClickOn(this.driver, SeleniumWebDriver.By.id('reset-button'));
await SeeNotification(this.driver, "error", "The passwords are different."); await SeeNotification(this.driver, "The passwords are different.");
}); });
} }

View File

@ -45,7 +45,7 @@ export default function() {
}); });
it("get a notification message", async function() { it("get a notification message", async function() {
await SeeNotification(this.driver, "error", AUTHENTICATION_TOTP_FAILED); await SeeNotification(this.driver, AUTHENTICATION_TOTP_FAILED);
}); });
}); });
} }