Feature Flags
<%= filter_ui(nonreq_feature_flags) %>
diff --git a/selenium/full-suite-management-ui b/selenium/full-suite-management-ui
index ceec03793e34..0c39f9278b92 100644
--- a/selenium/full-suite-management-ui
+++ b/selenium/full-suite-management-ui
@@ -22,3 +22,4 @@ mgt/exchanges.sh
mgt/limits.sh
mgt/mgt-only-exchanges.sh
mgt/queuesAndStreams.sh
+mgt/feature-flags.sh
\ No newline at end of file
diff --git a/selenium/package.json b/selenium/package.json
index c79d91274d10..b84708617f62 100644
--- a/selenium/package.json
+++ b/selenium/package.json
@@ -12,7 +12,7 @@
"author": "",
"license": "ISC",
"dependencies": {
- "chromedriver": "^137.0",
+ "chromedriver": "^139.0",
"ejs": "^3.1.8",
"express": "^4.18.2",
"geckodriver": "^3.0.2",
diff --git a/selenium/short-suite-management-ui b/selenium/short-suite-management-ui
index a0d4a3a86c38..03828544c99e 100644
--- a/selenium/short-suite-management-ui
+++ b/selenium/short-suite-management-ui
@@ -8,4 +8,5 @@ mgt/exchanges.sh
mgt/queuesAndStreams.sh
mgt/limits.sh
mgt/amqp10-connections.sh
-mgt/mqtt-connections.sh
\ No newline at end of file
+mgt/mqtt-connections.sh
+mgt/feature-flags.sh
\ No newline at end of file
diff --git a/selenium/suites/mgt/feature-flags.sh b/selenium/suites/mgt/feature-flags.sh
new file mode 100755
index 000000000000..87cd1082aa01
--- /dev/null
+++ b/selenium/suites/mgt/feature-flags.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+TEST_CASES_PATH=/feature-flags
+TEST_CONFIG_PATH=/basic-auth
+
+source $SCRIPT/../../bin/suite_template $@
+run
diff --git a/selenium/test/feature-flags/feature-flags.js b/selenium/test/feature-flags/feature-flags.js
new file mode 100644
index 000000000000..a64afbf5d203
--- /dev/null
+++ b/selenium/test/feature-flags/feature-flags.js
@@ -0,0 +1,54 @@
+const { By, Key, until, Builder } = require('selenium-webdriver')
+require('chromedriver')
+const assert = require('assert')
+const { buildDriver, goToHome, captureScreensFor, teardown, findTableRow, delay } = require('../utils')
+
+const LoginPage = require('../pageobjects/LoginPage')
+const OverviewPage = require('../pageobjects/OverviewPage')
+const AdminTab = require('../pageobjects/AdminTab')
+const FeatureFlagsAdminTab = require('../pageobjects/FeatureFlagsAdminTab')
+
+describe('Feature flags in Admin tab', function () {
+ let login
+ let overview
+ let ffTab
+ let captureScreen
+
+ before(async function () {
+ driver = buildDriver()
+ await goToHome(driver)
+ login = new LoginPage(driver)
+ overview = new OverviewPage(driver)
+ adminTab = new AdminTab(driver)
+ ffTab = new FeatureFlagsAdminTab(driver)
+ captureScreen = captureScreensFor(driver, __filename)
+
+ await login.login('guest', 'guest')
+ if (!await overview.isLoaded()) {
+ throw new Error('Failed to login')
+ }
+ await overview.selectRefreshOption("Do not refresh")
+ })
+
+ it('it has at least one feature flag', async function () {
+ await overview.clickOnAdminTab()
+ await adminTab.clickOnFeatureFlags()
+ let ffTable = await ffTab.getAll()
+ assert(ffTable.length > 0)
+ })
+ it('it has khepri_db feature flag', async function () {
+ await overview.clickOnAdminTab()
+ await adminTab.clickOnFeatureFlags()
+ let ffTable = await ffTab.getAll()
+ assert(findTableRow(ffTable, function(row) {
+ return row[0] === 'khepri_db'
+ }))
+ let state = await ffTab.getState('khepri_db')
+ assert(await state.isSelected())
+ })
+
+
+ after(async function () {
+ await teardown(driver, this, captureScreen)
+ })
+})
diff --git a/selenium/test/pageobjects/BasePage.js b/selenium/test/pageobjects/BasePage.js
index 36bad7c4e0b4..63106a1fcbe8 100644
--- a/selenium/test/pageobjects/BasePage.js
+++ b/selenium/test/pageobjects/BasePage.js
@@ -33,7 +33,14 @@ module.exports = class BasePage {
this.polling = parseInt(process.env.SELENIUM_POLLING) || 500 // how frequent selenium searches for an element
this.interactionDelay = parseInt(process.env.SELENIUM_INTERACTION_DELAY) || 0 // slow down interactions (when rabbit is behind a http proxy)
}
-
+ async ensureSectionIsVisible(section) {
+ let classes = await this.driver.findElement(section).getAttribute("class")
+ if (classes.search('section-visible') < 0) {
+ return this.click(section)
+ } else {
+ return Promise.resolve(true)
+ }
+ }
async goTo(path) {
return driver.get(d.baseUrl + path)
}
diff --git a/selenium/test/pageobjects/FeatureFlagsAdminTab.js b/selenium/test/pageobjects/FeatureFlagsAdminTab.js
new file mode 100644
index 000000000000..9e871a1cd7d3
--- /dev/null
+++ b/selenium/test/pageobjects/FeatureFlagsAdminTab.js
@@ -0,0 +1,59 @@
+const { By, Key, until, Builder } = require('selenium-webdriver')
+const { delay } = require('../utils')
+
+const AdminTab = require('./AdminTab')
+
+
+const FEATURE_FLAGS_SECTION = By.css('div#main div#feature-flags')
+const FEATURE_FLAGS_TABLE = By.css('div#main div#feature-flags div#ff-table-section table')
+
+const ACCEPT_ENABLE_EXPERIMENTAL_FEATURE_FLAG = By.css('p#ff-exp-ack-supported')
+const CONFIRM_ENABLE_EXPERIMENTAL_FEATURE_FLAG = By.css('button#ff-exp-confirm')
+
+
+module.exports = class FeatureFlagsAdminTab extends AdminTab {
+ async isLoaded () {
+ await this.waitForDisplayed(FEATURE_FLAGS_SECTION)
+ }
+
+ async getAll() {
+ await this.ensureSectionIsVisible(FEATURE_FLAGS_SECTION)
+ try
+ {
+ return this.getTable(FEATURE_FLAGS_TABLE, 4)
+ } catch (NoSuchElement) {
+ return Promise.resolve([])
+ }
+ }
+
+ async enable(name, isExperimental = false) {
+ let state = await this.getState(name)
+ if (!await state.isSelected()) {
+ await this.driver.findElement(this.getParentCheckboxLocator(name)).click()
+ if (isExperimental) {
+ await delay(1000)
+ const dialog = await this.driver.wait(
+ until.elementLocated(By.css('dialog#ff-exp-dialog[open]')),
+ 10000 // 10 seconds timeout
+ )
+ await dialog.findElement(ACCEPT_ENABLE_EXPERIMENTAL_FEATURE_FLAG).click()
+ await dialog.findElement(CONFIRM_ENABLE_EXPERIMENTAL_FEATURE_FLAG).click()
+ return delay(1000)
+ }else {
+ return Promise.resolve()
+ }
+ }else {
+ return Promise.resolve()
+ }
+ }
+ getCheckboxLocator(name) {
+ return By.css('div#ff-table-section table input#ff-checkbox-' + name)
+ }
+ getParentCheckboxLocator(name) {
+ return By.css('div#ff-table-section table td#ff-td-' + name)
+ }
+ async getState(name) {
+ return this.driver.findElement(this.getCheckboxLocator(name))
+ }
+
+}