From 4bc4a9ec47f30987b8481bcfc10e4983d4d9a2ef Mon Sep 17 00:00:00 2001
From: Marcial Rosales
Date: Thu, 28 Aug 2025 10:51:09 +0200
Subject: [PATCH 1/4] Test feature flags
---
selenium/full-suite-management-ui | 1 +
selenium/package.json | 2 +-
selenium/short-suite-management-ui | 3 +-
selenium/suites/mgt/feature-flags.sh | 9 +++
selenium/test/feature-flags/feature-flags.js | 52 +++++++++++++++++
selenium/test/pageobjects/BasePage.js | 9 ++-
.../test/pageobjects/FeatureFlagsAdminTab.js | 56 +++++++++++++++++++
7 files changed, 129 insertions(+), 3 deletions(-)
create mode 100644 selenium/suites/mgt/feature-flags.sh
create mode 100644 selenium/test/feature-flags/feature-flags.js
create mode 100644 selenium/test/pageobjects/FeatureFlagsAdminTab.js
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 100644
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..736cbfb33e8c
--- /dev/null
+++ b/selenium/test/feature-flags/feature-flags.js
@@ -0,0 +1,52 @@
+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'
+ }))
+ })
+
+
+ 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..71f21f9e5d70
--- /dev/null
+++ b/selenium/test/pageobjects/FeatureFlagsAdminTab.js
@@ -0,0 +1,56 @@
+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) {
+ let state = await this.getState(name)
+ if (!await state.isSelected()) {
+ await this.driver.findElement(this.getParentCheckboxLocator(name)).click()
+ 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()
+ }
+ }
+ 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))
+ }
+
+}
From a809c3000ead685dee8002a30ab7f1a7ca211d53 Mon Sep 17 00:00:00 2001
From: Marcial Rosales
Date: Thu, 28 Aug 2025 11:02:06 +0200
Subject: [PATCH 2/4] Test feature flags in management ui with selenium
---
deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs | 2 +-
selenium/suites/mgt/feature-flags.sh | 0
2 files changed, 1 insertion(+), 1 deletion(-)
mode change 100644 => 100755 selenium/suites/mgt/feature-flags.sh
diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs
index 0ab4d6a16f55..4d3a78d43d7b 100644
--- a/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs
+++ b/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs
@@ -20,7 +20,7 @@
-
+
Feature Flags
<%= filter_ui(nonreq_feature_flags) %>
diff --git a/selenium/suites/mgt/feature-flags.sh b/selenium/suites/mgt/feature-flags.sh
old mode 100644
new mode 100755
From fbd83663e332a9487105cb8a43a46af1ddf2e1eb Mon Sep 17 00:00:00 2001
From: Marcial Rosales
Date: Thu, 28 Aug 2025 11:03:31 +0200
Subject: [PATCH 3/4] Check that since 4.2 onwards khepri_db is enabled
---
selenium/test/feature-flags/feature-flags.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/selenium/test/feature-flags/feature-flags.js b/selenium/test/feature-flags/feature-flags.js
index 736cbfb33e8c..a64afbf5d203 100644
--- a/selenium/test/feature-flags/feature-flags.js
+++ b/selenium/test/feature-flags/feature-flags.js
@@ -42,7 +42,9 @@ describe('Feature flags in Admin tab', function () {
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())
})
From 4aa666e08a7d3bf1bbc5ec808606dd664ada3a34 Mon Sep 17 00:00:00 2001
From: Marcial Rosales
Date: Thu, 28 Aug 2025 11:40:22 +0200
Subject: [PATCH 4/4] Take into account experimental ff
---
.../test/pageobjects/FeatureFlagsAdminTab.js | 23 +++++++++++--------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/selenium/test/pageobjects/FeatureFlagsAdminTab.js b/selenium/test/pageobjects/FeatureFlagsAdminTab.js
index 71f21f9e5d70..9e871a1cd7d3 100644
--- a/selenium/test/pageobjects/FeatureFlagsAdminTab.js
+++ b/selenium/test/pageobjects/FeatureFlagsAdminTab.js
@@ -26,19 +26,22 @@ module.exports = class FeatureFlagsAdminTab extends AdminTab {
}
}
- async enable(name) {
+ async enable(name, isExperimental = false) {
let state = await this.getState(name)
if (!await state.isSelected()) {
await this.driver.findElement(this.getParentCheckboxLocator(name)).click()
- 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)
+ 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()
}