diff --git a/ui/config/environment.js b/ui/config/environment.js
index 401413bc7..4896b1ad8 100644
--- a/ui/config/environment.js
+++ b/ui/config/environment.js
@@ -56,6 +56,9 @@ module.exports = function(environment) {
ENV.APP.rootElement = '#ember-testing';
ENV.APP.autoboot = false;
ENV.flashMessageDefaults.timeout = 50;
+ ENV['ember-cli-mirage'] = {
+ enabled: false,
+ };
}
if (environment !== 'production') {
ENV.APP.DEFAULT_PAGE_SIZE = 15;
diff --git a/ui/mirage/config.js b/ui/mirage/config.js
index 4c145a75d..5fb7e4a01 100644
--- a/ui/mirage/config.js
+++ b/ui/mirage/config.js
@@ -19,5 +19,15 @@ export default function() {
data: db['metrics/configs'].first(),
};
});
+
+ this.get('/sys/internal/ui/feature-flags', db => {
+ const featuresResponse = db.features.first();
+ return {
+ data: {
+ feature_flags: featuresResponse ? featuresResponse.feature_flags : null,
+ },
+ };
+ });
+
this.passthrough();
}
diff --git a/ui/mirage/factories/feature.js b/ui/mirage/factories/feature.js
new file mode 100644
index 000000000..832aee5ed
--- /dev/null
+++ b/ui/mirage/factories/feature.js
@@ -0,0 +1,7 @@
+import { Factory } from 'ember-cli-mirage';
+
+export default Factory.extend({
+ feature_flags() {
+ return []; // VAULT_CLOUD_ADMIN_NAMESPACE
+ },
+});
diff --git a/ui/mirage/factories/user.js b/ui/mirage/factories/user.js
deleted file mode 100644
index fa39d34cc..000000000
--- a/ui/mirage/factories/user.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import Mirage from 'ember-cli-mirage';
-
-export default Mirage.Factory.extend({
- name(i) {
- return `Person ${i}`;
- },
- age: 28,
- admin: false,
-});
diff --git a/ui/mirage/models/feature.js b/ui/mirage/models/feature.js
new file mode 100644
index 000000000..4f84f3bd7
--- /dev/null
+++ b/ui/mirage/models/feature.js
@@ -0,0 +1,5 @@
+import { Model } from 'ember-cli-mirage';
+
+export default Model.extend({
+ feature_flags: null,
+});
diff --git a/ui/mirage/scenarios/default.js b/ui/mirage/scenarios/default.js
index 216d7fcb2..5e6544827 100644
--- a/ui/mirage/scenarios/default.js
+++ b/ui/mirage/scenarios/default.js
@@ -1,3 +1,4 @@
export default function(server) {
server.create('metrics/config');
+ server.create('feature', { feature_flags: ['SOME_FLAG', 'VAULT_CLOUD_ADMIN_NAMESPACE'] });
}
diff --git a/ui/tests/acceptance/enterprise-namespaces-test.js b/ui/tests/acceptance/enterprise-namespaces-test.js
index 6188fcf48..5df7780bc 100644
--- a/ui/tests/acceptance/enterprise-namespaces-test.js
+++ b/ui/tests/acceptance/enterprise-namespaces-test.js
@@ -1,4 +1,4 @@
-import { click, settled, visit } from '@ember/test-helpers';
+import { click, settled, visit, fillIn, currentURL } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { create } from 'ember-cli-page-object';
@@ -71,4 +71,22 @@ module('Acceptance | Enterprise | namespaces', function(hooks) {
.dom('[data-test-namespace-link="beep/boop/bop"]')
.exists('renders the link to the nested namespace');
});
+
+ test('it shows the regular namespace toolbar when not managed', async function(assert) {
+ // This test is the opposite of the test in managed-namespace-test
+ await logout.visit();
+ assert.equal(currentURL(), '/vault/auth?with=token', 'Does not redirect');
+ assert.dom('[data-test-namespace-toolbar]').exists('Normal namespace toolbar exists');
+ assert
+ .dom('[data-test-managed-namespace-toolbar]')
+ .doesNotExist('Managed namespace toolbar does not exist');
+ assert.dom('input#namespace').hasAttribute('placeholder', '/ (Root)');
+ await fillIn('input#namespace', '/foo');
+ let encodedNamespace = encodeURIComponent('/foo');
+ assert.equal(
+ currentURL(),
+ `/vault/auth?namespace=${encodedNamespace}&with=token`,
+ 'Does not prepend root to namespace'
+ );
+ });
});
diff --git a/ui/tests/acceptance/init-test.js b/ui/tests/acceptance/init-test.js
index bb2fdc543..8c257906f 100644
--- a/ui/tests/acceptance/init-test.js
+++ b/ui/tests/acceptance/init-test.js
@@ -77,6 +77,7 @@ module('Acceptance | init', function(hooks) {
this.server.get('/v1/sys/health', () => {
return [200, { 'Content-Type': 'application/json' }, JSON.stringify(HEALTH_RESPONSE)];
});
+ this.server.get('/v1/sys/internal/ui/feature-flags', this.server.passthrough);
});
hooks.afterEach(function() {
diff --git a/ui/tests/acceptance/managed-namespace-test.js b/ui/tests/acceptance/managed-namespace-test.js
new file mode 100644
index 000000000..0437c8dc8
--- /dev/null
+++ b/ui/tests/acceptance/managed-namespace-test.js
@@ -0,0 +1,51 @@
+import { module, test } from 'qunit';
+import { currentURL, visit, fillIn } from '@ember/test-helpers';
+import { setupApplicationTest } from 'ember-qunit';
+import Pretender from 'pretender';
+
+const FEATURE_FLAGS_RESPONSE = {
+ data: {
+ feature_flags: ['VAULT_CLOUD_ADMIN_NAMESPACE'],
+ },
+};
+
+module('Acceptance | Enterprise | Managed namespace root', function(hooks) {
+ setupApplicationTest(hooks);
+
+ hooks.beforeEach(function() {
+ /**
+ * Since the features are fetched on the application load,
+ * we have to populate them on the beforeEach hook because
+ * the fetch won't trigger again within the tests
+ */
+ this.server = new Pretender(function() {
+ this.get('/v1/sys/internal/ui/feature-flags', () => {
+ return [200, { 'Content-Type': 'application/json' }, JSON.stringify(FEATURE_FLAGS_RESPONSE)];
+ });
+ this.get('/v1/sys/health', this.passthrough);
+ this.get('/v1/sys/seal-status', this.passthrough);
+ this.get('/v1/sys/license/features', this.passthrough);
+ });
+ });
+
+ hooks.afterEach(function() {
+ this.server.shutdown();
+ });
+
+ test('it shows the managed namespace toolbar when feature flag exists', async function(assert) {
+ await visit('/vault/auth');
+ assert.equal(currentURL(), '/vault/auth?namespace=admin&with=token', 'Redirected to base namespace');
+
+ assert.dom('[data-test-namespace-toolbar]').doesNotExist('Normal namespace toolbar does not exist');
+ assert.dom('[data-test-managed-namespace-toolbar]').exists('Managed namespace toolbar exists');
+ assert.dom('[data-test-managed-namespace-root]').hasText('/admin', 'Shows /admin namespace prefix');
+ assert.dom('input#namespace').hasAttribute('placeholder', '/ (Default)');
+ await fillIn('input#namespace', '/foo');
+ let encodedNamespace = encodeURIComponent('admin/foo');
+ assert.equal(
+ currentURL(),
+ `/vault/auth?namespace=${encodedNamespace}&with=token`,
+ 'Correctly prepends root to namespace'
+ );
+ });
+});
diff --git a/ui/tests/unit/services/feature-flag-test.js b/ui/tests/unit/services/feature-flag-test.js
new file mode 100644
index 000000000..d9da25577
--- /dev/null
+++ b/ui/tests/unit/services/feature-flag-test.js
@@ -0,0 +1,24 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module('Unit | Service | feature-flag', function(hooks) {
+ setupTest(hooks);
+
+ test('it exists', function(assert) {
+ let service = this.owner.lookup('service:feature-flag');
+ assert.ok(service);
+ });
+
+ test('it returns the namespace root when flag is present', function(assert) {
+ let service = this.owner.lookup('service:feature-flag');
+ assert.equal(service.managedNamespaceRoot, null, 'Managed namespace root is null by default');
+ service.setFeatureFlags(['VAULT_CLOUD_ADMIN_NAMESPACE']);
+ assert.equal(service.managedNamespaceRoot, 'admin', 'Managed namespace is admin when flag present');
+ service.setFeatureFlags(['SOMETHING_ELSE']);
+ assert.equal(
+ service.managedNamespaceRoot,
+ null,
+ 'Flags were overwritten and root namespace is null again'
+ );
+ });
+});