diff --git a/ui-v2/app/templates/dc/acls/-notifications.hbs b/ui-v2/app/templates/dc/acls/-notifications.hbs
new file mode 100644
index 000000000..1b577c6ef
--- /dev/null
+++ b/ui-v2/app/templates/dc/acls/-notifications.hbs
@@ -0,0 +1,32 @@
+{{#if (eq type 'create')}}
+ {{#if (eq status 'success') }}
+ Your ACL token has been added.
+ {{else}}
+ There was an error adding your ACL token.
+ {{/if}}
+{{else if (eq type 'update') }}
+ {{#if (eq status 'success') }}
+ Your ACL token has been saved.
+ {{else}}
+ There was an error saving your ACL token.
+ {{/if}}
+{{ else if (eq type 'delete')}}
+ {{#if (eq status 'success') }}
+ Your ACL token was deleted.
+ {{else}}
+ There was an error deleting your ACL token.
+ {{/if}}
+{{ else if (eq type 'use')}}
+ {{#if (eq status 'success') }}
+ Now using new ACL token.
+ {{else}}
+ There was an error using that ACL token.
+ {{/if}}
+{{ else if (eq type 'clone')}}
+ {{#if (eq status 'success') }}
+ Your ACL token was cloned.
+ {{else}}
+ There was an error cloning your ACL token.
+ {{/if}}
+{{/if}}
+
diff --git a/ui-v2/app/templates/dc/acls/edit.hbs b/ui-v2/app/templates/dc/acls/edit.hbs
index 4a6f70301..dde086202 100644
--- a/ui-v2/app/templates/dc/acls/edit.hbs
+++ b/ui-v2/app/templates/dc/acls/edit.hbs
@@ -1,4 +1,7 @@
{{#app-view class="acl edit" loading=isLoading}}
+ {{#block-slot 'notification' as |status type|}}
+ {{partial 'dc/acls/notifications'}}
+ {{/block-slot}}
{{#block-slot 'breadcrumbs'}}
Intentions
diff --git a/ui-v2/app/templates/dc/intentions/notifications.hbs b/ui-v2/app/templates/dc/intentions/notifications.hbs
new file mode 100644
index 000000000..cdef9d5f2
--- /dev/null
+++ b/ui-v2/app/templates/dc/intentions/notifications.hbs
@@ -0,0 +1,22 @@
+{{#if (eq type 'create')}}
+ {{#if (eq status 'success') }}
+ Your intention has been added.
+ {{else if (eq status 'exists') }}
+ An intention already exists for this Source-Destination pair. Please enter a different combination of Services, or search the intentions to edit an existing intention.
+ {{else}}
+ There was an error adding your intention.
+ {{/if}}
+{{else if (eq type 'update') }}
+ {{#if (eq status 'success') }}
+ Your intention has been saved.
+ {{else}}
+ There was an error saving your intention.
+ {{/if}}
+{{ else if (eq type 'delete')}}
+ {{#if (eq status 'success') }}
+ Your intention was deleted.
+ {{else}}
+ There was an error deleting your intention.
+ {{/if}}
+{{/if}}
+
diff --git a/ui-v2/app/templates/dc/kv/-notifications.hbs b/ui-v2/app/templates/dc/kv/-notifications.hbs
new file mode 100644
index 000000000..7399dc2cd
--- /dev/null
+++ b/ui-v2/app/templates/dc/kv/-notifications.hbs
@@ -0,0 +1,20 @@
+{{#if (eq type 'create')}}
+ {{#if (eq status 'success') }}
+ Your key has been added.
+ {{else}}
+ There was an error adding your key.
+ {{/if}}
+{{else if (eq type 'update') }}
+ {{#if (eq status 'success') }}
+ Your key has been saved.
+ {{else}}
+ There was an error saving your key.
+ {{/if}}
+{{ else if (eq type 'delete')}}
+ {{#if (eq status 'success') }}
+ Your key was deleted.
+ {{else}}
+ There was an error deleting your key.
+ {{/if}}
+{{/if}}
+
diff --git a/ui-v2/app/templates/dc/kv/edit.hbs b/ui-v2/app/templates/dc/kv/edit.hbs
index 22c7b3973..7bf538afb 100644
--- a/ui-v2/app/templates/dc/kv/edit.hbs
+++ b/ui-v2/app/templates/dc/kv/edit.hbs
@@ -1,4 +1,7 @@
{{#app-view class="kv edit" loading=isLoading}}
+ {{#block-slot 'notification' as |status type|}}
+ {{partial 'dc/kv/notifications'}}
+ {{/block-slot}}
{{#block-slot 'breadcrumbs'}}
{{#confirmation-dialog message='Are you sure you want to invalidate this session?'}}
{{#block-slot 'action' as |confirm|}}
-
+
{{/block-slot}}
{{#block-slot 'dialog' as |execute cancel message|}}
diff --git a/ui-v2/app/templates/dc/services/index.hbs b/ui-v2/app/templates/dc/services/index.hbs
index 5267078b6..64c9a54e3 100644
--- a/ui-v2/app/templates/dc/services/index.hbs
+++ b/ui-v2/app/templates/dc/services/index.hbs
@@ -1,4 +1,8 @@
{{#app-view class="service list"}}
+ {{!TODO: Look at the item passed through to figure what partial to show, also move into its own service partial, for the moment keeping here for visibility}}
+ {{#block-slot 'notification' as |status type|}}
+ {{partial 'dc/acls/notifications'}}
+ {{/block-slot}}
{{#block-slot 'header'}}
Services
diff --git a/ui-v2/app/templates/settings.hbs b/ui-v2/app/templates/settings.hbs
index 8406d57a0..97390b6a6 100644
--- a/ui-v2/app/templates/settings.hbs
+++ b/ui-v2/app/templates/settings.hbs
@@ -1,5 +1,20 @@
{{#hashicorp-consul id="wrapper" dcs=dcs dc=dc}}
{{#app-view class="settings show"}}
+ {{#block-slot 'notification' as |status type|}}
+ {{#if (eq type 'update')}}
+ {{#if (eq status 'success') }}
+ Your settings were saved.
+ {{else}}
+ There was an error saving your settings.
+ {{/if}}
+ {{ else if (eq type 'delete')}}
+ {{#if (eq status 'success') }}
+ You settings have been reset.
+ {{else}}
+ There was an error resetting your settings.
+ {{/if}}
+ {{/if}}
+ {{/block-slot}}
{{#block-slot 'header'}}
Settings
diff --git a/ui-v2/ember-cli-build.js b/ui-v2/ember-cli-build.js
index f1a9b6d14..c097b895c 100644
--- a/ui-v2/ember-cli-build.js
+++ b/ui-v2/ember-cli-build.js
@@ -45,6 +45,9 @@ module.exports = function(defaults) {
"ie 11"
]
},
+ 'ember-cli-string-helpers': {
+ only: ['lowercase']
+ }
});
// Use `app.import` to add additional libraries to the generated
// output files.
diff --git a/ui-v2/package.json b/ui-v2/package.json
index 68caedaa4..7f822a76d 100644
--- a/ui-v2/package.json
+++ b/ui-v2/package.json
@@ -59,6 +59,7 @@
"ember-cli-sass": "^7.1.4",
"ember-cli-shims": "^1.2.0",
"ember-cli-sri": "^2.1.0",
+ "ember-cli-string-helpers": "^1.9.0",
"ember-cli-uglify": "^2.0.0",
"ember-cli-yadda": "^0.4.0",
"ember-collection": "^1.0.0-alpha.7",
diff --git a/ui-v2/tests/acceptance/dc/acls/update.feature b/ui-v2/tests/acceptance/dc/acls/update.feature
index f79d637c7..42a77b3e2 100644
--- a/ui-v2/tests/acceptance/dc/acls/update.feature
+++ b/ui-v2/tests/acceptance/dc/acls/update.feature
@@ -1,6 +1,6 @@
@setupApplicationTest
Feature: dc / acls / update: ACL Update
- Scenario: Update to [Name], [Type], [Rules]
+ Background:
Given 1 datacenter model with the value "datacenter"
And 1 acl model from yaml
---
@@ -12,6 +12,7 @@ Feature: dc / acls / update: ACL Update
acl: key
---
Then the url should be /datacenter/acls/key
+ Scenario: Update to [Name], [Type], [Rules]
Then I fill in with yaml
---
name: [Name]
@@ -23,6 +24,9 @@ Feature: dc / acls / update: ACL Update
Name: [Name]
Type: [Type]
---
+ Then the url should be /datacenter/acls
+ And "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "success" class
Where:
----------------------------------------------------------
| Name | Type | Rules |
@@ -31,9 +35,15 @@ Feature: dc / acls / update: ACL Update
| key%20name | client | node "0" {policy = "read"} |
| utf8? | management | node "0" {policy = "write"} |
----------------------------------------------------------
-@ignore
- Scenario: Rules can be edited/updated
- Then ok
-@ignore
- Scenario: The feedback dialog says success or failure
- Then ok
+ Scenario: There was an error saving the key
+ Given the url "/v1/acl/update" responds with a 500 status
+ And I submit
+ Then the url should be /datacenter/acls/key
+ Then "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "error" class
+# @ignore
+ # Scenario: Rules can be edited/updated
+ # Then ok
+# @ignore
+ # Scenario: The feedback dialog says success or failure
+ # Then ok
diff --git a/ui-v2/tests/acceptance/dc/acls/use.feature b/ui-v2/tests/acceptance/dc/acls/use.feature
index 6ad722487..cfc1e0eb0 100644
--- a/ui-v2/tests/acceptance/dc/acls/use.feature
+++ b/ui-v2/tests/acceptance/dc/acls/use.feature
@@ -18,6 +18,8 @@ Feature: dc / acls / use: Using an ACL token
And I click actions on the acls
And I click use on the acls
And I click confirmUse on the acls
+ Then "[data-notification]" has the "notification-use" class
+ And "[data-notification]" has the "success" class
Then I have settings like yaml
---
token: token
@@ -34,6 +36,8 @@ Feature: dc / acls / use: Using an ACL token
---
And I click use
And I click confirmUse
+ Then "[data-notification]" has the "notification-use" class
+ And "[data-notification]" has the "success" class
Then I have settings like yaml
---
token: token
diff --git a/ui-v2/tests/acceptance/dc/intentions/update.feature b/ui-v2/tests/acceptance/dc/intentions/update.feature
new file mode 100644
index 000000000..7d46b636a
--- /dev/null
+++ b/ui-v2/tests/acceptance/dc/intentions/update.feature
@@ -0,0 +1,41 @@
+@setupApplicationTest
+Feature: dc / intentions / update: Intention Update
+ Background:
+ Given 1 datacenter model with the value "datacenter"
+ And 1 intention model from yaml
+ ---
+ ID: intention-id
+ ---
+ When I visit the intention page for yaml
+ ---
+ dc: datacenter
+ intention: intention-id
+ ---
+ Then the url should be /datacenter/intentions/intention-id
+ Scenario: Update to [Description], [Action], [Rules]
+ Then I fill in with yaml
+ ---
+ Description: [Name]
+ ---
+ And I click "[value=[Action]]"
+ And I submit
+ Then a PUT request is made to "/v1/connect/intentions/intention-id?dc=datacenter" with the body from yaml
+ ---
+ Description: [Name]
+ Action: [Action]
+ ---
+ Then the url should be /datacenter/intentions
+ And "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "success" class
+ Where:
+ ------------------------------
+ | Description | Action |
+ | Desc | allow |
+ ------------------------------
+ Scenario: There was an error saving the intention
+ Given the url "/v1/connect/intentions/intention-id" responds with a 500 status
+ And I submit
+ Then the url should be /datacenter/intentions/intention-id
+ Then "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "error" class
+
diff --git a/ui-v2/tests/acceptance/dc/kvs/sessions/invalidate.feature b/ui-v2/tests/acceptance/dc/kvs/sessions/invalidate.feature
new file mode 100644
index 000000000..01cf8918d
--- /dev/null
+++ b/ui-v2/tests/acceptance/dc/kvs/sessions/invalidate.feature
@@ -0,0 +1,32 @@
+@setupApplicationTest
+Feature: dc / kvs / sessions / invalidate: Invalidate Lock Sessions
+ In order to invalidate a lock session
+ As a user
+ I should be able to invalidate a lock session by clicking a button and confirming
+ Background:
+ Given 1 datacenter model with the value "datacenter"
+ And 1 kv model from yaml
+ ---
+ Key: key
+ ---
+ When I visit the kv page for yaml
+ ---
+ dc: datacenter
+ kv: key
+ ---
+ Then the url should be /datacenter/kv/key/edit
+
+ Scenario: Invalidating the lock session
+ And I click delete on the session
+ And I click confirmDelete on the session
+ Then the last PUT request was made to "/v1/session/destroy/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter"
+ Then the url should be /datacenter/kv/key/edit
+ And "[data-notification]" has the "notification-delete" class
+ And "[data-notification]" has the "success" class
+ Scenario: Invalidating a lock session and receiving an error
+ Given the url "/v1/session/destroy/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter" responds with a 500 status
+ And I click delete on the session
+ And I click confirmDelete on the session
+ Then the url should be /datacenter/kv/key/edit
+ And "[data-notification]" has the "notification-delete" class
+ And "[data-notification]" has the "error" class
diff --git a/ui-v2/tests/acceptance/dc/kvs/update.feature b/ui-v2/tests/acceptance/dc/kvs/update.feature
index ba54ebf6c..970d3ef61 100644
--- a/ui-v2/tests/acceptance/dc/kvs/update.feature
+++ b/ui-v2/tests/acceptance/dc/kvs/update.feature
@@ -19,6 +19,8 @@ Feature: dc / kvs / update: KV Update
---
And I submit
Then a PUT request is made to "/v1/kv/[Name]?dc=datacenter" with the body "[Value]"
+ And "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "success" class
Where:
--------------------------------------------
| Name | Value |
@@ -43,6 +45,9 @@ Feature: dc / kvs / update: KV Update
---
And I submit
Then a PUT request is made to "/v1/kv/key?dc=datacenter" with the body " "
+ Then the url should be /datacenter/kv
+ And "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "success" class
Scenario: Update to a key change value to ''
And 1 kv model from yaml
---
@@ -60,6 +65,9 @@ Feature: dc / kvs / update: KV Update
---
And I submit
Then a PUT request is made to "/v1/kv/key?dc=datacenter" with no body
+ Then the url should be /datacenter/kv
+ And "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "success" class
Scenario: Update to a key when the value is empty
And 1 kv model from yaml
---
@@ -74,9 +82,22 @@ Feature: dc / kvs / update: KV Update
Then the url should be /datacenter/kv/key/edit
And I submit
Then a PUT request is made to "/v1/kv/key?dc=datacenter" with no body
-@ignore
- Scenario: The feedback dialog says success or failure
- Then ok
+ Then the url should be /datacenter/kv
+ And "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "success" class
+ Scenario: There was an error saving the key
+ When I visit the kv page for yaml
+ ---
+ dc: datacenter
+ kv: key
+ ---
+ Then the url should be /datacenter/kv/key/edit
+
+ Given the url "/v1/kv/key" responds with a 500 status
+ And I submit
+ Then the url should be /datacenter/kv/key/edit
+ Then "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "error" class
@ignore
Scenario: KV's with spaces are saved correctly
Then ok
diff --git a/ui-v2/tests/acceptance/dc/nodes/sessions/invalidate.feature b/ui-v2/tests/acceptance/dc/nodes/sessions/invalidate.feature
index 786b9e4fd..ae77c20f0 100644
--- a/ui-v2/tests/acceptance/dc/nodes/sessions/invalidate.feature
+++ b/ui-v2/tests/acceptance/dc/nodes/sessions/invalidate.feature
@@ -3,7 +3,7 @@ Feature: dc / nodes / sessions / invalidate: Invalidate Lock Sessions
In order to invalidate a lock session
As a user
I should be able to invalidate a lock session by clicking a button and confirming
- Scenario: Given 2 lock sessions
+ Background:
Given 1 datacenter model with the value "dc1"
And 1 node model from yaml
---
@@ -19,8 +19,20 @@ Feature: dc / nodes / sessions / invalidate: Invalidate Lock Sessions
dc: dc1
node: node-0
---
+ Then the url should be /dc1/nodes/node-0
And I click lockSessions on the tabs
Then I see lockSessionsIsSelected on the tabs
+ Scenario: Invalidating the lock session
And I click delete on the sessions
And I click confirmDelete on the sessions
Then a PUT request is made to "/v1/session/destroy/7bbbd8bb-fff3-4292-b6e3-cfedd788546a?dc=dc1"
+ Then the url should be /dc1/nodes/node-0
+ And "[data-notification]" has the "notification-delete" class
+ And "[data-notification]" has the "success" class
+ Scenario: Invalidating a lock session and receiving an error
+ Given the url "/v1/session/destroy/7bbbd8bb-fff3-4292-b6e3-cfedd788546a?dc=dc1" responds with a 500 status
+ And I click delete on the sessions
+ And I click confirmDelete on the sessions
+ Then the url should be /dc1/nodes/node-0
+ And "[data-notification]" has the "notification-delete" class
+ And "[data-notification]" has the "error" class
diff --git a/ui-v2/tests/acceptance/deleting.feature b/ui-v2/tests/acceptance/deleting.feature
index 5f7132881..19fabce57 100644
--- a/ui-v2/tests/acceptance/deleting.feature
+++ b/ui-v2/tests/acceptance/deleting.feature
@@ -1,8 +1,9 @@
@setupApplicationTest
-Feature: deleting: Deleting from the listing and the detail page with confirmation
- Scenario: Deleting a [Model] from the [Model] listing page
+Feature: deleting: Deleting items with confirmations, success and error notifications
+ Background:
Given 1 datacenter model with the value "datacenter"
- And 1 [Model] model from json
+ Scenario: Deleting a [Model] from the [Model] listing page
+ Given 1 [Model] model from json
---
[Data]
---
@@ -14,6 +15,26 @@ Feature: deleting: Deleting from the listing and the detail page with confirmati
And I click delete on the [Model]s
And I click confirmDelete on the [Model]s
Then a [Method] request is made to "[URL]"
+ And "[data-notification]" has the "notification-delete" class
+ And "[data-notification]" has the "success" class
+ When I visit the [Model] page for yaml
+ ---
+ dc: datacenter
+ [Slug]
+ ---
+ Given the url "[URL]" responds with a 500 status
+ And I click delete
+ And I click confirmDelete
+ And "[data-notification]" has the "notification-delete" class
+ And "[data-notification]" has the "error" class
+ Where:
+ ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ | Model | Method | URL | Data | Slug |
+ | acl | PUT | /v1/acl/destroy/something?dc=datacenter | {"Name": "something", "ID": "something"} | acl: something |
+ | kv | DELETE | /v1/kv/key-name?dc=datacenter | ["key-name"] | kv: key-name |
+ | intention | DELETE | /v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter | {"SourceName": "name", "ID": "ee52203d-989f-4f7a-ab5a-2bef004164ca"} | intention: ee52203d-989f-4f7a-ab5a-2bef004164ca |
+ ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Scenario: Deleting a [Model] from the [Model] detail page
When I visit the [Model] page for yaml
---
dc: datacenter
@@ -22,6 +43,18 @@ Feature: deleting: Deleting from the listing and the detail page with confirmati
And I click delete
And I click confirmDelete
Then a [Method] request is made to "[URL]"
+ And "[data-notification]" has the "notification-delete" class
+ And "[data-notification]" has the "success" class
+ When I visit the [Model] page for yaml
+ ---
+ dc: datacenter
+ [Slug]
+ ---
+ Given the url "[URL]" responds with a 500 status
+ And I click delete
+ And I click confirmDelete
+ And "[data-notification]" has the "notification-delete" class
+ And "[data-notification]" has the "error" class
Where:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Model | Method | URL | Data | Slug |
diff --git a/ui-v2/tests/acceptance/settings/update.feature b/ui-v2/tests/acceptance/settings/update.feature
index 300a9123a..46d5b8feb 100644
--- a/ui-v2/tests/acceptance/settings/update.feature
+++ b/ui-v2/tests/acceptance/settings/update.feature
@@ -16,4 +16,7 @@ Feature: settings / update: Update Settings
---
token: ''
---
+ And the url should be /settings
+ And "[data-notification]" has the "notification-update" class
+ And "[data-notification]" has the "success" class
diff --git a/ui-v2/tests/acceptance/steps/dc/intentions/update-steps.js b/ui-v2/tests/acceptance/steps/dc/intentions/update-steps.js
new file mode 100644
index 000000000..a7eff3228
--- /dev/null
+++ b/ui-v2/tests/acceptance/steps/dc/intentions/update-steps.js
@@ -0,0 +1,10 @@
+import steps from '../../steps';
+
+// step definitions that are shared between features should be moved to the
+// tests/acceptance/steps/steps.js file
+
+export default function(assert) {
+ return steps(assert).then('I should find a file', function() {
+ assert.ok(true, this.step);
+ });
+}
diff --git a/ui-v2/tests/acceptance/steps/dc/kvs/sessions/invalidate-steps.js b/ui-v2/tests/acceptance/steps/dc/kvs/sessions/invalidate-steps.js
new file mode 100644
index 000000000..9bfbe9ac9
--- /dev/null
+++ b/ui-v2/tests/acceptance/steps/dc/kvs/sessions/invalidate-steps.js
@@ -0,0 +1,10 @@
+import steps from '../../../steps';
+
+// step definitions that are shared between features should be moved to the
+// tests/acceptance/steps/steps.js file
+
+export default function(assert) {
+ return steps(assert).then('I should find a file', function() {
+ assert.ok(true, this.step);
+ });
+}
diff --git a/ui-v2/tests/lib/page-object/createDeletable.js b/ui-v2/tests/lib/page-object/createDeletable.js
index cc3aff4c5..88a328d96 100644
--- a/ui-v2/tests/lib/page-object/createDeletable.js
+++ b/ui-v2/tests/lib/page-object/createDeletable.js
@@ -1,10 +1,13 @@
export default function(clickable) {
- return function(obj) {
+ return function(obj, scope = '') {
+ if (scope !== '') {
+ scope = scope + ' ';
+ }
return {
...obj,
...{
- delete: clickable('[data-test-delete]'),
- confirmDelete: clickable('button.type-delete'),
+ delete: clickable(scope + '[data-test-delete]'),
+ confirmDelete: clickable(scope + 'button.type-delete'),
},
};
};
diff --git a/ui-v2/tests/pages/dc/kv/edit.js b/ui-v2/tests/pages/dc/kv/edit.js
index b644876cc..0e7b9ea1a 100644
--- a/ui-v2/tests/pages/dc/kv/edit.js
+++ b/ui-v2/tests/pages/dc/kv/edit.js
@@ -3,6 +3,7 @@ export default function(visitable, submitable, deletable, cancelable) {
cancelable(
deletable({
visit: visitable(['/:dc/kv/:kv/edit', '/:dc/kv/create'], str => str),
+ session: deletable({}, '[data-test-session]'),
})
)
);
diff --git a/ui-v2/tests/steps.js b/ui-v2/tests/steps.js
index 287c0900a..f2a3bd671 100644
--- a/ui-v2/tests/steps.js
+++ b/ui-v2/tests/steps.js
@@ -60,7 +60,7 @@ export default function(assert) {
)
// TODO: Abstract this away from HTTP
.given(['the url "$url" responds with a $status status'], function(url, status) {
- return api.server.respondWithStatus(url, parseInt(status));
+ return api.server.respondWithStatus(url.split('?')[0], parseInt(status));
})
// interactions
.when('I visit the $name page', function(name) {
@@ -390,10 +390,16 @@ export default function(assert) {
// TODO: These should be mergeable
.then(['"$selector" has the "$class" class'], function(selector, cls) {
// because `find` doesn't work, guessing its sandboxed to ember's container
- assert.ok(document.querySelector(selector).classList.contains(cls));
+ assert.ok(
+ document.querySelector(selector).classList.contains(cls),
+ `Expected [class] to contain ${cls} on ${selector}`
+ );
})
.then(['"$selector" doesn\'t have the "$class" class'], function(selector, cls) {
- assert.ok(!document.querySelector(selector).classList.contains(cls));
+ assert.ok(
+ !document.querySelector(selector).classList.contains(cls),
+ `Expected [class] not to contain ${cls} on ${selector}`
+ );
})
.then('ok', function() {
assert.ok(true);
diff --git a/ui-v2/tests/unit/mixins/acl/with-actions-test.js b/ui-v2/tests/unit/mixins/acl/with-actions-test.js
index d92d1c86f..ade74b60f 100644
--- a/ui-v2/tests/unit/mixins/acl/with-actions-test.js
+++ b/ui-v2/tests/unit/mixins/acl/with-actions-test.js
@@ -1,15 +1,25 @@
-import EmberObject from '@ember/object';
-import AclWithActionsMixin from 'consul-ui/mixins/acl/with-actions';
-import { moduleFor, test } from 'ember-qunit';
+import { moduleFor } from 'ember-qunit';
+import test from 'ember-sinon-qunit/test-support/test';
+import { getOwner } from '@ember/application';
+import Route from 'consul-ui/routes/dc/acls/index';
+import Service from '@ember/service';
+
+import Mixin from 'consul-ui/mixins/acl/with-actions';
moduleFor('mixin:acl/with-actions', 'Unit | Mixin | acl/with actions', {
// Specify the other units that are required for this test.
- needs: ['mixin:with-feedback'],
+ needs: [
+ 'mixin:with-blocking-actions',
+ 'service:feedback',
+ 'service:flashMessages',
+ 'service:logger',
+ 'service:settings',
+ 'service:acls',
+ ],
subject: function() {
- const AclWithActionsObject = EmberObject.extend(AclWithActionsMixin);
- this.register('test-container:acl/with-actions-object', AclWithActionsObject);
- // TODO: May need to actually get this from the container
- return AclWithActionsObject;
+ const MixedIn = Route.extend(Mixin);
+ this.register('test-container:acl/with-actions-object', MixedIn);
+ return getOwner(this).lookup('test-container:acl/with-actions-object');
},
});
@@ -18,3 +28,64 @@ test('it works', function(assert) {
const subject = this.subject();
assert.ok(subject);
});
+test('use persists the token and calls transitionTo correctly', function(assert) {
+ assert.expect(4);
+ this.register(
+ 'service:feedback',
+ Service.extend({
+ execute: function(cb, name) {
+ assert.equal(name, 'use');
+ return cb();
+ },
+ })
+ );
+ const item = { ID: 'id' };
+ this.register(
+ 'service:settings',
+ Service.extend({
+ persist: function(actual) {
+ assert.equal(actual.token, item.ID);
+ return Promise.resolve(actual);
+ },
+ })
+ );
+ const subject = this.subject();
+ const expected = 'dc.services';
+ const transitionTo = this.stub(subject, 'transitionTo').returnsArg(0);
+ return subject.actions.use
+ .bind(subject)(item)
+ .then(function(actual) {
+ assert.ok(transitionTo.calledOnce);
+ assert.equal(actual, expected);
+ });
+});
+test('clone clones the token and calls afterDelete correctly', function(assert) {
+ assert.expect(4);
+ this.register(
+ 'service:feedback',
+ Service.extend({
+ execute: function(cb, name) {
+ assert.equal(name, 'clone');
+ return cb();
+ },
+ })
+ );
+ const expected = { ID: 'id' };
+ this.register(
+ 'service:acls',
+ Service.extend({
+ clone: function(actual) {
+ assert.deepEqual(actual, expected);
+ return Promise.resolve(actual);
+ },
+ })
+ );
+ const subject = this.subject();
+ const afterDelete = this.stub(subject, 'afterDelete').returnsArg(0);
+ return subject.actions.clone
+ .bind(subject)(expected)
+ .then(function(actual) {
+ assert.ok(afterDelete.calledOnce);
+ assert.equal(actual, expected);
+ });
+});
diff --git a/ui-v2/tests/unit/mixins/intention/with-actions-test.js b/ui-v2/tests/unit/mixins/intention/with-actions-test.js
index 30f86de97..a33881eda 100644
--- a/ui-v2/tests/unit/mixins/intention/with-actions-test.js
+++ b/ui-v2/tests/unit/mixins/intention/with-actions-test.js
@@ -1,15 +1,21 @@
-import EmberObject from '@ember/object';
-import IntentionWithActionsMixin from 'consul-ui/mixins/intention/with-actions';
-import { moduleFor, test } from 'ember-qunit';
+import { moduleFor } from 'ember-qunit';
+import test from 'ember-sinon-qunit/test-support/test';
+import { getOwner } from '@ember/application';
+import Route from '@ember/routing/route';
+import Mixin from 'consul-ui/mixins/intention/with-actions';
moduleFor('mixin:intention/with-actions', 'Unit | Mixin | intention/with actions', {
// Specify the other units that are required for this test.
- needs: ['service:feedback'],
+ needs: [
+ 'mixin:with-blocking-actions',
+ 'service:feedback',
+ 'service:flashMessages',
+ 'service:logger',
+ ],
subject: function() {
- const IntentionWithActionsObject = EmberObject.extend(IntentionWithActionsMixin);
- this.register('test-container:intention/with-actions-object', IntentionWithActionsObject);
- // TODO: May need to actually get this from the container
- return IntentionWithActionsObject;
+ const MixedIn = Route.extend(Mixin);
+ this.register('test-container:intention/with-actions-object', MixedIn);
+ return getOwner(this).lookup('test-container:intention/with-actions-object');
},
});
@@ -18,3 +24,17 @@ test('it works', function(assert) {
const subject = this.subject();
assert.ok(subject);
});
+test('errorCreate returns a different status code if a duplicate intention is found', function(assert) {
+ const subject = this.subject();
+ const expected = 'exists';
+ const actual = subject.errorCreate('error', {
+ errors: [{ status: '500', detail: 'duplicate intention found:' }],
+ });
+ assert.equal(actual, expected);
+});
+test('errorCreate returns the same code if there is no error', function(assert) {
+ const subject = this.subject();
+ const expected = 'error';
+ const actual = subject.errorCreate('error', {});
+ assert.equal(actual, expected);
+});
diff --git a/ui-v2/tests/unit/mixins/kv/with-actions-test.js b/ui-v2/tests/unit/mixins/kv/with-actions-test.js
index eccc3a141..d42d3c0e8 100644
--- a/ui-v2/tests/unit/mixins/kv/with-actions-test.js
+++ b/ui-v2/tests/unit/mixins/kv/with-actions-test.js
@@ -1,15 +1,21 @@
-import EmberObject from '@ember/object';
-import KvWithActionsMixin from 'consul-ui/mixins/kv/with-actions';
-import { moduleFor, test } from 'ember-qunit';
+import { moduleFor, skip } from 'ember-qunit';
+import test from 'ember-sinon-qunit/test-support/test';
+import { getOwner } from '@ember/application';
+import Route from '@ember/routing/route';
+import Mixin from 'consul-ui/mixins/kv/with-actions';
moduleFor('mixin:kv/with-actions', 'Unit | Mixin | kv/with actions', {
// Specify the other units that are required for this test.
- needs: ['mixin:with-feedback'],
+ needs: [
+ 'mixin:with-blocking-actions',
+ 'service:feedback',
+ 'service:flashMessages',
+ 'service:logger',
+ ],
subject: function() {
- const KvWithActionsObject = EmberObject.extend(KvWithActionsMixin);
- this.register('test-container:kv/with-actions-object', KvWithActionsObject);
- // TODO: May need to actually get this from the container
- return KvWithActionsObject;
+ const MixedIn = Route.extend(Mixin);
+ this.register('test-container:kv/with-actions-object', MixedIn);
+ return getOwner(this).lookup('test-container:kv/with-actions-object');
},
});
@@ -18,3 +24,29 @@ test('it works', function(assert) {
const subject = this.subject();
assert.ok(subject);
});
+test('afterUpdate calls transitionTo index when the key is a single slash', function(assert) {
+ const subject = this.subject();
+ const expected = 'dc.kv.index';
+ const transitionTo = this.stub(subject, 'transitionTo').returnsArg(0);
+ const actual = subject.afterUpdate({}, { Key: '/' });
+ assert.equal(actual, expected);
+ assert.ok(transitionTo.calledOnce);
+});
+test('afterUpdate calls transitionTo folder when the key is not a single slash', function(assert) {
+ const subject = this.subject();
+ const expected = 'dc.kv.folder';
+ const transitionTo = this.stub(subject, 'transitionTo').returnsArg(0);
+ ['', '/key', 'key/name'].forEach(item => {
+ const actual = subject.afterUpdate({}, { Key: item });
+ assert.equal(actual, expected);
+ });
+ assert.ok(transitionTo.calledThrice);
+});
+test('afterDelete calls refresh folder when the routeName is `folder`', function(assert) {
+ const subject = this.subject();
+ subject.routeName = 'dc.kv.folder';
+ const refresh = this.stub(subject, 'refresh');
+ subject.afterDelete({}, {});
+ assert.ok(refresh.calledOnce);
+});
+skip('action invalidateSession test');
diff --git a/ui-v2/tests/unit/mixins/with-blocking-actions-test.js b/ui-v2/tests/unit/mixins/with-blocking-actions-test.js
new file mode 100644
index 000000000..73ee2aa8e
--- /dev/null
+++ b/ui-v2/tests/unit/mixins/with-blocking-actions-test.js
@@ -0,0 +1,74 @@
+import { moduleFor, skip } from 'ember-qunit';
+import test from 'ember-sinon-qunit/test-support/test';
+import { getOwner } from '@ember/application';
+import Route from '@ember/routing/route';
+import Mixin from 'consul-ui/mixins/with-blocking-actions';
+
+moduleFor('mixin:with-blocking-actions', 'Unit | Mixin | with blocking actions', {
+ // Specify the other units that are required for this test.
+ needs: ['service:feedback', 'service:flashMessages', 'service:logger'],
+ subject: function() {
+ const MixedIn = Route.extend(Mixin);
+ this.register('test-container:with-blocking-actions-object', MixedIn);
+ return getOwner(this).lookup('test-container:with-blocking-actions-object');
+ },
+});
+
+// Replace this with your real tests.
+test('it works', function(assert) {
+ const subject = this.subject();
+ assert.ok(subject);
+});
+skip('init sets up feedback properly');
+test('afterCreate just calls afterUpdate', function(assert) {
+ const subject = this.subject();
+ const expected = [1, 2, 3, 4];
+ const afterUpdate = this.stub(subject, 'afterUpdate').returns(expected);
+ const actual = subject.afterCreate(expected);
+ assert.deepEqual(actual, expected);
+ assert.ok(afterUpdate.calledOnce);
+});
+test('afterUpdate calls transitionTo without the last part of the current route name', function(assert) {
+ const subject = this.subject();
+ const expected = 'dc.kv';
+ subject.routeName = expected + '.edit';
+ const transitionTo = this.stub(subject, 'transitionTo').returnsArg(0);
+ const actual = subject.afterUpdate();
+ assert.equal(actual, expected);
+ assert.ok(transitionTo.calledOnce);
+});
+test('afterDelete calls transitionTo without the last part of the current route name if the last part is not `index`', function(assert) {
+ const subject = this.subject();
+ const expected = 'dc.kv';
+ subject.routeName = expected + '.edit';
+ const transitionTo = this.stub(subject, 'transitionTo').returnsArg(0);
+ const actual = subject.afterDelete();
+ assert.equal(actual, expected);
+ assert.ok(transitionTo.calledOnce);
+});
+test('afterDelete calls refresh if the last part is `index`', function(assert) {
+ const subject = this.subject();
+ subject.routeName = 'dc.kv.index';
+ const expected = 'refresh';
+ const refresh = this.stub(subject, 'refresh').returns(expected);
+ const actual = subject.afterDelete();
+ assert.equal(actual, expected);
+ assert.ok(refresh.calledOnce);
+});
+test('the error hooks return type', function(assert) {
+ const subject = this.subject();
+ const expected = 'success';
+ ['errorCreate', 'errorUpdate', 'errorDelete'].forEach(function(item) {
+ const actual = subject[item](expected, {});
+ assert.equal(actual, expected);
+ });
+});
+test('action cancel just calls afterUpdate', function(assert) {
+ const subject = this.subject();
+ const expected = [1, 2, 3, 4];
+ const afterUpdate = this.stub(subject, 'afterUpdate').returns(expected);
+ // TODO: unsure as to whether ember testing should actually bind this for you?
+ const actual = subject.actions.cancel.bind(subject)(expected);
+ assert.deepEqual(actual, expected);
+ assert.ok(afterUpdate.calledOnce);
+});
diff --git a/ui-v2/tests/unit/mixins/with-feedback-test.js b/ui-v2/tests/unit/mixins/with-feedback-test.js
deleted file mode 100644
index 6e79cf62f..000000000
--- a/ui-v2/tests/unit/mixins/with-feedback-test.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import EmberObject from '@ember/object';
-import WithFeedbackMixin from 'consul-ui/mixins/with-feedback';
-import { moduleFor, test } from 'ember-qunit';
-
-moduleFor('mixin:with-feedback', 'Unit | Mixin | with feedback', {
- // Specify the other units that are required for this test.
- needs: ['service:feedback'],
- subject: function() {
- const WithFeedbackObject = EmberObject.extend(WithFeedbackMixin);
- this.register('test-container:with-feedback-object', WithFeedbackObject);
- // TODO: May need to actually get this from the container
- return WithFeedbackObject;
- },
-});
-
-// Replace this with your real tests.
-test('it works', function(assert) {
- const subject = this.subject();
- assert.ok(subject);
-});
diff --git a/ui-v2/yarn.lock b/ui-v2/yarn.lock
index 5c91f370f..7b11ec92c 100644
--- a/ui-v2/yarn.lock
+++ b/ui-v2/yarn.lock
@@ -3360,6 +3360,13 @@ ember-cli-sri@^2.1.0:
dependencies:
broccoli-sri-hash "^2.1.0"
+ember-cli-string-helpers@^1.9.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/ember-cli-string-helpers/-/ember-cli-string-helpers-1.9.0.tgz#2c1605bc5768ff58cecd2fa1bd0d13d81e47f3d3"
+ dependencies:
+ broccoli-funnel "^1.0.1"
+ ember-cli-babel "^6.6.0"
+
ember-cli-string-utils@^1.0.0, ember-cli-string-utils@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ember-cli-string-utils/-/ember-cli-string-utils-1.1.0.tgz#39b677fc2805f55173735376fcef278eaa4452a1"