diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/popup.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/popup.ejs index bf9081fab6cd..d36180221720 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/popup.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/popup.ejs @@ -2,5 +2,5 @@ <%= text %>

- Close + Close diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/vhosts.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/vhosts.ejs index c3dacaad7ce3..ce9613a56c45 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/vhosts.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/vhosts.ejs @@ -1,12 +1,12 @@

Virtual Hosts

-
+

All virtual hosts

<%= filter_ui(vhosts) %>
<% if (vhosts.length > 0) { %> - +
<%= group_heading('vhosts', 'Overview', [true, true, true]) %> diff --git a/selenium/.node-xmlhttprequest-sync-88011 b/selenium/.node-xmlhttprequest-sync-88011 new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/selenium/test/exchanges/management.js b/selenium/test/exchanges/management.js index 1111fe251640..631acaebdfe5 100644 --- a/selenium/test/exchanges/management.js +++ b/selenium/test/exchanges/management.js @@ -1,7 +1,7 @@ const { By, Key, until, Builder } = require('selenium-webdriver') require('chromedriver') const assert = require('assert') -const { buildDriver, goToHome, captureScreensFor, teardown, delay } = require('../utils') +const { buildDriver, goToHome, captureScreensFor, teardown, doWhile } = require('../utils') const LoginPage = require('../pageobjects/LoginPage') const OverviewPage = require('../pageobjects/OverviewPage') @@ -66,6 +66,41 @@ describe('Exchange management', function () { assert.equal("amq.fanout", await exchange.getName()) }) + it('queue selectable columns', async function () { + await overview.clickOnOverviewTab() + await overview.clickOnExchangesTab() + await doWhile(async function() { return exchanges.getExchangesTable() }, + function(table) { + return table.length > 0 + }) + + await exchanges.clickOnSelectTableColumns() + let table = await exchanges.getSelectableTableColumns() + + assert.equal(2, table.length) + let overviewGroup = { + "name" : "Overview:", + "columns": [ + {"name:":"Type","id":"checkbox-exchanges-type"}, + {"name:":"Features (with policy)","id":"checkbox-exchanges-features"}, + {"name:":"Features (no policy)","id":"checkbox-exchanges-features_no_policy"}, + {"name:":"Policy","id":"checkbox-exchanges-policy"} + ] + } + assert.equal(JSON.stringify(table[0]), JSON.stringify(overviewGroup)) + + let messageRatesGroup = { + "name" : "Message rates:", + "columns": [ + {"name:":"rate in","id":"checkbox-exchanges-rate-in"}, + {"name:":"rate out","id":"checkbox-exchanges-rate-out"} + ] + } + assert.equal(JSON.stringify(table[1]), JSON.stringify(messageRatesGroup)) + + }) + + after(async function () { await teardown(driver, this, captureScreen) }) diff --git a/selenium/test/mgt-api.js b/selenium/test/mgt-api.js new file mode 100644 index 000000000000..2ff69328a690 --- /dev/null +++ b/selenium/test/mgt-api.js @@ -0,0 +1,112 @@ +const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest +const {log, error} = require('./utils.js') + +const baseUrl = randomly_pick_baseurl(process.env.RABBITMQ_URL || 'http://localhost:15672/') +const otherBaseUrl = randomly_pick_baseurl(process.env.OTHER_RABBITMQ_URL || 'http://localhost:15675/') +const hostname = process.env.RABBITMQ_HOSTNAME || 'localhost' +const otherHostname = process.env.OTHER_RABBITMQ_HOSTNAME || 'localhost' + +function randomly_pick_baseurl (baseUrl) { + urls = baseUrl.split(",") + return urls[getRandomInt(urls.length)] +} +function getRandomInt(max) { + return Math.floor(Math.random() * max) +} + +module.exports = { + + getManagementUrl: () => { + return baseUrl + }, + + geOtherManagementUrl: () => { + return otherBaseUrl + }, + + setPolicy: (url, vhost, name, pattern, definition, appliedTo = "queues") => { + let policy = { + "pattern": pattern, + "apply-to": appliedTo, + "definition": definition + } + log("Setting policy " + JSON.stringify(policy) + + " with name " + name + " for vhost " + vhost + " on "+ url) + const req = new XMLHttpRequest() + let base64Credentials = btoa('administrator-only' + ":" + 'guest') + let finalUrl = url + "/api/policies/" + encodeURIComponent(vhost) + "/" + + encodeURIComponent(name) + req.open('PUT', finalUrl, false) + req.setRequestHeader("Authorization", "Basic " + base64Credentials) + req.setRequestHeader('Content-Type', 'application/json') + + req.send(JSON.stringify(policy)) + if (req.status == 200 || req.status == 204 || req.status == 201) { + log("Succesfully set policy " + name) + return + }else { + error("status:" + req.status + " : " + req.responseText) + throw new Error(req.responseText) + } + }, + deletePolicy: (url, vhost, name) => { + log("Deleting policy " + name + " on vhost " + vhost) + const req = new XMLHttpRequest() + let base64Credentials = btoa('administrator-only' + ":" + 'guest') + let finalUrl = url + "/api/policies/" + encodeURIComponent(vhost) + "/" + + encodeURIComponent(name) + req.open('DELETE', finalUrl, false) + req.setRequestHeader("Authorization", "Basic " + base64Credentials) + + req.send() + if (req.status == 200 || req.status == 204) { + log("Succesfully deleted policy " + name) + return + }else { + error("status:" + req.status + " : " + req.responseText) + throw new Error(req.responseText) + } + }, + createVhost: (url, name, description = "", tags = []) => { + let vhost = { + "description": description, + "tags": tags + } + log("Create vhost " + JSON.stringify(vhost) + + " with name " + name + " on " + url) + const req = new XMLHttpRequest() + let base64Credentials = btoa('administrator-only' + ":" + 'guest') + let finalUrl = url + "/api/vhosts/" + encodeURIComponent(name) + req.open('PUT', finalUrl, false) + req.setRequestHeader("Authorization", "Basic " + base64Credentials) + req.setRequestHeader('Content-Type', 'application/json') + + req.send(JSON.stringify(vhost)) + if (req.status == 200 || req.status == 204 || req.status == 201) { + log("Succesfully created vhost " + name) + return + }else { + error("status:" + req.status + " : " + req.responseText) + throw new Error(req.responseText) + } + }, + deleteVhost: (url, vhost) => { + log("Deleting vhost " + vhost) + const req = new XMLHttpRequest() + let base64Credentials = btoa('administrator-only' + ":" + 'guest') + let finalUrl = url + "/api/vhosts/" + encodeURIComponent(vhost) + req.open('DELETE', finalUrl, false) + req.setRequestHeader("Authorization", "Basic " + base64Credentials) + + req.send() + if (req.status == 200 || req.status == 204) { + log("Succesfully deleted vhost " + vhost) + return + }else { + error("status:" + req.status + " : " + req.responseText) + throw new Error(req.responseText) + } + } + + +} diff --git a/selenium/test/pageobjects/BasePage.js b/selenium/test/pageobjects/BasePage.js index 989460b6072f..2b4f40ba476f 100644 --- a/selenium/test/pageobjects/BasePage.js +++ b/selenium/test/pageobjects/BasePage.js @@ -13,8 +13,13 @@ const EXCHANGES_TAB = By.css('div#menu ul#tabs li#exchanges') const ADMIN_TAB = By.css('div#menu ul#tabs li#admin') const STREAM_CONNECTIONS_TAB = By.css('div#menu ul#tabs li#stream-connections') -const FORM_POPUP = By.css('div.form-popup-warn') -const FORM_POPUP_CLOSE_BUTTON = By.css('div.form-popup-warn span') +const FORM_POPUP_WARNING = By.css('div.form-popup-warn') +const FORM_POPUP_WARNING_CLOSE_BUTTON = By.css('div.form-popup-warn span#close') + +const FORM_POPUP_OPTIONS = By.css('div.form-popup-options') +const ADD_MINUS_BUTTON = By.css('div#main table.list thead tr th.plus-minus') +const TABLE_COLUMNS_POPUP = By.css('div.form-popup-options table.form') +const FORM_POPUP_OPTIONS_CLOSE_BUTTON = By.css('div.form-popup-options span#close') module.exports = class BasePage { driver @@ -136,6 +141,7 @@ module.exports = class BasePage { } + async getTable(tableLocator, firstNColumns, rowClass) { const table = await this.waitForDisplayed(tableLocator) const rows = await table.findElements(rowClass == undefined ? @@ -145,7 +151,9 @@ module.exports = class BasePage { let columns = await row.findElements(By.css('td')) let table_row = [] for (let column of columns) { - if (table_row.length < firstNColumns) table_row.push(await column.getText()) + if (firstNColumns == undefined || table_row.length < firstNColumns) { + table_row.push(await column.getText()) + } } table_model.push(table_row) } @@ -153,7 +161,7 @@ module.exports = class BasePage { } async isPopupWarningDisplayed() { try { - let element = await driver.findElement(FORM_POPUP) + let element = await driver.findElement(FORM_POPUP_WARNING) return element.isDisplayed() } catch(e) { return Promise.resolve(false) @@ -171,7 +179,7 @@ module.exports = class BasePage { } async isPopupWarningNotDisplayed() { - return this.isElementNotVisible(FORM_POPUP) + return this.isElementNotVisible(FORM_POPUP_WARNING) } async isElementNotVisible(locator) { @@ -191,14 +199,45 @@ module.exports = class BasePage { } } async getPopupWarning() { - let element = await driver.findElement(FORM_POPUP) + let element = await driver.findElement(FORM_POPUP_WARNING) return this.driver.wait(until.elementIsVisible(element), this.timeout, 'Timed out after [timeout=' + this.timeout + ';polling=' + this.polling + '] awaiting till visible ' + element, this.polling).getText().then((value) => value.substring(0, value.search('\n\nClose'))) } async closePopupWarning() { - return this.click(FORM_POPUP_CLOSE_BUTTON) + return this.click(FORM_POPUP_WARNING_CLOSE_BUTTON) } + async clickOnSelectTableColumns() { + return this.click(ADD_MINUS_BUTTON) + } + async getSelectableTableColumns() { + const table = await this.waitForDisplayed(TABLE_COLUMNS_POPUP) + const rows = await table.findElements(By.css('tbody tr')) + let table_model = [] + for (let i = 1; i < rows.length; i++) { // skip first row + let groupNameLabel = await rows[i].findElement(By.css('th label')) + let groupName = await groupNameLabel.getText() + let columns = await rows[i].findElements(By.css('td label')) + let table_row = [] + for (let column of columns) { + let checkbox = await column.findElement(By.css('input')) + table_row.push({"name:" : await column.getText(), "id" : await checkbox.getAttribute("id")}) + } + let group = {"name": groupName, "columns": table_row} + table_model.push(group) + } + return table_model + } + async selectTableColumnsById(arrayOfColumnsIds) { + await this.clickOnSelectTableColumns() + const table = await this.waitForDisplayed(TABLE_COLUMNS_POPUP) + for (let id of arrayOfColumnsIds) { + let checkbox = await table.findElement(By.css('tbody tr input#'+id)) + await checkbox.click() + } + await this.click(FORM_POPUP_OPTIONS_CLOSE_BUTTON) + } + async isDisplayed(locator) { try { let element = await driver.findElement(locator) diff --git a/selenium/test/pageobjects/VhostsAdminTab.js b/selenium/test/pageobjects/VhostsAdminTab.js index 34ae729da33d..e7762e013aaf 100644 --- a/selenium/test/pageobjects/VhostsAdminTab.js +++ b/selenium/test/pageobjects/VhostsAdminTab.js @@ -2,19 +2,22 @@ const { By, Key, until, Builder } = require('selenium-webdriver') const AdminTab = require('./AdminTab') +const MAIN_SECTION = By.css('div#main div#vhosts.section') + const SELECTED_VHOSTS_ON_RHM = By.css('div#rhs ul li a[href="#/vhosts"]') const FILTER_VHOST = By.css('div#main div.filter input#filter') const CHECKBOX_REGEX = By.css('div#main div.filter input#filter-regex-mode') const VHOSTS_TABLE_ROWS = By.css('div#main table.list tbody tr') +const TABLE_SECTION = By.css('div#main div#vhosts.section table.list') module.exports = class VhostsAdminTab extends AdminTab { async isLoaded () { - await this.waitForDisplayed(SELECTED_VHOSTS_ON_RHM) + await this.waitForDisplayed(MAIN_SECTION) } async searchForVhosts(vhost, regex = false) { await this.sendKeys(FILTER_VHOST, vhost) - await this.sendKeys(FILTER_VHOST, Key.RETURN) + //await this.sendKeys(FILTER_VHOST, Key.RETURN) if (regex) { await this.click(CHECKBOX_REGEX) } @@ -28,9 +31,12 @@ module.exports = class VhostsAdminTab extends AdminTab { const links = await vhost_rows.findElements(By.css("td a")) for (let link of links) { let text = await link.getText() - if ( text === "/" ) return link.click() + if ( text === vhost ) return link.click() } throw "Vhost " + vhost + " not found" } + async getVhostsTable(firstNColumns) { + return this.getTable(TABLE_SECTION, firstNColumns) + } } diff --git a/selenium/test/queuesAndStreams/list.js b/selenium/test/queuesAndStreams/list.js index 094d8beb1195..cd871435b9bc 100644 --- a/selenium/test/queuesAndStreams/list.js +++ b/selenium/test/queuesAndStreams/list.js @@ -1,7 +1,7 @@ const { By, Key, until, Builder } = require('selenium-webdriver') require('chromedriver') const assert = require('assert') -const { buildDriver, goToHome, captureScreensFor, teardown, delay } = require('../utils') +const { buildDriver, goToHome, captureScreensFor, teardown, doWhile } = require('../utils') const LoginPage = require('../pageobjects/LoginPage') const OverviewPage = require('../pageobjects/OverviewPage') @@ -41,6 +41,66 @@ describe('Queues and Streams management', function () { assert.equal(true, text.startsWith('All queues') ) }) + it('queue selectable columns', async function () { + await overview.clickOnOverviewTab() + await overview.clickOnQueuesTab() + await doWhile(async function() { return queuesAndStreams.getQueuesTable() }, + function(table) { + return table.length > 0 + }) + + await queuesAndStreams.clickOnSelectTableColumns() + let table = await queuesAndStreams.getSelectableTableColumns() + + assert.equal(4, table.length) + let overviewGroup = { + "name" : "Overview:", + "columns": [ + {"name:":"Type","id":"checkbox-queues-type"}, + {"name:":"Features (with policy)","id":"checkbox-queues-features"}, + {"name:":"Features (no policy)","id":"checkbox-queues-features_no_policy"}, + {"name:":"Policy","id":"checkbox-queues-policy"}, + {"name:":"Consumer count","id":"checkbox-queues-consumers"}, + {"name:":"Consumer capacity","id":"checkbox-queues-consumer_capacity"}, + {"name:":"State","id":"checkbox-queues-state"} + ] + } + assert.equal(JSON.stringify(table[0]), JSON.stringify(overviewGroup)) + let messagesGroup = { + "name" : "Messages:", + "columns": [ + {"name:":"Ready","id":"checkbox-queues-msgs-ready"}, + {"name:":"Unacknowledged","id":"checkbox-queues-msgs-unacked"}, + {"name:":"In memory","id":"checkbox-queues-msgs-ram"}, + {"name:":"Persistent","id":"checkbox-queues-msgs-persistent"}, + {"name:":"Total","id":"checkbox-queues-msgs-total"} + ] + } + assert.equal(JSON.stringify(table[1]), JSON.stringify(messagesGroup)) + let messageBytesGroup = { + "name" : "Message bytes:", + "columns": [ + {"name:":"Ready","id":"checkbox-queues-msg-bytes-ready"}, + {"name:":"Unacknowledged","id":"checkbox-queues-msg-bytes-unacked"}, + {"name:":"In memory","id":"checkbox-queues-msg-bytes-ram"}, + {"name:":"Persistent","id":"checkbox-queues-msg-bytes-persistent"}, + {"name:":"Total","id":"checkbox-queues-msg-bytes-total"} + ] + } + assert.equal(JSON.stringify(table[2]), JSON.stringify(messageBytesGroup)) + let messageRatesGroup = { + "name" : "Message rates:", + "columns": [ + {"name:":"incoming","id":"checkbox-queues-rate-incoming"}, + {"name:":"deliver / get","id":"checkbox-queues-rate-deliver"}, + {"name:":"redelivered","id":"checkbox-queues-rate-redeliver"}, + {"name:":"ack","id":"checkbox-queues-rate-ack"} + ] + } + assert.equal(JSON.stringify(table[3]), JSON.stringify(messageRatesGroup)) + + }) + after(async function () { await teardown(driver, this, captureScreen) }) diff --git a/selenium/test/utils.js b/selenium/test/utils.js index b7db51d25341..3068f68240a7 100644 --- a/selenium/test/utils.js +++ b/selenium/test/utils.js @@ -5,6 +5,7 @@ const path = require('path') const { By, Key, until, Builder, logging, Capabilities } = require('selenium-webdriver') const proxy = require('selenium-webdriver/proxy') require('chromedriver') +var chrome = require("selenium-webdriver/chrome"); const UAALoginPage = require('./pageobjects/UAALoginPage') const KeycloakLoginPage = require('./pageobjects/KeycloakLoginPage') const assert = require('assert') @@ -47,7 +48,9 @@ module.exports = { log: (message) => { console.log(new Date() + " " + message) }, - + error: (message) => { + console.error(new Date() + " " + message) + }, hasProfile: (profile) => { return profiles.includes(profile) }, @@ -58,19 +61,34 @@ module.exports = { builder = builder.usingServer(seleniumUrl) } let chromeCapabilities = Capabilities.chrome(); - chromeCapabilities.setAcceptInsecureCerts(true); + const options = new chrome.Options() + chromeCapabilities.setAcceptInsecureCerts(true); chromeCapabilities.set('goog:chromeOptions', { + excludeSwitches: [ // disable info bar + 'enable-automation', + ], + prefs: { + 'profile.password_manager_enabled' : false + }, args: [ + "--enable-automation", + "guest", + "disable-infobars", + "--disable-notifications", "--lang=en", "--disable-search-engine-choice-screen", - "--disable-popup-blocking", + "disable-popup-blocking", "--credentials_enable_service=false", - "--profile.password_manager_enabled=false", - "--profile.password_manager_leak_detection=false" + "profile.password_manager_enabled=false", + "profile.reduce-security-for-testing", + "profile.managed_default_content_settings.popups=1", + "profile.managed_default_content_settings.notifications.popups=1", + "profile.password_manager_leak_detection=false" ] }); driver = builder .forBrowser('chrome') + //.setChromeOptions(options.excludeSwitches("disable-popup-blocking", "enable-automation")) .withCapabilities(chromeCapabilities) .build() driver.manage().setTimeouts( { pageLoad: 35000 } ) @@ -111,6 +129,34 @@ module.exports = { return new CaptureScreenshot(driver, require('path').basename(test)) }, + doWhile: async (doCallback, booleanCallback, delayMs = 1000, message = "doWhile failed") => { + let done = false + let attempts = 10 + let ret + do { + try { + //console.log("Calling doCallback (attempts:" + attempts + ") ... ") + ret = await doCallback() + //console.log("Calling booleanCallback (attempts:" + attempts + ") with arg " + ret + " ... ") + done = booleanCallback(ret) + }catch(error) { + console.log("Caught " + error + " on doWhile callback...") + + }finally { + if (!done) { + //console.log("Waiting until next attempt") + await module.exports.delay(delayMs) + } + } + attempts-- + } while (attempts > 0 && !done) + if (!done) { + throw new Error(message) + }else { + return ret + } + }, + idpLoginPage: (driver, preferredIdp) => { if (!preferredIdp) { if (process.env.PROFILES.includes("uaa")) { diff --git a/selenium/test/vhosts/admin-vhosts.js b/selenium/test/vhosts/admin-vhosts.js index 68ca103eb473..8f815d8d8adb 100644 --- a/selenium/test/vhosts/admin-vhosts.js +++ b/selenium/test/vhosts/admin-vhosts.js @@ -1,7 +1,8 @@ const { By, Key, until, Builder } = require('selenium-webdriver') require('chromedriver') const assert = require('assert') -const { buildDriver, goToHome, captureScreensFor, teardown, delay } = require('../utils') +const { buildDriver, goToHome, captureScreensFor, teardown, doWhile, log, delay } = require('../utils') +const { getManagementUrl, createVhost, deleteVhost } = require('../mgt-api') const LoginPage = require('../pageobjects/LoginPage') const OverviewPage = require('../pageobjects/OverviewPage') @@ -28,7 +29,7 @@ describe('Virtual Hosts in Admin tab', function () { if (!await overview.isLoaded()) { throw new Error('Failed to login') } - + await overview.selectRefreshOption("Do not refresh") }) it('find default vhost', async function () { @@ -37,6 +38,7 @@ describe('Virtual Hosts in Admin tab', function () { assert.equal(true, await vhostsTab.hasVhosts("/")) }) it('find default vhost and view it', async function () { + await overview.clickOnOverviewTab() await overview.clickOnAdminTab() await adminTab.clickOnVhosts() await vhostsTab.clickOnVhost(await vhostsTab.searchForVhosts("/"), "/") @@ -45,7 +47,92 @@ describe('Virtual Hosts in Admin tab', function () { } assert.equal("/", await vhostTab.getName()) }) + + it('vhost selectable columns', async function () { + await overview.clickOnOverviewTab() + await overview.clickOnAdminTab() + await adminTab.clickOnVhosts() + await vhostsTab.searchForVhosts("/") + await doWhile(async function() { return vhostsTab.getVhostsTable() }, + function(table) { + return table.length>0 + }) + + await vhostsTab.clickOnSelectTableColumns() + let table = await vhostsTab.getSelectableTableColumns() + + assert.equal(4, table.length) + let overviewGroup = { + "name" : "Overview:", + "columns": [ + {"name:":"Default queue type","id":"checkbox-vhosts-default-queue-type"}, + {"name:":"Cluster state","id":"checkbox-vhosts-cluster-state"}, + {"name:":"Description","id":"checkbox-vhosts-description"}, + {"name:":"Tags","id":"checkbox-vhosts-tags"} + ] + } + assert.equal(JSON.stringify(table[0]), JSON.stringify(overviewGroup)) + let messagesGroup = { + "name" : "Messages:", + "columns": [ + {"name:":"Ready","id":"checkbox-vhosts-msgs-ready"}, + {"name:":"Unacknowledged","id":"checkbox-vhosts-msgs-unacked"}, + {"name:":"Total","id":"checkbox-vhosts-msgs-total"} + ] + } + assert.equal(JSON.stringify(table[1]), JSON.stringify(messagesGroup)) + let networkGroup = { + "name" : "Network:", + "columns": [ + {"name:":"From client","id":"checkbox-vhosts-from_client"}, + {"name:":"To client","id":"checkbox-vhosts-to_client"} + ] + } + assert.equal(JSON.stringify(table[2]), JSON.stringify(networkGroup)) + let messageRatesGroup = { + "name" : "Message rates:", + "columns": [ + {"name:":"publish","id":"checkbox-vhosts-rate-publish"}, + {"name:":"deliver / get","id":"checkbox-vhosts-rate-deliver"} + ] + } + assert.equal(JSON.stringify(table[3]), JSON.stringify(messageRatesGroup)) + + }) + + describe('given there is a new virtualhost with a tag', async function() { + let vhost = "test_" + Math.floor(Math.random() * 1000) + before(async function() { + log("Creating vhost") + createVhost(getManagementUrl(), vhost, "selenium", "selenium-tag") + await overview.clickOnOverviewTab() + await overview.clickOnAdminTab() + await adminTab.clickOnVhosts() + }) + it('vhost is listed with tag', async function () { + log("Searching for vhost " + vhost) + await vhostsTab.searchForVhosts(vhost) + await doWhile(async function() { return vhostsTab.getVhostsTable()}, + function(table) { + log("table: "+ JSON.stringify(table) + " table[0][0]:" + table[0][0]) + return table.length==1 && table[0][0].localeCompare(vhost) == 0 + }) + log("Found vhost " + vhost) + await vhostsTab.selectTableColumnsById(["checkbox-vhosts-tags"]) + + await doWhile(async function() { return vhostsTab.getVhostsTable() }, + function(table) { + return table.length==1 && table[0][3].localeCompare("selenium-tag") == 0 + }) + }) + after(async function () { + log("Deleting vhost") + deleteVhost(getManagementUrl(), vhost) + }) + + }) + after(async function () { await teardown(driver, this, captureScreen)