diff --git a/Gruntfile.js b/Gruntfile.js index de98367a..eba59579 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -492,7 +492,12 @@ module.exports = function (grunt) { keepAlive: false, // If true, protractor will not use colors in its output noColor: false, - args: {} + args: ( + ( + process.env.PROTRACTOR_ARGS && + JSON.parse(process.env.PROTRACTOR_ARGS) + ) || {} + ) }, all: { options: { diff --git a/avAdmin/admin-controller/admin-controller.js b/avAdmin/admin-controller/admin-controller.js index 6a90515c..68703a2c 100644 --- a/avAdmin/admin-controller/admin-controller.js +++ b/avAdmin/admin-controller/admin-controller.js @@ -29,7 +29,7 @@ angular $scope, $state, $stateParams, - $timeout, + $interval, $q, $window, $modal @@ -336,7 +336,7 @@ angular $scope.has_draft = false; function updateDraft(el) { - $timeout(function () { + $interval(function () { $scope.draft = el; $scope.has_draft = ("{}" !== JSON.stringify(el)); }); diff --git a/avAdmin/admin-directives/activity-log/activity-log.js b/avAdmin/admin-directives/activity-log/activity-log.js index d16acef6..637604f9 100644 --- a/avAdmin/admin-directives/activity-log/activity-log.js +++ b/avAdmin/admin-directives/activity-log/activity-log.js @@ -23,7 +23,7 @@ angular.module('avAdmin') ElectionsApi, ConfigService, NextButtonService, - $timeout, + $interval, $stateParams ) { // we use it as something similar to a controller here @@ -108,8 +108,8 @@ angular.module('avAdmin') // debounced reloading function reloadActivityDebounce() { - $timeout.cancel(scope.filterTimeout); - scope.filterTimeout = $timeout(function() { + $interval.cancel(scope.filterTimeout); + scope.filterTimeout = $interval(function() { scope.reloadActivity(); }, 500); } diff --git a/avAdmin/admin-directives/ballot-box/ballot-box.js b/avAdmin/admin-directives/ballot-box/ballot-box.js index d2170520..6679291f 100644 --- a/avAdmin/admin-directives/ballot-box/ballot-box.js +++ b/avAdmin/admin-directives/ballot-box/ballot-box.js @@ -22,7 +22,7 @@ angular.module('avAdmin') Authmethod, ConfigService, NextButtonService, - $timeout, + $interval, $i18next, $modal, $location, @@ -105,8 +105,8 @@ angular.module('avAdmin') // debounced reloading function reloadDebounce() { - $timeout.cancel(scope.filterTimeout); - scope.filterTimeout = $timeout(function() { + $interval.cancel(scope.filterTimeout); + scope.filterTimeout = $interval(function() { scope.reload(); }, 500); } diff --git a/avAdmin/admin-directives/dashboard/send-auth-codes-modal-confirm.js b/avAdmin/admin-directives/dashboard/send-auth-codes-modal-confirm.js index 8e22586f..90c3d333 100644 --- a/avAdmin/admin-directives/dashboard/send-auth-codes-modal-confirm.js +++ b/avAdmin/admin-directives/dashboard/send-auth-codes-modal-confirm.js @@ -23,7 +23,7 @@ angular.module('avAdmin') function( ConfigService, $location, - $timeout, + $interval, $scope, $modalInstance, $i18next, @@ -192,7 +192,7 @@ angular.module('avAdmin') }); // use a click to an element with a specific class to close the dialog - $timeout(function() { + $interval(function() { $(".av-plugin-modal-close").click(function() { var data = $(this).data("response"); diff --git a/avAdmin/admin-directives/elcensus/elcensus.js b/avAdmin/admin-directives/elcensus/elcensus.js index a685deee..afaeab93 100644 --- a/avAdmin/admin-directives/elcensus/elcensus.js +++ b/avAdmin/admin-directives/elcensus/elcensus.js @@ -33,7 +33,7 @@ angular.module('avAdmin') $filter, $stateParams, $state, - $timeout, + $interval, ConfigService, CsvLoad, NextButtonService) @@ -1116,8 +1116,8 @@ angular.module('avAdmin') } function reloadCensusDebounce() { - $timeout.cancel(scope.filterTimeout); - scope.filterTimeout = $timeout(function() { + $interval.cancel(scope.filterTimeout); + scope.filterTimeout = $interval(function() { scope.reloadCensus(); }, 500); } diff --git a/avAdmin/admin-directives/elections/elections.js b/avAdmin/admin-directives/elections/elections.js index dfb68f35..3999000c 100644 --- a/avAdmin/admin-directives/elections/elections.js +++ b/avAdmin/admin-directives/elections/elections.js @@ -18,8 +18,14 @@ angular.module('avAdmin') .directive( 'avAdminElections', - function(Authmethod, ElectionsApi, DraftElection, AdminProfile, OnboardingTourService, $state, Plugins, $modal, $timeout, $window) - { + function( + Authmethod, + ElectionsApi, + AdminProfile, + OnboardingTourService, + Plugins, + $window + ) { // we use it as something similar to a controller here function link(scope, element, attrs) { scope.page = 1; diff --git a/avAdmin/admin-directives/tasks/view-task-logs-modal.js b/avAdmin/admin-directives/tasks/view-task-logs-modal.js index 299fea1b..a2801004 100644 --- a/avAdmin/admin-directives/tasks/view-task-logs-modal.js +++ b/avAdmin/admin-directives/tasks/view-task-logs-modal.js @@ -22,7 +22,7 @@ angular function( $scope, $modalInstance, - $timeout, + $interval, Authmethod, AnsiUpService, task @@ -73,10 +73,10 @@ angular task.status )) { if ($scope.taskUpdateTimeout) { - $timeout.cancel($scope.taskUpdateTimeout); + $interval.cancel($scope.taskUpdateTimeout); } } else { - $scope.taskUpdateTimeout = $timeout( + $scope.taskUpdateTimeout = $interval( function () { $scope.taskUpdateFunc(); @@ -86,7 +86,7 @@ angular $scope.updateTask(); } }; - $scope.taskUpdateTimeout = $timeout( + $scope.taskUpdateTimeout = $interval( function () { $scope.taskUpdateFunc(); @@ -103,7 +103,7 @@ angular { return; } - $timeout( + $interval( function () { $('.modal-body.view-task-logs-modal .console .end-marker')[0] @@ -204,7 +204,7 @@ angular $scope.updateLogs(); // scroll into the bottom of the modal on start - $timeout( + $interval( function () { $('.modal-body.view-task-logs-modal .autoscroll-span')[0] diff --git a/avAdmin/admin-directives/tasks/view-task-logs-modal.less b/avAdmin/admin-directives/tasks/view-task-logs-modal.less index 7877e42c..c6e1c30f 100644 --- a/avAdmin/admin-directives/tasks/view-task-logs-modal.less +++ b/avAdmin/admin-directives/tasks/view-task-logs-modal.less @@ -70,7 +70,7 @@ } .ansi-red-fg { - color: #DD948E; + color: #f00b0b; } .ansi-green-fg { @@ -134,7 +134,7 @@ } .ansi-red-bg { - background-color: #DD948E; + background-color: #f00b0b; } .ansi-green-bg { diff --git a/avAdmin/csv-load-service.js b/avAdmin/csv-load-service.js index b6408d7e..d2004088 100644 --- a/avAdmin/csv-load-service.js +++ b/avAdmin/csv-load-service.js @@ -20,7 +20,7 @@ angular.module('avAdmin') 'CsvLoad', function ( $q, - $timeout, + $interval, ConfigService, Plugins, Authmethod) @@ -190,7 +190,7 @@ angular.module('avAdmin') if (_.isFunction(csvLoadService.scope.processBatchPlugin)) { csvLoadService.scope.processBatchPlugin(processed) .then(function (ret) { - $timeout(function () { + $interval(function () { csvLoadService.scope.percent = ret.percent; csvLoadService.scope.exportListIndex = ret.exportListIndex; @@ -205,7 +205,7 @@ angular.module('avAdmin') }); }); } else { - $timeout(function () { + $interval(function () { csvLoadService.scope.percent = processed.percent; csvLoadService.scope.exportListIndex = processed.exportListIndex; diff --git a/avAdmin/draft-election.js b/avAdmin/draft-election.js index c5e5db17..a7dd4c2d 100644 --- a/avAdmin/draft-election.js +++ b/avAdmin/draft-election.js @@ -26,7 +26,7 @@ angular.module('avAdmin') ElectionsApi, $i18next, $http, - $timeout, + $interval, $modal, $state, $stateParams, @@ -85,7 +85,7 @@ angular.module('avAdmin') if (!draft_election.isEditingDraft()) { election = undefined; if (!_.isUndefined(promise)) { - $timeout.cancel(promise); + $interval.cancel(promise); } return; } @@ -104,14 +104,14 @@ angular.module('avAdmin') console.log("error uploading draft: " + response.data); } ); - promise = $timeout(draft_election.updateDraft, 60000); + promise = $interval(draft_election.updateDraft, 60000); }; draft_election.eraseDraft = function () { var deferred = $q.defer(); election = undefined; if (!_.isUndefined(promise)) { - $timeout.cancel(promise); + $interval.cancel(promise); } Authmethod .uploadUserDraft({}) diff --git a/avAdmin/new-election-send-webspec.js b/avAdmin/new-election-send-webspec.js deleted file mode 100644 index 71808632..00000000 --- a/avAdmin/new-election-send-webspec.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * This file is part of agora-gui-admin. - * Copyright (C) 2015-2016 Agora Voting SL - - * agora-gui-admin is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License. - - * agora-gui-admin is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - - * You should have received a copy of the GNU Affero General Public License - * along with agora-gui-admin. If not, see . -**/ - -var helpers = require('../test/helpers'); - -/* jshint ignore:start */ -describe("Admin Login tests", function() { - beforeEach(async function () { - await browser.get('/admin/login/'); - }); - - it( - "Admin Login site should load with avConfig defined page title", - async function () { - var avConfig = await helpers.getAvConfig(); - expect(await browser.getTitle()) - .toEqual(avConfig.webTitle); - } - ); - - /* - it("should open form for add new question", function() { - var el = element(by.id('newq')); - expect(element(by.id('nq')).isDisplayed()).toBe(false); - el.click(); - expect(element(by.id('nq')).isDisplayed()).toBe(true); - }); - - it("should throw error: Enter a valid election title", function() { - expect(element(by.repeater('e in election.errors')).isPresent()).toBe(false); - element(by.css('.glyphicon-save')).click(); - expect(element(by.repeater('e in election.errors')).isPresent()).toBe(true); - var results = element.all(by.repeater('e in election.errors')); - expect(results.count()).toEqual(3); - expect(results.first().getText()).toEqual('Enter a valid election title'); - expect(results.get(0).getText()).toEqual('Enter a valid election title'); - }); - - it("should throw error: Enter a valid election description", function() { - expect(element(by.repeater('e in election.errors')).isPresent()).toBe(false); - var name = element(by.id('name')); - name.sendKeys("test"); - element(by.css('.glyphicon-save')).click(); - expect(element(by.repeater('e in election.errors')).isPresent()).toBe(true); - var results = element.all(by.repeater('e in election.errors')); - expect(results.first().getText()).toEqual('Enter a valid election description'); - }); - - it("should throw error: Add at least one question", function() { - expect(element(by.repeater('e in election.errors')).isPresent()).toBe(false); - var name = element(by.id('name')); - name.sendKeys("test"); - var desc = element(by.id('desc')); - desc.sendKeys("test description"); - element(by.css('.glyphicon-save')).click(); - expect(element(by.repeater('e in election.errors')).isPresent()).toBe(true); - var results = element.all(by.repeater('e in election.errors')); - expect(results.first().getText()).toEqual('Add at least one question'); - }); - - it("should throw error: Enter a valid question title", function() { - expect(element(by.repeater('e in newquestion.errors')).isPresent()).toBe(false); - element(by.id('newq')).click(); - element(by.id('saveq')).click(); - expect(element(by.repeater('e in newquestion.errors')).isPresent()).toBe(true); - var results = element.all(by.repeater('e in newquestion.errors')); - expect(results.first().getText()).toEqual('Enter a valid question title'); - }); - - it("should throw error: Add at least one option", function() { - expect(element(by.repeater('e in newquestion.errors')).isPresent()).toBe(false); - element(by.id('newq')).click(); - var qtext = element(by.id('qtext')); - qtext.sendKeys("test question"); - element(by.id('saveq')).click(); - expect(element(by.repeater('e in newquestion.errors')).isPresent()).toBe(true); - var results = element.all(by.repeater('e in newquestion.errors')); - expect(results.first().getText()).toEqual('Add at least one option'); - }); - - it("should add one option", function() { - element(by.id('newq')).click(); - var qtext = element(by.id('qtext')); - qtext.sendKeys("test question"); - element(by.id('newopt')).click(); - element(by.id('saveq')).click(); - expect(element(by.repeater('e in newquestion.errors')).isPresent()).toBe(false); - expect(element(by.repeater('q in questions')).isPresent()).toBe(true); - var results = element.all(by.repeater('q in questions')); - expect(results.count()).toEqual(1); - }); - - it("should create a new election", function() { - var name = element(by.id('name')); - name.sendKeys("test"); - var desc = element(by.id('desc')); - desc.sendKeys("test description"); - - element(by.id('newq')).click(); - var qtext = element(by.id('qtext')); - qtext.sendKeys("test question"); - element(by.id('newopt')).click(); - element(by.id('saveq')).click(); - - element(by.css('.glyphicon-save')).click(); - expect(element(by.repeater('e in election.errors')).isPresent()).toBe(false); - }); - */ - -}); -/* jshint ignore:end */ - diff --git a/test/create-election-page.js b/test/create-election-page.js new file mode 100644 index 00000000..518301e7 --- /dev/null +++ b/test/create-election-page.js @@ -0,0 +1,122 @@ +/** + * This file is part of agora-gui-admin. + * Copyright (C) 2022 Sequent Tech Inc + + * agora-gui-admin is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License. + + * agora-gui-admin is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with agora-gui-admin. If not, see . +**/ + +/* jshint ignore:start */ +var CreateElectionPage = function () +{ + /** + * Creates an election using the Edit JSON button. + * @param {*} electionData Data of the election(s) to be created. + * @returns {Number} Id of the (parent) created election + * + * Requirements: Requires Admin to be logged in, you can use helpers.login() + * for that. + */ + this.createElectionEditJson = async function (electionData) + { + var EC = protractor.ExpectedConditions; + + // go to create the election + console.log("entering /admin/new/"); + await browser.get('/admin/new/'); + browser.wait( + EC.urlIs('/admin/basic/'), + browser.params.timeout.ECstandards, + "/admin/new/ didn't redirect to /admin/basic/ as expected" + ); + + // go to the last create-election step + var createEl = element( + by.css('[av-admin-sidebar] [href="/admin/create/"]') + ); + expect(createEl.isPresent()).toBe(true); + console.log("clicking /admin/create/"); + await createEl.click(); + browser.wait( + EC.urlIs('/admin/create/'), + browser.params.timeout.ECstandards, + "/admin/basic/ didn't redirect to /admin/create/ as expected" + ); + console.log("entered /admin/create/"); + + // click on edit json to change it to our test-election json + var editJsonEl = element( + by.css('[av-admin-create] .fa-pencil') + ); + expect(editJsonEl.isPresent()).toBe(true); + console.log("entering edit json modal"); + await editJsonEl.click(); + + // change textarea with election json + var electionJsonEl = element(by.model('electionJson.model')); + expect(electionJsonEl.isPresent()).toBe(true); + console.log("editing json election"); + await electionJsonEl.clear().sendKeys(electionData); + + // save the new json + var saveEl = element( + by.css('[modal-window] .modal-footer button.btn-success') + ); + expect(saveEl.isPresent()).toBe(true); + console.log("saving election json election"); + await saveEl.click(); + + // Expect no errors in the json + var errorsEls = element.all(by.repeater('error in errors')); + expect(errorsEls.count()).toEqual(0); + + // Procceed to create the election + var createButtonEl = element( + by.css('[av-admin-create] .form-group button.btn.btn-block.btn-success') + ); + expect(createButtonEl.isPresent()).toBe(true); + expect(createButtonEl.getAttribute('disabled')).toBe(null); + console.log("clicking create election button"); + await createButtonEl.click(); + + // Wait for either an error to appear or the redirect to the created + // election dashboard page to happen + var dashboardUrl = EC.urlContains('/admin/dashboard/'); + var creationError = electionCreationErrorEl( + element( + by.css('[av-admin-create] [av-scroll-to-bottom] p.text-brand-danger') + ) + ); + console.log("waiting for election to be created and redirect to dashboard"); + browser.wait( + EC.or(dashboardUrl, creationError), + browser.params.timeout.CreateElections, + "Election creation timedout with no visible error" + ); + + // We reached here because either we were redirected to the dashboard or + // there was an election creation error. Check that it's the first case. + var currentUrl = await browser.getCurrentUrl(); + expect(currentUrl) + .toContain('/admin/dashboard/', 'Election creation error'); + + // Success! Return the current election id + console.log("waiting for election to be created and redirect to dashboard"); + var splitUrl = currentUrl.split('/admin/dashboard/'); + var electionId = Number.parseInt(splitUrl[splitUrl.length - 1]); + console.log("returning election id = " + electionId); + + return electionId; + }; +}; +module.exports = new CreateElectionPage(); +/* jshint ignore:end */ diff --git a/test/e2e.conf.js b/test/e2e.conf.js index d2061c4f..d878f347 100644 --- a/test/e2e.conf.js +++ b/test/e2e.conf.js @@ -32,7 +32,8 @@ exports.config = { acceptInsecureCerts : true }, specs: [ - '../avAdmin/**/*-webspec.js' + '../avAdmin/**/*-webspec.js', + './**/*-webspec.js' ], jasmineNodeOpts: { // remove ugly protractor dot reporter @@ -42,7 +43,13 @@ exports.config = { }, getPageTimeout: 30000, allScriptsTimeout: 20000, - onPrepare: function () { + + // Use native async/await + // More info: https://www.protractortest.org/#/async-await + SELENIUM_PROMISE_MANAGER: false, + + onPrepare: function () + { var SpecReporter = require('jasmine-spec-reporter').SpecReporter; jasmine .getEnv() diff --git a/test/full-election-cycle-webspec.js b/test/full-election-cycle-webspec.js new file mode 100644 index 00000000..ab204be4 --- /dev/null +++ b/test/full-election-cycle-webspec.js @@ -0,0 +1,214 @@ +/** + * This file is part of agora-gui-admin. + * Copyright (C) 2022 Sequent Tech Inc + + * agora-gui-admin is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License. + + * agora-gui-admin is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with agora-gui-admin. If not, see . +**/ + +/* jshint ignore:start */ +describe( + "Admin Full Election Cycle Tests", + function() + { + var helpers = require('./helpers'); + var CreateElectionPage = require('./create-election-page.js'); + var adminLogin = null; + var simpleElection = [ + { + "title": "E2E test election", + "description": "This is the description of the election. You can add simple html like bold or links to websites.\n\n

You need to use two br element for new paragraphs.", + "authorities": [ + "dev-a2" + ], + "director": "dev-a1", + "presentation": { + "theme": "default", + "share_text": [ + { + "network": "Twitter", + "button_text": "", + "social_message": "I have just voted in election __URL__, you can too! #nvotes", + "active": true + } + ], + "urls": [], + "allow_tally": true, + "theme_css": "" + }, + "layout": "simple", + "num_successful_logins_allowed": 0, + "resultsConfig": { + "version": "master", + "pipes": [ + { + "type": "agora_results.pipes.results.do_tallies", + "params": {} + }, + { + "type": "agora_results.pipes.sort.sort_non_iterative", + "params": {} + } + ] + }, + "census": { + "has_ballot_boxes": false, + "voters": [], + "auth_method": "email", + "census": "close", + "extra_fields": [ + { + "name": "email", + "type": "email", + "required": true, + "unique": true, + "min": 4, + "max": 255, + "required_on_authentication": true, + "must": true + } + ], + "admin_fields": [], + "config": { + "allow_user_resend": false, + "msg": "Vote in __URL__ with code __CODE__", + "subject": "Vote now with nVotes", + "authentication-action": { + "mode": "vote", + "mode-config": { + "url": "" + } + }, + "registration-action": { + "mode": "vote", + "mode-config": null + } + } + }, + "questions": [ + { + "answer_total_votes_percentage": "over-total-valid-votes", + "answers": [ + { + "category": "", + "details": "This is an option with an simple example description.", + "id": 0, + "sort_order": 0, + "text": "Example option 1", + "urls": [ + { + "title": "URL", + "url": "" + }, + { + "title": "Image URL", + "url": "" + } + ] + }, + { + "category": "", + "details": "An option can contain a description. You can add simple html like bold or links to websites. You can also set an image url below, but be sure it's HTTPS or else it won't load.\n\n

You need to use two br element for new paragraphs.", + "id": 1, + "sort_order": 1, + "text": "Example option 2", + "urls": [ + { + "title": "URL", + "url": "https://sequentech.io" + }, + { + "title": "Image URL", + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/df/The_Fabs.JPG/220px-The_Fabs.JPG" + } + ] + }, + { + "category": "", + "details": "", + "id": 2, + "sort_order": 2, + "text": "Example option 3", + "urls": [ + { + "title": "URL", + "url": "" + }, + { + "title": "Image URL", + "url": "" + } + ] + } + ], + "description": "This is the description of this question. You can have multiple questions. You can add simple html like bold or links to websites.\n\n

You need to use two br element for new paragraphs.", + "layout": "accordion", + "max": 1, + "min": 1, + "num_winners": 1, + "tally_type": "plurality-at-large", + "title": "Test question title", + "extra_options": { + "shuffle_categories": true, + "shuffle_all_options": true, + "shuffle_category_list": [], + "show_points": false + }, + "active": true + } + ], + "extra_data": {} + } + ]; + + /** + * Updates the elections config with the details of current deployment + * configuration: + * - election authorities + * - results config version + */ + function updateElectionConfig(elections, avConfig) + { + for (var i = 0; i < elections.length; i++) + { + var election = elections[i]; + election.director = avConfig.director; + election.authorities = avConfig.authorities; + election.resultsConfig.version = avConfig.mainVersion; + } + } + + /** + * Setup the tests by logging in as an admin user. + */ + beforeEach( + async function () + { + adminLogin = new helpers.adminLogin(); + await adminLogin.login(); + } + ); + + it( + "Admin Full Election cycle", + async function () + { + var avConfig = await helpers.getAvConfig(); + updateElectionConfig(simpleElection, avConfig); + + // create the election + await CreateElectionPage.createElectionEditJson(simpleElection); + } + ); +}); +/* jshint ignore:end */ + diff --git a/test/helpers.js b/test/helpers.js index 0e70c5c4..18674666 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -17,13 +17,60 @@ /* jshint ignore:start */ module.exports = { - getAvConfig: async function() { + /** + * @returns the Config object + */ + getAvConfig: async function() + { return await browser .executeAsyncScript( - function(callback) { + function(callback) + { callback(window.avConfigData); } ); + }, + + /** + * Assumes username + password is the admin authentication method. + */ + adminLogin: function () + { + var usernameEl = element(by.css('[av-login] form input#input1')); + var passwordEl = element( + by.css('[av-login] form input[type=\"password\"]') + ); + var submitEl = element(by.css('[av-login] form button[type=\"submit\"')); + var adminUserEl = element(by.css('[av-admin-head] .profile-dropdown')); + + /** + * Gets the login page and logins the admin user. + */ + this.login = async function() + { + var EC = protractor.ExpectedConditions; + + // enter the login page and write username and password + await browser.get('/admin/login/'); + await usernameEl.sendKeys(browser.params.login.username); + await passwordEl.sendKeys(browser.params.login.password); + + // submit should be enabled -> then submit + expect(submitEl.getAttribute('disable')).toBeNull(); + await submitEl.click(); + + // wait for login to work and redirect to /admin/elections + browser.wait( + EC.urlIs('/admin/elections'), + browser.params.timeout.ECstandards, + "Login didn't redirect to /admin/elections" + ); + + // check that it's logged in with the correct username shown in the top + // navbar + expect(await adminUserEl.getText()) + .toContain(browser.params.login.username); + }; } }; -/* jshint ignore:end */ \ No newline at end of file +/* jshint ignore:end */