ui: Acceptance test improvements to prepare for more NS tests (#6980)

* ui: Acceptance test improvements to prepare for more NS tests

* ui: Namespace acceptance testing (#7005)

* Update api-double and consul-api-double for http.body

* Adds places where we missed passing the nspace through

* Hardcode nspace CRUD to use the default nspace for policies and roles

* Alter test helpers to allow us to control nspaces from the outside

* Amends to allow tests to account for namespace, move ns from queryParam

1. We decided to move how we pass the namespace value through to the
backend when performing write actions (create, update). Previoulsy we
were using the queryParam although using the post body is the preferred
method to send the Namespace details through to the backend.
2. Other various amends to take into account testing across multiple
namespaced scenarios

* Enable nspace testing by default

* Remove last few occurances of old style http assertions

We had informally 'deprecated' our old style of http assertions that
relied on the order of http calls (even though that order was not
important for the assertion). Following on from our namespace work we
removed the majority of the old occrances of these old style assertions.

This commit removes the remaining few, and also then cleans up the
assertions/http.js file to only include the ones we are using.

This reduces our available step count further and prevents any confusion
over the usage of the old types and the new types.

* ui: Namespace CRUD acceptance tests (#7016)

* Upgrade consul-api-double

* Add all the things required for testing:

1. edit and index page objects
2. enable CONSUL_NSPACE_COUNT cookie setting
3. enable mutating HTTP response bodies based on URL

* Add acceptance test for nspace edit/delete/list and searching
This commit is contained in:
John Cowen 2020-01-24 12:26:28 +00:00 committed by GitHub
parent b836a772fc
commit 327aac9fe9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 713 additions and 529 deletions

View File

@ -4,6 +4,9 @@ import { SLUG_KEY } from 'consul-ui/models/policy';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc'; import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { NSPACE_KEY } from 'consul-ui/models/nspace'; import { NSPACE_KEY } from 'consul-ui/models/nspace';
import nonEmptySet from 'consul-ui/utils/non-empty-set';
const Namespace = nonEmptySet('Namespace');
// TODO: Update to use this.formatDatacenter() // TODO: Update to use this.formatDatacenter()
export default Adapter.extend({ export default Adapter.extend({
requestForQuery: function(request, { dc, ns, index, id }) { requestForQuery: function(request, { dc, ns, index, id }) {
@ -32,7 +35,6 @@ export default Adapter.extend({
requestForCreateRecord: function(request, serialized, data) { requestForCreateRecord: function(request, serialized, data) {
const params = { const params = {
...this.formatDatacenter(data[DATACENTER_KEY]), ...this.formatDatacenter(data[DATACENTER_KEY]),
...this.formatNspace(data[NSPACE_KEY]),
}; };
return request` return request`
PUT /v1/acl/policy?${params} PUT /v1/acl/policy?${params}
@ -42,13 +44,13 @@ export default Adapter.extend({
Description: serialized.Description, Description: serialized.Description,
Rules: serialized.Rules, Rules: serialized.Rules,
Datacenters: serialized.Datacenters, Datacenters: serialized.Datacenters,
...Namespace(serialized.Namespace),
}} }}
`; `;
}, },
requestForUpdateRecord: function(request, serialized, data) { requestForUpdateRecord: function(request, serialized, data) {
const params = { const params = {
...this.formatDatacenter(data[DATACENTER_KEY]), ...this.formatDatacenter(data[DATACENTER_KEY]),
...this.formatNspace(data[NSPACE_KEY]),
}; };
return request` return request`
PUT /v1/acl/policy/${data[SLUG_KEY]}?${params} PUT /v1/acl/policy/${data[SLUG_KEY]}?${params}
@ -58,6 +60,7 @@ export default Adapter.extend({
Description: serialized.Description, Description: serialized.Description,
Rules: serialized.Rules, Rules: serialized.Rules,
Datacenters: serialized.Datacenters, Datacenters: serialized.Datacenters,
Namespace: serialized.Namespace,
}} }}
`; `;
}, },

View File

@ -3,7 +3,9 @@ import Adapter from './application';
import { SLUG_KEY } from 'consul-ui/models/role'; import { SLUG_KEY } from 'consul-ui/models/role';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc'; import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { NSPACE_KEY } from 'consul-ui/models/nspace'; import { NSPACE_KEY } from 'consul-ui/models/nspace';
import nonEmptySet from 'consul-ui/utils/non-empty-set';
const Namespace = nonEmptySet('Namespace');
// TODO: Update to use this.formatDatacenter() // TODO: Update to use this.formatDatacenter()
export default Adapter.extend({ export default Adapter.extend({
requestForQuery: function(request, { dc, ns, index, id }) { requestForQuery: function(request, { dc, ns, index, id }) {
@ -32,7 +34,6 @@ export default Adapter.extend({
requestForCreateRecord: function(request, serialized, data) { requestForCreateRecord: function(request, serialized, data) {
const params = { const params = {
...this.formatDatacenter(data[DATACENTER_KEY]), ...this.formatDatacenter(data[DATACENTER_KEY]),
...this.formatNspace(data[NSPACE_KEY]),
}; };
return request` return request`
PUT /v1/acl/role?${params} PUT /v1/acl/role?${params}
@ -40,16 +41,15 @@ export default Adapter.extend({
${{ ${{
Name: serialized.Name, Name: serialized.Name,
Description: serialized.Description, Description: serialized.Description,
Namespace: serialized.Namespace,
Policies: serialized.Policies, Policies: serialized.Policies,
ServiceIdentities: serialized.ServiceIdentities, ServiceIdentities: serialized.ServiceIdentities,
...Namespace(serialized.Namespace),
}} }}
`; `;
}, },
requestForUpdateRecord: function(request, serialized, data) { requestForUpdateRecord: function(request, serialized, data) {
const params = { const params = {
...this.formatDatacenter(data[DATACENTER_KEY]), ...this.formatDatacenter(data[DATACENTER_KEY]),
...this.formatNspace(data[NSPACE_KEY]),
}; };
return request` return request`
PUT /v1/acl/role/${data[SLUG_KEY]}?${params} PUT /v1/acl/role/${data[SLUG_KEY]}?${params}

View File

@ -4,7 +4,9 @@ import { inject as service } from '@ember/service';
import { SLUG_KEY } from 'consul-ui/models/token'; import { SLUG_KEY } from 'consul-ui/models/token';
import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc'; import { FOREIGN_KEY as DATACENTER_KEY } from 'consul-ui/models/dc';
import { NSPACE_KEY } from 'consul-ui/models/nspace'; import { NSPACE_KEY } from 'consul-ui/models/nspace';
import nonEmptySet from 'consul-ui/utils/non-empty-set';
const Namespace = nonEmptySet('Namespace');
// TODO: Update to use this.formatDatacenter() // TODO: Update to use this.formatDatacenter()
export default Adapter.extend({ export default Adapter.extend({
store: service('store'), store: service('store'),
@ -35,7 +37,6 @@ export default Adapter.extend({
requestForCreateRecord: function(request, serialized, data) { requestForCreateRecord: function(request, serialized, data) {
const params = { const params = {
...this.formatDatacenter(data[DATACENTER_KEY]), ...this.formatDatacenter(data[DATACENTER_KEY]),
...this.formatNspace(data[NSPACE_KEY]),
}; };
return request` return request`
PUT /v1/acl/token?${params} PUT /v1/acl/token?${params}
@ -46,6 +47,7 @@ export default Adapter.extend({
Roles: serialized.Roles, Roles: serialized.Roles,
ServiceIdentities: serialized.ServiceIdentities, ServiceIdentities: serialized.ServiceIdentities,
Local: serialized.Local, Local: serialized.Local,
...Namespace(serialized.Namespace),
}} }}
`; `;
}, },
@ -66,13 +68,13 @@ export default Adapter.extend({
} }
const params = { const params = {
...this.formatDatacenter(data[DATACENTER_KEY]), ...this.formatDatacenter(data[DATACENTER_KEY]),
...this.formatNspace(data[NSPACE_KEY]),
}; };
return request` return request`
PUT /v1/acl/token/${data[SLUG_KEY]}?${params} PUT /v1/acl/token/${data[SLUG_KEY]}?${params}
${{ ${{
Description: serialized.Description, Description: serialized.Description,
Namespace: serialized.Namespace,
Policies: serialized.Policies, Policies: serialized.Policies,
Roles: serialized.Roles, Roles: serialized.Roles,
ServiceIdentities: serialized.ServiceIdentities, ServiceIdentities: serialized.ServiceIdentities,

View File

@ -25,7 +25,7 @@ export default Component.extend(SlotsMixin, WithListeners, {
this._super(...arguments); this._super(...arguments);
this.searchable = this.container.searchable(this.type); this.searchable = this.container.searchable(this.type);
this.form = this.formContainer.form(this.type); this.form = this.formContainer.form(this.type);
this.form.clear({ Datacenter: this.dc }); this.form.clear({ Datacenter: this.dc, Namespace: this.nspace });
}, },
options: computed('selectedOptions.[]', 'allOptions.[]', function() { options: computed('selectedOptions.[]', 'allOptions.[]', function() {
// It's not massively important here that we are defaulting `items` and // It's not massively important here that we are defaulting `items` and
@ -52,7 +52,7 @@ export default Component.extend(SlotsMixin, WithListeners, {
}); });
}, },
reset: function() { reset: function() {
this.form.clear({ Datacenter: this.dc }); this.form.clear({ Datacenter: this.dc, Namespace: this.nspace });
}, },
open: function() { open: function() {
if (!get(this, 'allOptions.closed')) { if (!get(this, 'allOptions.closed')) {

View File

@ -8,12 +8,14 @@ export default Mixin.create(WithBlockingActions, {
actions: { actions: {
use: function(item) { use: function(item) {
return this.feedback.execute(() => { return this.feedback.execute(() => {
// old style legacy ACLs don't have AccessorIDs // old style legacy ACLs don't have AccessorIDs or Namespaces
// therefore set it to null, this way the frontend knows // therefore set AccessorID to null, this way the frontend knows
// to use legacy ACLs // to use legacy ACLs
// set the Namespace to just use default
return this.settings return this.settings
.persist({ .persist({
token: { token: {
Namespace: 'default',
AccessorID: null, AccessorID: null,
SecretID: get(item, 'ID'), SecretID: get(item, 'ID'),
}, },

View File

@ -16,9 +16,9 @@ export default Route.extend(WithBlockingActions, {
return this.settings return this.settings
.persist({ .persist({
token: { token: {
Namespace: get(item, 'Namespace'),
AccessorID: get(item, 'AccessorID'), AccessorID: get(item, 'AccessorID'),
SecretID: secret, SecretID: secret,
Namespace: get(item, 'Namespace'),
}, },
}) })
.then(item => { .then(item => {

View File

@ -15,7 +15,7 @@ export default Route.extend(WithIntentionActions, {
}, },
model: function(params) { model: function(params) {
const dc = this.modelFor('dc').dc.Name; const dc = this.modelFor('dc').dc.Name;
const nspace = this.modelFor('nspace').nspace.substr(1); const nspace = '*';
this.item = this.repo.create({ this.item = this.repo.create({
Datacenter: dc, Datacenter: dc,
}); });

View File

@ -21,6 +21,6 @@
{{#yield-slot name='policy' params=(block-params item)}} {{#yield-slot name='policy' params=(block-params item)}}
{{yield}} {{yield}}
{{else}} {{else}}
{{policy-selector dc=dc items=item.Policies}} {{policy-selector dc=dc nspace=nspace items=item.Policies}}
{{/yield-slot}} {{/yield-slot}}
</fieldset> </fieldset>

View File

@ -9,10 +9,10 @@
{{#block-slot name='body'}} {{#block-slot name='body'}}
<input id="{{name}}_state_role" type="radio" name="{{name}}[state]" value="role" checked={{if (eq state 'role') 'checked'}} onchange={{action 'change'}} /> <input id="{{name}}_state_role" type="radio" name="{{name}}[state]" value="role" checked={{if (eq state 'role') 'checked'}} onchange={{action 'change'}} />
{{#role-form form=form dc=dc}} {{#role-form form=form dc=dc nspace=nspace}}
{{#block-slot name='policy'}} {{#block-slot name='policy'}}
{{#policy-selector source=source dc=dc items=item.Policies}} {{#policy-selector source=source dc=dc nspace=nspace items=item.Policies}}
{{#block-slot name='trigger'}} {{#block-slot name='trigger'}}
<label for="{{name}}_state_policy" data-test-create-policy class="type-dialog"> <label for="{{name}}_state_policy" data-test-create-policy class="type-dialog">
<span>Create new policy</span> <span>Create new policy</span>

View File

@ -1,5 +1,5 @@
<form> <form>
{{role-form form=form item=item dc=dc}} {{role-form form=form item=item dc=dc nspace=nspace}}
{{#if (and (not create) (gt items.length 0))}} {{#if (and (not create) (gt items.length 0))}}
<h2>Where is this role used?</h2> <h2>Where is this role used?</h2>
<p> <p>

View File

@ -23,14 +23,14 @@
<p> <p>
By adding roles to this namespaces, you will apply them to all tokens created within this namespace. By adding roles to this namespaces, you will apply them to all tokens created within this namespace.
</p> </p>
{{role-selector dc=dc items=item.ACLs.RoleDefaults}} {{role-selector dc=dc nspace='default' items=item.ACLs.RoleDefaults}}
</fieldset> </fieldset>
<fieldset id="policies"> <fieldset id="policies">
<h2>Policies</h2> <h2>Policies</h2>
<p> <p>
By adding policies to this namespaces, you will apply them to all tokens created within this namespace. By adding policies to this namespaces, you will apply them to all tokens created within this namespace.
</p> </p>
{{policy-selector dc=dc items=item.ACLs.PolicyDefaults}} {{policy-selector dc=dc nspace='default' items=item.ACLs.PolicyDefaults}}
</fieldset> </fieldset>
{{/if}} {{/if}}
<div> <div>

View File

@ -60,6 +60,7 @@
<li role="none"> <li role="none">
<a data-test-edit role="menuitem" tabindex="-1" href={{href-to 'dc.nspaces.edit' item.Name}}>Edit</a> <a data-test-edit role="menuitem" tabindex="-1" href={{href-to 'dc.nspaces.edit' item.Name}}>Edit</a>
</li> </li>
{{#if (not-eq item.Name 'default') }}
<li role="none" class="dangerous"> <li role="none" class="dangerous">
<label for={{confirm}} role="menuitem" tabindex="-1" onkeypress={{keypressClick}} data-test-delete>Delete</label> <label for={{confirm}} role="menuitem" tabindex="-1" onkeypress={{keypressClick}} data-test-delete>Delete</label>
<div role="menu"> <div role="menu">
@ -83,6 +84,7 @@
</div> </div>
</div> </div>
</li> </li>
{{/if}}
{{/block-slot}} {{/block-slot}}
{{/popover-menu}} {{/popover-menu}}
{{/block-slot}} {{/block-slot}}

View File

@ -0,0 +1,11 @@
export default function(prop) {
return function(value) {
if (typeof value === 'undefined' || value === null || value === '') {
return {};
} else {
return {
[prop]: value,
};
}
};
}

View File

@ -82,6 +82,7 @@ module.exports = function(environment) {
case environment === 'test': case environment === 'test':
ENV = Object.assign({}, ENV, { ENV = Object.assign({}, ENV, {
locationType: 'none', locationType: 'none',
CONSUL_NSPACES_TEST: true,
CONSUL_ACLS_ENABLED: true, CONSUL_ACLS_ENABLED: true,
'@hashicorp/ember-cli-api-double': { '@hashicorp/ember-cli-api-double': {
'auto-import': false, 'auto-import': false,

View File

@ -1,4 +1,5 @@
@setupApplicationTest @setupApplicationTest
@notNamespaceable
Feature: components / acl filter: Acl Filter Feature: components / acl filter: Acl Filter
In order to find the acl token I'm looking for easier In order to find the acl token I'm looking for easier
As a user As a user

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: components / intention filter: Intention Filter Feature: components / intention-filter: Intention Filter
In order to find the intention I'm looking for easier In order to find the intention I'm looking for easier
As a user As a user
I should be able to filter by 'policy' (allow/deny) and freetext search tokens by source and destination I should be able to filter by 'policy' (allow/deny) and freetext search tokens by source and destination

View File

@ -1,5 +1,6 @@
@setupApplicationTest @setupApplicationTest
Feature: acl forwarding @notNamespaceable
Feature: dc / acls / index: acl forwarding
In order to arrive at a useful page when only specifying 'acls' in the url In order to arrive at a useful page when only specifying 'acls' in the url
As a user As a user
I should be redirected to the tokens page I should be redirected to the tokens page

View File

@ -1,4 +1,5 @@
@setupApplicationTest @setupApplicationTest
@notNamespaceable
Feature: dc / acls / list-order Feature: dc / acls / list-order
In order to be able to find ACL tokens easier In order to be able to find ACL tokens easier
As a user As a user

View File

@ -27,8 +27,9 @@ Feature: dc / acls / policies / as many / add existing: Add existing policy
And I click ".ember-power-select-option:nth-child(1)" And I click ".ember-power-select-option:nth-child(1)"
And I see 2 policy models on the policies component And I see 2 policy models on the policies component
And I submit And I submit
Then a PUT request is made to "/v1/acl/[Model]/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/[Model]/key?dc=datacenter" with the body from yaml
--- ---
Namespace: @namespace
Policies: Policies:
- ID: policy-1 - ID: policy-1
Name: Policy 1 Name: Policy 1

View File

@ -22,18 +22,22 @@ Feature: dc / acls / policies / as many / add new: Add new policy
Rules: key {} Rules: key {}
--- ---
And I click submit on the policies.form And I click submit on the policies.form
Then the last PUT request was made to "/v1/acl/policy?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/policy?dc=datacenter" from yaml
--- ---
body:
Name: New-Policy Name: New-Policy
Description: New Policy Description Description: New Policy Description
Namespace: @namespace
Rules: key {} Rules: key {}
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/acl/[Model]/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/[Model]/key?dc=datacenter" from yaml
--- ---
body:
Namespace: @namespace
Policies: Policies:
- Name: New-Policy - ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1
ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1 Name: New-Policy
--- ---
Then the url should be /datacenter/acls/[Model]s Then the url should be /datacenter/acls/[Model]s
And "[data-notification]" has the "notification-update" class And "[data-notification]" has the "notification-update" class
@ -53,8 +57,10 @@ Feature: dc / acls / policies / as many / add new: Add new policy
And I click serviceIdentity on the policies.form And I click serviceIdentity on the policies.form
And I click submit on the policies.form And I click submit on the policies.form
And I submit And I submit
Then a PUT request is made to "/v1/acl/[Model]/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/[Model]/key?dc=datacenter" from yaml
--- ---
body:
Namespace: @namespace
ServiceIdentities: ServiceIdentities:
- ServiceName: New-Service-Identity - ServiceName: New-Service-Identity
--- ---
@ -81,10 +87,11 @@ Feature: dc / acls / policies / as many / add new: Add new policy
Rules: key {} Rules: key {}
--- ---
And I click submit on the policies.form And I click submit on the policies.form
Then the last PUT request was made to "/v1/acl/policy?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/policy?dc=datacenter" with the body from yaml
--- ---
Name: New-Policy Name: New-Policy
Description: New Policy Description Description: New Policy Description
Namespace: @namespace
Rules: key {} Rules: key {}
--- ---
And I see error on the policies.form.rules like 'Invalid service policy: acl.ServicePolicy{Name:"service", Policy:"", Sentinel:acl.Sentinel{Code:"", EnforcementLevel:""}, Intentions:""}' And I see error on the policies.form.rules like 'Invalid service policy: acl.ServicePolicy{Name:"service", Policy:"", Sentinel:acl.Sentinel{Code:"", EnforcementLevel:""}, Intentions:""}'

View File

@ -17,13 +17,15 @@ Feature: dc / acls / policies / as many / remove: Remove
Then the url should be /datacenter/acls/[Model]s/key Then the url should be /datacenter/acls/[Model]s/key
And I see 1 policy model on the policies component And I see 1 policy model on the policies component
And I click expand on the policies.selectedOptions And I click expand on the policies.selectedOptions
And the last GET request was made to "/v1/acl/policy/00000000-0000-0000-0000-000000000001?dc=datacenter" And a GET request was made to "/v1/acl/policy/00000000-0000-0000-0000-000000000001?dc=datacenter&ns=@namespace"
And I click delete on the policies.selectedOptions And I click delete on the policies.selectedOptions
And I click confirmDelete on the policies.selectedOptions And I click confirmDelete on the policies.selectedOptions
And I see 0 policy models on the policies component And I see 0 policy models on the policies component
And I submit And I submit
Then a PUT request is made to "/v1/acl/[Model]/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/[Model]/key?dc=datacenter" from yaml
--- ---
body:
Namespace: @namespace
Policies: [[]] Policies: [[]]
--- ---
Then the url should be /datacenter/acls/[Model]s Then the url should be /datacenter/acls/[Model]s

View File

@ -14,10 +14,10 @@ Feature: dc / acls / policies / delete: Policy Delete
And I click actions on the policies And I click actions on the policies
And I click delete on the policies And I click delete on the policies
And I click confirmDelete on the policies And I click confirmDelete on the policies
Then a DELETE request is made to "/v1/acl/policy/1981f51d-301a-497b-89a0-05112ef02b4b?dc=datacenter&ns=default" Then a DELETE request was made to "/v1/acl/policy/1981f51d-301a-497b-89a0-05112ef02b4b?dc=datacenter&ns=@!namespace"
And "[data-notification]" has the "notification-delete" class And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Given the url "/v1/acl/policy/1981f51d-301a-497b-89a0-05112ef02b4b?dc=datacenter&ns=default" responds with a 500 status Given the url "/v1/acl/policy/1981f51d-301a-497b-89a0-05112ef02b4b?dc=datacenter&ns=@namespace" responds with a 500 status
And I click actions on the policies And I click actions on the policies
And I click delete on the policies And I click delete on the policies
And I click confirmDelete on the policies And I click confirmDelete on the policies
@ -31,7 +31,7 @@ Feature: dc / acls / policies / delete: Policy Delete
--- ---
And I click delete And I click delete
And I click confirmDelete on the deleteModal And I click confirmDelete on the deleteModal
Then a DELETE request is made to "/v1/acl/policy/1981f51d-301a-497b-89a0-05112ef02b4b?dc=datacenter&ns=default" Then a DELETE request was made to "/v1/acl/policy/1981f51d-301a-497b-89a0-05112ef02b4b?dc=datacenter&ns=@!namespace"
And "[data-notification]" has the "notification-delete" class And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
When I visit the policy page for yaml When I visit the policy page for yaml
@ -39,7 +39,7 @@ Feature: dc / acls / policies / delete: Policy Delete
dc: datacenter dc: datacenter
policy: 1981f51d-301a-497b-89a0-05112ef02b4b policy: 1981f51d-301a-497b-89a0-05112ef02b4b
--- ---
Given the url "/v1/acl/policy/1981f51d-301a-497b-89a0-05112ef02b4b?dc=datacenter&ns=default" responds with a 500 status Given the url "/v1/acl/policy/1981f51d-301a-497b-89a0-05112ef02b4b?dc=datacenter&ns=@namespace" responds with a 500 status
And I click delete And I click delete
And I click confirmDelete on the deleteModal And I click confirmDelete on the deleteModal
And "[data-notification]" has the "notification-delete" class And "[data-notification]" has the "notification-delete" class

View File

@ -25,13 +25,16 @@ Feature: dc / acls / policies / update: ACL Policy Update
And I click validDatacenters And I click validDatacenters
And I click datacenter And I click datacenter
And I submit And I submit
Then a PUT request is made to "/v1/acl/policy/policy-id?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/policy/policy-id?dc=datacenter" from yaml
--- ---
body:
Name: [Name] Name: [Name]
Description: [Description] Description: [Description]
Rules: [Rules] Rules: [Rules]
Namespace: @namespace
Datacenters: Datacenters:
- datacenter - datacenter
--- ---
Then the url should be /datacenter/acls/policies Then the url should be /datacenter/acls/policies
And "[data-notification]" has the "notification-update" class And "[data-notification]" has the "notification-update" class

View File

@ -32,8 +32,10 @@ Feature: dc / acls / roles / as many / add existing: Add existing
Description: The Description Description: The Description
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/acl/token/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/token/key?dc=datacenter" from yaml
--- ---
body:
Namespace: @namespace
Description: The Description Description: The Description
Roles: Roles:
- ID: role-1 - ID: role-1

View File

@ -1,5 +1,6 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / acls / roles / as many / add new: Add new @notNamespaceable
Feature: dc / acls / roles / as-many / add-new: Add new
Background: Background:
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
And 1 token model from yaml And 1 token model from yaml
@ -29,15 +30,19 @@ Feature: dc / acls / roles / as many / add new: Add new
--- ---
Scenario: Add Policy-less Role Scenario: Add Policy-less Role
And I click submit on the roles.form And I click submit on the roles.form
Then the last PUT request was made to "/v1/acl/role?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/role?dc=datacenter" from yaml
--- ---
body:
Name: New-Role Name: New-Role
Namespace: @namespace
Description: New Role Description Description: New Role Description
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/acl/token/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/token/key?dc=datacenter" from yaml
--- ---
body:
Description: The Description Description: The Description
Namespace: @namespace
Roles: Roles:
- Name: New-Role - Name: New-Role
ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1 ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1
@ -49,18 +54,22 @@ Feature: dc / acls / roles / as many / add new: Add new
And I click "#new-role-toggle + div .ember-power-select-trigger" And I click "#new-role-toggle + div .ember-power-select-trigger"
And I click ".ember-power-select-option:first-child" And I click ".ember-power-select-option:first-child"
And I click submit on the roles.form And I click submit on the roles.form
Then the last PUT request was made to "/v1/acl/role?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/role?dc=datacenter" from yaml
--- ---
body:
Name: New-Role Name: New-Role
Description: New Role Description Description: New Role Description
Namespace: @namespace
Policies: Policies:
- ID: policy-1 - ID: policy-1
Name: policy Name: policy
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/acl/token/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/token/key?dc=datacenter" from yaml
--- ---
body:
Description: The Description Description: The Description
Namespace: @namespace
Roles: Roles:
- Name: New-Role - Name: New-Role
ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1 ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1
@ -78,26 +87,32 @@ Feature: dc / acls / roles / as many / add new: Add new
--- ---
# This next line is actually the popped up policyForm due to the way things currently work # This next line is actually the popped up policyForm due to the way things currently work
And I click submit on the roles.form And I click submit on the roles.form
Then the last PUT request was made to "/v1/acl/policy?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/policy?dc=datacenter" from yaml
--- ---
body:
Name: New-Policy Name: New-Policy
Description: New Policy Description Description: New Policy Description
Namespace: @namespace
Rules: key {} Rules: key {}
--- ---
And I click submit on the roles.form And I click submit on the roles.form
Then the last PUT request was made to "/v1/acl/role?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/role?dc=datacenter" from yaml
--- ---
body:
Name: New-Role Name: New-Role
Description: New Role Description Description: New Role Description
Namespace: @namespace
Policies: Policies:
# TODO: Ouch, we need to do deep partial comparisons here # TODO: Ouch, we need to do deep partial comparisons here
- ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1 - ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1
Name: New-Policy Name: New-Policy
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/acl/token/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/token/key?dc=datacenter" from yaml
--- ---
body:
Description: The Description Description: The Description
Namespace: @namespace
Roles: Roles:
- Name: New-Role - Name: New-Role
ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1 ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1
@ -115,17 +130,21 @@ Feature: dc / acls / roles / as many / add new: Add new
# This next line is actually the popped up policyForm due to the way things currently work # This next line is actually the popped up policyForm due to the way things currently work
And I click submit on the roles.form And I click submit on the roles.form
And I click submit on the roles.form And I click submit on the roles.form
Then the last PUT request was made to "/v1/acl/role?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/role?dc=datacenter" from yaml
--- ---
body:
Name: New-Role Name: New-Role
Description: New Role Description Description: New Role Description
Namespace: @namespace
ServiceIdentities: ServiceIdentities:
- ServiceName: New-Service-Identity - ServiceName: New-Service-Identity
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/acl/token/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/token/key?dc=datacenter" from yaml
--- ---
body:
Description: The Description Description: The Description
Namespace: @namespace
Roles: Roles:
- Name: New-Role - Name: New-Role
ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1 ID: ee52203d-989f-4f7a-ab5a-2bef004164ca-1

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / acls / roles / as many / list: List Feature: dc / acls / roles / as-many / list: List
Scenario: Scenario:
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
And 1 token model from yaml And 1 token model from yaml

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / acls / roles / as many / remove: Remove Feature: dc / acls / roles / as-many / remove: Remove
Scenario: Scenario:
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
And 1 token model from yaml And 1 token model from yaml
@ -21,8 +21,10 @@ Feature: dc / acls / roles / as many / remove: Remove
And I click confirmDelete on the roles.selectedOptions And I click confirmDelete on the roles.selectedOptions
And I see 0 role models on the roles component And I see 0 role models on the roles component
And I submit And I submit
Then a PUT request is made to "/v1/acl/token/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/token/key?dc=datacenter" from yaml
--- ---
body:
Namespace: @namespace
Roles: [] Roles: []
--- ---
Then the url should be /datacenter/acls/tokens Then the url should be /datacenter/acls/tokens

View File

@ -21,8 +21,10 @@ Feature: dc / acls / roles / update: ACL Role Update
Description: [Description] Description: [Description]
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/acl/role/role-id?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/role/role-id?dc=datacenter" from yaml
--- ---
body:
Namespace: @namespace
Name: [Name] Name: [Name]
Description: [Description] Description: [Description]
--- ---

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / acls / tokens / anonymous no delete: The anonymous token has no delete buttons Feature: dc / acls / tokens / anonymous-no-delete: The anonymous token has no delete buttons
Background: Background:
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
And 1 token model from yaml And 1 token model from yaml

View File

@ -14,7 +14,7 @@ Feature: dc / acls / tokens / clone: Cloning an ACL token
--- ---
And I click actions on the tokens And I click actions on the tokens
And I click clone on the tokens And I click clone on the tokens
Then a PUT request is made to "/v1/acl/token/token/clone?dc=datacenter&ns=default" Then a PUT request was made to "/v1/acl/token/token/clone?dc=datacenter&ns=@!namespace"
Then "[data-notification]" has the "notification-clone" class Then "[data-notification]" has the "notification-clone" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Scenario: Using an ACL token from the detail page Scenario: Using an ACL token from the detail page

View File

@ -1,4 +1,5 @@
@setupApplicationTest @setupApplicationTest
@notNamespaceable
Feature: dc / acls / tokens / legacy / update: ACL Token Update Feature: dc / acls / tokens / legacy / update: ACL Token Update
Background: Background:
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
@ -24,9 +25,10 @@ Feature: dc / acls / tokens / legacy / update: ACL Token Update
# TODO: Remove this when I'm 100% sure token types are gone # TODO: Remove this when I'm 100% sure token types are gone
# And I click "[value=[Type]]" # And I click "[value=[Type]]"
And I submit And I submit
Then a PUT request is made to "/v1/acl/update?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/update?dc=datacenter" from yaml
# You can no longer edit Type but make sure it gets sent # You can no longer edit Type but make sure it gets sent
--- ---
body:
ID: secret ID: secret
Name: [Name] Name: [Name]
Type: client Type: client

View File

@ -1,9 +1,9 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / acls / tokens / index: ACL Login Errors Feature: dc / acls / tokens / login-errors: ACL Login Errors
Scenario: I get any 500 error that is not the specific legacy token cluster one Scenario: I get any 500 error that is not the specific legacy token cluster one
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
Given the url "/v1/acl/tokens" responds with a 500 status Given the url "/v1/acl/tokens?ns=@namespace" responds with a 500 status
When I visit the tokens page for yaml When I visit the tokens page for yaml
--- ---
dc: dc-1 dc: dc-1
@ -12,7 +12,7 @@ Feature: dc / acls / tokens / index: ACL Login Errors
Then I see the text "500 (The backend responded with an error)" in "[data-test-error]" Then I see the text "500 (The backend responded with an error)" in "[data-test-error]"
Scenario: I get a 500 error from acl/tokens that is the specific legacy one Scenario: I get a 500 error from acl/tokens that is the specific legacy one
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
And the url "/v1/acl/tokens" responds with from yaml And the url "/v1/acl/tokens?ns=@namespace" responds with from yaml
--- ---
status: 500 status: 500
body: "rpc error making call: rpc: can't find method ACL.TokenRead" body: "rpc error making call: rpc: can't find method ACL.TokenRead"
@ -23,9 +23,10 @@ Feature: dc / acls / tokens / index: ACL Login Errors
--- ---
Then the url should be /dc-1/acls/tokens Then the url should be /dc-1/acls/tokens
Then ".app-view" has the "unauthorized" class Then ".app-view" has the "unauthorized" class
@notNamespaceable
Scenario: I get a 500 error from acl/token/self that is the specific legacy one Scenario: I get a 500 error from acl/token/self that is the specific legacy one
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
Given the url "/v1/acl/tokens" responds with from yaml Given the url "/v1/acl/tokens?ns=@namespace" responds with from yaml
--- ---
status: 500 status: 500
body: "rpc error making call: rpc: can't find method ACL.TokenRead" body: "rpc error making call: rpc: can't find method ACL.TokenRead"

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / acls / tokens / own no delete: The your current token has no delete buttons Feature: dc / acls / tokens / own-no-delete: The your current token has no delete buttons
Background: Background:
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
And 1 token model from yaml And 1 token model from yaml
@ -24,7 +24,7 @@ Feature: dc / acls / tokens / own no delete: The your current token has no delet
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Then I have settings like yaml Then I have settings like yaml
--- ---
consul:token: "{\"AccessorID\":\"token\",\"SecretID\":\"ee52203d-989f-4f7a-ab5a-2bef004164ca\",\"Namespace\":\"default\"}" consul:token: "{\"AccessorID\":\"token\",\"SecretID\":\"ee52203d-989f-4f7a-ab5a-2bef004164ca\",\"Namespace\":\"@namespace\"}"
--- ---
And I click actions on the tokens And I click actions on the tokens
Then I don't see delete on the tokens Then I don't see delete on the tokens

View File

@ -19,8 +19,10 @@ Feature: dc / acls / tokens / update: ACL Token Update
Description: [Description] Description: [Description]
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/acl/token/key?dc=datacenter&ns=default" with the body from yaml Then a PUT request was made to "/v1/acl/token/key?dc=datacenter" from yaml
--- ---
body:
Namespace: @namespace
Description: [Description] Description: [Description]
--- ---
Then the url should be /datacenter/acls/tokens Then the url should be /datacenter/acls/tokens

View File

@ -23,7 +23,7 @@ Feature: dc / acls / tokens / use: Using an ACL token
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Then I have settings like yaml Then I have settings like yaml
--- ---
consul:token: "{\"AccessorID\":\"token\",\"SecretID\":\"ee52203d-989f-4f7a-ab5a-2bef004164ca\",\"Namespace\":\"default\"}" consul:token: "{\"AccessorID\":\"token\",\"SecretID\":\"ee52203d-989f-4f7a-ab5a-2bef004164ca\",\"Namespace\":\"@namespace\"}"
--- ---
Scenario: Using an ACL token from the detail page Scenario: Using an ACL token from the detail page
When I visit the token page for yaml When I visit the token page for yaml
@ -41,5 +41,5 @@ Feature: dc / acls / tokens / use: Using an ACL token
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Then I have settings like yaml Then I have settings like yaml
--- ---
consul:token: "{\"AccessorID\":\"token\",\"SecretID\":\"ee52203d-989f-4f7a-ab5a-2bef004164ca\",\"Namespace\":\"default\"}" consul:token: "{\"AccessorID\":\"token\",\"SecretID\":\"ee52203d-989f-4f7a-ab5a-2bef004164ca\",\"Namespace\":\"@namespace\"}"
--- ---

View File

@ -1,4 +1,5 @@
@setupApplicationTest @setupApplicationTest
@notNamespaceable
Feature: dc / acls / update: ACL Update Feature: dc / acls / update: ACL Update
Background: Background:
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
@ -20,8 +21,9 @@ Feature: dc / acls / update: ACL Update
--- ---
And I click "[value=[Type]]" And I click "[value=[Type]]"
And I submit And I submit
Then a PUT request is made to "/v1/acl/update?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/acl/update?dc=datacenter" from yaml
--- ---
body:
Name: [Name] Name: [Name]
Type: [Type] Type: [Type]
--- ---

View File

@ -1,4 +1,5 @@
@setupApplicationTest @setupApplicationTest
@notNamespaceable
Feature: dc / acls / use: Using an ACL token Feature: dc / acls / use: Using an ACL token
Background: Background:
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
@ -14,7 +15,7 @@ Feature: dc / acls / use: Using an ACL token
--- ---
Then I have settings like yaml Then I have settings like yaml
--- ---
consul:token: '{"AccessorID":null,"SecretID":"id"}' consul:token: '{"Namespace":"@namespace","AccessorID":null,"SecretID":"id"}'
--- ---
And I click actions on the acls And I click actions on the acls
And I click use on the acls And I click use on the acls
@ -23,7 +24,7 @@ Feature: dc / acls / use: Using an ACL token
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Then I have settings like yaml Then I have settings like yaml
--- ---
consul:token: '{"AccessorID":null,"SecretID":"token"}' consul:token: '{"Namespace":"@namespace","AccessorID":null,"SecretID":"token"}'
--- ---
Scenario: Using an ACL token from the detail page Scenario: Using an ACL token from the detail page
When I visit the acl page for yaml When I visit the acl page for yaml
@ -33,7 +34,7 @@ Feature: dc / acls / use: Using an ACL token
--- ---
Then I have settings like yaml Then I have settings like yaml
--- ---
consul:token: '{"AccessorID":null,"SecretID":"id"}' consul:token: '{"Namespace":"@namespace","AccessorID":null,"SecretID":"id"}'
--- ---
And I click use And I click use
And I click confirmUse And I click confirmUse
@ -41,5 +42,5 @@ Feature: dc / acls / use: Using an ACL token
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Then I have settings like yaml Then I have settings like yaml
--- ---
consul:token: '{"AccessorID":null,"SecretID":"token"}' consul:token: '{"Namespace":"@namespace","AccessorID":null,"SecretID":"token"}'
--- ---

View File

@ -1,8 +1,9 @@
@setupApplicationTest @setupApplicationTest
Feature: dc forwarding Feature: dc / forwarding
In order to arrive at a useful page when only specifying a dc in the url In order to arrive at a useful page when only specifying a dc in the url
As a user As a user
I should be redirected to the services page for the dc I should be redirected to the services page for the dc
@notNamespaceable
Scenario: Arriving at the datacenter index page with no other url info Scenario: Arriving at the datacenter index page with no other url info
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
When I visit the dcs page for yaml When I visit the dcs page for yaml

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: Datacenters Feature: dc / index: Datacenters
@ignore @ignore
Scenario: Arriving at the service page Scenario: Arriving at the service page
Given 10 datacenter models Given 10 datacenter models

View File

@ -32,7 +32,7 @@ Feature: dc / intentions / create: Intention Create
# Specifically set deny # Specifically set deny
And I click "[value=deny]" And I click "[value=deny]"
And I submit And I submit
Then a POST request is made to "/v1/connect/intentions?dc=datacenter" with the body from yaml Then a POST request was made to "/v1/connect/intentions?dc=datacenter" with the body from yaml
--- ---
SourceName: web SourceName: web
DestinationName: db DestinationName: db

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / intentions / filtered select: Intention Service Select Dropdowns Feature: dc / intentions / filtered-select: Intention Service Select Dropdowns
In order to use services as intention sources and destinations In order to use services as intention sources and destinations
As a user As a user
I want to be able to choose see existing services in the dropdown, but not existing proxy services I want to be able to choose see existing services in the dropdown, but not existing proxy services

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / intentions / form select: Intention Service Select Dropdowns Feature: dc / intentions / form-select: Intention Service Select Dropdowns
In order to set future Consul services as intention sources and destinations In order to set future Consul services as intention sources and destinations
As a user As a user
I want to type into the autocomplete and select what I've typed to use it as the future service I want to type into the autocomplete and select what I've typed to use it as the future service

View File

@ -19,7 +19,7 @@ Feature: dc / intentions / update: Intention Update
--- ---
And I click "[value=[Action]]" And I click "[value=[Action]]"
And I submit And I submit
Then a PUT request is made to "/v1/connect/intentions/intention-id?dc=datacenter" with the body from yaml Then a PUT request was made to "/v1/connect/intentions/intention-id?dc=datacenter" with the body from yaml
--- ---
Description: [Description] Description: [Description]
Action: [Action] Action: [Action]

View File

@ -19,12 +19,12 @@ Feature: dc / kvs / sessions / invalidate: Invalidate Lock Sessions
Scenario: Invalidating the lock session Scenario: Invalidating the lock session
And I click delete on the session And I click delete on the session
And I click confirmDelete 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&ns=default" Then a PUT request was made to "/v1/session/destroy/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter&ns=@!namespace"
Then the url should be /datacenter/kv/key/edit Then the url should be /datacenter/kv/key/edit
And "[data-notification]" has the "notification-deletesession" class And "[data-notification]" has the "notification-deletesession" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Scenario: Invalidating a lock session and receiving an error Scenario: Invalidating a lock session and receiving an error
Given the url "/v1/session/destroy/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter&ns=default" responds with a 500 status Given the url "/v1/session/destroy/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter&ns=@!namespace" responds with a 500 status
And I click delete on the session And I click delete on the session
And I click confirmDelete on the session And I click confirmDelete on the session
Then the url should be /datacenter/kv/key/edit Then the url should be /datacenter/kv/key/edit

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / kvs / trailing slash Feature: dc / kvs / trailing-slash
Scenario: I have 10 folders Scenario: I have 10 folders
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
And 10 kv models from yaml And 10 kv models from yaml
@ -9,11 +9,11 @@ Feature: dc / kvs / trailing slash
kv: foo/bar kv: foo/bar
--- ---
Then the url should be /datacenter/kv/foo/bar/ Then the url should be /datacenter/kv/foo/bar/
And the last GET request was made to "/v1/kv/foo/bar/?keys&dc=datacenter&separator=%2F" And a GET request was made to "/v1/kv/foo/bar/?keys&dc=datacenter&separator=%2F&ns=@namespace"
When I visit the kvs page for yaml When I visit the kvs page for yaml
--- ---
dc: datacenter dc: datacenter
kv: foo/bar/ kv: foo/bar/
--- ---
Then the url should be /datacenter/kv/foo/bar/ Then the url should be /datacenter/kv/foo/bar/
And the last GET request was made to "/v1/kv/foo/bar/?keys&dc=datacenter&separator=%2F" And a GET request was made to "/v1/kv/foo/bar/?keys&dc=datacenter&separator=%2F&ns=@namespace"

View File

@ -20,7 +20,7 @@ Feature: dc / kvs / update: KV Update
value: [Value] value: [Value]
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/kv/[EncodedName]?dc=datacenter&ns=default" with the body "[Value]" Then a PUT request was made to "/v1/kv/[EncodedName]?dc=datacenter&ns=@!namespace" with the body "[Value]"
And "[data-notification]" has the "notification-update" class And "[data-notification]" has the "notification-update" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Where: Where:
@ -50,7 +50,7 @@ Feature: dc / kvs / update: KV Update
value: ' ' value: ' '
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/kv/key?dc=datacenter&ns=default" with the body " " Then a PUT request was made to "/v1/kv/key?dc=datacenter&ns=@!namespace" with the body " "
Then the url should be /datacenter/kv Then the url should be /datacenter/kv
And "[data-notification]" has the "notification-update" class And "[data-notification]" has the "notification-update" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
@ -72,7 +72,7 @@ Feature: dc / kvs / update: KV Update
value: '' value: ''
--- ---
And I submit And I submit
Then a PUT request is made to "/v1/kv/key?dc=datacenter&ns=default" with no body Then a PUT request was made to "/v1/kv/key?dc=datacenter&ns=@!namespace" with no body
Then the url should be /datacenter/kv Then the url should be /datacenter/kv
And "[data-notification]" has the "notification-update" class And "[data-notification]" has the "notification-update" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
@ -89,7 +89,7 @@ Feature: dc / kvs / update: KV Update
--- ---
Then the url should be /datacenter/kv/key/edit Then the url should be /datacenter/kv/key/edit
And I submit And I submit
Then a PUT request is made to "/v1/kv/key?dc=datacenter&ns=default" with no body Then a PUT request was made to "/v1/kv/key?dc=datacenter&ns=@!namespace" with no body
Then the url should be /datacenter/kv Then the url should be /datacenter/kv
And "[data-notification]" has the "notification-update" class And "[data-notification]" has the "notification-update" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc > list: List Models Feature: dc / list: List Models
Scenario: Listing [Model] Scenario: Listing [Model]
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
And 3 [Model] models And 3 [Model] models

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: Hedge for if nodes come in over the API with no ID Feature: dc / nodes / empty-ids: Hedge for if nodes come in over the API with no ID
Scenario: A node list with some missing IDs Scenario: A node list with some missing IDs
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
And 5 node models from yaml And 5 node models from yaml

View File

@ -25,12 +25,12 @@ Feature: dc / nodes / sessions / invalidate: Invalidate Lock Sessions
Scenario: Invalidating the lock session Scenario: Invalidating the lock session
And I click delete on the sessions And I click delete on the sessions
And I click confirmDelete on the sessions And I click confirmDelete on the sessions
Then the last PUT request was made to "/v1/session/destroy/7bbbd8bb-fff3-4292-b6e3-cfedd788546a?dc=dc1&ns=default" Then a PUT request was made to "/v1/session/destroy/7bbbd8bb-fff3-4292-b6e3-cfedd788546a?dc=dc1&ns=@!namespace"
Then the url should be /dc1/nodes/node-0 Then the url should be /dc1/nodes/node-0
And "[data-notification]" has the "notification-delete" class And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Scenario: Invalidating a lock session and receiving an error Scenario: Invalidating a lock session and receiving an error
Given the url "/v1/session/destroy/7bbbd8bb-fff3-4292-b6e3-cfedd788546a?dc=dc1&ns=default" responds with a 500 status Given the url "/v1/session/destroy/7bbbd8bb-fff3-4292-b6e3-cfedd788546a?dc=dc1&ns=@!namespace" responds with a 500 status
And I click delete on the sessions And I click delete on the sessions
And I click confirmDelete on the sessions And I click confirmDelete on the sessions
Then the url should be /dc1/nodes/node-0 Then the url should be /dc1/nodes/node-0

View File

@ -0,0 +1,31 @@
@setupApplicationTest
Feature: dc / nspaces / index: Nspaces List
Background:
Given settings from yaml
---
consul:token:
SecretID: secret
AccessorID: accessor
Namespace: default
---
And 1 datacenter model with the value "dc-1"
And 3 nspace models
When I visit the nspaces page for yaml
---
dc: dc-1
---
Then the url should be /dc-1/namespaces
Scenario:
Then I see 3 nspace models
Scenario: Searching the nspaces
Then I see 3 nspace models
Then I fill in with yaml
---
s: default
---
And I see 1 nspace model
And I see 1 nspace model with the description "The default namespace"
Scenario: The default namespace can't be deleted
Then I see 3 nspace models
And I click actions on the nspaces
Then I don't see delete on the nspaces

View File

@ -0,0 +1,42 @@
@setupApplicationTest
Feature: dc / nspaces / update: Nspace Update
Background:
Given 1 datacenter model with the value "datacenter"
And 1 nspace model from yaml
---
Name: namespace
Description: empty
PolicyDefaults: ~
---
When I visit the nspace page for yaml
---
dc: datacenter
namespace: namespace
---
Then the url should be /datacenter/namespaces/namespace
Scenario: Update to [Description]
Then I fill in with yaml
---
Description: [Description]
---
And I submit
Then a PUT request was made to "/v1/namespace/namespace" from yaml
---
body:
Description: [Description]
---
Then the url should be /datacenter/namespaces
And "[data-notification]" has the "notification-update" class
And "[data-notification]" has the "success" class
Where:
---------------------------
| Description |
| description |
| description with spaces |
---------------------------
Scenario: There was an error saving the key
Given the url "/v1/namespace/namespace" responds with a 500 status
And I submit
Then the url should be /datacenter/namespaces/namespace
Then "[data-notification]" has the "notification-update" class
And "[data-notification]" has the "error" class

View File

@ -11,6 +11,7 @@ Feature: dc / services / error
dc: 404-datacenter dc: 404-datacenter
--- ---
Then I see the text "404 (Page not found)" in "[data-test-error]" Then I see the text "404 (Page not found)" in "[data-test-error]"
@notNamespaceable
Scenario: Arriving at the service page Scenario: Arriving at the service page
Given 2 datacenter models from yaml Given 2 datacenter models from yaml
--- ---
@ -23,5 +24,8 @@ Feature: dc / services / error
dc: dc-1 dc: dc-1
--- ---
Then I see the text "500 (The backend responded with an error)" in "[data-test-error]" Then I see the text "500 (The backend responded with an error)" in "[data-test-error]"
# This is the actual step that works slightly differently
# When running through namespaces as the dc menu says 'Error'
# which is still kind of ok
When I click dc on the navigation When I click dc on the navigation
And I see 2 datacenter models And I see 2 datacenter models

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: dc / services: List Services Feature: dc / services / index: List Services
Scenario: Scenario:
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
And 6 service models from yaml And 6 service models from yaml

View File

@ -17,17 +17,18 @@ Feature: deleting: Deleting items with confirmations, success and error notifica
And I click actions on the [Listing] And I click actions on the [Listing]
And I click delete on the [Listing] And I click delete on the [Listing]
And I click confirmDelete on the [Listing] And I click confirmDelete on the [Listing]
Then a [Method] request is made to "[URL]" Then a [Method] request was made to "[URL]"
And "[data-notification]" has the "notification-delete" class And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
Where: Where:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Edit | Listing | Method | URL | Data | Slug | | Edit | Listing | Method | URL | Data |
# | acl | acls | PUT | /v1/acl/destroy/something?dc=datacenter | {"Name": "something", "ID": "something"} | acl: something | | kv | kvs | DELETE | /v1/kv/key-name?dc=datacenter&ns=@!namespace | ["key-name"] |
| kv | kvs | DELETE | /v1/kv/key-name?dc=datacenter&ns=default | ["key-name"] | kv: key-name | | intention | intentions | DELETE | /v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter | {"SourceName": "name", "ID": "ee52203d-989f-4f7a-ab5a-2bef004164ca"} |
| intention | intentions | 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 | | token | tokens | DELETE | /v1/acl/token/001fda31-194e-4ff1-a5ec-589abf2cafd0?dc=datacenter&ns=@!namespace | {"AccessorID": "001fda31-194e-4ff1-a5ec-589abf2cafd0"} |
| token | tokens | DELETE | /v1/acl/token/001fda31-194e-4ff1-a5ec-589abf2cafd0?dc=datacenter&ns=default | {"AccessorID": "001fda31-194e-4ff1-a5ec-589abf2cafd0"} | token: 001fda31-194e-4ff1-a5ec-589abf2cafd0 | | nspace | nspaces | DELETE | /v1/namespace/a-namespace | {"Name": "a-namespace"} |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ # | acl | acls | PUT | /v1/acl/destroy/something?dc=datacenter | {"Name": "something", "ID": "something"} |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Scenario: Deleting a [Model] from the [Model] detail page Scenario: Deleting a [Model] from the [Model] detail page
When I visit the [Model] page for yaml When I visit the [Model] page for yaml
--- ---
@ -36,7 +37,7 @@ Feature: deleting: Deleting items with confirmations, success and error notifica
--- ---
And I click delete And I click delete
And I click confirmDelete And I click confirmDelete
Then a [Method] request is made to "[URL]" Then a [Method] request was made to "[URL]"
And "[data-notification]" has the "notification-delete" class And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "success" class And "[data-notification]" has the "success" class
When I visit the [Model] page for yaml When I visit the [Model] page for yaml
@ -50,13 +51,14 @@ Feature: deleting: Deleting items with confirmations, success and error notifica
And "[data-notification]" has the "notification-delete" class And "[data-notification]" has the "notification-delete" class
And "[data-notification]" has the "error" class And "[data-notification]" has the "error" class
Where: Where:
------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------
| Model | Method | URL | Slug | | Model | Method | URL | Slug |
# | acl | PUT | /v1/acl/destroy/something?dc=datacenter | acl: something | | kv | DELETE | /v1/kv/key-name?dc=datacenter&ns=@!namespace | kv: key-name |
| kv | DELETE | /v1/kv/key-name?dc=datacenter&ns=default | kv: key-name |
| intention | DELETE | /v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter | intention: ee52203d-989f-4f7a-ab5a-2bef004164ca | | intention | DELETE | /v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=datacenter | intention: ee52203d-989f-4f7a-ab5a-2bef004164ca |
| token | DELETE | /v1/acl/token/001fda31-194e-4ff1-a5ec-589abf2cafd0?dc=datacenter&ns=default | token: 001fda31-194e-4ff1-a5ec-589abf2cafd0 | | token | DELETE | /v1/acl/token/001fda31-194e-4ff1-a5ec-589abf2cafd0?dc=datacenter&ns=@!namespace | token: 001fda31-194e-4ff1-a5ec-589abf2cafd0 |
------------------------------------------------------------------------------------------------------------------------------------------------------- | nspace | DELETE | /v1/namespace/a-namespace | namespace: a-namespace |
# | acl | PUT | /v1/acl/destroy/something?dc=datacenter | acl: something |
-----------------------------------------------------------------------------------------------------------------------------------------------------------
@ignore @ignore
Scenario: Sort out the wide tables ^ Scenario: Sort out the wide tables ^
Then ok Then ok

View File

@ -1,5 +1,6 @@
@setupApplicationTest @setupApplicationTest
Feature: index forwarding @notNamespaceable
Feature: index-forwarding
Scenario: Arriving at the index page when there is only one datacenter Scenario: Arriving at the index page when there is only one datacenter
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
When I visit the index page When I visit the index page

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: Page Navigation Feature: page-navigation
In order to view all the data in consul In order to view all the data in consul
As a user As a user
I should be able to visit every page and view data in a HTML from the API I should be able to visit every page and view data in a HTML from the API
@ -11,7 +11,7 @@ Feature: Page Navigation
dc: dc-1 dc: dc-1
--- ---
Then the url should be /dc-1/services Then the url should be /dc-1/services
Then the last GET request was made to "/v1/internal/ui/services?dc=dc-1" Then a GET request was made to "/v1/internal/ui/services?dc=dc-1&ns=@namespace"
Scenario: Clicking [Link] in the navigation takes me to [URL] Scenario: Clicking [Link] in the navigation takes me to [URL]
When I visit the services page for yaml When I visit the services page for yaml
--- ---
@ -19,16 +19,16 @@ Feature: Page Navigation
--- ---
When I click [Link] on the navigation When I click [Link] on the navigation
Then the url should be [URL] Then the url should be [URL]
Then the last GET request was made to "[Endpoint]" Then a GET request was made to "[Endpoint]"
Where: Where:
----------------------------------------------------------------------- -------------------------------------------------------------------------------------
| Link | URL | Endpoint | | Link | URL | Endpoint |
| nodes | /dc-1/nodes | /v1/internal/ui/nodes?dc=dc-1 | | nodes | /dc-1/nodes | /v1/internal/ui/nodes?dc=dc-1 |
| kvs | /dc-1/kv | /v1/kv/?keys&dc=dc-1&separator=%2F | | kvs | /dc-1/kv | /v1/kv/?keys&dc=dc-1&separator=%2F&ns=@namespace |
| acls | /dc-1/acls/tokens | /v1/acl/tokens?dc=dc-1 | | acls | /dc-1/acls/tokens | /v1/acl/tokens?dc=dc-1&ns=@namespace |
| intentions | /dc-1/intentions | /v1/connect/intentions?dc=dc-1 | | intentions | /dc-1/intentions | /v1/connect/intentions?dc=dc-1 |
# | settings | /settings | /v1/catalog/datacenters | # | settings | /settings | /v1/catalog/datacenters |
----------------------------------------------------------------------- -------------------------------------------------------------------------------------
Scenario: Clicking a [Item] in the [Model] listing and back again Scenario: Clicking a [Item] in the [Model] listing and back again
When I visit the [Model] page for yaml When I visit the [Model] page for yaml
--- ---
@ -40,11 +40,11 @@ Feature: Page Navigation
And I click "[data-test-back]" And I click "[data-test-back]"
Then the url should be [Back] Then the url should be [Back]
Where: Where:
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Item | Model | URL | Endpoint | Back | | Item | Model | URL | Endpoint | Back |
| service | services | /dc-1/services/service-0 | /v1/discovery-chain/service-0?dc=dc-1 | /dc-1/services | | service | services | /dc-1/services/service-0 | /v1/discovery-chain/service-0?dc=dc-1&ns=@namespace | /dc-1/services |
| node | nodes | /dc-1/nodes/node-0 | /v1/session/node/node-0?dc=dc-1 | /dc-1/nodes | | node | nodes | /dc-1/nodes/node-0 | /v1/session/node/node-0?dc=dc-1&ns=@namespace | /dc-1/nodes |
| kv | kvs | /dc-1/kv/0-key-value/edit | /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 | /dc-1/kv | | kv | kvs | /dc-1/kv/0-key-value/edit | /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1&ns=@namespace | /dc-1/kv |
# | acl | acls | /dc-1/acls/anonymous | /v1/acl/info/anonymous?dc=dc-1 | /dc-1/acls | # | acl | acls | /dc-1/acls/anonymous | /v1/acl/info/anonymous?dc=dc-1 | /dc-1/acls |
| intention | intentions | /dc-1/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/internal/ui/services?dc=dc-1&ns=* | /dc-1/intentions | | intention | intentions | /dc-1/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca | /v1/internal/ui/services?dc=dc-1&ns=* | /dc-1/intentions |
# These Endpoints will be datacenters due to the datacenters checkbox selectors # These Endpoints will be datacenters due to the datacenters checkbox selectors
@ -66,7 +66,7 @@ Feature: Page Navigation
- /v1/namespaces - /v1/namespaces
- /v1/internal/ui/node/node-0?dc=dc-1 - /v1/internal/ui/node/node-0?dc=dc-1
- /v1/coordinate/nodes?dc=dc-1 - /v1/coordinate/nodes?dc=dc-1
- /v1/session/node/node-0?dc=dc-1 - /v1/session/node/node-0?dc=dc-1&ns=@namespace
--- ---
Scenario: The kv detail page calls the correct API endpoints Scenario: The kv detail page calls the correct API endpoints
When I visit the kv page for yaml When I visit the kv page for yaml
@ -79,8 +79,8 @@ Feature: Page Navigation
--- ---
- /v1/catalog/datacenters - /v1/catalog/datacenters
- /v1/namespaces - /v1/namespaces
- /v1/kv/keyname?dc=dc-1 - /v1/kv/keyname?dc=dc-1&ns=@namespace
- /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1 - /v1/session/info/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc-1&ns=@namespace
--- ---
Scenario: The policies page/tab calls the correct API endpoints Scenario: The policies page/tab calls the correct API endpoints
When I visit the policies page for yaml When I visit the policies page for yaml
@ -92,7 +92,7 @@ Feature: Page Navigation
--- ---
- /v1/catalog/datacenters - /v1/catalog/datacenters
- /v1/namespaces - /v1/namespaces
- /v1/acl/policies?dc=dc-1 - /v1/acl/policies?dc=dc-1&ns=@namespace
--- ---
Scenario: The intention detail page calls the correct API endpoints Scenario: The intention detail page calls the correct API endpoints
When I visit the intention page for yaml When I visit the intention page for yaml

View File

@ -3,6 +3,7 @@ Feature: startup
In order to give users an indication as early as possible that they are at the right place In order to give users an indication as early as possible that they are at the right place
As a user As a user
I should be able to see a startup logo I should be able to see a startup logo
@notNamespaceable
Scenario: When loading the index.html file into a browser Scenario: When loading the index.html file into a browser
Given 1 datacenter model with the value "dc-1" Given 1 datacenter model with the value "dc-1"
Then the url should be '' Then the url should be ''

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -1,91 +1,5 @@
/* eslint no-console: "off", no-control-regex: "off" */
import YAML from 'js-yaml';
import Inflector from 'ember-inflector';
import utils from '@ember/test-helpers';
import Yadda from 'yadda';
import pages from 'consul-ui/tests/pages';
import api from 'consul-ui/tests/helpers/api';
import steps from 'consul-ui/tests/steps'; import steps from 'consul-ui/tests/steps';
const pluralize = function(str) { export default function({ assert, library }) {
return Inflector.inflector.pluralize(str); return steps(assert, library);
};
export default function(assert) {
const library = Yadda.localisation.English.library(
new Yadda.Dictionary()
.define('json', /([^\u0000]*)/, function(val, cb) {
cb(null, JSON.parse(val));
})
.define('yaml', /([^\u0000]*)/, function(val, cb) {
cb(null, YAML.safeLoad(val));
})
.define('model', /(\w+)/, function(model, cb) {
switch (model) {
case 'datacenter':
case 'datacenters':
case 'dcs':
model = 'dc';
break;
case 'services':
model = 'service';
break;
case 'nodes':
model = 'node';
break;
case 'kvs':
model = 'kv';
break;
case 'acls':
model = 'acl';
break;
case 'sessions':
model = 'session';
break;
case 'intentions':
model = 'intention';
break;
}
cb(null, model);
})
.define('number', /(\d+)/, Yadda.converters.integer)
);
const create = function(number, name, value) {
// don't return a promise here as
// I don't need it to wait
api.server.createList(name, number, value);
};
const respondWith = function(url, data) {
api.server.respondWith(url.split('?')[0], data);
};
const setCookie = function(key, value) {
api.server.setCookie(key, value);
};
const getLastNthRequest = function(arr) {
return function(n, method) {
let requests = arr.slice(0).reverse();
if (method) {
requests = requests.filter(function(item) {
return item.method === method;
});
}
if (n == null) {
return requests;
}
return requests[n];
};
};
return steps(assert, library, pages, {
pluralize: pluralize,
triggerKeyEvent: utils.triggerKeyEvent,
currentURL: utils.currentURL,
click: utils.click,
fillIn: utils.fillIn,
find: utils.find,
lastNthRequest: getLastNthRequest(api.server.history),
respondWith: respondWith,
create: create,
set: setCookie,
});
} }

View File

@ -1,5 +1,5 @@
@setupApplicationTest @setupApplicationTest
Feature: submit blank Feature: submit-blank
In order to prevent form's being saved without values In order to prevent form's being saved without values
As a user As a user
I shouldn't be able to submit a blank form I shouldn't be able to submit a blank form

View File

@ -1,5 +1,6 @@
@setupApplicationTest @setupApplicationTest
Feature: token headers @notNamespaceable
Feature: token-header
In order to authenticate with tokens In order to authenticate with tokens
As a user As a user
I need to be able to specify a ACL token AND/OR leave it blank to authenticate with the API I need to be able to specify a ACL token AND/OR leave it blank to authenticate with the API
@ -7,7 +8,7 @@ Feature: token headers
Given 1 datacenter model with the value "datacenter" Given 1 datacenter model with the value "datacenter"
When I visit the index page When I visit the index page
Then the url should be /datacenter/services Then the url should be /datacenter/services
And a GET request is made to "/v1/namespaces" from yaml And a GET request was made to "/v1/namespaces" from yaml
--- ---
headers: headers:
X-Consul-Token: '' X-Consul-Token: ''

80
ui-v2/tests/dictionary.js Normal file
View File

@ -0,0 +1,80 @@
/* eslint no-control-regex: "off" */
import Yadda from 'yadda';
import YAML from 'js-yaml';
export default function(nspace, dict = new Yadda.Dictionary()) {
dict
.define('model', /(\w+)/, function(model, cb) {
switch (model) {
case 'datacenter':
case 'datacenters':
case 'dcs':
model = 'dc';
break;
case 'services':
model = 'service';
break;
case 'nodes':
model = 'node';
break;
case 'kvs':
model = 'kv';
break;
case 'acls':
model = 'acl';
break;
case 'sessions':
model = 'session';
break;
case 'intentions':
model = 'intention';
break;
}
cb(null, model);
})
.define('number', /(\d+)/, Yadda.converters.integer)
.define('json', /([^\u0000]*)/, function(val, cb) {
// replace any instance of @namespace in the string
val = val.replace(
/@namespace/g,
typeof nspace === 'undefined' || nspace === '' ? 'default' : nspace
);
cb(null, JSON.parse(val));
})
.define('yaml', /([^\u0000]*)/, function(val, cb) {
// sometimes we need to always force a namespace queryParam
// mainly for DELETEs
val = val.replace(/ns=@!namespace/g, `ns=${nspace || 'default'}`);
if (typeof nspace === 'undefined' || nspace === '') {
val = val.replace(/Namespace: @namespace/g, '').replace(/&ns=@namespace/g, '');
}
// replace any other instance of @namespace in the string
val = val.replace(
/@namespace/g,
typeof nspace === 'undefined' || nspace === '' ? 'default' : nspace
);
cb(null, YAML.safeLoad(val));
})
.define('endpoint', /([^\u0000]*)/, function(val, cb) {
// if is @namespace is !important, always replace with namespace
// or if its undefined or empty then use default
val = val.replace(/ns=@!namespace/g, `ns=${nspace || 'default'}`);
// for endpoints if namespace isn't specified it should
// never add the ns= unless its !important...
if (typeof nspace !== 'undefined' && nspace !== '') {
val = val.replace(/ns=@namespace/g, `ns=${nspace}`);
} else {
val = val
.replace(/&ns=@namespace/g, '')
.replace(/ns=@namespace&/g, '')
.replace(/ns=@namespace/g, '');
}
cb(null, val);
});
if (typeof nspace !== 'undefined' && nspace !== '') {
dict.define('url', /([^\u0000]*)/, function(val, cb) {
val = `/~${nspace}${val}`;
cb(null, val);
});
}
return dict;
}

View File

@ -42,6 +42,9 @@ export default function(type, value) {
key = 'CONSUL_TOKEN_COUNT'; key = 'CONSUL_TOKEN_COUNT';
obj['CONSUL_ACLS_ENABLE'] = 1; obj['CONSUL_ACLS_ENABLE'] = 1;
break; break;
case 'nspace':
key = 'CONSUL_NSPACE_COUNT';
break;
} }
if (key) { if (key) {
obj[key] = value; obj[key] = value;

View File

@ -32,6 +32,9 @@ export default function(type) {
case 'token': case 'token':
requests = ['/v1/acl/tokens', '/v1/acl/token/']; requests = ['/v1/acl/tokens', '/v1/acl/token/'];
break; break;
case 'nspace':
requests = ['/v1/namespaces', '/v1/namespace/'];
break;
} }
// TODO: An instance of URL should come in here (instead of 2 args) // TODO: An instance of URL should come in here (instead of 2 args)
return function(url, method) { return function(url, method) {

View File

@ -1,10 +1,14 @@
import ENV from '../../config/environment'; import { skip, test } from 'qunit';
import { skip } from 'qunit'; import { setupApplicationTest } from 'ember-qunit';
import { setupApplicationTest, setupRenderingTest, setupTest } from 'ember-qunit'; import { Promise } from 'rsvp';
import api from 'consul-ui/tests/helpers/api'; import Yadda from 'yadda';
import { env } from '../../env';
import api from './api';
import getDictionary from '../dictionary';
const staticClassList = [...document.documentElement.classList]; const staticClassList = [...document.documentElement.classList];
function reset() { const reset = function() {
window.localStorage.clear(); window.localStorage.clear();
api.server.reset(); api.server.reset();
const list = document.documentElement.classList; const list = document.documentElement.classList;
@ -14,84 +18,75 @@ function reset() {
staticClassList.forEach(function(item) { staticClassList.forEach(function(item) {
list.add(item); list.add(item);
}); });
} };
// this logic could be anything, but in this case...
// if @ignore, then return skip (for backwards compatibility) const runTest = function(context, libraries, steps, scenarioContext) {
// if have annotations in config, then only run those that have a matching annotation return new Promise((resolve, reject) => {
function checkAnnotations(annotations) { Yadda.Yadda(libraries, context).yadda(steps, scenarioContext, function next(err, result) {
// if ignore is set then we want to skip for backwards compatibility if (err) {
reject(err);
}
resolve(result);
});
});
};
const checkAnnotations = function(annotations, isScenario) {
annotations = {
namespaceable: env('CONSUL_NSPACES_TEST'),
...annotations,
};
if (annotations.ignore) { if (annotations.ignore) {
return ignoreIt; return function(test) {
skip(`${test.title}`, function(assert) {});
};
} }
if (isScenario) {
// if have annotations set in config, the only run those that have a matching annotation return function(scenario, feature, yadda, yaddaAnnotations, library) {
if (ENV.annotations && ENV.annotations.length >= 0) { test(`Scenario: ${scenario.title}`, function(assert) {
for (let annotation in annotations) { const libraries = library.default({
if (ENV.annotations.indexOf(annotation) >= 0) { assert: assert,
// have match, so test it library: Yadda.localisation.English.library(getDictionary()),
return 'testIt'; // return something other than a function });
const scenarioContext = {
ctx: {},
};
return runTest(this, libraries, scenario.steps, scenarioContext);
});
if (annotations.namespaceable && !annotations.notnamespaceable) {
['', 'default', 'team-1', undefined].forEach(function(item) {
test(`Scenario: ${
scenario.title
} with the ${item === '' ? 'empty' : typeof item === 'undefined' ? 'undefined' : item} namespace set`, function(assert) {
const libraries = library.default({
assert: assert,
library: Yadda.localisation.English.library(getDictionary(item)),
});
const scenarioContext = {
ctx: {
nspace: item,
},
};
return runTest(this, libraries, scenario.steps, scenarioContext);
});
});
} }
};
} }
};
// no match, so don't run it export const setupFeature = function(featureAnnotations) {
return logIt; return setupApplicationTest;
} };
} export const setupScenario = function(featureAnnotations, scenarioAnnotations) {
// call back functions
function ignoreIt(testElement) {
skip(`${testElement.title}`, function(/*assert*/) {});
}
function logIt(testElement) {
console.info(`Not running or skipping: "${testElement.title}"`); // eslint-disable-line no-console
}
// exported functions
function runFeature(annotations) {
return checkAnnotations(annotations);
}
function runScenario(featureAnnotations, scenarioAnnotations) {
return checkAnnotations(scenarioAnnotations);
}
// setup tests
// you can override these function to add additional setup setups, or handle new setup related annotations
function setupFeature(featureAnnotations) {
return setupYaddaTest(featureAnnotations);
}
function setupScenario(featureAnnotations, scenarioAnnotations) {
let setupFn = setupYaddaTest(scenarioAnnotations);
if (
setupFn &&
(featureAnnotations.setupapplicationtest ||
featureAnnotations.setuprenderingtest ||
featureAnnotations.setuptest)
) {
throw new Error(
'You must not assign any @setupapplicationtest, @setuprenderingtest or @setuptest annotations to a scenario as well as its feature!'
);
}
return function(model) { return function(model) {
model.afterEach(function() { model.afterEach(function() {
reset(); reset();
}); });
}; };
// return setupFn; };
} export const runFeature = function(annotations) {
return checkAnnotations(annotations);
};
function setupYaddaTest(annotations) { export const runScenario = function(featureAnnotations, scenarioAnnotations) {
if (annotations.setupapplicationtest) { return checkAnnotations({ ...featureAnnotations, ...scenarioAnnotations }, true);
return setupApplicationTest; };
}
if (annotations.setuprenderingtest) {
return setupRenderingTest;
}
if (annotations.setuptest) {
return setupTest;
}
}
export { runFeature, runScenario, setupFeature, setupScenario };

View File

@ -43,9 +43,7 @@ module('Integration | Adapter | policy', function(hooks) {
test(`requestForCreateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) { test(`requestForCreateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) {
const adapter = this.owner.lookup('adapter:policy'); const adapter = this.owner.lookup('adapter:policy');
const client = this.owner.lookup('service:client/http'); const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/policy?dc=${dc}${ const expected = `PUT /v1/acl/policy?dc=${dc}`;
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
}`;
const actual = adapter const actual = adapter
.requestForCreateRecord( .requestForCreateRecord(
client.url, client.url,
@ -62,9 +60,7 @@ module('Integration | Adapter | policy', function(hooks) {
test(`requestForUpdateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) { test(`requestForUpdateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) {
const adapter = this.owner.lookup('adapter:policy'); const adapter = this.owner.lookup('adapter:policy');
const client = this.owner.lookup('service:client/http'); const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/policy/${id}?dc=${dc}${ const expected = `PUT /v1/acl/policy/${id}?dc=${dc}`;
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
}`;
const actual = adapter const actual = adapter
.requestForUpdateRecord( .requestForUpdateRecord(
client.url, client.url,

View File

@ -36,9 +36,7 @@ module('Integration | Adapter | role', function(hooks) {
test(`requestForCreateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) { test(`requestForCreateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) {
const adapter = this.owner.lookup('adapter:role'); const adapter = this.owner.lookup('adapter:role');
const client = this.owner.lookup('service:client/http'); const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/role?dc=${dc}${ const expected = `PUT /v1/acl/role?dc=${dc}`;
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
}`;
const actual = adapter const actual = adapter
.requestForCreateRecord( .requestForCreateRecord(
client.url, client.url,
@ -55,9 +53,7 @@ module('Integration | Adapter | role', function(hooks) {
test(`requestForUpdateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) { test(`requestForUpdateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) {
const adapter = this.owner.lookup('adapter:role'); const adapter = this.owner.lookup('adapter:role');
const client = this.owner.lookup('service:client/http'); const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/role/${id}?dc=${dc}${ const expected = `PUT /v1/acl/role/${id}?dc=${dc}`;
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
}`;
const actual = adapter const actual = adapter
.requestForUpdateRecord( .requestForUpdateRecord(
client.url, client.url,

View File

@ -64,9 +64,7 @@ module('Integration | Adapter | token', function(hooks) {
test(`requestForCreateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) { test(`requestForCreateRecord returns the correct url/method when nspace is ${nspace}`, function(assert) {
const adapter = this.owner.lookup('adapter:token'); const adapter = this.owner.lookup('adapter:token');
const client = this.owner.lookup('service:client/http'); const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/token?dc=${dc}${ const expected = `PUT /v1/acl/token?dc=${dc}`;
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
}`;
const actual = adapter const actual = adapter
.requestForCreateRecord( .requestForCreateRecord(
client.url, client.url,
@ -83,9 +81,7 @@ module('Integration | Adapter | token', function(hooks) {
test(`requestForUpdateRecord returns the correct url (without Rules it uses the v2 API) when nspace is ${nspace}`, function(assert) { test(`requestForUpdateRecord returns the correct url (without Rules it uses the v2 API) when nspace is ${nspace}`, function(assert) {
const adapter = this.owner.lookup('adapter:token'); const adapter = this.owner.lookup('adapter:token');
const client = this.owner.lookup('service:client/http'); const client = this.owner.lookup('service:client/http');
const expected = `PUT /v1/acl/token/${id}?dc=${dc}${ const expected = `PUT /v1/acl/token/${id}?dc=${dc}`;
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
}`;
const actual = adapter const actual = adapter
.requestForUpdateRecord( .requestForUpdateRecord(
client.url, client.url,

View File

@ -61,7 +61,10 @@ export function visitable(path, encoder = encodeURIComponent) {
return executionContext.runAsync(context => { return executionContext.runAsync(context => {
var params; var params;
let fullPath = (function _try(paths) { let fullPath = (function _try(paths) {
const path = paths.shift(); let path = paths.shift();
if (typeof dynamicSegmentsAndQueryParams.nspace !== 'undefined') {
path = `/:nspace${path}`;
}
params = assign({}, dynamicSegmentsAndQueryParams); params = assign({}, dynamicSegmentsAndQueryParams);
var fullPath; var fullPath;
try { try {

View File

@ -40,6 +40,8 @@ import tokens from 'consul-ui/tests/pages/dc/acls/tokens/index';
import token from 'consul-ui/tests/pages/dc/acls/tokens/edit'; import token from 'consul-ui/tests/pages/dc/acls/tokens/edit';
import intentions from 'consul-ui/tests/pages/dc/intentions/index'; import intentions from 'consul-ui/tests/pages/dc/intentions/index';
import intention from 'consul-ui/tests/pages/dc/intentions/edit'; import intention from 'consul-ui/tests/pages/dc/intentions/edit';
import nspaces from 'consul-ui/tests/pages/dc/nspaces/index';
import nspace from 'consul-ui/tests/pages/dc/nspaces/edit';
const deletable = createDeletable(clickable); const deletable = createDeletable(clickable);
const submitable = createSubmitable(clickable, is); const submitable = createSubmitable(clickable, is);
@ -104,5 +106,9 @@ export default {
intentions(visitable, deletable, creatable, clickable, attribute, collection, intentionFilter) intentions(visitable, deletable, creatable, clickable, attribute, collection, intentionFilter)
), ),
intention: create(intention(visitable, submitable, deletable, cancelable)), intention: create(intention(visitable, submitable, deletable, cancelable)),
nspaces: create(
nspaces(visitable, deletable, creatable, clickable, attribute, collection, text, freetextFilter)
),
nspace: create(nspace(visitable, submitable, deletable, cancelable)),
settings: create(settings(visitable, submitable)), settings: create(settings(visitable, submitable)),
}; };

View File

@ -0,0 +1,8 @@
export default function(visitable, submitable, deletable, cancelable) {
return {
visit: visitable(['/:dc/namespaces/:namespace', '/:dc/namespaces/create']),
...submitable({}, 'form > div'),
...cancelable({}, 'form > div'),
...deletable({}, 'form > div'),
};
}

View File

@ -0,0 +1,24 @@
export default function(
visitable,
deletable,
creatable,
clickable,
attribute,
collection,
text,
filter
) {
return creatable({
visit: visitable('/:dc/namespaces'),
nspaces: collection(
'[data-test-tabular-row]',
deletable({
action: attribute('data-test-nspace-action', '[data-test-nspace-action]'),
description: text('[data-test-description]'),
nspace: clickable('a'),
actions: clickable('label'),
})
),
filter: filter,
});
}

View File

@ -1,3 +1,9 @@
import pages from 'consul-ui/tests/pages';
import Inflector from 'ember-inflector';
import utils from '@ember/test-helpers';
import api from 'consul-ui/tests/helpers/api';
import models from './steps/doubles/model'; import models from './steps/doubles/model';
import http from './steps/doubles/http'; import http from './steps/doubles/http';
import visit from './steps/interactions/visit'; import visit from './steps/interactions/visit';
@ -12,16 +18,38 @@ import assertForm from './steps/assertions/form';
// const dont = `( don't| shouldn't| can't)?`; // const dont = `( don't| shouldn't| can't)?`;
export default function(assert, library, pages, utils) { const pluralize = function(str) {
var currentPage; return Inflector.inflector.pluralize(str);
const getCurrentPage = function() { };
return currentPage; const getLastNthRequest = function(arr) {
return function(n, method) {
let requests = arr.slice(0).reverse();
if (method) {
requests = requests.filter(function(item) {
return item.method === method;
});
}
if (n == null) {
return requests;
}
return requests[n];
}; };
const setCurrentPage = function(page) { };
currentPage = page; const mb = function(path) {
return page; return function(obj) {
return (
path.map(function(prop) {
obj = obj || {};
if (isNaN(parseInt(prop))) {
return (obj = obj[prop]);
} else {
return (obj = obj.objectAt(parseInt(prop)));
}
}) && obj
);
}; };
};
export default function(assert, library) {
const pauseUntil = function(cb) { const pauseUntil = function(cb) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let count = 0; let count = 0;
@ -38,20 +66,27 @@ export default function(assert, library, pages, utils) {
}, 100); }, 100);
}); });
}; };
const mb = function(path) { const lastNthRequest = getLastNthRequest(api.server.history);
return function(obj) { const create = function(number, name, value) {
return ( // don't return a promise here as
path.map(function(prop) { // I don't need it to wait
obj = obj || {}; api.server.createList(name, number, value);
if (isNaN(parseInt(prop))) {
return (obj = obj[prop]);
} else {
return (obj = obj.objectAt(parseInt(prop)));
}
}) && obj
);
}; };
const respondWith = function(url, data) {
api.server.respondWith(url.split('?')[0], data);
}; };
const setCookie = function(key, value) {
api.server.setCookie(key, value);
};
let currentPage;
const getCurrentPage = function() {
return currentPage;
};
const setCurrentPage = function(page) {
currentPage = page;
return page;
};
const find = function(path) { const find = function(path) {
const page = getCurrentPage(); const page = getCurrentPage();
const parts = path.split('.'); const parts = path.split('.');
@ -73,19 +108,23 @@ export default function(assert, library, pages, utils) {
const clipboard = function() { const clipboard = function() {
return window.localStorage.getItem('clipboard'); return window.localStorage.getItem('clipboard');
}; };
models(library, utils.create); models(library, create);
http(library, utils.respondWith, utils.set); http(library, respondWith, setCookie);
visit(library, pages, setCurrentPage); visit(library, pages, setCurrentPage);
click(library, find, utils.click); click(library, find, utils.click);
form(library, find, utils.fillIn, utils.triggerKeyEvent, getCurrentPage); form(library, find, utils.fillIn, utils.triggerKeyEvent, getCurrentPage);
debug(library, assert, utils.currentURL); debug(library, assert, utils.currentURL);
assertHttp(library, assert, utils.lastNthRequest); assertHttp(library, assert, lastNthRequest);
assertModel(library, assert, find, getCurrentPage, pauseUntil, utils.pluralize); assertModel(library, assert, find, getCurrentPage, pauseUntil, pluralize);
assertPage(library, assert, find, getCurrentPage); assertPage(library, assert, find, getCurrentPage);
assertDom(library, assert, pauseUntil, utils.find, utils.currentURL, clipboard); assertDom(library, assert, pauseUntil, utils.find, utils.currentURL, clipboard);
assertForm(library, assert, find, getCurrentPage); assertForm(library, assert, find, getCurrentPage);
return library.given(["I'm using a legacy token"], function(number, model, data) { return library.given(["I'm using a legacy token"], function(number, model, data) {
window.localStorage['consul:token'] = JSON.stringify({ AccessorID: null, SecretID: 'id' }); window.localStorage['consul:token'] = JSON.stringify({
Namespace: 'default',
AccessorID: null,
SecretID: 'id',
});
}); });
} }

View File

@ -1,7 +1,3 @@
// TODO: This entire file/steps need refactoring out so that they don't depend on order
// unless you specifically ask it to assert for order of requests
// this should also let us simplify the entire API for these steps
// an reword them to make more sense
export default function(scenario, assert, lastNthRequest) { export default function(scenario, assert, lastNthRequest) {
// lastNthRequest should return a // lastNthRequest should return a
// { // {
@ -9,109 +5,7 @@ export default function(scenario, assert, lastNthRequest) {
// requestBody: '', // requestBody: '',
// requestHeaders: '' // requestHeaders: ''
// } // }
const assertRequest = function(request, method, url) {
assert.equal(
request.method,
method,
`Expected the request method to be ${method}, was ${request.method}`
);
assert.equal(request.url, url, `Expected the request url to be ${url}, was ${request.url}`);
};
scenario scenario
.then('a $method request is made to "$url" with the body from yaml\n$yaml', function(
method,
url,
data
) {
const request = lastNthRequest(1);
assertRequest(request, method, url);
const body = JSON.parse(request.requestBody);
Object.keys(data).forEach(function(key, i, arr) {
assert.deepEqual(
body[key],
data[key],
`Expected the payload to contain ${key} equaling ${data[key]}, ${key} was ${body[key]}`
);
});
})
// TODO: This one can replace the above one, it covers more use cases
// also DRY it out a bit
.then('a $method request is made to "$url" from yaml\n$yaml', function(method, url, yaml) {
const request = lastNthRequest(1);
assertRequest(request, method, url);
let data = yaml.body || {};
const body = JSON.parse(request.requestBody);
Object.keys(data).forEach(function(key, i, arr) {
assert.equal(
body[key],
data[key],
`Expected the payload to contain ${key} to equal ${body[key]}, ${key} was ${data[key]}`
);
});
data = yaml.headers || {};
const headers = request.requestHeaders;
Object.keys(data).forEach(function(key, i, arr) {
assert.equal(
headers[key],
data[key],
`Expected the payload to contain ${key} to equal ${headers[key]}, ${key} was ${data[key]}`
);
});
})
.then('a $method request is made to "$url" with the body "$body"', function(method, url, data) {
const request = lastNthRequest(1);
assertRequest(request, method, url);
assert.equal(
request.requestBody,
data,
`Expected the request body to be ${data}, was ${request.requestBody}`
);
})
.then('a $method request is made to "$url" with no body', function(method, url) {
const request = lastNthRequest(1);
assertRequest(request, method, url);
assert.equal(
request.requestBody,
null,
`Expected the request body to be null, was ${request.requestBody}`
);
})
.then('a $method request is made to "$url"', function(method, url) {
const request = lastNthRequest(1);
assertRequest(request, method, url);
})
.then('the last $method request was made to "$url"', function(method, url) {
const request = lastNthRequest(0, method);
assertRequest(request, method, url);
})
.then('the last $method request was made to "$url" with the body from yaml\n$yaml', function(
method,
url,
data
) {
const request = lastNthRequest(0, method);
const body = JSON.parse(request.requestBody);
assert.ok(request, `Expected a ${method} request`);
assertRequest(request, method, url);
Object.keys(data).forEach(function(key, i, arr) {
assert.deepEqual(
body[key],
data[key],
`Expected the payload to contain ${key} equaling ${data[key]}, ${key} was ${body[key]}`
);
});
})
.then('the last $method requests were like yaml\n$yaml', function(method, data) {
const requests = lastNthRequest(null, method);
data.reverse().forEach(function(item, i, arr) {
assert.equal(
requests[i].url,
item,
`Expected the request url to be ${item}, was ${requests[i].url}`
);
});
})
.then('the last $method requests included from yaml\n$yaml', function(method, data) { .then('the last $method requests included from yaml\n$yaml', function(method, data) {
const requests = lastNthRequest(null, method); const requests = lastNthRequest(null, method);
const a = new Set(data); const a = new Set(data);
@ -127,14 +21,40 @@ export default function(scenario, assert, lastNthRequest) {
); );
assert.equal(diff.size, 0, `Expected requests "${[...diff].join(', ')}"`); assert.equal(diff.size, 0, `Expected requests "${[...diff].join(', ')}"`);
}) })
.then('a $method request was made to "$url"', function(method, url) { .then('a $method request was made to "$endpoint"', function(method, url) {
const requests = lastNthRequest(null, method); const requests = lastNthRequest(null, method);
const request = requests.find(function(item) { const request = requests.find(function(item) {
return method === item.method && url === item.url; return method === item.method && url === item.url;
}); });
assert.ok(request, `Expected a ${method} request url to ${url}`); assert.ok(request, `Expected a ${method} request url to ${url}`);
}) })
.then('a $method request was made to "$url" from yaml\n$yaml', function(method, url, yaml) { .then('a $method request was made to "$endpoint" with no body', function(method, url) {
const requests = lastNthRequest(null, method);
const request = requests.find(function(item) {
return method === item.method && url === item.url;
});
assert.equal(
request.requestBody,
null,
`Expected the request body to be null, was ${request.requestBody}`
);
})
.then('a $method request was made to "$endpoint" with the body "$body"', function(
method,
url,
body
) {
const requests = lastNthRequest(null, method);
const request = requests.find(function(item) {
return method === item.method && url === item.url;
});
assert.ok(request, `Expected a ${method} request url to ${url} with the body "${body}"`);
})
.then('a $method request was made to "$endpoint" from yaml\n$yaml', function(
method,
url,
yaml
) {
const requests = lastNthRequest(null, method); const requests = lastNthRequest(null, method);
const request = requests.find(function(item) { const request = requests.find(function(item) {
return method === item.method && url === item.url; return method === item.method && url === item.url;
@ -142,19 +62,23 @@ export default function(scenario, assert, lastNthRequest) {
let data = yaml.body || {}; let data = yaml.body || {};
const body = JSON.parse(request.requestBody); const body = JSON.parse(request.requestBody);
Object.keys(data).forEach(function(key, i, arr) { Object.keys(data).forEach(function(key, i, arr) {
assert.equal( assert.deepEqual(
body[key], body[key],
data[key], data[key],
`Expected the payload to contain ${key} to equal ${body[key]}, ${key} was ${data[key]}` `Expected the payload to contain ${key} equaling ${JSON.stringify(
data[key]
)}, ${key} was ${JSON.stringify(body[key])}`
); );
}); });
data = yaml.headers || {}; data = yaml.headers || {};
const headers = request.requestHeaders; const headers = request.requestHeaders;
Object.keys(data).forEach(function(key, i, arr) { Object.keys(data).forEach(function(key, i, arr) {
assert.equal( assert.deepEqual(
headers[key], headers[key],
data[key], data[key],
`Expected the payload to contain ${key} to equal ${headers[key]}, ${key} was ${data[key]}` `Expected the payload to contain ${key} equaling ${JSON.stringify(
data[key]
)}, ${key} was ${JSON.stringify(headers[key])}`
); );
}); });
}); });

View File

@ -1,12 +1,12 @@
export default function(scenario, respondWith, set) { export default function(scenario, respondWith, set) {
// respondWith should set the url to return a certain response shape // respondWith should set the url to return a certain response shape
scenario scenario
.given(['the url "$url" responds with a $status status'], function(url, status) { .given(['the url "$endpoint" responds with a $status status'], function(url, status) {
respondWith(url, { respondWith(url, {
status: parseInt(status), status: parseInt(status),
}); });
}) })
.given(['the url "$url" responds with from yaml\n$yaml'], function(url, data) { .given(['the url "$endpoint" responds with from yaml\n$yaml'], function(url, data) {
respondWith(url, data); respondWith(url, data);
}) })
.given('a network latency of $number', function(number) { .given('a network latency of $number', function(number) {

View File

@ -11,6 +11,10 @@ export default function(scenario, pages, set) {
.when( .when(
['I visit the $name page for yaml\n$yaml', 'I visit the $name page for json\n$json'], ['I visit the $name page for yaml\n$yaml', 'I visit the $name page for json\n$json'],
function(name, data) { function(name, data) {
const nspace = this.ctx.nspace;
if (nspace !== '' && typeof nspace !== 'undefined') {
data.nspace = `~${nspace}`;
}
// TODO: Consider putting an assertion here for testing the current url // TODO: Consider putting an assertion here for testing the current url
// do I absolutely definitely need that all the time? // do I absolutely definitely need that all the time?
return set(pages[name]).visit(data); return set(pages[name]).visit(data);

View File

@ -34,7 +34,7 @@ module('Unit | Mixin | acl/with actions', function(hooks) {
}) })
); );
const item = { ID: 'id' }; const item = { ID: 'id' };
const expectedToken = { AccessorID: null, SecretID: item.ID }; const expectedToken = { Namespace: 'default', AccessorID: null, SecretID: item.ID };
this.owner.register( this.owner.register(
'service:settings', 'service:settings',
Service.extend({ Service.extend({

View File

@ -0,0 +1,10 @@
import nonEmptySet from 'consul-ui/utils/non-empty-set';
import { module, test } from 'qunit';
module('Unit | Utility | nonEmptySet', function() {
// Replace this with your real tests.
test('it works', function(assert) {
let result = nonEmptySet();
assert.ok(result);
});
});

View File

@ -979,9 +979,9 @@
"@glimmer/util" "^0.42.0" "@glimmer/util" "^0.42.0"
"@hashicorp/api-double@^1.3.0": "@hashicorp/api-double@^1.3.0":
version "1.6.0" version "1.6.1"
resolved "https://registry.yarnpkg.com/@hashicorp/api-double/-/api-double-1.6.0.tgz#23c48d1982b81b6c9164067354d7653320ba761f" resolved "https://registry.yarnpkg.com/@hashicorp/api-double/-/api-double-1.6.1.tgz#67c4c4c5cbf9f51f3b8bc992ab2df21acf63b318"
integrity sha512-U11NttTVvJUVOFH4bgS8eZ+0s6j4/4DYPz9xkJM2ciZBnG353l2G7LYeivt55QajnCl3ImevEO4vSlvvFf4I4Q== integrity sha512-JkQZIsH/2B9T2oK5SQNDakvqlHjxQHu0I9ftmmrxqkxYvYoLN+Whp7dzQ8HswOp1vIJyqbvUhSw06XfH/eimZA==
dependencies: dependencies:
array-range "^1.0.1" array-range "^1.0.1"
backtick-template "^0.2.0" backtick-template "^0.2.0"
@ -3345,9 +3345,9 @@ caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805, caniuse-lite@^1.0.300009
integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw== integrity sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw==
caniuse-lite@^1.0.30000844: caniuse-lite@^1.0.30000844:
version "1.0.30001021" version "1.0.30001022"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001021.tgz#e75ed1ef6dbadd580ac7e7720bb16f07b083f254" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001022.tgz#9eeffe580c3a8f110b7b1742dcf06a395885e4c6"
integrity sha512-wuMhT7/hwkgd8gldgp2jcrUjOU9RXJ4XxGumQeOsUr91l3WwmM68Cpa/ymCnWEDqakwFXhuDQbaKNHXBPgeE9g== integrity sha512-FjwPPtt/I07KyLPkBQ0g7/XuZg6oUkYBVnPHNj3VHJbOjmmJ/GdSo/GUY6MwINEQvjhP6WZVbX8Tvms8xh0D5A==
capture-exit@^2.0.0: capture-exit@^2.0.0:
version "2.0.0" version "2.0.0"
@ -4302,9 +4302,9 @@ electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.30:
integrity sha512-NWJ5TztDnjExFISZHFwpoJjMbLUifsNBnx7u2JI0gCw6SbKyQYYWWtBHasO/jPtHym69F4EZuTpRNGN11MT/jg== integrity sha512-NWJ5TztDnjExFISZHFwpoJjMbLUifsNBnx7u2JI0gCw6SbKyQYYWWtBHasO/jPtHym69F4EZuTpRNGN11MT/jg==
electron-to-chromium@^1.3.47: electron-to-chromium@^1.3.47:
version "1.3.335" version "1.3.340"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.335.tgz#5fb6084a25cb1e2542df91e62b62e1931a602303" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.340.tgz#5d4fe78e984d4211194cf5a52e08069543da146f"
integrity sha512-ngKsDGd/xr2lAZvilxTfdvfEiQKmavyXd6irlswaHnewmXoz6JgbM9FUNwgp3NFIUHHegh1F87H8f5BJ8zABxw== integrity sha512-hRFBAglhcj5iVYH+o8QU0+XId1WGoc0VGowJB1cuJAt3exHGrivZvWeAO5BRgBZqwZtwxjm8a5MQeGoT/Su3ww==
elegant-spinner@^1.0.1: elegant-spinner@^1.0.1:
version "1.0.1" version "1.0.1"
@ -9959,9 +9959,9 @@ resolve@^1.1.3, resolve@^1.1.7, resolve@^1.10.1, resolve@^1.11.1, resolve@^1.12.
path-parse "^1.0.6" path-parse "^1.0.6"
resolve@^1.10.0, resolve@^1.3.3: resolve@^1.10.0, resolve@^1.3.3:
version "1.14.2" version "1.15.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5"
integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==
dependencies: dependencies:
path-parse "^1.0.6" path-parse "^1.0.6"