Skip to content

Commit 13fc8f9

Browse files
Add tess for feature flags
1 parent 7413939 commit 13fc8f9

File tree

8 files changed

+220
-22
lines changed

8 files changed

+220
-22
lines changed

deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</p>
2121
<button id="ff-enable-all-button" onclick='enable_all_stable_feature_flags(this);'>Enable all stable feature flags</button>
2222
</div>
23-
<div class="section">
23+
<div class="section" id="feature-flags">
2424
<h2>Feature Flags</h2>
2525
<div class="hider">
2626
<%= filter_ui(nonreq_feature_flags) %>
@@ -301,7 +301,7 @@ function enable_feature_flag(checkbox, feature_flag) {
301301
</svg>
302302
<% } %>
303303
</td>
304-
<td class="c ff-stability-<%= feature_flag.stability %> ff-state-<%= feature_flag.state %>">
304+
<td id="ff-td-<%= feature_flag.name %>" class="c ff-stability-<%= feature_flag.stability %> ff-state-<%= feature_flag.state %>">
305305
<input id="ff-checkbox-<%= feature_flag.name %>" type="checkbox" class="toggle"
306306
<% if (feature_flag.state == 'enabled') { %>
307307
checked disabled
@@ -332,6 +332,7 @@ function enable_feature_flag(checkbox, feature_flag) {
332332
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
333333
<path d="M12 15H12.01M12 12V9M4.98207 19H19.0179C20.5615 19 21.5233 17.3256 20.7455 15.9923L13.7276 3.96153C12.9558 2.63852 11.0442 2.63852 10.2724 3.96153L3.25452 15.9923C2.47675 17.3256 3.43849 19 4.98207 19Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
334334
</svg>
335+
<div id="ff-exp-dialog">
335336
<h3>Enabling an experimental feature flag</h3>
336337
<p>
337338
<strong>The <code class="ff-name"></code> feature flag is experimental</strong>.
@@ -349,7 +350,7 @@ function enable_feature_flag(checkbox, feature_flag) {
349350
Therefore, upgrades to a later version of RabbitMQ with this feature flag
350351
enabled are supported.
351352
</p>
352-
<p>
353+
<p id="ff-exp-ack-supported">
353354
<input id="ff-exp-ack-supported-checkbox" type="checkbox"/>
354355
<label for="ff-exp-ack-supported-checkbox">I understand that this feature is experimental and should be tested first.</label>
355356
</p>
@@ -383,4 +384,5 @@ function enable_feature_flag(checkbox, feature_flag) {
383384
</ol>
384385
<button id="ff-exp-confirm">Enable <span class="ff-name"></span></button>
385386
<button id="ff-exp-cancel">Cancel</button>
387+
</div>
386388
</dialog>

selenium/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"author": "",
1313
"license": "ISC",
1414
"dependencies": {
15-
"chromedriver": "^137.0",
15+
"chromedriver": "^139.0",
1616
"ejs": "^3.1.8",
1717
"express": "^4.18.2",
1818
"geckodriver": "^3.0.2",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
3+
SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4+
5+
TEST_CASES_PATH=/feature-flags
6+
TEST_CONFIG_PATH=/basic-auth
7+
8+
source $SCRIPT/../../bin/suite_template $@
9+
run
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const { By, Key, until, Builder } = require('selenium-webdriver')
2+
require('chromedriver')
3+
const assert = require('assert')
4+
const { buildDriver, goToHome, captureScreensFor, teardown, findTableRow, delay } = require('../utils')
5+
6+
const LoginPage = require('../pageobjects/LoginPage')
7+
const OverviewPage = require('../pageobjects/OverviewPage')
8+
const AdminTab = require('../pageobjects/AdminTab')
9+
const FeatureFlagsAdminTab = require('../pageobjects/FeatureFlagsAdminTab')
10+
11+
describe('Feature flags in Admin tab', function () {
12+
let login
13+
let overview
14+
let ffTab
15+
let captureScreen
16+
17+
before(async function () {
18+
driver = buildDriver()
19+
await goToHome(driver)
20+
login = new LoginPage(driver)
21+
overview = new OverviewPage(driver)
22+
adminTab = new AdminTab(driver)
23+
ffTab = new FeatureFlagsAdminTab(driver)
24+
captureScreen = captureScreensFor(driver, __filename)
25+
26+
await login.login('guest', 'guest')
27+
if (!await overview.isLoaded()) {
28+
throw new Error('Failed to login')
29+
}
30+
await overview.selectRefreshOption("Do not refresh")
31+
})
32+
33+
it('it has at least one feature flag', async function () {
34+
await overview.clickOnAdminTab()
35+
await adminTab.clickOnFeatureFlags()
36+
let ffTable = await ffTab.getAll()
37+
assert(ffTable.length > 0)
38+
})
39+
it('it has khepri_db feature flag', async function () {
40+
await overview.clickOnAdminTab()
41+
await adminTab.clickOnFeatureFlags()
42+
let ffTable = await ffTab.getAll()
43+
assert(findTableRow(ffTable, function(row) {
44+
return row[0] === 'khepri_db'
45+
}))
46+
})
47+
it('enable khepri_db feature flag', async function () {
48+
await overview.clickOnAdminTab()
49+
await adminTab.clickOnFeatureFlags()
50+
let prev_state = await ffTab.getState('khepri_db')
51+
assert(!await prev_state.isSelected())
52+
await ffTab.enable('khepri_db')
53+
delay(2000)
54+
let state = await ffTab.getState('khepri_db')
55+
assert(await state.isSelected())
56+
})
57+
58+
59+
after(async function () {
60+
await teardown(driver, this, captureScreen)
61+
})
62+
})
Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1 @@
1-
[accept,amqp10_client,amqp_client,base64url,cowboy,cowlib,eetcd,gun,jose,
2-
oauth2_client,prometheus,rabbitmq_amqp1_0,rabbitmq_auth_backend_cache,
3-
rabbitmq_auth_backend_http,rabbitmq_auth_backend_ldap,
4-
rabbitmq_auth_backend_oauth2,rabbitmq_auth_mechanism_ssl,rabbitmq_aws,
5-
rabbitmq_consistent_hash_exchange,rabbitmq_event_exchange,
6-
rabbitmq_federation,rabbitmq_federation_management,
7-
rabbitmq_federation_prometheus,rabbitmq_jms_topic_exchange,
8-
rabbitmq_management,rabbitmq_management_agent,rabbitmq_mqtt,
9-
rabbitmq_peer_discovery_aws,rabbitmq_peer_discovery_common,
10-
rabbitmq_peer_discovery_consul,rabbitmq_peer_discovery_etcd,
11-
rabbitmq_peer_discovery_k8s,rabbitmq_prometheus,rabbitmq_random_exchange,
12-
rabbitmq_recent_history_exchange,rabbitmq_sharding,rabbitmq_shovel,
13-
rabbitmq_shovel_management,rabbitmq_shovel_prometheus,rabbitmq_stomp,
14-
rabbitmq_stream,rabbitmq_stream_common,rabbitmq_stream_management,
15-
rabbitmq_top,rabbitmq_tracing,rabbitmq_trust_store,rabbitmq_web_dispatch,
16-
rabbitmq_web_mqtt,rabbitmq_web_stomp].
1+
[rabbitmq_management].
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const { By, Key, until, Builder } = require('selenium-webdriver')
2+
require('chromedriver')
3+
const assert = require('assert')
4+
const { buildDriver, goToHome, captureScreensFor, teardown, idpLoginPage, delay } = require('../../utils')
5+
6+
const SSOHomePage = require('../../pageobjects/SSOHomePage')
7+
const OverviewPage = require('../../pageobjects/OverviewPage')
8+
const ExchangesPage = require('../../pageobjects/ExchangesPage')
9+
const QueuesAndStreamsPage = require('../../pageobjects/QueuesAndStreamsPage')
10+
const AdminTab = require('../../pageobjects/AdminTab')
11+
const FeatureFlagsAdminTab = require('../../pageobjects/FeatureFlagsAdminTab')
12+
13+
describe('After enabling khepri_db feature flag in Admin Tab', function () {
14+
let homePage
15+
let idpLogin
16+
let overview
17+
let exchanges
18+
let queues
19+
let captureScreen
20+
var driver
21+
let adminTab
22+
let ffTab
23+
24+
before(async function () {
25+
driver = buildDriver()
26+
await goToHome(driver)
27+
homePage = new SSOHomePage(driver)
28+
idpLogin = idpLoginPage(driver)
29+
overview = new OverviewPage(driver)
30+
exchanges = new ExchangesPage(driver)
31+
queues = new QueuesAndStreamsPage(driver)
32+
adminTab = new AdminTab(driver)
33+
ffTab = new FeatureFlagsAdminTab(driver)
34+
captureScreen = captureScreensFor(driver, __filename)
35+
36+
await homePage.clickToLogin()
37+
await idpLogin.login('rabbit_admin', 'rabbit_admin')
38+
if (!await overview.isLoaded()) {
39+
throw new Error('Failed to login')
40+
}
41+
await overview.selectRefreshOption("Do not refresh")
42+
await overview.clickOnAdminTab()
43+
await adminTab.clickOnFeatureFlags()
44+
let previousState = await ffTab.getState('khepri_db')
45+
assert(!await previousState.isSelected())
46+
await ffTab.enable('khepri_db')
47+
await delay(5000)
48+
let state = await ffTab.getState('khepri_db')
49+
assert(await state.isSelected())
50+
51+
})
52+
53+
it('the user can continue using the management ui', async function () {
54+
await overview.clickOnOverviewTab()
55+
if (!await overview.isLoaded()) {
56+
throw new Error('Failed to overview tab')
57+
}
58+
await overview.clickOnExchangesTab()
59+
if (!await exchanges.isLoaded()) {
60+
throw new Error('Failed to exchanges tab')
61+
}
62+
await overview.clickOnQueuesTab()
63+
if (!await queues.isLoaded()) {
64+
throw new Error('Failed to queues tab')
65+
}
66+
})
67+
68+
describe('and when the user logs out', async function() {
69+
70+
before(async function () {
71+
await overview.logout()
72+
await homePage.isLoaded()
73+
})
74+
75+
it ('it can log back in', async function() {
76+
await homePage.clickToLogin()
77+
await idpLogin.login('rabbit_admin', 'rabbit_admin')
78+
await overview.isLoaded()
79+
})
80+
})
81+
82+
after(async function () {
83+
await teardown(driver, this, captureScreen)
84+
})
85+
})

selenium/test/pageobjects/BasePage.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ module.exports = class BasePage {
3333
this.polling = parseInt(process.env.SELENIUM_POLLING) || 500 // how frequent selenium searches for an element
3434
this.interactionDelay = parseInt(process.env.SELENIUM_INTERACTION_DELAY) || 0 // slow down interactions (when rabbit is behind a http proxy)
3535
}
36-
36+
async ensureSectionIsVisible(section) {
37+
let classes = await this.driver.findElement(section).getAttribute("class")
38+
if (classes.search('section-visible') < 0) {
39+
return this.click(section)
40+
} else {
41+
return Promise.resolve(true)
42+
}
43+
}
3744
async goTo(path) {
3845
return driver.get(d.baseUrl + path)
3946
}
@@ -381,7 +388,6 @@ module.exports = class BasePage {
381388

382389
async click (locator) {
383390
if (this.interactionDelay) await this.driver.sleep(this.interactionDelay)
384-
385391
const element = await this.waitForDisplayed(locator)
386392
try {
387393
return element.click()
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const { By, Key, until, Builder } = require('selenium-webdriver')
2+
3+
const AdminTab = require('./AdminTab')
4+
5+
const FEATURE_FLAGS_SECTION = By.css('div#main div#feature-flags')
6+
const FEATURE_FLAGS_TABLE = By.css('div#main div#feature-flags div#ff-table-section table')
7+
8+
const WARNING_EXPERIMENTAL_FEATURE_FLAG = By.css('divff-exp-dialog')
9+
const ACCEPT_ENABLE_EXPERIMENTAL_FEATURE_FLAG = By.css('div#ff-exp-dialog p.ff-exp-ack-supported')
10+
const CONFIRM_ENABLE_EXPERIMENTAL_FEATURE_FLAG = By.css('div#ff-exp-dialog button.ff-exp-confirm')
11+
12+
13+
module.exports = class FeatureFlagsAdminTab extends AdminTab {
14+
async isLoaded () {
15+
await this.waitForDisplayed(FEATURE_FLAGS_SECTION)
16+
}
17+
18+
async getAll() {
19+
await this.ensureSectionIsVisible(FEATURE_FLAGS_SECTION)
20+
try
21+
{
22+
return this.getTable(FEATURE_FLAGS_TABLE, 4)
23+
} catch (NoSuchElement) {
24+
return Promise.resolve([])
25+
}
26+
}
27+
28+
async enable(name) {
29+
let state = await this.getState(name)
30+
if (!await state.isSelected()) {
31+
await this.driver.findElement(this.getParentCheckboxLocator(name)).click()
32+
await this.waitForVisible(WARNING_EXPERIMENTAL_FEATURE_FLAG)
33+
await this.driver.findElement(ACCEPT_ENABLE_EXPERIMENTAL_FEATURE_FLAG).click()
34+
return this.driver.findElement(CONFIRM_ENABLE_EXPERIMENTAL_FEATURE_FLAG).click()
35+
}else {
36+
return Promise.resolve()
37+
}
38+
}
39+
getCheckboxLocator(name) {
40+
return By.css('div#ff-table-section table input#ff-checkbox-' + name)
41+
}
42+
getParentCheckboxLocator(name) {
43+
return By.css('div#ff-table-section table td#ff-td-' + name)
44+
}
45+
async getState(name) {
46+
return this.driver.findElement(this.getCheckboxLocator(name))
47+
}
48+
49+
}

0 commit comments

Comments
 (0)