mirror of https://github.com/hashicorp/consul
Merge pull request #7235 from hashicorp/ui-staging
ui: UI Release Merge (ui-staging merge)pull/7236/head
commit
cb69613bf6
|
@ -497,7 +497,6 @@ jobs:
|
||||||
environment:
|
environment:
|
||||||
EMBER_TEST_PARALLEL: true #enables test parallelization with ember-exam
|
EMBER_TEST_PARALLEL: true #enables test parallelization with ember-exam
|
||||||
EMBER_TEST_REPORT: test-results/report.xml #outputs test report for CircleCI test summary
|
EMBER_TEST_REPORT: test-results/report.xml #outputs test report for CircleCI test summary
|
||||||
parallelism: 4
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
|
@ -506,7 +505,7 @@ jobs:
|
||||||
at: ui-v2
|
at: ui-v2
|
||||||
- run:
|
- run:
|
||||||
working_directory: ui-v2
|
working_directory: ui-v2
|
||||||
command: node_modules/ember-cli/bin/ember exam --split=$CIRCLE_NODE_TOTAL --partition=`expr $CIRCLE_NODE_INDEX + 1` --path dist --silent -r xunit
|
command: make test-ci
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: ui-v2/test-results
|
path: ui-v2/test-results
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ start-api: deps
|
||||||
test: deps test-node
|
test: deps test-node
|
||||||
yarn run test
|
yarn run test
|
||||||
|
|
||||||
|
test-ci: deps test-node
|
||||||
|
yarn run test:ci
|
||||||
|
|
||||||
test-view: deps test-node
|
test-view: deps test-node
|
||||||
yarn run test:view
|
yarn run test:view
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ export default Adapter.extend({
|
||||||
requestForUpdateRecord: function(request, serialized, data) {
|
requestForUpdateRecord: function(request, serialized, data) {
|
||||||
const params = {
|
const params = {
|
||||||
...this.formatDatacenter(data[DATACENTER_KEY]),
|
...this.formatDatacenter(data[DATACENTER_KEY]),
|
||||||
|
flags: data.Flags,
|
||||||
...this.formatNspace(data[NSPACE_KEY]),
|
...this.formatNspace(data[NSPACE_KEY]),
|
||||||
};
|
};
|
||||||
return request`
|
return request`
|
||||||
|
|
|
@ -39,6 +39,7 @@ const MENU_ITEMS = '[role^="menuitem"]';
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: '',
|
tagName: '',
|
||||||
dom: service('dom'),
|
dom: service('dom'),
|
||||||
|
router: service('router'),
|
||||||
guid: '',
|
guid: '',
|
||||||
expanded: false,
|
expanded: false,
|
||||||
orientation: 'vertical',
|
orientation: 'vertical',
|
||||||
|
@ -47,6 +48,7 @@ export default Component.extend({
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
set(this, 'guid', this.dom.guid(this));
|
set(this, 'guid', this.dom.guid(this));
|
||||||
this._listeners = this.dom.listeners();
|
this._listeners = this.dom.listeners();
|
||||||
|
this._routelisteners = this.dom.listeners();
|
||||||
},
|
},
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
// TODO: How do you detect whether the children have changed?
|
// TODO: How do you detect whether the children have changed?
|
||||||
|
@ -54,10 +56,14 @@ export default Component.extend({
|
||||||
this.$menu = this.dom.element(`#${COMPONENT_ID}menu-${this.guid}`);
|
this.$menu = this.dom.element(`#${COMPONENT_ID}menu-${this.guid}`);
|
||||||
const labelledBy = this.$menu.getAttribute('aria-labelledby');
|
const labelledBy = this.$menu.getAttribute('aria-labelledby');
|
||||||
this.$trigger = this.dom.element(`#${labelledBy}`);
|
this.$trigger = this.dom.element(`#${labelledBy}`);
|
||||||
|
this._routelisteners.add(this.router, {
|
||||||
|
routeWillChange: () => this.actions.close.apply(this, [{}]),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
willDestroyElement: function() {
|
willDestroyElement: function() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
this._listeners.remove();
|
this._listeners.remove();
|
||||||
|
this._routelisteners.remove();
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
keypressClick: function(e) {
|
keypressClick: function(e) {
|
||||||
|
|
|
@ -31,11 +31,13 @@ export default Component.extend({
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
this._viewportlistener.add(
|
this._viewportlistener.add(
|
||||||
this.dom.isInViewport(this.element, bool => {
|
this.dom.isInViewport(this.element, bool => {
|
||||||
set(this, 'isDisplayed', bool);
|
if (get(this, 'isDisplayed') !== bool) {
|
||||||
if (this.isDisplayed) {
|
set(this, 'isDisplayed', bool);
|
||||||
this.addPathListeners();
|
if (this.isDisplayed) {
|
||||||
} else {
|
this.addPathListeners();
|
||||||
this.ticker.destroy(this);
|
} else {
|
||||||
|
this.ticker.destroy(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -63,24 +65,29 @@ export default Component.extend({
|
||||||
!routes.find(item => get(item, 'Definition.Match.HTTP.PathPrefix') === '/') &&
|
!routes.find(item => get(item, 'Definition.Match.HTTP.PathPrefix') === '/') &&
|
||||||
!routes.find(item => typeof item.Definition === 'undefined')
|
!routes.find(item => typeof item.Definition === 'undefined')
|
||||||
) {
|
) {
|
||||||
let nextNode = `resolver:${this.chain.ServiceName}.${this.chain.Namespace}.${this.chain.Datacenter}`;
|
let nextNode;
|
||||||
const splitterID = `splitter:${this.chain.ServiceName}`;
|
const resolverID = `resolver:${this.chain.ServiceName}.${this.chain.Namespace}.${this.chain.Datacenter}`;
|
||||||
if (typeof this.chain.Nodes[splitterID] !== 'undefined') {
|
const splitterID = `splitter:${this.chain.ServiceName}.${this.chain.Namespace}`;
|
||||||
|
if (typeof this.chain.Nodes[resolverID] !== 'undefined') {
|
||||||
|
nextNode = resolverID;
|
||||||
|
} else if (typeof this.chain.Nodes[splitterID] !== 'undefined') {
|
||||||
nextNode = splitterID;
|
nextNode = splitterID;
|
||||||
}
|
}
|
||||||
routes.push({
|
if (typeof nextNode !== 'undefined') {
|
||||||
Default: true,
|
routes.push({
|
||||||
ID: `route:${this.chain.ServiceName}`,
|
Default: true,
|
||||||
Name: this.chain.ServiceName,
|
ID: `route:${this.chain.ServiceName}`,
|
||||||
Definition: {
|
Name: this.chain.ServiceName,
|
||||||
Match: {
|
Definition: {
|
||||||
HTTP: {
|
Match: {
|
||||||
PathPrefix: '/',
|
HTTP: {
|
||||||
|
PathPrefix: '/',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
NextNode: nextNode,
|
||||||
NextNode: nextNode,
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
return routes;
|
return routes;
|
||||||
}),
|
}),
|
||||||
|
@ -92,23 +99,17 @@ export default Component.extend({
|
||||||
get(this, 'chain.Nodes')
|
get(this, 'chain.Nodes')
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
graph: computed('chain.Nodes', function() {
|
graph: computed('splitters', 'routes', function() {
|
||||||
const graph = this.dataStructs.graph();
|
const graph = this.dataStructs.graph();
|
||||||
const router = this.chain.ServiceName;
|
const router = this.chain.ServiceName;
|
||||||
Object.entries(get(this, 'chain.Nodes')).forEach(([key, item]) => {
|
this.splitters.forEach(item => {
|
||||||
switch (item.Type) {
|
item.Splits.forEach(splitter => {
|
||||||
case 'splitter':
|
graph.addLink(item.ID, splitter.NextNode);
|
||||||
item.Splits.forEach(splitter => {
|
});
|
||||||
graph.addLink(item.ID, splitter.NextNode);
|
});
|
||||||
});
|
this.routes.forEach((route, i) => {
|
||||||
break;
|
route = createRoute(route, router, this.dom.guid);
|
||||||
case 'router':
|
graph.addLink(route.ID, route.NextNode);
|
||||||
item.Routes.forEach((route, i) => {
|
|
||||||
route = createRoute(route, router, this.dom.guid);
|
|
||||||
graph.addLink(route.ID, route.NextNode);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return graph;
|
return graph;
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -4,12 +4,6 @@ import { get, computed } from '@ember/object';
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
tagName: '',
|
tagName: '',
|
||||||
path: computed('item', function() {
|
path: computed('item', function() {
|
||||||
if (get(this, 'item.Default')) {
|
|
||||||
return {
|
|
||||||
type: 'Default',
|
|
||||||
value: '/',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return Object.entries(get(this, 'item.Definition.Match.HTTP') || {}).reduce(
|
return Object.entries(get(this, 'item.Definition.Match.HTTP') || {}).reduce(
|
||||||
function(prev, [key, value]) {
|
function(prev, [key, value]) {
|
||||||
if (key.toLowerCase().startsWith('path')) {
|
if (key.toLowerCase().startsWith('path')) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { hash } from 'rsvp';
|
import { hash } from 'rsvp';
|
||||||
|
import { get } from '@ember/object';
|
||||||
|
|
||||||
export default Route.extend({
|
export default Route.extend({
|
||||||
repo: service('repository/service'),
|
repo: service('repository/service'),
|
||||||
|
@ -17,9 +18,15 @@ export default Route.extend({
|
||||||
const nspace = this.modelFor('nspace').nspace.substr(1);
|
const nspace = this.modelFor('nspace').nspace.substr(1);
|
||||||
return hash({
|
return hash({
|
||||||
item: this.repo.findBySlug(params.name, dc, nspace),
|
item: this.repo.findBySlug(params.name, dc, nspace),
|
||||||
chain: this.chainRepo.findBySlug(params.name, dc, nspace),
|
|
||||||
urls: this.settings.findBySlug('urls'),
|
urls: this.settings.findBySlug('urls'),
|
||||||
dc: dc,
|
dc: dc,
|
||||||
|
}).then(model => {
|
||||||
|
return hash({
|
||||||
|
chain: ['connect-proxy', 'mesh-gateway'].includes(get(model, 'item.Service.Kind'))
|
||||||
|
? null
|
||||||
|
: this.chainRepo.findBySlug(params.name, dc, nspace),
|
||||||
|
...model,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setupController: function(controller, model) {
|
setupController: function(controller, model) {
|
||||||
|
|
|
@ -4,6 +4,9 @@ export default function(filterable) {
|
||||||
const sLower = s.toLowerCase();
|
const sLower = s.toLowerCase();
|
||||||
return (
|
return (
|
||||||
get(item, 'Node')
|
get(item, 'Node')
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(sLower) !== -1 ||
|
||||||
|
get(item, 'Address')
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.indexOf(sLower) !== -1
|
.indexOf(sLower) !== -1
|
||||||
);
|
);
|
||||||
|
|
|
@ -65,8 +65,13 @@
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
padding-left: 36px;
|
padding-left: 36px;
|
||||||
}
|
}
|
||||||
|
/* here the !important is only needed for what seems to be a difference */
|
||||||
|
/* with the CSS before and after compression */
|
||||||
|
/* i.e. before compression this style is applied */
|
||||||
|
/* after compression it is in the source but doesn't seem to get */
|
||||||
|
/* applied (unless you add the !important) */
|
||||||
%menu-panel .is-active {
|
%menu-panel .is-active {
|
||||||
position: relative;
|
position: relative !important;
|
||||||
}
|
}
|
||||||
%menu-panel .is-active > *::after {
|
%menu-panel .is-active > *::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{{!<form>}}
|
{{!<form>}}
|
||||||
{{freetext-filter searchable=searchable value=search placeholder="Search by name"}}
|
{{freetext-filter searchable=searchable value=search placeholder="Search"}}
|
||||||
{{radio-group keyboardAccess=true name="status" value=status items=(array
|
{{radio-group keyboardAccess=true name="status" value=status items=(array
|
||||||
(hash label='All (Any Status)' value='' )
|
(hash label='All (Any Status)' value='' )
|
||||||
(hash label='Critical Checks' value='critical')
|
(hash label='Critical Checks' value='critical')
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
{{#if dc}}
|
{{#if dc}}
|
||||||
<ul>
|
<ul>
|
||||||
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (gt nspaces.length 0))}}
|
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (gt nspaces.length 0))}}
|
||||||
<li>
|
<li data-test-nspace-menu>
|
||||||
{{#if (and (eq nspaces.length 1) (not canManageNspaces)) }}
|
{{#if (and (eq nspaces.length 1) (not canManageNspaces)) }}
|
||||||
<span data-test-nspace-selected={{nspace.Name}}>{{nspace.Name}}</span>
|
<span data-test-nspace-selected={{nspace.Name}}>{{nspace.Name}}</span>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{yield (concat 'popover-menu-' guid)}}
|
{{yield (concat 'popover-menu-' guid)}}
|
||||||
{{#aria-menu keyboardAccess=keyboardAccess as |change keypress ariaLabelledBy ariaControls ariaExpanded keypressClick|}}
|
{{#aria-menu keyboardAccess=keyboardAccess as |change keypress ariaLabelledBy ariaControls ariaExpanded keypressClick|}}
|
||||||
{{#toggle-button checked=expanded onchange=(queue change (action 'change')) as |click|}}
|
{{#toggle-button checked=ariaExpanded onchange=(queue change (action 'change')) as |click|}}
|
||||||
<button type="button" aria-haspopup="menu" onkeydown={{keypress}} onclick={{click}} id={{ariaLabelledBy}} aria-controls={{ariaControls}}>
|
<button type="button" aria-haspopup="menu" onkeydown={{keypress}} onclick={{click}} id={{ariaLabelledBy}} aria-controls={{ariaControls}}>
|
||||||
{{#yield-slot name='trigger'}}
|
{{#yield-slot name='trigger'}}
|
||||||
{{yield}}
|
{{yield}}
|
||||||
|
|
|
@ -12,9 +12,7 @@
|
||||||
{{path.type}}
|
{{path.type}}
|
||||||
</dt>
|
</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{{#if (not-eq path.type 'Default')}}
|
|
||||||
{{path.value}}
|
{{path.value}}
|
||||||
{{/if}}
|
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
@ -1,59 +1,59 @@
|
||||||
{{title item.Service.Service}}
|
{{title item.Service.Service}}
|
||||||
{{#app-view class="service show"}}
|
{{#app-view class="service show"}}
|
||||||
{{#block-slot name='notification' as |status type|}}
|
{{#block-slot name='notification' as |status type|}}
|
||||||
{{partial 'dc/services/notifications'}}
|
{{partial 'dc/services/notifications'}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot name='breadcrumbs'}}
|
{{#block-slot name='breadcrumbs'}}
|
||||||
<ol>
|
<ol>
|
||||||
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot name='header'}}
|
{{#block-slot name='header'}}
|
||||||
<h1>
|
<h1>
|
||||||
{{ item.Service.Service }}
|
{{item.Service.Service}}
|
||||||
{{#with (service/external-source item.Service) as |externalSource| }}
|
{{#with (service/external-source item.Service) as |externalSource|}}
|
||||||
{{#with (css-var (concat '--' externalSource '-color-svg') 'none') as |bg| }}
|
{{#with (css-var (concat '--' externalSource '-color-svg') 'none') as |bg|}}
|
||||||
{{#if (not-eq bg 'none') }}
|
{{#if (not-eq bg 'none')}}
|
||||||
<span data-test-external-source="{{externalSource}}" style={{{ concat 'background-image:' bg }}} data-tooltip="Registered via {{externalSource}}">Registered via {{externalSource}}</span>
|
<span data-test-external-source={{externalSource}} style={{{concat 'background-image:' bg}}} data-tooltip="Registered via {{externalSource}}">Registered via {{externalSource}}</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/with}}
|
{{/with}}
|
||||||
{{/with}}
|
{{/with}}
|
||||||
{{#if (eq item.Service.Kind 'connect-proxy')}}
|
{{#if (eq item.Service.Kind 'connect-proxy')}}
|
||||||
<span class="kind-proxy">Proxy</span>
|
<span class="kind-proxy">Proxy</span>
|
||||||
{{else if (eq item.Service.Kind 'mesh-gateway')}}
|
{{else if (eq item.Service.Kind 'mesh-gateway')}}
|
||||||
<span class="kind-proxy">Mesh Gateway</span>
|
<span class="kind-proxy">Mesh Gateway</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</h1>
|
</h1>
|
||||||
<label for="toolbar-toggle"></label>
|
<label for="toolbar-toggle"></label>
|
||||||
{{tab-nav
|
{{tab-nav
|
||||||
items=(compact
|
items=(compact
|
||||||
(array
|
(array
|
||||||
'Instances'
|
'Instances'
|
||||||
'Routing'
|
(if (not-eq chain null) 'Routing' '')
|
||||||
'Tags'
|
'Tags'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
selected=selectedTab
|
selected=selectedTab
|
||||||
}}
|
}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot name='actions'}}
|
{{#block-slot name='actions'}}
|
||||||
{{#if urls.service}}
|
{{#if urls.service}}
|
||||||
{{#templated-anchor data-test-dashboard-anchor href=urls.service vars=(hash Datacenter=dc Service=(hash Name=item.Service.Service)) rel="external"}}Open Dashboard{{/templated-anchor}}
|
{{#templated-anchor data-test-dashboard-anchor href=urls.service vars=(hash Datacenter=dc Service=(hash Name=item.Service.Service)) rel="external"}}Open Dashboard{{/templated-anchor}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{#block-slot name='content'}}
|
{{#block-slot name='content'}}
|
||||||
{{#each
|
{{#each
|
||||||
(compact
|
(compact
|
||||||
(array
|
(array
|
||||||
(hash id=(slugify 'Instances') partial='dc/services/instances')
|
(hash id=(slugify 'Instances') partial='dc/services/instances')
|
||||||
(hash id=(slugify 'Routing') partial='dc/services/routing')
|
(if (not-eq chain null) (hash id=(slugify 'Routing') partial='dc/services/routing') '')
|
||||||
(hash id=(slugify 'Tags') partial='dc/services/tags')
|
(hash id=(slugify 'Tags') partial='dc/services/tags')
|
||||||
)
|
)
|
||||||
) as |panel|
|
) as |panel|
|
||||||
}}
|
}}
|
||||||
{{#tab-section id=panel.id selected=(eq (if selectedTab selectedTab '') panel.id) onchange=(action "change")}}
|
{{#tab-section id=panel.id selected=(eq (if selectedTab selectedTab '') panel.id) onchange=(action 'change')}}
|
||||||
{{partial panel.partial}}
|
{{partial panel.partial}}
|
||||||
{{/tab-section}}
|
{{/tab-section}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/block-slot}}
|
{{/block-slot}}
|
||||||
{{/app-view}}
|
{{/app-view}}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{#if (not (env 'CONSUL_UI_DISABLE_REALTIME'))}}
|
{{#if (not (env 'CONSUL_UI_DISABLE_REALTIME'))}}
|
||||||
<fieldset>
|
<fieldset data-test-blocking-queries>
|
||||||
<h2>Blocking Queries</h2>
|
<h2>Blocking Queries</h2>
|
||||||
<p>Keep catalog info up-to-date without refreshing the page. Any changes made to services, nodes and intentions would be reflected in real time.</p>
|
<p>Keep catalog info up-to-date without refreshing the page. Any changes made to services, nodes and intentions would be reflected in real time.</p>
|
||||||
<div class="type-toggle">
|
<div class="type-toggle">
|
||||||
|
|
|
@ -37,7 +37,6 @@ export const getAlternateServices = function(targets, a) {
|
||||||
export const getSplitters = function(nodes) {
|
export const getSplitters = function(nodes) {
|
||||||
return getNodesByType(nodes, 'splitter').map(function(item) {
|
return getNodesByType(nodes, 'splitter').map(function(item) {
|
||||||
// Splitters need IDs adding so we can find them in the DOM later
|
// Splitters need IDs adding so we can find them in the DOM later
|
||||||
item.ID = `splitter:${item.Name}`;
|
|
||||||
// splitters have a service.nspace as a name
|
// splitters have a service.nspace as a name
|
||||||
// do the reverse dance to ensure we don't mess up any
|
// do the reverse dance to ensure we don't mess up any
|
||||||
// serivice names with dots in them
|
// serivice names with dots in them
|
||||||
|
@ -45,8 +44,11 @@ export const getSplitters = function(nodes) {
|
||||||
temp.reverse();
|
temp.reverse();
|
||||||
temp.shift();
|
temp.shift();
|
||||||
temp.reverse();
|
temp.reverse();
|
||||||
item.Name = temp.join('.');
|
return {
|
||||||
return item;
|
...item,
|
||||||
|
ID: `splitter:${item.Name}`,
|
||||||
|
Name: temp.join('.'),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
export const getRoutes = function(nodes, uid) {
|
export const getRoutes = function(nodes, uid) {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"start:consul": "ember serve --proxy=${CONSUL_HTTP_ADDR:-http://localhost:8500} --port=${EMBER_SERVE_PORT:-4200} --live-reload-port=${EMBER_LIVE_RELOAD_PORT:-7020}",
|
"start:consul": "ember serve --proxy=${CONSUL_HTTP_ADDR:-http://localhost:8500} --port=${EMBER_SERVE_PORT:-4200} --live-reload-port=${EMBER_LIVE_RELOAD_PORT:-7020}",
|
||||||
"start:api": "api-double --dir ./node_modules/@hashicorp/consul-api-double",
|
"start:api": "api-double --dir ./node_modules/@hashicorp/consul-api-double",
|
||||||
"test": "ember test --test-port=${EMBER_TEST_PORT:-7357}",
|
"test": "ember test --test-port=${EMBER_TEST_PORT:-7357}",
|
||||||
|
"test:ci": "ember test --test-port=${EMBER_TEST_PORT:-7357} --path dist --silent --reporter xunit",
|
||||||
"test:parallel": "EMBER_EXAM_PARALLEL=true ember exam --split=4 --parallel",
|
"test:parallel": "EMBER_EXAM_PARALLEL=true ember exam --split=4 --parallel",
|
||||||
"test:view": "ember test --server --test-port=${EMBER_TEST_PORT:-7357}",
|
"test:view": "ember test --server --test-port=${EMBER_TEST_PORT:-7357}",
|
||||||
"test:node": "tape ./node-tests/**/*.js",
|
"test:node": "tape ./node-tests/**/*.js",
|
||||||
|
|
|
@ -6,6 +6,7 @@ Feature: dc / kvs / update: KV Update
|
||||||
And 1 kv model from yaml
|
And 1 kv model from yaml
|
||||||
---
|
---
|
||||||
Key: "[Name]"
|
Key: "[Name]"
|
||||||
|
Flags: 12
|
||||||
---
|
---
|
||||||
When I visit the kv page for yaml
|
When I visit the kv page for yaml
|
||||||
---
|
---
|
||||||
|
@ -21,7 +22,7 @@ Feature: dc / kvs / update: KV Update
|
||||||
value: [Value]
|
value: [Value]
|
||||||
---
|
---
|
||||||
And I submit
|
And I submit
|
||||||
Then a PUT request was made to "/v1/kv/[EncodedName]?dc=datacenter&ns=@!namespace" with the body "[Value]"
|
Then a PUT request was made to "/v1/kv/[EncodedName]?dc=datacenter&flags=12&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:
|
||||||
|
@ -37,6 +38,7 @@ Feature: dc / kvs / update: KV Update
|
||||||
And 1 kv model from yaml
|
And 1 kv model from yaml
|
||||||
---
|
---
|
||||||
Key: key
|
Key: key
|
||||||
|
Flags: 12
|
||||||
---
|
---
|
||||||
When I visit the kv page for yaml
|
When I visit the kv page for yaml
|
||||||
---
|
---
|
||||||
|
@ -51,7 +53,7 @@ Feature: dc / kvs / update: KV Update
|
||||||
value: ' '
|
value: ' '
|
||||||
---
|
---
|
||||||
And I submit
|
And I submit
|
||||||
Then a PUT request was made to "/v1/kv/key?dc=datacenter&ns=@!namespace" with the body " "
|
Then a PUT request was made to "/v1/kv/key?dc=datacenter&flags=12&ns=@!namespace" with the body " "
|
||||||
Then the url should be /datacenter/kv
|
Then the url should be /datacenter/kv
|
||||||
And the title should be "Key/Value - Consul"
|
And the title should be "Key/Value - Consul"
|
||||||
And "[data-notification]" has the "notification-update" class
|
And "[data-notification]" has the "notification-update" class
|
||||||
|
@ -60,6 +62,7 @@ Feature: dc / kvs / update: KV Update
|
||||||
And 1 kv model from yaml
|
And 1 kv model from yaml
|
||||||
---
|
---
|
||||||
Key: key
|
Key: key
|
||||||
|
Flags: 12
|
||||||
---
|
---
|
||||||
When I visit the kv page for yaml
|
When I visit the kv page for yaml
|
||||||
---
|
---
|
||||||
|
@ -74,15 +77,16 @@ Feature: dc / kvs / update: KV Update
|
||||||
value: ''
|
value: ''
|
||||||
---
|
---
|
||||||
And I submit
|
And I submit
|
||||||
Then a PUT request was made to "/v1/kv/key?dc=datacenter&ns=@!namespace" with no body
|
Then a PUT request was made to "/v1/kv/key?dc=datacenter&flags=12&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
|
||||||
Scenario: Update to a key when the value is empty
|
Scenario: Update to a key when the value is empty
|
||||||
And 1 kv model from yaml
|
And 1 kv model from yaml
|
||||||
---
|
---
|
||||||
Key: key
|
Key: key
|
||||||
Value: ~
|
Value: ~
|
||||||
|
Flags: 12
|
||||||
---
|
---
|
||||||
When I visit the kv page for yaml
|
When I visit the kv page for yaml
|
||||||
---
|
---
|
||||||
|
@ -91,7 +95,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 was made to "/v1/kv/key?dc=datacenter&ns=@!namespace" with no body
|
Then a PUT request was made to "/v1/kv/key?dc=datacenter&flags=12&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
|
||||||
|
|
|
@ -50,3 +50,30 @@ Feature: dc / nodes / index
|
||||||
Then the url should be /dc-1/nodes
|
Then the url should be /dc-1/nodes
|
||||||
Then I see 3 node models
|
Then I see 3 node models
|
||||||
And I see leader on the healthyNodes
|
And I see leader on the healthyNodes
|
||||||
|
Scenario: Searching the nodes with name and IP address
|
||||||
|
Given 3 node models from yaml
|
||||||
|
---
|
||||||
|
- Node: node-01
|
||||||
|
Address: 10.0.0.0
|
||||||
|
- Node: node-02
|
||||||
|
Address: 10.0.0.1
|
||||||
|
- Node: node-03
|
||||||
|
Address: 10.0.0.2
|
||||||
|
---
|
||||||
|
When I visit the nodes page for yaml
|
||||||
|
---
|
||||||
|
dc: dc-1
|
||||||
|
---
|
||||||
|
And I see 3 node models
|
||||||
|
Then I fill in with yaml
|
||||||
|
---
|
||||||
|
s: node-01
|
||||||
|
---
|
||||||
|
And I see 1 node model
|
||||||
|
And I see 1 node model with the name "node-01"
|
||||||
|
Then I fill in with yaml
|
||||||
|
---
|
||||||
|
s: 10.0.0.1
|
||||||
|
---
|
||||||
|
And I see 1 node model
|
||||||
|
And I see 1 node model with the name "node-02"
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
@setupApplicationTest
|
||||||
|
Feature: dc / nspaces / manage : Managing Namespaces
|
||||||
|
Scenario:
|
||||||
|
Given settings from yaml
|
||||||
|
---
|
||||||
|
consul:token:
|
||||||
|
SecretID: secret
|
||||||
|
AccessorID: accessor
|
||||||
|
Namespace: default
|
||||||
|
---
|
||||||
|
And 1 datacenter models from yaml
|
||||||
|
---
|
||||||
|
- dc-1
|
||||||
|
---
|
||||||
|
And 6 service models
|
||||||
|
When I visit the services page for yaml
|
||||||
|
---
|
||||||
|
dc: dc-1
|
||||||
|
---
|
||||||
|
Then the url should be /dc-1/services
|
||||||
|
Then I see 6 service models
|
||||||
|
# In order to test this properly you have to click around a few times
|
||||||
|
# between services and nspace management
|
||||||
|
When I click nspace on the navigation
|
||||||
|
And I click manageNspaces on the navigation
|
||||||
|
Then the url should be /dc-1/namespaces
|
||||||
|
And I don't see manageNspacesIsVisible on the navigation
|
||||||
|
When I click services on the navigation
|
||||||
|
Then the url should be /dc-1/services
|
||||||
|
When I click nspace on the navigation
|
||||||
|
And I click manageNspaces on the navigation
|
||||||
|
Then the url should be /dc-1/namespaces
|
||||||
|
And I don't see manageNspacesIsVisible on the navigation
|
|
@ -0,0 +1,37 @@
|
||||||
|
@setupApplicationTest
|
||||||
|
Feature: dc / services / Show Routing for Serivce
|
||||||
|
Scenario: Given a service, the Routing tab should display
|
||||||
|
Given 1 datacenter model with the value "dc1"
|
||||||
|
And 1 node models
|
||||||
|
And 1 service model from yaml
|
||||||
|
---
|
||||||
|
- Service:
|
||||||
|
Kind: consul
|
||||||
|
Name: service-0
|
||||||
|
ID: service-0-with-id
|
||||||
|
---
|
||||||
|
When I visit the service page for yaml
|
||||||
|
---
|
||||||
|
dc: dc1
|
||||||
|
service: service-0
|
||||||
|
---
|
||||||
|
And the title should be "service-0 - Consul"
|
||||||
|
And I see routing on the tabs
|
||||||
|
Scenario: Given a service proxy, the Routing tab should not display
|
||||||
|
Given 1 datacenter model with the value "dc1"
|
||||||
|
And 1 node models
|
||||||
|
And 1 service model from yaml
|
||||||
|
---
|
||||||
|
- Service:
|
||||||
|
Kind: connect-proxy
|
||||||
|
Name: service-0-proxy
|
||||||
|
ID: service-0-proxy-with-id
|
||||||
|
---
|
||||||
|
When I visit the service page for yaml
|
||||||
|
---
|
||||||
|
dc: dc1
|
||||||
|
service: service-0-proxy
|
||||||
|
---
|
||||||
|
And the title should be "service-0-proxy - Consul"
|
||||||
|
And I don't see routing on the tabs
|
||||||
|
|
|
@ -2,8 +2,23 @@
|
||||||
@notNamespaceable
|
@notNamespaceable
|
||||||
|
|
||||||
Feature: settings / show: Show Settings Page
|
Feature: settings / show: Show Settings Page
|
||||||
Scenario:
|
Scenario: I see the Blocking queries
|
||||||
Given 1 datacenter model with the value "datacenter"
|
Given 1 datacenter model with the value "datacenter"
|
||||||
When I visit the settings page
|
When I visit the settings page
|
||||||
Then the url should be /setting
|
Then the url should be /setting
|
||||||
And the title should be "Settings - Consul"
|
And the title should be "Settings - Consul"
|
||||||
|
And I see blockingQueries
|
||||||
|
Scenario: Setting CONSUL_UI_DISABLE_REALTIME hides Blocking Queries
|
||||||
|
Given 1 datacenter model with the value "datacenter"
|
||||||
|
And settings from yaml
|
||||||
|
---
|
||||||
|
CONSUL_UI_DISABLE_REALTIME: 1
|
||||||
|
---
|
||||||
|
Then I have settings like yaml
|
||||||
|
---
|
||||||
|
CONSUL_UI_DISABLE_REALTIME: "1"
|
||||||
|
---
|
||||||
|
When I visit the settings page
|
||||||
|
Then the url should be /setting
|
||||||
|
And the title should be "Settings - Consul"
|
||||||
|
And I don't see blockingQueries
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
|
@ -58,7 +58,8 @@ module('Integration | Adapter | kv', 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:kv');
|
const adapter = this.owner.lookup('adapter:kv');
|
||||||
const client = this.owner.lookup('service:client/http');
|
const client = this.owner.lookup('service:client/http');
|
||||||
const expected = `PUT /v1/kv/${id}?dc=${dc}${
|
const flags = 12;
|
||||||
|
const expected = `PUT /v1/kv/${id}?dc=${dc}&flags=${flags}${
|
||||||
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
|
typeof nspace !== 'undefined' ? `&ns=${nspace}` : ``
|
||||||
}`;
|
}`;
|
||||||
let actual = adapter
|
let actual = adapter
|
||||||
|
@ -70,6 +71,7 @@ module('Integration | Adapter | kv', function(hooks) {
|
||||||
Key: id,
|
Key: id,
|
||||||
Value: '',
|
Value: '',
|
||||||
Namespace: nspace,
|
Namespace: nspace,
|
||||||
|
Flags: flags,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.split('\n')
|
.split('\n')
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
import { create, clickable, is, attribute, collection, text } from 'ember-cli-page-object';
|
import {
|
||||||
|
create,
|
||||||
|
clickable,
|
||||||
|
is,
|
||||||
|
attribute,
|
||||||
|
collection,
|
||||||
|
text,
|
||||||
|
isPresent,
|
||||||
|
} from 'ember-cli-page-object';
|
||||||
import { alias } from 'ember-cli-page-object/macros';
|
import { alias } from 'ember-cli-page-object/macros';
|
||||||
import { visitable } from 'consul-ui/tests/lib/page-object/visitable';
|
import { visitable } from 'consul-ui/tests/lib/page-object/visitable';
|
||||||
import createDeletable from 'consul-ui/tests/lib/page-object/createDeletable';
|
import createDeletable from 'consul-ui/tests/lib/page-object/createDeletable';
|
||||||
|
@ -59,8 +67,10 @@ const roleSelector = roleSelectorFactory(clickable, deletable, collection, alias
|
||||||
export default {
|
export default {
|
||||||
index: create(index(visitable, collection)),
|
index: create(index(visitable, collection)),
|
||||||
dcs: create(dcs(visitable, clickable, attribute, collection)),
|
dcs: create(dcs(visitable, clickable, attribute, collection)),
|
||||||
services: create(services(visitable, clickable, attribute, collection, page, catalogFilter)),
|
services: create(
|
||||||
service: create(service(visitable, attribute, collection, text, catalogFilter)),
|
services(visitable, clickable, attribute, collection, page, catalogFilter, radiogroup)
|
||||||
|
),
|
||||||
|
service: create(service(visitable, attribute, collection, text, catalogFilter, radiogroup)),
|
||||||
instance: create(instance(visitable, attribute, collection, text, radiogroup)),
|
instance: create(instance(visitable, attribute, collection, text, radiogroup)),
|
||||||
nodes: create(nodes(visitable, clickable, attribute, collection, catalogFilter)),
|
nodes: create(nodes(visitable, clickable, attribute, collection, catalogFilter)),
|
||||||
node: create(node(visitable, deletable, clickable, attribute, collection, radiogroup)),
|
node: create(node(visitable, deletable, clickable, attribute, collection, radiogroup)),
|
||||||
|
@ -112,5 +122,5 @@ export default {
|
||||||
nspace: create(
|
nspace: create(
|
||||||
nspace(visitable, submitable, deletable, cancelable, policySelector, roleSelector)
|
nspace(visitable, submitable, deletable, cancelable, policySelector, roleSelector)
|
||||||
),
|
),
|
||||||
settings: create(settings(visitable, submitable)),
|
settings: create(settings(visitable, submitable, isPresent)),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { clickable } from 'ember-cli-page-object';
|
import { clickable, is } from 'ember-cli-page-object';
|
||||||
const page = {
|
const page = {
|
||||||
navigation: ['services', 'nodes', 'kvs', 'acls', 'intentions', 'docs', 'settings'].reduce(
|
navigation: ['services', 'nodes', 'kvs', 'acls', 'intentions', 'docs', 'settings'].reduce(
|
||||||
function(prev, item, i, arr) {
|
function(prev, item, i, arr) {
|
||||||
|
@ -24,4 +24,10 @@ const page = {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
page.navigation.dc = clickable('[data-test-datacenter-menu] button');
|
page.navigation.dc = clickable('[data-test-datacenter-menu] button');
|
||||||
|
page.navigation.nspace = clickable('[data-test-nspace-menu] button');
|
||||||
|
page.navigation.manageNspaces = clickable('[data-test-main-nav-nspaces] a');
|
||||||
|
page.navigation.manageNspacesIsVisible = is(
|
||||||
|
':checked',
|
||||||
|
'[data-test-nspace-menu] > input[type="checkbox"]'
|
||||||
|
);
|
||||||
export default page;
|
export default page;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export default function(visitable, attribute, collection, text, filter) {
|
export default function(visitable, attribute, collection, text, filter, radiogroup) {
|
||||||
return {
|
return {
|
||||||
visit: visitable('/:dc/services/:service'),
|
visit: visitable('/:dc/services/:service'),
|
||||||
externalSource: attribute('data-test-external-source', 'h1 span'),
|
externalSource: attribute('data-test-external-source', 'h1 span'),
|
||||||
|
@ -8,6 +8,7 @@ export default function(visitable, attribute, collection, text, filter) {
|
||||||
dashboardAnchor: {
|
dashboardAnchor: {
|
||||||
href: attribute('href', '[data-test-dashboard-anchor]'),
|
href: attribute('href', '[data-test-dashboard-anchor]'),
|
||||||
},
|
},
|
||||||
|
tabs: radiogroup('tab', ['instances', 'routing', 'tags']),
|
||||||
filter: filter,
|
filter: filter,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export default function(visitable, submitable) {
|
export default function(visitable, submitable, isPresent) {
|
||||||
return submitable({
|
return submitable({
|
||||||
visit: visitable('/setting'),
|
visit: visitable('/setting'),
|
||||||
|
blockingQueries: isPresent('[data-test-blocking-queries]'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,30 @@
|
||||||
/* eslint no-console: "off" */
|
/* eslint no-console: "off" */
|
||||||
|
const notFound = 'Element not found';
|
||||||
|
const cannotDestructure = "Cannot destructure property 'context'";
|
||||||
|
const cannotReadContext = "Cannot read property 'context' of undefined";
|
||||||
|
// checking for existence of pageObjects is pretty difficult
|
||||||
|
// errors are thrown but we should check to make sure its the error that we
|
||||||
|
// want and not another real error
|
||||||
|
// to make things more difficult depending on how you reference the pageObject
|
||||||
|
// an error with a different message is thrown for example:
|
||||||
|
|
||||||
|
// pageObject[thing]() will give you a Element not found error
|
||||||
|
|
||||||
|
// whereas:
|
||||||
|
|
||||||
|
// const obj = pageObject[thing]; obj() will give you a 'cannot destructure error'
|
||||||
|
// and in CI it will give you a 'cannot read property' error
|
||||||
|
|
||||||
|
// the difference in CI could be a difference due to headless vs headed browser
|
||||||
|
// or difference in Chrome/browser versions
|
||||||
|
|
||||||
|
// ideally we wouldn't be checking on error messages at all, but we want to make sure
|
||||||
|
// that real errors are picked up by the tests, so if this gets unmanageable at any point
|
||||||
|
// look at checking for the instance of e being TypeError or similar
|
||||||
|
const isExpectedError = function(e) {
|
||||||
|
return [notFound, cannotDestructure, cannotReadContext].some(item => e.message.startsWith(item));
|
||||||
|
};
|
||||||
|
|
||||||
export default function(scenario, assert, find, currentPage) {
|
export default function(scenario, assert, find, currentPage) {
|
||||||
scenario
|
scenario
|
||||||
.then('I see $property on the $component like yaml\n$yaml', function(
|
.then('I see $property on the $component like yaml\n$yaml', function(
|
||||||
|
@ -64,34 +90,63 @@ export default function(scenario, assert, find, currentPage) {
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.then(["I don't see $property on the $component"], function(property, component) {
|
.then(["I don't see $property on the $component"], function(property, component) {
|
||||||
// Collection
|
const message = `Expected to not see ${property} on ${component}`;
|
||||||
var obj;
|
// Cope with collections
|
||||||
|
let obj;
|
||||||
if (typeof currentPage()[component].objectAt === 'function') {
|
if (typeof currentPage()[component].objectAt === 'function') {
|
||||||
obj = currentPage()[component].objectAt(0);
|
obj = currentPage()[component].objectAt(0);
|
||||||
} else {
|
} else {
|
||||||
obj = currentPage()[component];
|
obj = currentPage()[component];
|
||||||
}
|
}
|
||||||
assert.throws(
|
let prop;
|
||||||
function() {
|
try {
|
||||||
const func = obj[property].bind(obj);
|
prop = obj[property];
|
||||||
func();
|
} catch (e) {
|
||||||
},
|
if (isExpectedError(e)) {
|
||||||
function(e) {
|
assert.ok(true, message);
|
||||||
return e.message.startsWith('Element not found');
|
} else {
|
||||||
},
|
throw e;
|
||||||
`Expected to not see ${property} on ${component}`
|
}
|
||||||
);
|
}
|
||||||
|
if (typeof prop === 'function') {
|
||||||
|
assert.throws(
|
||||||
|
function() {
|
||||||
|
prop();
|
||||||
|
},
|
||||||
|
function(e) {
|
||||||
|
return isExpectedError(e);
|
||||||
|
},
|
||||||
|
message
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert.notOk(prop);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.then(["I don't see $property"], function(property) {
|
.then(["I don't see $property"], function(property) {
|
||||||
assert.throws(
|
const message = `Expected to not see ${property}`;
|
||||||
function() {
|
let prop;
|
||||||
return currentPage()[property]();
|
try {
|
||||||
},
|
prop = currentPage()[property];
|
||||||
function(e) {
|
} catch (e) {
|
||||||
return e.message.startsWith('Element not found');
|
if (isExpectedError(e)) {
|
||||||
},
|
assert.ok(true, message);
|
||||||
`Expected to not see ${property}`
|
} else {
|
||||||
);
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof prop === 'function') {
|
||||||
|
assert.throws(
|
||||||
|
function() {
|
||||||
|
prop();
|
||||||
|
},
|
||||||
|
function(e) {
|
||||||
|
return isExpectedError(e);
|
||||||
|
},
|
||||||
|
message
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert.notOk(prop);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.then(['I see $property'], function(property) {
|
.then(['I see $property'], function(property) {
|
||||||
assert.ok(currentPage()[property], `Expected to see ${property}`);
|
assert.ok(currentPage()[property], `Expected to see ${property}`);
|
||||||
|
|
|
@ -3,10 +3,11 @@ import { module, test } from 'qunit';
|
||||||
|
|
||||||
module('Unit | Search | Filter | node', function() {
|
module('Unit | Search | Filter | node', function() {
|
||||||
const filter = getFilter(cb => cb);
|
const filter = getFilter(cb => cb);
|
||||||
test('items are found by properties', function(assert) {
|
test('items are found by name', function(assert) {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
Node: 'node-HIT',
|
Node: 'node-HIT',
|
||||||
|
Address: '10.0.0.0',
|
||||||
},
|
},
|
||||||
].forEach(function(item) {
|
].forEach(function(item) {
|
||||||
const actual = filter(item, {
|
const actual = filter(item, {
|
||||||
|
@ -15,10 +16,24 @@ module('Unit | Search | Filter | node', function() {
|
||||||
assert.ok(actual);
|
assert.ok(actual);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('items are not found', function(assert) {
|
test('items are found by IP address', function(assert) {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
Node: 'node-HIT',
|
||||||
|
Address: '10.0.0.0',
|
||||||
|
},
|
||||||
|
].forEach(function(item) {
|
||||||
|
const actual = filter(item, {
|
||||||
|
s: '10',
|
||||||
|
});
|
||||||
|
assert.ok(actual);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
test('items are not found by name', function(assert) {
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
Node: 'name',
|
Node: 'name',
|
||||||
|
Address: '10.0.0.0',
|
||||||
},
|
},
|
||||||
].forEach(function(item) {
|
].forEach(function(item) {
|
||||||
const actual = filter(item, {
|
const actual = filter(item, {
|
||||||
|
@ -27,4 +42,17 @@ module('Unit | Search | Filter | node', function() {
|
||||||
assert.notOk(actual);
|
assert.notOk(actual);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
test('items are not found by IP address', function(assert) {
|
||||||
|
[
|
||||||
|
{
|
||||||
|
Node: 'name',
|
||||||
|
Address: '10.0.0.0',
|
||||||
|
},
|
||||||
|
].forEach(function(item) {
|
||||||
|
const actual = filter(item, {
|
||||||
|
s: '9',
|
||||||
|
});
|
||||||
|
assert.notOk(actual);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -992,9 +992,9 @@
|
||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
|
|
||||||
"@hashicorp/consul-api-double@^2.6.2":
|
"@hashicorp/consul-api-double@^2.6.2":
|
||||||
version "2.11.0"
|
version "2.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.11.0.tgz#0b833893ccc5cfb9546b1513127d5e92d30f2262"
|
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.12.0.tgz#725078f770bbd0ef75a5f2498968c5c8891f90a2"
|
||||||
integrity sha512-2MO1jiwuJyPlSGQ4AeFtLKJWmLSj0msoiaRHPtj6YPjm69ZkY/t4U4SU3cfpVn2Dx7wHzXe//9GvNHI1gRxAzg==
|
integrity sha512-8OcgesUjWQ8AjaXzbz3tGJQn1kM0sN6pLidGM7isNPUyYmIjIEXQzaeUQYzsfv0N2Ko9ZuOXYUsaBl8IK1KGow==
|
||||||
|
|
||||||
"@hashicorp/ember-cli-api-double@^2.0.0":
|
"@hashicorp/ember-cli-api-double@^2.0.0":
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
|
|
Loading…
Reference in New Issue