Merge remote-tracking branch 'origin/master' into release-build-updates
# Conflicts: # scripts/consul-builder/Dockerfile # ui-v2/GNUmakefile
This commit is contained in:
commit
9653a66f92
|
@ -7,6 +7,7 @@ FEATURES:
|
|||
IMPROVEMENTS:
|
||||
|
||||
* agent: A Consul user-agent string is now sent to providers when making retry-join requests [GH-4013](https://github.com/hashicorp/consul/pull/4013)
|
||||
* client: Add metrics for failed RPCs [PR-4220](https://github.com/hashicorp/consul/pull/4220)
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
|
|
|
@ -276,6 +276,7 @@ TRY:
|
|||
|
||||
// Move off to another server, and see if we can retry.
|
||||
c.logger.Printf("[ERR] consul: %q RPC failed to server %s: %v", method, server.Addr, rpcErr)
|
||||
metrics.IncrCounterWithLabels([]string{"client", "rpc", "failed"}, 1, []metrics.Label{{Name: "server", Value: server.Name}})
|
||||
c.routers.NotifyFailedServer(server)
|
||||
if retry := canRetry(args, rpcErr); !retry {
|
||||
return rpcErr
|
||||
|
|
|
@ -8,3 +8,5 @@
|
|||
/yarn-error.log
|
||||
/testem.log
|
||||
|
||||
/public/consul-api-double
|
||||
|
||||
|
|
|
@ -3,13 +3,8 @@ ROOT:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
|||
server:
|
||||
yarn run start
|
||||
|
||||
init:
|
||||
yarn install
|
||||
|
||||
build:
|
||||
dist:
|
||||
yarn run build
|
||||
|
||||
dist: build
|
||||
mv dist ../pkg/web_ui/v2
|
||||
|
||||
lint:
|
||||
|
@ -17,4 +12,19 @@ lint:
|
|||
format:
|
||||
yarn run format:js
|
||||
|
||||
.PHONY: server build dist lint format
|
||||
.PHONY: server dist lint format
|
||||
|
||||
.DEFAULT_GOAL=all
|
||||
.PHONY: deps test all build start
|
||||
all: deps
|
||||
deps: node_modules yarn.lock package.json
|
||||
node_modules:
|
||||
yarn
|
||||
build:
|
||||
yarn run build
|
||||
start:
|
||||
yarn run start
|
||||
test:
|
||||
yarn run test
|
||||
test-view:
|
||||
yarn run test:view
|
||||
|
|
|
@ -1,25 +1,67 @@
|
|||
import Component from 'ember-collection/components/ember-collection';
|
||||
import needsRevalidate from 'ember-collection/utils/needs-revalidate';
|
||||
import identity from 'ember-collection/utils/identity';
|
||||
import Grid from 'ember-collection/layouts/grid';
|
||||
import SlotsMixin from 'ember-block-slots';
|
||||
import style from 'ember-computed-style';
|
||||
import qsaFactory from 'consul-ui/utils/qsa-factory';
|
||||
|
||||
import { computed, get, set } from '@ember/object';
|
||||
/**
|
||||
* Heavily extended `ember-collection` component
|
||||
* This adds support for z-index calculations to enable
|
||||
* Popup menus to pop over either rows above or below
|
||||
* the popup.
|
||||
* Additionally adds calculations for figuring out what the height
|
||||
* of the tabular component should be depending on the other elements
|
||||
* in the page.
|
||||
* Currently everything is here together for clarity, but to be split up
|
||||
* in the future
|
||||
*/
|
||||
|
||||
const $$ = document.querySelectorAll.bind(document);
|
||||
// ember doesn't like you using `$` hence `$$`
|
||||
const $$ = qsaFactory();
|
||||
// basic pseudo CustomEvent interface
|
||||
// TODO: use actual custom events once I've reminded
|
||||
// myself re: support/polyfills
|
||||
const createSizeEvent = function(detail) {
|
||||
return {
|
||||
detail: { width: window.innerWidth, height: window.innerHeight },
|
||||
};
|
||||
};
|
||||
// need to copy Cell in wholesale as there is no way to import it
|
||||
// there is no change made to `Cell` here, its only here as its
|
||||
// private in `ember-collection`
|
||||
// TODO: separate both Cell and ZIndexedGrid out
|
||||
class Cell {
|
||||
constructor(key, item, index, style) {
|
||||
this.key = key;
|
||||
this.hidden = false;
|
||||
this.item = item;
|
||||
this.index = index;
|
||||
this.style = style;
|
||||
}
|
||||
}
|
||||
// this is an amount of rows in the table NOT items
|
||||
// unlikely to have 10000 DOM rows ever :)
|
||||
const maxZIndex = 10000;
|
||||
// Adds z-index styling to the default Grid
|
||||
class ZIndexedGrid extends Grid {
|
||||
formatItemStyle(index, w, h) {
|
||||
let style = super.formatItemStyle(...arguments);
|
||||
style += 'z-index: ' + (10000 - index);
|
||||
formatItemStyle(index, w, h, checked) {
|
||||
let style = super.formatItemStyle(index, w, h);
|
||||
// count backwards from maxZIndex
|
||||
let zIndex = maxZIndex - index;
|
||||
// apart from the row that contains an opened dropdown menu
|
||||
// this one should be highest z-index, so use max plus 1
|
||||
if (checked == index) {
|
||||
zIndex = maxZIndex + 1;
|
||||
}
|
||||
style += 'z-index: ' + zIndex;
|
||||
return style;
|
||||
}
|
||||
}
|
||||
// TODO instead of degrading gracefully
|
||||
// basic DOM closest utility to cope with no support
|
||||
// TODO: instead of degrading gracefully
|
||||
// add a while polyfill for closest
|
||||
const closest = function(sel, el) {
|
||||
try {
|
||||
|
@ -28,6 +70,32 @@ const closest = function(sel, el) {
|
|||
return;
|
||||
}
|
||||
};
|
||||
const sibling = function(el, name) {
|
||||
let sibling = el;
|
||||
while ((sibling = sibling.nextSibling)) {
|
||||
if (sibling.nodeType === 1) {
|
||||
if (sibling.nodeName.toLowerCase() === name) {
|
||||
return sibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The tabular-collection can contain 'actions' the UI for which
|
||||
* uses dropdown 'action groups', so a group of different actions.
|
||||
* State makes use of native HTML state using radiogroups
|
||||
* to ensure that only a single dropdown can be open at one time.
|
||||
* Therefore we listen to change events to do anything extra when
|
||||
* a dropdown is opened (the change function is bound to the instance of
|
||||
* the `tabular-component` on init, hoisted here for visibility)
|
||||
*
|
||||
* The extra functionality we have here is to detect whether the opened
|
||||
* dropdown menu would be cut off or not if it 'dropped down'.
|
||||
* If it would be cut off we use CSS to 'drop it up' instead.
|
||||
* We also set this row to have the max z-index here, and mark this
|
||||
* row as the 'checked row' for when a scroll/grid re-calculation is
|
||||
* performed
|
||||
*/
|
||||
const change = function(e) {
|
||||
if (e instanceof MouseEvent) {
|
||||
return;
|
||||
|
@ -37,6 +105,23 @@ const change = function(e) {
|
|||
const value = e.currentTarget.value;
|
||||
if (value != get(this, 'checked')) {
|
||||
set(this, 'checked', value);
|
||||
// 'actions_close' would mean that all menus have been closed
|
||||
// therefore we don't need to calculate
|
||||
if (e.currentTarget.getAttribute('id') !== 'actions_close') {
|
||||
const $tr = closest('tr', e.currentTarget);
|
||||
const $group = sibling(e.currentTarget, 'ul');
|
||||
const $footer = [...$$('footer[role="contentinfo"]')][0];
|
||||
const groupRect = $group.getBoundingClientRect();
|
||||
const footerRect = $footer.getBoundingClientRect();
|
||||
const groupBottom = groupRect.top + $group.clientHeight;
|
||||
const footerTop = footerRect.top;
|
||||
if (groupBottom > footerTop) {
|
||||
$group.classList.add('above');
|
||||
} else {
|
||||
$group.classList.remove('above');
|
||||
}
|
||||
$tr.style.zIndex = maxZIndex + 1;
|
||||
}
|
||||
} else {
|
||||
set(this, 'checked', null);
|
||||
}
|
||||
|
@ -62,7 +147,7 @@ export default Component.extend(SlotsMixin, {
|
|||
this._super(...arguments);
|
||||
this.change = change.bind(this);
|
||||
this.confirming = [];
|
||||
// TODO: This should auto calculate properly from the CSS
|
||||
// TODO: The row height should auto calculate properly from the CSS
|
||||
this['cell-layout'] = new ZIndexedGrid(get(this, 'width'), 50);
|
||||
this.handler = () => {
|
||||
this.resize(createSizeEvent());
|
||||
|
@ -79,23 +164,34 @@ export default Component.extend(SlotsMixin, {
|
|||
},
|
||||
didInsertElement: function() {
|
||||
this._super(...arguments);
|
||||
// TODO: Consider moving all DOM lookups here
|
||||
// this seems to be the earliest place I can get them
|
||||
window.addEventListener('resize', this.handler);
|
||||
this.handler();
|
||||
this.didAppear();
|
||||
},
|
||||
willDestroyElement: function() {
|
||||
window.removeEventListener('resize', this.handler);
|
||||
},
|
||||
didAppear: function() {
|
||||
this.handler();
|
||||
},
|
||||
resize: function(e) {
|
||||
const $footer = [...$$('#wrapper > footer')][0];
|
||||
const $thead = [...$$('main > div')][0];
|
||||
if ($thead) {
|
||||
// TODO: This should auto calculate properly from the CSS
|
||||
this.set('height', Math.max(0, new Number(e.detail.height - ($footer.clientHeight + 218))));
|
||||
this['cell-layout'] = new ZIndexedGrid($thead.clientWidth, 50);
|
||||
const $tbody = [...$$('tbody', this.element)][0];
|
||||
const $appContent = [...$$('main > div')][0];
|
||||
if ($appContent) {
|
||||
const rect = $tbody.getBoundingClientRect();
|
||||
const $footer = [...$$('footer[role="contentinfo"]')][0];
|
||||
const space = rect.top + $footer.clientHeight;
|
||||
const height = new Number(e.detail.height - space);
|
||||
this.set('height', Math.max(0, height));
|
||||
// TODO: The row height should auto calculate properly from the CSS
|
||||
this['cell-layout'] = new ZIndexedGrid($appContent.clientWidth, 50);
|
||||
this.updateItems();
|
||||
this.updateScrollPosition();
|
||||
}
|
||||
},
|
||||
// `ember-collection` bug workaround
|
||||
// https://github.com/emberjs/ember-collection/issues/138
|
||||
_needsRevalidate: function() {
|
||||
if (this.isDestroyed || this.isDestroying) {
|
||||
return;
|
||||
|
@ -106,8 +202,115 @@ export default Component.extend(SlotsMixin, {
|
|||
needsRevalidate(this);
|
||||
}
|
||||
},
|
||||
// need to overwrite this completely so I can pass through the checked index
|
||||
// unfortunately the nicest way I could think to do this is to copy this in wholesale
|
||||
// to add an extra argument for `formatItemStyle` in 3 places
|
||||
// tradeoff between changing as little code as possible in the original code
|
||||
updateCells: function() {
|
||||
if (!this._items) {
|
||||
return;
|
||||
}
|
||||
const numItems = get(this._items, 'length');
|
||||
if (this._cellLayout.length !== numItems) {
|
||||
this._cellLayout.length = numItems;
|
||||
}
|
||||
|
||||
var priorMap = this._cellMap;
|
||||
var cellMap = Object.create(null);
|
||||
|
||||
var index = this._cellLayout.indexAt(
|
||||
this._scrollLeft,
|
||||
this._scrollTop,
|
||||
this._clientWidth,
|
||||
this._clientHeight
|
||||
);
|
||||
var count = this._cellLayout.count(
|
||||
this._scrollLeft,
|
||||
this._scrollTop,
|
||||
this._clientWidth,
|
||||
this._clientHeight
|
||||
);
|
||||
var items = this._items;
|
||||
var bufferBefore = Math.min(index, this._buffer);
|
||||
index -= bufferBefore;
|
||||
count += bufferBefore;
|
||||
count = Math.min(count + this._buffer, get(items, 'length') - index);
|
||||
var i, style, itemIndex, itemKey, cell;
|
||||
|
||||
var newItems = [];
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
itemIndex = index + i;
|
||||
itemKey = identity(items.objectAt(itemIndex));
|
||||
if (priorMap) {
|
||||
cell = priorMap[itemKey];
|
||||
}
|
||||
if (cell) {
|
||||
// additional `checked` argument
|
||||
style = this._cellLayout.formatItemStyle(
|
||||
itemIndex,
|
||||
this._clientWidth,
|
||||
this._clientHeight,
|
||||
this.checked
|
||||
);
|
||||
set(cell, 'style', style);
|
||||
set(cell, 'hidden', false);
|
||||
set(cell, 'key', itemKey);
|
||||
cellMap[itemKey] = cell;
|
||||
} else {
|
||||
newItems.push(itemIndex);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < this._cells.length; i++) {
|
||||
cell = this._cells[i];
|
||||
if (!cellMap[cell.key]) {
|
||||
if (newItems.length) {
|
||||
itemIndex = newItems.pop();
|
||||
let item = items.objectAt(itemIndex);
|
||||
itemKey = identity(item);
|
||||
// additional `checked` argument
|
||||
style = this._cellLayout.formatItemStyle(
|
||||
itemIndex,
|
||||
this._clientWidth,
|
||||
this._clientHeight,
|
||||
this.checked
|
||||
);
|
||||
set(cell, 'style', style);
|
||||
set(cell, 'key', itemKey);
|
||||
set(cell, 'index', itemIndex);
|
||||
set(cell, 'item', item);
|
||||
set(cell, 'hidden', false);
|
||||
cellMap[itemKey] = cell;
|
||||
} else {
|
||||
set(cell, 'hidden', true);
|
||||
set(cell, 'style', 'height: 0; display: none;');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < newItems.length; i++) {
|
||||
itemIndex = newItems[i];
|
||||
let item = items.objectAt(itemIndex);
|
||||
itemKey = identity(item);
|
||||
// additional `checked` argument
|
||||
style = this._cellLayout.formatItemStyle(
|
||||
itemIndex,
|
||||
this._clientWidth,
|
||||
this._clientHeight,
|
||||
this.checked
|
||||
);
|
||||
cell = new Cell(itemKey, item, itemIndex, style);
|
||||
cellMap[itemKey] = cell;
|
||||
this._cells.pushObject(cell);
|
||||
}
|
||||
this._cellMap = cellMap;
|
||||
},
|
||||
actions: {
|
||||
click: function(e) {
|
||||
// click on row functionality
|
||||
// so if you click the actual row but not a link
|
||||
// find the first link and fire that instead
|
||||
const name = e.target.nodeName.toLowerCase();
|
||||
switch (name) {
|
||||
case 'input':
|
||||
|
|
|
@ -28,10 +28,14 @@ export default Controller.extend(WithFiltering, {
|
|||
});
|
||||
}),
|
||||
filter: function(item, { s = '', type = '' }) {
|
||||
const sLower = s.toLowerCase();
|
||||
return (
|
||||
get(item, 'Name')
|
||||
(get(item, 'Name')
|
||||
.toLowerCase()
|
||||
.indexOf(s.toLowerCase()) !== -1 &&
|
||||
.indexOf(sLower) !== -1 ||
|
||||
get(item, 'ID')
|
||||
.toLowerCase()
|
||||
.indexOf(sLower) !== -1) &&
|
||||
(type === '' || get(item, 'Type') === type)
|
||||
);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
import Controller from './index';
|
||||
export default Controller.extend();
|
|
@ -0,0 +1,18 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get } from '@ember/object';
|
||||
import WithFiltering from 'consul-ui/mixins/with-filtering';
|
||||
import rightTrim from 'consul-ui/utils/right-trim';
|
||||
export default Controller.extend(WithFiltering, {
|
||||
queryParams: {
|
||||
s: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
filter: function(item, { s = '' }) {
|
||||
const key = rightTrim(get(item, 'Key'), '/')
|
||||
.split('/')
|
||||
.pop();
|
||||
return key.toLowerCase().indexOf(s.toLowerCase()) !== -1;
|
||||
},
|
||||
});
|
|
@ -1,7 +1,11 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, set } from '@ember/object';
|
||||
import { getOwner } from '@ember/application';
|
||||
import WithFiltering from 'consul-ui/mixins/with-filtering';
|
||||
import qsaFactory from 'consul-ui/utils/qsa-factory';
|
||||
import getComponentFactory from 'consul-ui/utils/get-component-factory';
|
||||
|
||||
const $$ = qsaFactory();
|
||||
export default Controller.extend(WithFiltering, {
|
||||
queryParams: {
|
||||
s: {
|
||||
|
@ -14,13 +18,30 @@ export default Controller.extend(WithFiltering, {
|
|||
set(this, 'selectedTab', 'health-checks');
|
||||
},
|
||||
filter: function(item, { s = '' }) {
|
||||
const term = s.toLowerCase();
|
||||
return (
|
||||
get(item, 'Service')
|
||||
.toLowerCase()
|
||||
.indexOf(s.toLowerCase()) !== -1
|
||||
.indexOf(term) !== -1 ||
|
||||
get(item, 'Port')
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.indexOf(term) !== -1
|
||||
);
|
||||
},
|
||||
actions: {
|
||||
change: function(e) {
|
||||
set(this, 'selectedTab', e.target.value);
|
||||
const getComponent = getComponentFactory(getOwner(this));
|
||||
// Ensure tabular-collections sizing is recalculated
|
||||
// now it is visible in the DOM
|
||||
[...$$('.tab-section input[type="radio"]:checked + div table')].forEach(function(item) {
|
||||
const component = getComponent(item);
|
||||
if (component && typeof component.didAppear === 'function') {
|
||||
getComponent(item).didAppear();
|
||||
}
|
||||
});
|
||||
},
|
||||
sortChecksByImportance: function(a, b) {
|
||||
const statusA = get(a, 'Status');
|
||||
const statusB = get(b, 'Status');
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
import leftTrim from 'consul-ui/utils/left-trim';
|
||||
|
||||
export function leftTrim([str = '', search = ''], hash) {
|
||||
return str.indexOf(search) === 0 ? str.substr(search.length) : str;
|
||||
}
|
||||
|
||||
export default helper(leftTrim);
|
||||
export default helper(function([str = '', search = ''], hash) {
|
||||
return leftTrim(str, search);
|
||||
});
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
|
||||
export function rightTrim([str = '', search = ''], hash) {
|
||||
const pos = str.length - search.length;
|
||||
return str.indexOf(search) === pos ? str.substr(0, pos) : str;
|
||||
}
|
||||
import rightTrim from 'consul-ui/utils/right-trim';
|
||||
|
||||
export default helper(rightTrim);
|
||||
export default helper(function([str = '', search = ''], hash) {
|
||||
return rightTrim(str, search);
|
||||
});
|
||||
|
|
|
@ -3,9 +3,13 @@ import Mixin from '@ember/object/mixin';
|
|||
import { next } from '@ember/runloop';
|
||||
import { get } from '@ember/object';
|
||||
const isOutside = function(element, e) {
|
||||
const isRemoved = !e.target || !document.contains(e.target);
|
||||
const isInside = element === e.target || element.contains(e.target);
|
||||
return !isRemoved && !isInside;
|
||||
if (element) {
|
||||
const isRemoved = !e.target || !document.contains(e.target);
|
||||
const isInside = element === e.target || element.contains(e.target);
|
||||
return !isRemoved && !isInside;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const handler = function(e) {
|
||||
const el = get(this, 'element');
|
||||
|
|
|
@ -7,7 +7,12 @@ export const SLUG_KEY = 'ID';
|
|||
export default Model.extend({
|
||||
[PRIMARY_KEY]: attr('string'),
|
||||
[SLUG_KEY]: attr('string'),
|
||||
Name: attr('string'),
|
||||
Name: attr('string', {
|
||||
// TODO: Why didn't I have to do this for KV's?
|
||||
// this is to ensure that Name is '' and not null when creating
|
||||
// maybe its due to the fact that `Key` is the primaryKey in Kv's
|
||||
defaultValue: '',
|
||||
}),
|
||||
Type: attr('string'),
|
||||
Rules: attr('string'),
|
||||
CreateIndex: attr('number'),
|
||||
|
|
|
@ -5,6 +5,12 @@ import { get } from '@ember/object';
|
|||
import WithKvActions from 'consul-ui/mixins/kv/with-actions';
|
||||
|
||||
export default Route.extend(WithKvActions, {
|
||||
queryParams: {
|
||||
s: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
repo: service('kv'),
|
||||
model: function(params) {
|
||||
const key = params.key || '/';
|
||||
|
|
|
@ -70,19 +70,30 @@
|
|||
%action-group ul {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
top: 35px;
|
||||
padding: 1px;
|
||||
}
|
||||
%action-group ul::before {
|
||||
position: absolute;
|
||||
right: 18px;
|
||||
top: -6px;
|
||||
content: '';
|
||||
display: block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
%action-group ul:not(.above) {
|
||||
top: 35px;
|
||||
}
|
||||
%action-group ul:not(.above)::before {
|
||||
top: -6px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
%action-group ul.above {
|
||||
bottom: 35px;
|
||||
}
|
||||
%action-group ul.above::before {
|
||||
bottom: -6px;
|
||||
transform: rotate(225deg);
|
||||
}
|
||||
%action-group li {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
margin-left: 12px;
|
||||
}
|
||||
%filter-bar fieldset {
|
||||
min-width: 210px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
display: block;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
padding: 0.625em;
|
||||
min-height: 70px;
|
||||
padding: 0.625em 15px;
|
||||
resize: vertical;
|
||||
line-height: 1.5;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
%form-element [type='password'] {
|
||||
|
@ -37,14 +39,16 @@
|
|||
box-shadow: none;
|
||||
border-radius: $radius-small;
|
||||
}
|
||||
.has-error > input {
|
||||
.has-error > input,
|
||||
.has-error > textarea {
|
||||
border: 1px solid;
|
||||
}
|
||||
%form-element > span {
|
||||
color: $text-gray;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
%form-element [type='password'] {
|
||||
%form-element [type='password'],
|
||||
%form-element textarea {
|
||||
color: $user-text-gray;
|
||||
}
|
||||
%form-element [type='text'],
|
||||
|
|
|
@ -63,6 +63,7 @@ h2,
|
|||
body,
|
||||
pre code,
|
||||
input,
|
||||
textarea,
|
||||
td {
|
||||
font-size: $size-6;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{{!<form>}}
|
||||
{{freetext-filter onchange=(action onchange) value=search placeholder="Search by name"}}
|
||||
{{freetext-filter onchange=(action onchange) value=search placeholder="Search by name/token"}}
|
||||
{{radio-group name="type" value=type items=filters onchange=(action onchange)}}
|
||||
{{!</form>}}
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
<main>
|
||||
{{yield}}
|
||||
</main>
|
||||
<footer data-test-footer>
|
||||
<footer role="contentinfo" data-test-footer>
|
||||
<a data-test-footer-copyright href="{{env 'CONSUL_COPYRIGHT_URL'}}/" rel="noopener noreferrer" target="_blank">© {{env 'CONSUL_COPYRIGHT_YEAR'}} HashiCorp</a>
|
||||
<p data-test-footer-version>Consul {{env 'CONSUL_VERSION'}}</p>
|
||||
<a data-test-footer-docs href="{{env 'CONSUL_DOCUMENTATION_URL'}}/index.html" rel="help noopener noreferrer" target="_blank">Documentation</a>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
{{#ember-native-scrollable tagName='tbody' content-size=_contentSize scroll-left=_scrollLeft scroll-top=_scrollTop scrollChange=(action "scrollChange") clientSizeChange=(action "clientSizeChange")}}
|
||||
<tr></tr>
|
||||
{{~#each _cells as |cell|~}}
|
||||
<tr style={{{cell.style}}} onclick={{action 'click'}}>
|
||||
<tr data-test-tabular-row style={{{cell.style}}} onclick={{action 'click'}}>
|
||||
{{#yield-slot 'row'}}{{yield cell.item cell.index}}{{/yield-slot}}
|
||||
{{#if hasActions }}
|
||||
<td class="actions">
|
||||
|
|
|
@ -26,12 +26,13 @@
|
|||
</fieldset>
|
||||
<div>
|
||||
{{#if create }}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{! we only need to check for an empty name here as ember munges autofocus, once we have autofocus back revisit this}}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or item.isPristine item.isInvalid (eq item.Name '')) 'disabled'}}>Save</button>
|
||||
{{ else }}
|
||||
<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{/if}}
|
||||
<button type="reset" {{ action "cancel" item}}>Cancel</button>
|
||||
{{# if (and item.ID (not-eq item.ID 'anonymous')) }}
|
||||
{{# if (and (not create) (not-eq item.ID 'anonymous')) }}
|
||||
{{#confirmation-dialog message='Are you sure you want to delete this ACL token?'}}
|
||||
{{#block-slot 'action' as |confirm|}}
|
||||
<button type="button" class="type-delete" {{action confirm 'delete' item parent}}>Delete</button>
|
||||
|
|
|
@ -39,17 +39,19 @@
|
|||
{{#action-group index=index onchange=(action change) checked=(if (eq checked index) 'checked')}}
|
||||
<ul>
|
||||
<li>
|
||||
<a href={{href-to 'dc.acls.edit' item.ID}}>Edit</a>
|
||||
<a data-test-edit href={{href-to 'dc.acls.edit' item.ID}}>Edit</a>
|
||||
</li>
|
||||
<li>
|
||||
<a onclick={{queue (action confirm 'use' item) (action change)}}>Use</a>
|
||||
<a data-test-use onclick={{queue (action confirm 'use' item) (action change)}}>Use</a>
|
||||
</li>
|
||||
<li>
|
||||
<a onclick={{action 'sendClone' item}}>Clone</a>
|
||||
<a data-test-clone onclick={{action 'sendClone' item}}>Clone</a>
|
||||
</li>
|
||||
{{# if (not-eq item.ID 'anonymous') }}
|
||||
<li>
|
||||
<a onclick={{action confirm 'delete' item}}>Delete</a>
|
||||
<a data-test-delete onclick={{action confirm 'delete' item}}>Delete</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
{{/action-group}}
|
||||
{{/block-slot}}
|
||||
|
|
|
@ -11,21 +11,23 @@
|
|||
<div>
|
||||
<label class="type-toggle">
|
||||
<input type="checkbox" name="json" checked={{if json 'checked' }} onchange={{action 'change'}} />
|
||||
<span>JSON</span>
|
||||
<span>Code</span>
|
||||
</label>
|
||||
<label class="type-text{{if item.error.Value ' has-error'}}">
|
||||
<span>Value</span>
|
||||
{{#if json}}
|
||||
{{ code-editor value=(atob item.Value) onkeyup=(action 'change') }}
|
||||
{{else}}
|
||||
<input autofocus={{not create}} type="text" name="value" value={{atob item.Value}} onkeyup={{action 'change'}} />
|
||||
<textarea autofocus={{not create}} name="value" onkeyup={{action 'change'}}>{{atob item.Value}}</textarea>
|
||||
{{/if}}
|
||||
</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
{{!TODO This has a <div> around it in acls, remove or add for consistency }}
|
||||
{{#if create }}
|
||||
<button type="submit" {{ action "create" item parent}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
{{! we only need to check for an empty keyname here as ember munges autofocus, once we have autofocus back revisit this}}
|
||||
<button type="submit" {{ action "create" item parent}} disabled={{if (or item.isPristine item.isInvalid (eq (left-trim item.Key parent.Key) '')) 'disabled'}}>Save</button>
|
||||
{{ else }}
|
||||
<button type="submit" {{ action "update" item parent}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
<button type="reset" {{ action "cancel" item parent}}>Cancel changes</button>
|
||||
|
|
|
@ -18,6 +18,13 @@
|
|||
{{/if}}
|
||||
</h1>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'toolbar'}}
|
||||
{{#if (gt items.length 0) }}
|
||||
<form class="filter-bar">
|
||||
{{freetext-filter onchange=(action 'filter') value=filter.s placeholder="Search by name"}}
|
||||
</form>
|
||||
{{/if}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'actions'}}
|
||||
{{#if (not-eq parent.Key '/') }}
|
||||
<a href="{{href-to 'dc.kv.create' parent.Key}}" class="type-create">Create</a>
|
||||
|
@ -26,9 +33,9 @@
|
|||
{{/if}}
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'content'}}
|
||||
{{#if (gt items.length 0)}}
|
||||
{{#if (gt filtered.length 0)}}
|
||||
{{#tabular-collection
|
||||
items=(sort-by 'isFolder:desc' items) as |item index|
|
||||
items=(sort-by 'isFolder:desc' 'Key:asc' filtered) as |item index|
|
||||
}}
|
||||
{{#block-slot 'header'}}
|
||||
<th>Name</th>
|
||||
|
@ -44,10 +51,10 @@
|
|||
{{#action-group index=index onchange=(action change) checked=(if (eq checked index) 'checked')}}
|
||||
<ul>
|
||||
<li>
|
||||
<a href={{href-to (if item.isFolder 'dc.kv.folder' 'dc.kv.edit') item.Key}}>{{if item.isFolder 'View' 'Edit'}}</a>
|
||||
<a data-test-edit href={{href-to (if item.isFolder 'dc.kv.folder' 'dc.kv.edit') item.Key}}>{{if item.isFolder 'View' 'Edit'}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a onclick={{action confirm 'delete' item}}>Delete</a>
|
||||
<a data-test-delete onclick={{action confirm 'delete' item}}>Delete</a>
|
||||
</li>
|
||||
</ul>
|
||||
{{/action-group}}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<ul>
|
||||
<ul data-test-node-healthchecks>
|
||||
{{#each (sort-by (action 'sortChecksByImportance') item.Checks) as |check| }}
|
||||
{{healthcheck-status tagName='li' name=check.Name class=check.Status status=check.Status notes=check.Notes output=check.Output}}
|
||||
{{healthcheck-status data-test-node-healthcheck=check.Name tagName='li' name=check.Name class=check.Status status=check.Status notes=check.Notes output=check.Output}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
{{#if (gt items.length 0) }}
|
||||
<form class="filter-bar">
|
||||
{{freetext-filter onchange=(action 'filter') value=filter.s placeholder="Search by name"}}
|
||||
{{freetext-filter onchange=(action 'filter') value=filter.s placeholder="Search by name/port"}}
|
||||
</form>
|
||||
{{/if}}
|
||||
{{#if (gt filtered.length 0)}}
|
||||
{{#tabular-collection
|
||||
data-test-services
|
||||
items=filtered as |item index|
|
||||
}}
|
||||
{{#block-slot 'header'}}
|
||||
|
@ -13,10 +14,10 @@
|
|||
<th>Tags</th>
|
||||
{{/block-slot}}
|
||||
{{#block-slot 'row'}}
|
||||
<td>
|
||||
<td data-test-service-name="{{item.Service}}">
|
||||
<a href={{href-to 'dc.services.show' item.Service }}>{{item.Service}}</a>
|
||||
</td>
|
||||
<td>
|
||||
<td data-test-service-port="{{item.Port}}" class="port">
|
||||
{{item.Port}}
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
)
|
||||
) as |panel|
|
||||
}}
|
||||
{{#tab-section id=panel.id selected=(eq (if selectedTab selectedTab 'health-checks') panel.id) onchange=(action (mut selectedTab) value="target.value")}}
|
||||
{{#tab-section id=panel.id selected=(eq (if selectedTab selectedTab 'health-checks') panel.id) onchange=(action "change")}}
|
||||
{{partial panel.partial}}
|
||||
{{/tab-section}}
|
||||
{{/each}}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
export default function(owner, key = '-view-registry:main') {
|
||||
const components = owner.lookup(key);
|
||||
return function(el) {
|
||||
const id = el.getAttribute('id');
|
||||
if (id) {
|
||||
return components[id];
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export default function leftTrim(str = '', search = '') {
|
||||
return str.indexOf(search) === 0 ? str.substr(search.length) : str;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export default function(doc = document) {
|
||||
return function(sel, context = doc) {
|
||||
return context.querySelectorAll(sel);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export default function rightTrim(str = '', search = '') {
|
||||
const pos = str.length - search.length;
|
||||
return str.lastIndexOf(search) === pos ? str.substr(0, pos) : str;
|
||||
}
|
|
@ -2,5 +2,4 @@ import { validatePresence, validateLength } from 'ember-changeset-validations/va
|
|||
export default {
|
||||
Name: [validatePresence(true), validateLength({ min: 1 })],
|
||||
Type: validatePresence(true),
|
||||
ID: validateLength({ min: 1 }),
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
|
||||
|
||||
const stew = require('broccoli-stew');
|
||||
module.exports = function(defaults) {
|
||||
let app = new EmberApp(defaults, {
|
||||
'ember-cli-babel': {
|
||||
|
@ -43,6 +43,9 @@ module.exports = function(defaults) {
|
|||
// modules that you would like to import into your application
|
||||
// please specify an object with the list of modules as keys
|
||||
// along with the exports of each module as its value.
|
||||
|
||||
return app.toTree();
|
||||
let tree = app.toTree();
|
||||
if (app.env === 'production') {
|
||||
tree = stew.rm(tree, 'consul-api-double');
|
||||
}
|
||||
return tree;
|
||||
};
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
"format:js": "prettier --write \"{app,config,lib,server,tests}/**/*.js\" ./*.js ./.*.js",
|
||||
"start": "ember serve",
|
||||
"test": "ember test",
|
||||
"precommit": "lint-staged"
|
||||
"test:view": "ember test --server",
|
||||
"precommit": "lint-staged",
|
||||
"postinstall": "rsync -aq ./node_modules/@hashicorp/consul-api-double/ ./public/consul-api-double/"
|
||||
},
|
||||
"lint-staged": {
|
||||
"{app,config,lib,server,tests}/**/*.js": [
|
||||
|
@ -27,10 +29,14 @@
|
|||
"git add"
|
||||
]
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@hashicorp/consul-api-double": "^1.0.0",
|
||||
"@hashicorp/ember-cli-api-double": "^1.0.2",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"base64-js": "^1.3.0",
|
||||
"broccoli-asset-rev": "^2.4.5",
|
||||
"broccoli-stew": "^1.5.0",
|
||||
"ember-ajax": "^3.0.0",
|
||||
"ember-block-slots": "^1.1.11",
|
||||
"ember-browserify": "^1.2.2",
|
||||
|
@ -40,6 +46,7 @@
|
|||
"ember-cli-app-version": "^3.0.0",
|
||||
"ember-cli-autoprefixer": "^0.8.1",
|
||||
"ember-cli-babel": "^6.6.0",
|
||||
"ember-cli-cjs-transform": "^1.2.0",
|
||||
"ember-cli-clipboard": "^0.9.0",
|
||||
"ember-cli-code-coverage": "^1.0.0-beta.4",
|
||||
"ember-cli-dependency-checker": "^2.0.0",
|
||||
|
@ -55,6 +62,7 @@
|
|||
"ember-cli-shims": "^1.2.0",
|
||||
"ember-cli-sri": "^2.1.0",
|
||||
"ember-cli-uglify": "^2.0.0",
|
||||
"ember-cli-yadda": "^0.4.0",
|
||||
"ember-collection": "^1.0.0-alpha.7",
|
||||
"ember-composable-helpers": "^2.1.0",
|
||||
"ember-computed-style": "^0.2.0",
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / components /acl filter: Acl Filter
|
||||
In order to find the acl token I'm looking for easier
|
||||
As a user
|
||||
I should be able to filter by type and freetext search tokens by name and token
|
||||
Scenario: Filtering [Model]
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 2 [Model] models
|
||||
When I visit the [Page] page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be [Url]
|
||||
|
||||
Then I see 2 [Model] models
|
||||
And I see allIsSelected on the filter
|
||||
|
||||
When I click management on the filter
|
||||
Then I see managementIsSelected on the filter
|
||||
And I see 1 [Model] model
|
||||
|
||||
When I click client on the filter
|
||||
Then I see clientIsSelected on the filter
|
||||
And I see 1 [Model] model
|
||||
|
||||
When I click all on the filter
|
||||
Then I see allIsSelected on the filter
|
||||
Then I type with yaml
|
||||
---
|
||||
s: Anonymous Token
|
||||
---
|
||||
And I see 1 [Model] model with the name "Anonymous Token"
|
||||
Then I type with yaml
|
||||
---
|
||||
s: secret
|
||||
---
|
||||
And I see 1 [Model] model with the name "Master Token"
|
||||
|
||||
Where:
|
||||
-------------------------------------------------
|
||||
| Model | Page | Url |
|
||||
| acl | acls | /dc-1/acls |
|
||||
-------------------------------------------------
|
|
@ -0,0 +1,88 @@
|
|||
@setupApplicationTest
|
||||
Feature: components / catalog-filter
|
||||
Scenario: Filtering [Model]
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 3 service models from yaml
|
||||
---
|
||||
- ChecksPassing: 1
|
||||
ChecksWarning: 0
|
||||
ChecksCritical: 0
|
||||
- ChecksPassing: 0
|
||||
ChecksWarning: 1
|
||||
ChecksCritical: 0
|
||||
- ChecksPassing: 0
|
||||
ChecksWarning: 0
|
||||
ChecksCritical: 1
|
||||
---
|
||||
And 3 node models from yaml
|
||||
---
|
||||
- Checks:
|
||||
- Status: passing
|
||||
- Checks:
|
||||
- Status: warning
|
||||
- Checks:
|
||||
- Status: critical
|
||||
|
||||
---
|
||||
When I visit the [Page] page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be [Url]
|
||||
|
||||
Then I see 3 [Model] models
|
||||
And I see allIsSelected on the filter
|
||||
|
||||
When I click passing on the filter
|
||||
And I see passingIsSelected on the filter
|
||||
And I see 1 [Model] model
|
||||
|
||||
When I click warning on the filter
|
||||
And I see warningIsSelected on the filter
|
||||
And I see 1 [Model] model
|
||||
|
||||
When I click critical on the filter
|
||||
And I see criticalIsSelected on the filter
|
||||
And I see 1 [Model] model
|
||||
|
||||
When I click all on the filter
|
||||
And I see allIsSelected on the filter
|
||||
Then I type with yaml
|
||||
---
|
||||
s: [Model]-0
|
||||
---
|
||||
And I see 1 [Model] model with the name "[Model]-0"
|
||||
|
||||
Where:
|
||||
-------------------------------------------------
|
||||
| Model | Page | Url |
|
||||
| service | services | /dc-1/services |
|
||||
| node | nodes | /dc-1/nodes |
|
||||
-------------------------------------------------
|
||||
Scenario: Filtering [Model] in [Page]
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
And 2 node models from yaml
|
||||
---
|
||||
- ID: node-0
|
||||
---
|
||||
When I visit the node page for yaml
|
||||
---
|
||||
dc: dc1
|
||||
node: node-0
|
||||
---
|
||||
# And I see 3 healthcheck model with the name "Disk Util"
|
||||
# And then pause for 5000
|
||||
When I click services on the tabs
|
||||
And I see servicesIsSelected on the tabs
|
||||
|
||||
Then I type with yaml
|
||||
---
|
||||
s: 65535
|
||||
---
|
||||
And I see 1 [Model] model
|
||||
And I see 1 [Model] model with the port "65535"
|
||||
Where:
|
||||
-------------------------------------------------
|
||||
| Model | Page | Url |
|
||||
| service | node | /dc-1/nodes/node-0 |
|
||||
-------------------------------------------------
|
|
@ -0,0 +1,26 @@
|
|||
@setupApplicationTest
|
||||
Feature: components / kv-filter
|
||||
Scenario: Filtering using the freetext filter
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 2 [Model] models from yaml
|
||||
---
|
||||
- hi
|
||||
- there
|
||||
---
|
||||
When I visit the [Page] page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be [Url]
|
||||
Then I type with yaml
|
||||
---
|
||||
s: [Text]
|
||||
---
|
||||
And I see 1 [Model] model with the name "[Text]"
|
||||
|
||||
Where:
|
||||
----------------------------------------------------------------
|
||||
| Model | Page | Url | Text | Property |
|
||||
| kv | kvs | /dc-1/kv | hi | name |
|
||||
| kv | kvs | /dc-1/kv | there | name |
|
||||
----------------------------------------------------------------
|
|
@ -0,0 +1,17 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / acls / delete: ACL Delete
|
||||
Scenario: Delete ACL
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And 1 acl model from yaml
|
||||
---
|
||||
Name: something
|
||||
ID: key
|
||||
---
|
||||
When I visit the acls page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
---
|
||||
And I click actions on the acls
|
||||
And I click delete on the acls
|
||||
And I click confirmDelete on the acls
|
||||
Then a PUT request is made to "/v1/acl/destroy/key?dc=datacenter"
|
|
@ -0,0 +1,14 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / acls / index: ACL List
|
||||
|
||||
Scenario:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 3 acl models
|
||||
When I visit the acls page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/acls
|
||||
And I click actions on the acls
|
||||
Then I don't see delete on the acls
|
||||
Then I see 3 acl models
|
|
@ -0,0 +1,39 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / acls / update: ACL Update
|
||||
Scenario: Update to [Name], [Type], [Rules]
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And 1 acl model from yaml
|
||||
---
|
||||
ID: key
|
||||
---
|
||||
When I visit the acl page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
acl: key
|
||||
---
|
||||
Then the url should be /datacenter/acls/key
|
||||
Then I type with yaml
|
||||
---
|
||||
name: [Name]
|
||||
---
|
||||
And I click "[value=[Type]]"
|
||||
And I submit
|
||||
Then a PUT request is made to "/v1/acl/update?dc=datacenter" with the body from yaml
|
||||
---
|
||||
Name: [Name]
|
||||
Type: [Type]
|
||||
---
|
||||
Where:
|
||||
----------------------------------------------------------
|
||||
| Name | Type | Rules |
|
||||
| key-name | client | node "0" {policy = "read"} |
|
||||
| key name | management | node "0" {policy = "write"} |
|
||||
| key%20name | client | node "0" {policy = "read"} |
|
||||
| utf8? | management | node "0" {policy = "write"} |
|
||||
----------------------------------------------------------
|
||||
@ignore
|
||||
Scenario: Rules can be edited/updated
|
||||
Then ok
|
||||
@ignore
|
||||
Scenario: The feedback dialog says success or failure
|
||||
Then ok
|
|
@ -0,0 +1,8 @@
|
|||
@setupApplicationTest
|
||||
Feature: Datacenters
|
||||
@ignore
|
||||
Scenario: Arriving at the service page
|
||||
Given 10 datacenter models
|
||||
When I visit the index page
|
||||
And I click "[data-test-datacenter-selected]"
|
||||
Then I see 10 datacenter models
|
|
@ -0,0 +1,16 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / kvs / delete: KV Delete
|
||||
Scenario: Delete ACL
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And 1 kv model from yaml
|
||||
---
|
||||
- key-name
|
||||
---
|
||||
When I visit the kvs page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
---
|
||||
And I click actions on the kvs
|
||||
And I click delete on the kvs
|
||||
And I click confirmDelete on the kvs
|
||||
Then a DELETE request is made to "/v1/kv/key-name?dc=datacenter"
|
|
@ -0,0 +1,56 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / kvs / list-order
|
||||
In order to be able to find key values easier
|
||||
As a user
|
||||
I want to see the Key/Values listed alphabetically
|
||||
|
||||
Scenario: I have 19 folders
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And 19 kv models from yaml
|
||||
---
|
||||
- __secretzzz/
|
||||
- a-thing-service/
|
||||
- a-thing-y-again-service/
|
||||
- a-thing-y-againzz-service/
|
||||
- a-z-search-service/
|
||||
- blood-pressure-service/
|
||||
- callToAction-items/
|
||||
- configuration/
|
||||
- content-service/
|
||||
- currentRepository-jobs/
|
||||
- currentRepository-service/
|
||||
- first-service/
|
||||
- logs-service/
|
||||
- rabmq-svc/
|
||||
- rabmqUtilities/
|
||||
- schedule-service/
|
||||
- vanApp-service/
|
||||
- vanCat-service/
|
||||
- vanTaxi-service/
|
||||
---
|
||||
When I visit the kvs page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
---
|
||||
Then I see name on the kvs like yaml
|
||||
---
|
||||
- __secretzzz/
|
||||
- a-thing-service/
|
||||
- a-thing-y-again-service/
|
||||
- a-thing-y-againzz-service/
|
||||
- a-z-search-service/
|
||||
- blood-pressure-service/
|
||||
- callToAction-items/
|
||||
- configuration/
|
||||
- content-service/
|
||||
- currentRepository-jobs/
|
||||
- currentRepository-service/
|
||||
- first-service/
|
||||
- logs-service/
|
||||
- rabmq-svc/
|
||||
- rabmqUtilities/
|
||||
- schedule-service/
|
||||
- vanApp-service/
|
||||
- vanCat-service/
|
||||
- vanTaxi-service/
|
||||
---
|
|
@ -0,0 +1,36 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / kvs / update: KV Update
|
||||
Scenario: Update to [Name] change value to [Value]
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
And 1 kv model from yaml
|
||||
---
|
||||
Key: [Name]
|
||||
---
|
||||
When I visit the kv page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
kv: [Name]
|
||||
---
|
||||
Then the url should be /datacenter/kv/[Name]/edit
|
||||
Then I type with yaml
|
||||
---
|
||||
value: [Value]
|
||||
---
|
||||
And I submit
|
||||
Then a PUT request is made to "/v1/kv/[Name]?dc=datacenter" with the body "[Value]"
|
||||
Where:
|
||||
--------------------------------------------
|
||||
| Name | Value |
|
||||
| key | value |
|
||||
| key-name | a value |
|
||||
| folder/key-name | a value |
|
||||
--------------------------------------------
|
||||
@ignore
|
||||
Scenario: The feedback dialog says success or failure
|
||||
Then ok
|
||||
@ignore
|
||||
Scenario: KV's with spaces are saved correctly
|
||||
Then ok
|
||||
@ignore
|
||||
Scenario: KV's with returns are saved correctly
|
||||
Then ok
|
|
@ -0,0 +1,20 @@
|
|||
@setupApplicationTest
|
||||
Feature: List Models
|
||||
Scenario: Listing [Model]
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 3 [Model] models
|
||||
When I visit the [Page] page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be [Url]
|
||||
|
||||
Then I see 3 [Model] models
|
||||
Where:
|
||||
-------------------------------------------------
|
||||
| Model | Page | Url |
|
||||
| service | services | /dc-1/services |
|
||||
| node | nodes | /dc-1/nodes |
|
||||
| kv | kvs | /dc-1/kv |
|
||||
| acl | acls | /dc-1/acls |
|
||||
-------------------------------------------------
|
|
@ -0,0 +1,11 @@
|
|||
@setupApplicationTest
|
||||
Feature: Nodes
|
||||
Scenario:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 3 node models
|
||||
When I visit the nodes page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/nodes
|
||||
Then I see 3 node models
|
|
@ -0,0 +1,16 @@
|
|||
@setupApplicationTest
|
||||
Feature: Search services within nodes by name and port
|
||||
Scenario: Given 1 node
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
And 1 node models from yaml
|
||||
---
|
||||
- ID: node-0
|
||||
---
|
||||
When I visit the node page for yaml
|
||||
---
|
||||
dc: dc1
|
||||
node: node-0
|
||||
---
|
||||
When I click services on the tabs
|
||||
And I see servicesIsSelected on the tabs
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
@setupApplicationTest
|
||||
Feature: Show node
|
||||
Scenario: Given 2 nodes all the tabs are visible and clickable
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
And 2 node models from yaml
|
||||
---
|
||||
- ID: node-0
|
||||
- ID: node-1
|
||||
---
|
||||
When I visit the node page for yaml
|
||||
---
|
||||
dc: dc1
|
||||
node: node-0
|
||||
---
|
||||
And I see healthChecksIsSelected on the tabs
|
||||
|
||||
When I click services on the tabs
|
||||
And I see servicesIsSelected on the tabs
|
||||
|
||||
When I click roundTripTime on the tabs
|
||||
And I see roundTripTimeIsSelected on the tabs
|
||||
|
||||
When I click lockSessions on the tabs
|
||||
And I see lockSessionsIsSelected on the tabs
|
||||
@ignore
|
||||
Scenario: Given 1 node all the tabs are visible and clickable and the RTT one isn't there
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
And 1 node models from yaml
|
||||
---
|
||||
- ID: node-0
|
||||
---
|
||||
When I visit the node page for yaml
|
||||
---
|
||||
dc: dc1
|
||||
node: node-0
|
||||
---
|
||||
And I see healthChecksIsSelected on the tabs
|
||||
|
||||
When I click services on the tabs
|
||||
And I see servicesIsSelected on the tabs
|
||||
|
||||
And I don't see roundTripTime on the tabs
|
||||
|
||||
When I click lockSessions on the tabs
|
||||
And I see lockSessionsIsSelected on the tabs
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
@setupApplicationTest
|
||||
Feature: Services
|
||||
Scenario:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 3 service models
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/services
|
||||
Then I see 3 service models
|
|
@ -0,0 +1,6 @@
|
|||
@setupApplicationTest
|
||||
Feature: index forwarding
|
||||
Scenario: Arriving at the index page when there is only one datacenter
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
When I visit the index page
|
||||
Then the url should be /datacenter/services
|
|
@ -0,0 +1,43 @@
|
|||
@setupApplicationTest
|
||||
Feature: Page Navigation
|
||||
Background:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
Scenario: Visiting the index page
|
||||
When I visit the index page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/services
|
||||
Scenario: Clicking [Link] in the navigation takes me to [Url]
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
When I click [Link] on the navigation
|
||||
Then the url should be [Url]
|
||||
Where:
|
||||
--------------------------------------
|
||||
| Link | Url |
|
||||
| nodes | /dc-1/nodes |
|
||||
| kvs | /dc-1/kv |
|
||||
| acls | /dc-1/acls |
|
||||
| settings | /settings |
|
||||
--------------------------------------
|
||||
Scenario: Clicking a [Item] in the [Model] listing
|
||||
When I visit the [Model] page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
When I click [Item] on the [Model]
|
||||
Then the url should be [Url]
|
||||
Where:
|
||||
--------------------------------------------------------
|
||||
| Item | Model | Url |
|
||||
| service | services | /dc-1/services/service-0 |
|
||||
| node | nodes | /dc-1/nodes/node-0 |
|
||||
| kv | kvs | /dc-1/kv/necessitatibus-0/edit |
|
||||
| acl | acls | /dc-1/acls/anonymous |
|
||||
--------------------------------------------------------
|
||||
@ignore
|
||||
Scenario: Clicking a kv in the kvs listing, without depending on the salt ^
|
||||
Then ok
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
import steps from 'consul-ui/tests/steps';
|
||||
export default steps;
|
|
@ -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,24 @@
|
|||
@setupApplicationTest
|
||||
Feature: submit blank
|
||||
In order to prevent form's being saved without values
|
||||
As a user
|
||||
I shouldn't be able to submit a blank form
|
||||
Scenario: Visiting a blank form for [Model]
|
||||
Given 1 datacenter model with the value "datacenter"
|
||||
When I visit the [Model] page for yaml
|
||||
---
|
||||
dc: datacenter
|
||||
---
|
||||
Then the url should be /datacenter/[Slug]/create
|
||||
And I submit
|
||||
Then the url should be /datacenter/[Slug]/create
|
||||
Where:
|
||||
------------------
|
||||
| Model | Slug |
|
||||
| kv | kv |
|
||||
| acl | acls |
|
||||
------------------
|
||||
@ignore
|
||||
Scenario: The button is disabled
|
||||
Then ok
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
import getAPI from '@hashicorp/ember-cli-api-double';
|
||||
import setCookies from 'consul-ui/tests/helpers/set-cookies';
|
||||
import typeToURL from 'consul-ui/tests/helpers/type-to-url';
|
||||
export default getAPI('/consul-api-double', setCookies, typeToURL);
|
|
@ -0,0 +1,25 @@
|
|||
export default function(type, count, obj) {
|
||||
var key = '';
|
||||
switch (type) {
|
||||
case 'dc':
|
||||
key = 'CONSUL_DATACENTER_COUNT';
|
||||
break;
|
||||
case 'service':
|
||||
key = 'CONSUL_SERVICE_COUNT';
|
||||
break;
|
||||
case 'node':
|
||||
key = 'CONSUL_NODE_COUNT';
|
||||
break;
|
||||
case 'kv':
|
||||
key = 'CONSUL_KV_COUNT';
|
||||
break;
|
||||
case 'acl':
|
||||
key = 'CONSUL_ACL_COUNT';
|
||||
obj['CONSUL_ENABLE_ACLS'] = 1;
|
||||
break;
|
||||
}
|
||||
if (key) {
|
||||
obj[key] = count;
|
||||
}
|
||||
return obj;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
export default function(type) {
|
||||
let url = null;
|
||||
switch (type) {
|
||||
case 'dc':
|
||||
url = '/v1/catalog/datacenters';
|
||||
break;
|
||||
case 'service':
|
||||
url = '/v1/internal/ui/services';
|
||||
break;
|
||||
case 'node':
|
||||
url = '/v1/internal/ui/nodes';
|
||||
// url = '/v1/health/service/_';
|
||||
break;
|
||||
case 'kv':
|
||||
url = '/v1/kv/';
|
||||
break;
|
||||
case 'acl':
|
||||
url = '/v1/acl/list';
|
||||
// url = '/v1/acl/info/_';
|
||||
break;
|
||||
}
|
||||
return url;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import ENV from '../../config/environment';
|
||||
import { skip } from 'qunit';
|
||||
import { setupApplicationTest, setupRenderingTest, setupTest } from 'ember-qunit';
|
||||
import api from 'consul-ui/tests/helpers/api';
|
||||
|
||||
// this logic could be anything, but in this case...
|
||||
// if @ignore, then return skip (for backwards compatibility)
|
||||
// if have annotations in config, then only run those that have a matching annotation
|
||||
function checkAnnotations(annotations) {
|
||||
// if ignore is set then we want to skip for backwards compatibility
|
||||
if (annotations.ignore) {
|
||||
return ignoreIt;
|
||||
}
|
||||
|
||||
// if have annotations set in config, the only run those that have a matching annotation
|
||||
if (ENV.annotations && ENV.annotations.length >= 0) {
|
||||
for (let annotation in annotations) {
|
||||
if (ENV.annotations.indexOf(annotation) >= 0) {
|
||||
// have match, so test it
|
||||
return 'testIt'; // return something other than a function
|
||||
}
|
||||
}
|
||||
|
||||
// no match, so don't run it
|
||||
return logIt;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
model.afterEach(function() {
|
||||
api.server.reset();
|
||||
});
|
||||
};
|
||||
// return setupFn;
|
||||
}
|
||||
|
||||
function setupYaddaTest(annotations) {
|
||||
if (annotations.setupapplicationtest) {
|
||||
return setupApplicationTest;
|
||||
}
|
||||
if (annotations.setuprenderingtest) {
|
||||
return setupRenderingTest;
|
||||
}
|
||||
if (annotations.setuptest) {
|
||||
return setupTest;
|
||||
}
|
||||
}
|
||||
|
||||
export { runFeature, runScenario, setupFeature, setupScenario };
|
|
@ -0,0 +1,2 @@
|
|||
import yadda from 'npm:yadda';
|
||||
export default yadda;
|
|
@ -1,4 +1,4 @@
|
|||
import { moduleForComponent, skip } from 'ember-qunit';
|
||||
import { moduleForComponent, test } from 'ember-qunit';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
moduleForComponent('left trim', 'helper:left-trim', {
|
||||
|
@ -6,7 +6,7 @@ moduleForComponent('left trim', 'helper:left-trim', {
|
|||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
skip('it renders', function(assert) {
|
||||
test('it renders', function(assert) {
|
||||
this.set('inputValue', '1234');
|
||||
|
||||
this.render(hbs`{{left-trim inputValue}}`);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { moduleForComponent, skip } from 'ember-qunit';
|
||||
import { moduleForComponent, test } from 'ember-qunit';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
moduleForComponent('right-trim', 'helper:right-trim', {
|
||||
|
@ -6,7 +6,7 @@ moduleForComponent('right-trim', 'helper:right-trim', {
|
|||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
skip('it renders', function(assert) {
|
||||
test('it renders', function(assert) {
|
||||
this.set('inputValue', '1234');
|
||||
|
||||
this.render(hbs`{{right-trim inputValue}}`);
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
// import { assign } from '../-private/helpers';
|
||||
const assign = Object.assign;
|
||||
import { getExecutionContext } from 'ember-cli-page-object/-private/execution_context';
|
||||
|
||||
import $ from '-jquery';
|
||||
|
||||
function fillInDynamicSegments(path, params, encoder) {
|
||||
return path
|
||||
.split('/')
|
||||
.map(function(segment) {
|
||||
let match = segment.match(/^:(.+)$/);
|
||||
|
||||
if (match) {
|
||||
let [, key] = match;
|
||||
let value = params[key];
|
||||
|
||||
if (typeof value === 'undefined') {
|
||||
throw new Error(`Missing parameter for '${key}'`);
|
||||
}
|
||||
|
||||
// Remove dynamic segment key from params
|
||||
delete params[key];
|
||||
return encoder(value);
|
||||
}
|
||||
|
||||
return segment;
|
||||
})
|
||||
.join('/');
|
||||
}
|
||||
|
||||
function appendQueryParams(path, queryParams) {
|
||||
if (Object.keys(queryParams).length) {
|
||||
path += `?${$.param(queryParams)}`;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
/**
|
||||
* Custom implementation of `visitable`
|
||||
* Currently aims to be compatible and as close as possible to the
|
||||
* actual `ember-cli-page-object` version
|
||||
*
|
||||
* Additions:
|
||||
* 1. Injectable encoder, for when you don't want your segments to be encoded
|
||||
* or you have specific encoding needs
|
||||
* Specifically in my case for KV urls where the `Key`/Slug shouldn't be encoded,
|
||||
* defaults to the browsers `encodeURIComponent` for compatibility and ease.
|
||||
* 2. `path` can be an array of (string) paths OR a string for compatibility.
|
||||
* If a path cannot be generated due to a lack of properties on the
|
||||
* dynamic segment params, if will keep trying 'path' in the array
|
||||
* until it finds one that it can construct. This follows the same thinking
|
||||
* as 'if you don't specify an item, then we are looking to create one'
|
||||
*/
|
||||
export function visitable(path, encoder = encodeURIComponent) {
|
||||
return {
|
||||
isDescriptor: true,
|
||||
|
||||
value(dynamicSegmentsAndQueryParams = {}) {
|
||||
let executionContext = getExecutionContext(this);
|
||||
|
||||
return executionContext.runAsync(context => {
|
||||
var params;
|
||||
let fullPath = (function _try(paths) {
|
||||
const path = paths.shift();
|
||||
params = assign({}, dynamicSegmentsAndQueryParams);
|
||||
var fullPath;
|
||||
try {
|
||||
fullPath = fillInDynamicSegments(path, params, encoder);
|
||||
} catch (e) {
|
||||
if (paths.length > 0) {
|
||||
fullPath = _try(paths);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return fullPath;
|
||||
})(typeof path === 'string' ? [path] : path.slice(0));
|
||||
fullPath = appendQueryParams(fullPath, params);
|
||||
|
||||
return context.visit(fullPath);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
import { create, visitable, fillable, clickable } from 'ember-cli-page-object';
|
||||
import { create, clickable, triggerable } from 'ember-cli-page-object';
|
||||
import { visitable } from 'consul-ui/tests/lib/page-object/visitable';
|
||||
|
||||
export default create({
|
||||
visit: visitable('/:dc/acls/:acl'),
|
||||
fillIn: fillable('input, textarea, [contenteditable]'),
|
||||
// custom visitable
|
||||
visit: visitable(['/:dc/acls/:acl', '/:dc/acls/create']),
|
||||
// fillIn: fillable('input, textarea, [contenteditable]'),
|
||||
name: triggerable('keypress', '[name="name"]'),
|
||||
submit: clickable('[type=submit]'),
|
||||
});
|
||||
|
|
|
@ -3,9 +3,12 @@ import { create, visitable, collection, attribute, clickable } from 'ember-cli-p
|
|||
import filter from 'consul-ui/tests/pages/components/acl-filter';
|
||||
export default create({
|
||||
visit: visitable('/:dc/acls'),
|
||||
acls: collection('[data-test-acl]', {
|
||||
name: attribute('data-test-acl'),
|
||||
acls: collection('[data-test-tabular-row]', {
|
||||
name: attribute('data-test-acl', '[data-test-acl]'),
|
||||
acl: clickable('a'),
|
||||
actions: clickable('label'),
|
||||
delete: clickable('[data-test-delete]'),
|
||||
confirmDelete: clickable('button.type-delete'),
|
||||
}),
|
||||
filter: filter,
|
||||
});
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { create, visitable, fillable, clickable } from 'ember-cli-page-object';
|
||||
import { create, clickable } from 'ember-cli-page-object';
|
||||
import { visitable } from 'consul-ui/tests/lib/page-object/visitable';
|
||||
|
||||
export default create({
|
||||
visit: visitable('/:dc/kv/:kv'),
|
||||
fillIn: fillable('input, textarea, [contenteditable]'),
|
||||
// custom visitable
|
||||
visit: visitable(['/:dc/kv/:kv/edit', '/:dc/kv/create'], str => str),
|
||||
// fillIn: fillable('input, textarea, [contenteditable]'),
|
||||
// name: triggerable('keypress', '[name="additional"]'),
|
||||
submit: clickable('[type=submit]'),
|
||||
});
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import { create, visitable, collection } from 'ember-cli-page-object';
|
||||
import { create, visitable, collection, attribute, clickable } from 'ember-cli-page-object';
|
||||
|
||||
export default create({
|
||||
visit: visitable('/:dc/kv'),
|
||||
kvs: collection('[data-test-kv]'),
|
||||
kvs: collection('[data-test-tabular-row]', {
|
||||
name: attribute('data-test-kv', '[data-test-kv]'),
|
||||
kv: clickable('a'),
|
||||
actions: clickable('label'),
|
||||
delete: clickable('[data-test-delete]'),
|
||||
confirmDelete: clickable('button.type-delete'),
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import { create, visitable } from 'ember-cli-page-object';
|
||||
import { create, visitable, collection, attribute } from 'ember-cli-page-object';
|
||||
|
||||
import radiogroup from 'consul-ui/tests/lib/page-object/radiogroup';
|
||||
export default create({
|
||||
visit: visitable('/:dc/nodes/:node'),
|
||||
tabs: radiogroup('tab', ['health-checks', 'services', 'round-trip-time', 'lock-sessions']),
|
||||
healthchecks: collection('[data-test-node-healthcheck]', {
|
||||
name: attribute('data-test-node-healthcheck'),
|
||||
}),
|
||||
services: collection('#services [data-test-tabular-row]', {
|
||||
port: attribute('data-test-service-port', '.port'),
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
/* eslint no-console: "off" */
|
||||
import yadda from './helpers/yadda';
|
||||
import { currentURL, click, triggerKeyEvent } from '@ember/test-helpers';
|
||||
import getDictionary from '@hashicorp/ember-cli-api-double/dictionary';
|
||||
import pages from 'consul-ui/tests/pages';
|
||||
import api from 'consul-ui/tests/helpers/api';
|
||||
|
||||
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);
|
||||
};
|
||||
var currentPage;
|
||||
export default function(assert) {
|
||||
return (
|
||||
yadda.localisation.English.library(
|
||||
getDictionary(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;
|
||||
}
|
||||
cb(null, model);
|
||||
}, yadda)
|
||||
)
|
||||
// doubles
|
||||
.given(['$number $model model', '$number $model models'], function(number, model) {
|
||||
return create(number, model);
|
||||
})
|
||||
.given(['$number $model model with the value "$value"'], function(number, model, value) {
|
||||
return create(number, model, value);
|
||||
})
|
||||
.given(
|
||||
['$number $model model[s]? from yaml\n$yaml', '$number $model model from json\n$json'],
|
||||
function(number, model, data) {
|
||||
return create(number, model, data);
|
||||
}
|
||||
)
|
||||
// interactions
|
||||
.when('I visit the $name page', function(name) {
|
||||
currentPage = pages[name];
|
||||
return currentPage.visit();
|
||||
})
|
||||
.when('I visit the $name page for the "$id" $model', function(name, id, model) {
|
||||
currentPage = pages[name];
|
||||
return currentPage.visit({
|
||||
[model]: id,
|
||||
});
|
||||
})
|
||||
.when(
|
||||
['I visit the $name page for yaml\n$yaml', 'I visit the $name page for json\n$json'],
|
||||
function(name, data) {
|
||||
currentPage = pages[name];
|
||||
// TODO: Consider putting an assertion here for testing the current url
|
||||
// do I absolutely definitely need that all the time?
|
||||
return pages[name].visit(data);
|
||||
}
|
||||
)
|
||||
.when('I click "$selector"', function(selector) {
|
||||
return click(selector);
|
||||
})
|
||||
.when('I click $prop on the $component', function(prop, component) {
|
||||
// Collection
|
||||
var obj;
|
||||
if (typeof currentPage[component].objectAt === 'function') {
|
||||
obj = currentPage[component].objectAt(0);
|
||||
} else {
|
||||
obj = currentPage[component];
|
||||
}
|
||||
const func = obj[prop].bind(obj);
|
||||
try {
|
||||
return func();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error(`The '${prop}' property on the '${component}' page object doesn't exist`);
|
||||
}
|
||||
})
|
||||
.when('I submit', function(selector) {
|
||||
return currentPage.submit();
|
||||
})
|
||||
.then('I fill in "$name" with "$value"', function(name, value) {
|
||||
return currentPage.fillIn(name, value);
|
||||
})
|
||||
.then(['I fill in with yaml\n$yaml', 'I fill in with json\n$json'], function(data) {
|
||||
return Object.keys(data).reduce(function(prev, item, i, arr) {
|
||||
return prev.fillIn(item, data[item]);
|
||||
}, currentPage);
|
||||
})
|
||||
.then(['I type with yaml\n$yaml'], function(data) {
|
||||
const keys = Object.keys(data);
|
||||
return keys
|
||||
.reduce(function(prev, item, i, arr) {
|
||||
return prev.fillIn(item, data[item]);
|
||||
}, currentPage)
|
||||
.then(function() {
|
||||
return Promise.all(
|
||||
keys.map(function(item) {
|
||||
return triggerKeyEvent(`[name="${item}"]`, 'keyup', 83);
|
||||
})
|
||||
);
|
||||
});
|
||||
})
|
||||
// debugging helpers
|
||||
.then('print the current url', function(url) {
|
||||
console.log(currentURL());
|
||||
return Promise.resolve();
|
||||
})
|
||||
.then('log the "$text"', function(text) {
|
||||
console.log(text);
|
||||
return Promise.resolve();
|
||||
})
|
||||
.then('pause for $milliseconds', function(milliseconds) {
|
||||
return new Promise(function(resolve) {
|
||||
setTimeout(resolve, milliseconds);
|
||||
});
|
||||
})
|
||||
// assertions
|
||||
.then('a $method request is made to "$url" with the body from yaml\n$yaml', function(
|
||||
method,
|
||||
url,
|
||||
data
|
||||
) {
|
||||
const request = api.server.history[api.server.history.length - 2];
|
||||
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}`);
|
||||
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]}`
|
||||
);
|
||||
});
|
||||
})
|
||||
.then('a $method request is made to "$url" with the body "$body"', function(
|
||||
method,
|
||||
url,
|
||||
data
|
||||
) {
|
||||
const request = api.server.history[api.server.history.length - 2];
|
||||
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}`);
|
||||
const body = request.requestBody;
|
||||
assert.equal(
|
||||
body,
|
||||
data,
|
||||
`Expected the request body to be ${body}, was ${request.requestBody}`
|
||||
);
|
||||
})
|
||||
.then('a $method request is made to "$url"', function(method, url) {
|
||||
const request = api.server.history[api.server.history.length - 2];
|
||||
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}`);
|
||||
})
|
||||
.then('the url should be $url', function(url) {
|
||||
const current = currentURL();
|
||||
assert.equal(current, url, `Expected the url to be ${url} was ${current}`);
|
||||
})
|
||||
.then(['I see $num $model', 'I see $num $model model', 'I see $num $model models'], function(
|
||||
num,
|
||||
model
|
||||
) {
|
||||
const len = currentPage[`${model}s`].filter(function(item) {
|
||||
return item.isVisible;
|
||||
}).length;
|
||||
|
||||
assert.equal(len, num, `Expected ${num} ${model}s, saw ${len}`);
|
||||
})
|
||||
.then(['I see $num $model model with the $property "$value"'], function(
|
||||
num,
|
||||
model,
|
||||
property,
|
||||
value
|
||||
) {
|
||||
const len = currentPage[`${model}s`].filter(function(item) {
|
||||
return item.isVisible && item[property] == value;
|
||||
}).length;
|
||||
assert.equal(
|
||||
len,
|
||||
num,
|
||||
`Expected ${num} ${model}s with ${property} set to "${value}", saw ${len}`
|
||||
);
|
||||
})
|
||||
.then('I see $property on the $component like yaml\n$yaml', function(
|
||||
property,
|
||||
component,
|
||||
yaml
|
||||
) {
|
||||
const _component = currentPage[component];
|
||||
const iterator = new Array(_component.length).fill(true);
|
||||
iterator.forEach(function(item, i, arr) {
|
||||
const actual = _component.objectAt(i)[property];
|
||||
const expected = yaml[i];
|
||||
assert.deepEqual(
|
||||
actual,
|
||||
expected,
|
||||
`Expected to see ${property} on ${component}[${i}] as ${JSON.stringify(
|
||||
expected
|
||||
)}, was ${JSON.stringify(actual)}`
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(['I see $property on the $component'], function(property, component) {
|
||||
// TODO: Time to work on repetition
|
||||
// Collection
|
||||
var obj;
|
||||
if (typeof currentPage[component].objectAt === 'function') {
|
||||
obj = currentPage[component].objectAt(0);
|
||||
} else {
|
||||
obj = currentPage[component];
|
||||
}
|
||||
let _component;
|
||||
if (typeof obj === 'function') {
|
||||
const func = obj[property].bind(obj);
|
||||
try {
|
||||
_component = func();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
throw new Error(
|
||||
`The '${property}' property on the '${component}' page object doesn't exist`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
_component = obj;
|
||||
}
|
||||
assert.ok(_component[property], `Expected to see ${property} on ${component}`);
|
||||
})
|
||||
.then(["I don't see $property on the $component"], function(property, component) {
|
||||
// Collection
|
||||
var obj;
|
||||
if (typeof currentPage[component].objectAt === 'function') {
|
||||
obj = currentPage[component].objectAt(0);
|
||||
} else {
|
||||
obj = currentPage[component];
|
||||
}
|
||||
const func = obj[property].bind(obj);
|
||||
assert.throws(
|
||||
function() {
|
||||
func();
|
||||
},
|
||||
function(e) {
|
||||
return e.toString().indexOf('Element not found') !== -1;
|
||||
},
|
||||
`Expected to not see ${property} on ${component}`
|
||||
);
|
||||
})
|
||||
.then(['I see $property'], function(property, component) {
|
||||
assert.ok(currentPage[property], `Expected to see ${property}`);
|
||||
})
|
||||
.then('ok', function() {
|
||||
assert.ok(true);
|
||||
})
|
||||
);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:dc/kv/folder', 'Unit | Controller | dc/kv/folder', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:dc/kv/index', 'Unit | Controller | dc/kv/index', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
import getComponentFactory from 'consul-ui/utils/get-component-factory';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | get component factory');
|
||||
|
||||
test("it uses lookup to locate the instance of the component based on the DOM element's id", function(assert) {
|
||||
const expected = 'name';
|
||||
let getComponent = getComponentFactory({
|
||||
lookup: function() {
|
||||
return { id: expected };
|
||||
},
|
||||
});
|
||||
assert.equal(typeof getComponent, 'function', 'returns a function');
|
||||
const actual = getComponent({
|
||||
getAttribute: function(name) {
|
||||
return 'id';
|
||||
},
|
||||
});
|
||||
assert.equal(actual, expected, 'performs a lookup based on the id');
|
||||
});
|
||||
test("it returns null if it can't find it", function(assert) {
|
||||
const expected = null;
|
||||
let getComponent = getComponentFactory({
|
||||
lookup: function() {
|
||||
return { id: '' };
|
||||
},
|
||||
});
|
||||
const actual = getComponent({
|
||||
getAttribute: function(name) {
|
||||
return 'non-existent';
|
||||
},
|
||||
});
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
test('it returns null if there is no id', function(assert) {
|
||||
const expected = null;
|
||||
let getComponent = getComponentFactory({
|
||||
lookup: function() {
|
||||
return { id: '' };
|
||||
},
|
||||
});
|
||||
const actual = getComponent({
|
||||
getAttribute: function(name) {
|
||||
return;
|
||||
},
|
||||
});
|
||||
assert.equal(actual, expected);
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
import hasStatus from 'consul-ui/utils/hasStatus';
|
||||
import { module, test, skip } from 'qunit';
|
||||
|
||||
module('Unit | Utility | has status');
|
||||
|
||||
const checks = {
|
||||
filterBy: function(prop, value) {
|
||||
return { length: 0 };
|
||||
},
|
||||
};
|
||||
test('it returns true when passing an empty string (therefore "all")', function(assert) {
|
||||
assert.ok(hasStatus(checks, ''));
|
||||
});
|
||||
test('it returns false when passing an actual status', function(assert) {
|
||||
['passing', 'critical', 'warning'].forEach(function(item) {
|
||||
assert.ok(!hasStatus(checks, item), `, with ${item}`);
|
||||
});
|
||||
});
|
||||
skip('it works as a factory, passing ember `get` in to create the function');
|
|
@ -0,0 +1,44 @@
|
|||
import { module } from 'ember-qunit';
|
||||
import test from 'ember-sinon-qunit/test-support/test';
|
||||
import leftTrim from 'consul-ui/utils/left-trim';
|
||||
module('Unit | Utility | left trim');
|
||||
|
||||
test('it trims characters from the left hand side', function(assert) {
|
||||
[
|
||||
{
|
||||
args: ['/a/folder/here', '/'],
|
||||
expected: 'a/folder/here',
|
||||
},
|
||||
{
|
||||
args: ['/a/folder/here', ''],
|
||||
expected: '/a/folder/here',
|
||||
},
|
||||
{
|
||||
args: ['a/folder/here', '/'],
|
||||
expected: 'a/folder/here',
|
||||
},
|
||||
{
|
||||
args: ['a/folder/here/', '/'],
|
||||
expected: 'a/folder/here/',
|
||||
},
|
||||
{
|
||||
args: [],
|
||||
expected: '',
|
||||
},
|
||||
{
|
||||
args: ['/a/folder/here', '/a/folder'],
|
||||
expected: '/here',
|
||||
},
|
||||
{
|
||||
args: ['/a/folder/here/', '/a/folder/here'],
|
||||
expected: '/',
|
||||
},
|
||||
{
|
||||
args: ['/a/folder/here/', '/a/folder/here/'],
|
||||
expected: '',
|
||||
},
|
||||
].forEach(function(item) {
|
||||
const actual = leftTrim(...item.args);
|
||||
assert.equal(actual, item.expected);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
import qsaFactory from 'consul-ui/utils/qsa-factory';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | qsa factory');
|
||||
|
||||
test('querySelectorAll is called on `document` when called with document', function(assert) {
|
||||
assert.expect(2);
|
||||
const expected = 'html';
|
||||
const $$ = qsaFactory({
|
||||
querySelectorAll: function(sel) {
|
||||
assert.equal(sel, expected);
|
||||
return true;
|
||||
},
|
||||
});
|
||||
assert.ok($$(expected));
|
||||
});
|
||||
test('querySelectorAll is called on `context` when called with context', function(assert) {
|
||||
assert.expect(2);
|
||||
const expected = 'html';
|
||||
const context = {
|
||||
querySelectorAll: function(sel) {
|
||||
assert.equal(sel, expected);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
const $$ = qsaFactory({
|
||||
// this should never be called
|
||||
querySelectorAll: function(sel) {
|
||||
assert.equal(sel, expected);
|
||||
return false;
|
||||
},
|
||||
});
|
||||
assert.ok($$(expected, context));
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
import { module } from 'ember-qunit';
|
||||
import test from 'ember-sinon-qunit/test-support/test';
|
||||
import rightTrim from 'consul-ui/utils/right-trim';
|
||||
module('Unit | Utility | right trim');
|
||||
|
||||
test('it trims characters from the right hand side', function(assert) {
|
||||
[
|
||||
{
|
||||
args: ['/a/folder/here/', '/'],
|
||||
expected: '/a/folder/here',
|
||||
},
|
||||
{
|
||||
args: ['/a/folder/here', ''],
|
||||
expected: '/a/folder/here',
|
||||
},
|
||||
{
|
||||
args: ['a/folder/here', '/'],
|
||||
expected: 'a/folder/here',
|
||||
},
|
||||
{
|
||||
args: ['a/folder/here/', '/'],
|
||||
expected: 'a/folder/here',
|
||||
},
|
||||
{
|
||||
args: [],
|
||||
expected: '',
|
||||
},
|
||||
{
|
||||
args: ['/a/folder/here', '/folder/here'],
|
||||
expected: '/a',
|
||||
},
|
||||
{
|
||||
args: ['/a/folder/here', 'a/folder/here'],
|
||||
expected: '/',
|
||||
},
|
||||
{
|
||||
args: ['/a/folder/here/', '/a/folder/here/'],
|
||||
expected: '',
|
||||
},
|
||||
].forEach(function(item) {
|
||||
const actual = rightTrim(...item.args);
|
||||
assert.equal(actual, item.expected);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,93 @@
|
|||
import sumOfUnhealthy from 'consul-ui/utils/sumOfUnhealthy';
|
||||
import { module, test, skip } from 'qunit';
|
||||
|
||||
module('Unit | Utility | sum of unhealthy');
|
||||
|
||||
test('it returns the correct single count', function(assert) {
|
||||
const expected = 1;
|
||||
[
|
||||
[
|
||||
{
|
||||
Status: 'critical',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
],
|
||||
].forEach(function(checks) {
|
||||
const actual = sumOfUnhealthy(checks);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
||||
test('it returns the correct single count when there are none', function(assert) {
|
||||
const expected = 0;
|
||||
[
|
||||
[
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
],
|
||||
].forEach(function(checks) {
|
||||
const actual = sumOfUnhealthy(checks);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
||||
test('it returns the correct multiple count', function(assert) {
|
||||
const expected = 3;
|
||||
[
|
||||
[
|
||||
{
|
||||
Status: 'critical',
|
||||
},
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'critical',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
],
|
||||
].forEach(function(checks) {
|
||||
const actual = sumOfUnhealthy(checks);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
||||
skip('it works as a factory, passing ember `get` in to create the function');
|
282
ui-v2/yarn.lock
282
ui-v2/yarn.lock
|
@ -17,6 +17,48 @@
|
|||
ember-cli-babel "^6.10.0"
|
||||
ember-cli-htmlbars-inline-precompile "^1.0.0"
|
||||
|
||||
"@gardenhq/component-factory@^1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/component-factory/-/component-factory-1.4.0.tgz#f5da8ddf2050fde9c69f4426d61fe55de043e78d"
|
||||
dependencies:
|
||||
"@gardenhq/domino" "^1.0.0"
|
||||
"@gardenhq/tick-control" "^2.0.0"
|
||||
classtrophobic-es5 "^0.2.1"
|
||||
hyperhtml "^0.15.5"
|
||||
|
||||
"@gardenhq/domino@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/domino/-/domino-1.0.0.tgz#832c493f3f05697b7df4ccce00c4cf620dc60923"
|
||||
optionalDependencies:
|
||||
min-document "^2.19.0"
|
||||
unfetch "^2.1.2"
|
||||
xhr2 "^0.1.4"
|
||||
|
||||
"@gardenhq/o@^8.0.1":
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/o/-/o-8.0.1.tgz#d6772cec7e4295a951165284cf43fbd0a373b779"
|
||||
dependencies:
|
||||
"@gardenhq/component-factory" "^1.4.0"
|
||||
"@gardenhq/tick-control" "^2.0.0"
|
||||
"@gardenhq/willow" "^6.2.0"
|
||||
babel-standalone "^6.24.2"
|
||||
file-saver "^1.3.3"
|
||||
mousetrap "^1.6.1"
|
||||
ncp "^2.0.0"
|
||||
rollup "^0.41.6"
|
||||
rollup-plugin-memory "^2.0.0"
|
||||
uglify-es "^3.0.17"
|
||||
optionalDependencies:
|
||||
js-yaml "^3.8.4"
|
||||
|
||||
"@gardenhq/tick-control@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/tick-control/-/tick-control-2.0.0.tgz#f84fe38ca7a09b7b2b52f42945c50429ba639897"
|
||||
|
||||
"@gardenhq/willow@^6.2.0":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@gardenhq/willow/-/willow-6.2.0.tgz#3e4bc220a89099732746ead3385cc097bfb70186"
|
||||
|
||||
"@glimmer/di@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@glimmer/di/-/di-0.2.0.tgz#73bfd4a6ee4148a80bf092e8a5d29bcac9d4ce7e"
|
||||
|
@ -27,12 +69,46 @@
|
|||
dependencies:
|
||||
"@glimmer/di" "^0.2.0"
|
||||
|
||||
"@hashicorp/api-double@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/api-double/-/api-double-1.1.0.tgz#299d3c560090dfe9c335db64d63c3ef0c5da79c4"
|
||||
dependencies:
|
||||
"@gardenhq/o" "^8.0.1"
|
||||
"@gardenhq/tick-control" "^2.0.0"
|
||||
array-range "^1.0.1"
|
||||
cookie-parser "^1.4.3"
|
||||
express "^4.16.2"
|
||||
faker "^4.1.0"
|
||||
js-yaml "^3.10.0"
|
||||
|
||||
"@hashicorp/consul-api-double@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-1.1.0.tgz#658f9e89208fa23f251ca66c66aeb7241a13f23f"
|
||||
|
||||
"@hashicorp/ember-cli-api-double@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-1.0.2.tgz#684d418cc2a981254cc23035ceb452c86f0cd934"
|
||||
dependencies:
|
||||
"@hashicorp/api-double" "^1.1.0"
|
||||
array-range "^1.0.1"
|
||||
ember-cli-babel "^6.6.0"
|
||||
js-yaml "^3.11.0"
|
||||
pretender "^2.0.0"
|
||||
|
||||
"@sinonjs/formatio@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2"
|
||||
dependencies:
|
||||
samsam "1.3.0"
|
||||
|
||||
"@types/estree@0.0.38":
|
||||
version "0.0.38"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.38.tgz#c1be40aa933723c608820a99a373a16d215a1ca2"
|
||||
|
||||
"@types/node@*":
|
||||
version "10.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.0.8.tgz#37b4d91d4e958e4c2ba0be2b86e7ed4ff19b0858"
|
||||
|
||||
JSONStream@^1.0.3:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea"
|
||||
|
@ -257,6 +333,10 @@ array-map@~0.0.0:
|
|||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
|
||||
|
||||
array-range@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/array-range/-/array-range-1.0.1.tgz#f56e46591843611c6a56f77ef02eda7c50089bfc"
|
||||
|
||||
array-reduce@~0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b"
|
||||
|
@ -1023,6 +1103,10 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
|
|||
core-js "^2.4.0"
|
||||
regenerator-runtime "^0.11.0"
|
||||
|
||||
babel-standalone@^6.24.2:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-standalone/-/babel-standalone-6.26.0.tgz#15fb3d35f2c456695815ebf1ed96fe7f015b6886"
|
||||
|
||||
babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
|
||||
|
@ -1391,7 +1475,7 @@ broccoli-config-replace@^1.1.2:
|
|||
debug "^2.2.0"
|
||||
fs-extra "^0.24.0"
|
||||
|
||||
broccoli-debug@^0.6.1, broccoli-debug@^0.6.2, broccoli-debug@^0.6.3:
|
||||
broccoli-debug@^0.6.1, broccoli-debug@^0.6.2, broccoli-debug@^0.6.3, broccoli-debug@^0.6.4:
|
||||
version "0.6.4"
|
||||
resolved "https://registry.yarnpkg.com/broccoli-debug/-/broccoli-debug-0.6.4.tgz#986eb3d2005e00e3bb91f9d0a10ab137210cd150"
|
||||
dependencies:
|
||||
|
@ -1842,6 +1926,10 @@ builtin-modules@^1.0.0:
|
|||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
|
||||
|
||||
builtin-modules@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-2.0.0.tgz#60b7ef5ae6546bd7deefa74b08b62a43a232648e"
|
||||
|
||||
builtin-status-codes@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||
|
@ -2058,6 +2146,10 @@ class-utils@^0.3.5:
|
|||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classtrophobic-es5@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/classtrophobic-es5/-/classtrophobic-es5-0.2.1.tgz#9bbfa62a9928abf26f385440032fb49da1cda88f"
|
||||
|
||||
clean-base-url@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/clean-base-url/-/clean-base-url-1.0.0.tgz#c901cf0a20b972435b0eccd52d056824a4351b7b"
|
||||
|
@ -2376,6 +2468,13 @@ convert-source-map@~1.1.0:
|
|||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
|
||||
|
||||
cookie-parser@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.3.tgz#0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5"
|
||||
dependencies:
|
||||
cookie "0.3.1"
|
||||
cookie-signature "1.0.6"
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
|
@ -2798,6 +2897,10 @@ dom-serializer@0:
|
|||
domelementtype "~1.1.1"
|
||||
entities "~1.1.1"
|
||||
|
||||
dom-walk@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
|
||||
|
||||
domain-browser@~1.1.0:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
|
||||
|
@ -2992,6 +3095,21 @@ ember-cli-broccoli-sane-watcher@^2.0.4:
|
|||
rsvp "^3.0.18"
|
||||
sane "^2.4.1"
|
||||
|
||||
ember-cli-cjs-transform@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-cjs-transform/-/ember-cli-cjs-transform-1.2.0.tgz#34a0d2667673caec0248f500a954f45668027e8b"
|
||||
dependencies:
|
||||
broccoli-debug "^0.6.4"
|
||||
broccoli-plugin "^1.3.0"
|
||||
ember-cli-babel "^6.6.0"
|
||||
fs-extra "^5.0.0"
|
||||
hash-for-dep "^1.2.3"
|
||||
pkg-dir "^2.0.0"
|
||||
rollup "^0.58.1"
|
||||
rollup-plugin-commonjs "^9.1.0"
|
||||
rollup-plugin-node-resolve "^3.3.0"
|
||||
username "^3.0.0"
|
||||
|
||||
ember-cli-clipboard@^0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-clipboard/-/ember-cli-clipboard-0.9.0.tgz#c0cfce1a8a81ba1646e54bff9d41249b8bc507f7"
|
||||
|
@ -3270,6 +3388,14 @@ ember-cli-version-checker@^2.0.0, ember-cli-version-checker@^2.1.0:
|
|||
resolve "^1.3.3"
|
||||
semver "^5.3.0"
|
||||
|
||||
ember-cli-yadda@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli-yadda/-/ember-cli-yadda-0.4.0.tgz#0faaa3a4d945b3fc0974d0535e358c2897fdf170"
|
||||
dependencies:
|
||||
broccoli-persistent-filter "^1.4.3"
|
||||
ember-cli-babel "^6.6.0"
|
||||
yadda "*"
|
||||
|
||||
ember-cli@~2.18.2:
|
||||
version "2.18.2"
|
||||
resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-2.18.2.tgz#bb15313a15139a85248a86d203643f918ba40f57"
|
||||
|
@ -3896,6 +4022,10 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1:
|
|||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
|
||||
|
||||
estree-walker@^0.5.1, estree-walker@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39"
|
||||
|
||||
esutils@^2.0.0, esutils@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
|
||||
|
@ -3956,6 +4086,18 @@ execa@^0.10.0:
|
|||
signal-exit "^3.0.0"
|
||||
strip-eof "^1.0.0"
|
||||
|
||||
execa@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
|
||||
dependencies:
|
||||
cross-spawn "^5.0.1"
|
||||
get-stream "^3.0.0"
|
||||
is-stream "^1.1.0"
|
||||
npm-run-path "^2.0.0"
|
||||
p-finally "^1.0.0"
|
||||
signal-exit "^3.0.0"
|
||||
strip-eof "^1.0.0"
|
||||
|
||||
execa@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da"
|
||||
|
@ -4024,7 +4166,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
|
|||
dependencies:
|
||||
homedir-polyfill "^1.0.1"
|
||||
|
||||
express@^4.10.7, express@^4.12.3:
|
||||
express@^4.10.7, express@^4.12.3, express@^4.16.2:
|
||||
version "4.16.3"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
|
||||
dependencies:
|
||||
|
@ -4123,6 +4265,14 @@ eyes@0.1.x:
|
|||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
|
||||
|
||||
fake-xml-http-request@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fake-xml-http-request/-/fake-xml-http-request-2.0.0.tgz#41a92f0ca539477700cb1dafd2df251d55dac8ff"
|
||||
|
||||
faker@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/faker/-/faker-4.1.0.tgz#1e45bbbecc6774b3c195fad2835109c6d748cc3f"
|
||||
|
||||
fast-deep-equal@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
|
||||
|
@ -4192,6 +4342,10 @@ file-entry-cache@^2.0.0:
|
|||
flat-cache "^1.2.1"
|
||||
object-assign "^4.0.1"
|
||||
|
||||
file-saver@^1.3.3:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-1.3.8.tgz#e68a30c7cb044e2fb362b428469feb291c2e09d8"
|
||||
|
||||
filename-regex@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
|
||||
|
@ -4795,7 +4949,7 @@ hash-base@^3.0.0:
|
|||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
hash-for-dep@^1.0.2:
|
||||
hash-for-dep@^1.0.2, hash-for-dep@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/hash-for-dep/-/hash-for-dep-1.2.3.tgz#5ec69fca32c23523972d52acb5bb65ffc3664cab"
|
||||
dependencies:
|
||||
|
@ -4960,6 +5114,10 @@ husky@^0.14.3:
|
|||
normalize-path "^1.0.0"
|
||||
strip-indent "^2.0.0"
|
||||
|
||||
hyperhtml@^0.15.5:
|
||||
version "0.15.10"
|
||||
resolved "https://registry.yarnpkg.com/hyperhtml/-/hyperhtml-0.15.10.tgz#5e5f42393d4fc30cd803063fb88a5c9d97625e1c"
|
||||
|
||||
iconv-lite@0.4.19:
|
||||
version "0.4.19"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
||||
|
@ -5259,6 +5417,10 @@ is-integer@^1.0.4:
|
|||
dependencies:
|
||||
is-finite "^1.0.0"
|
||||
|
||||
is-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||
|
||||
is-my-ip-valid@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824"
|
||||
|
@ -5542,7 +5704,7 @@ js-yaml@0.3.x:
|
|||
version "0.3.7"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-0.3.7.tgz#d739d8ee86461e54b354d6a7d7d1f2ad9a167f62"
|
||||
|
||||
js-yaml@^3.2.5, js-yaml@^3.2.7, js-yaml@^3.6.1, js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1:
|
||||
js-yaml@^3.10.0, js-yaml@^3.11.0, js-yaml@^3.2.5, js-yaml@^3.2.7, js-yaml@^3.6.1, js-yaml@^3.7.0, js-yaml@^3.8.4, js-yaml@^3.9.0, js-yaml@^3.9.1:
|
||||
version "3.11.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef"
|
||||
dependencies:
|
||||
|
@ -6251,6 +6413,12 @@ lru-cache@^4.0.1:
|
|||
pseudomap "^1.0.2"
|
||||
yallist "^2.1.2"
|
||||
|
||||
magic-string@^0.22.4:
|
||||
version "0.22.5"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e"
|
||||
dependencies:
|
||||
vlq "^0.2.2"
|
||||
|
||||
make-dir@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
|
||||
|
@ -6338,6 +6506,12 @@ media-typer@0.3.0:
|
|||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
|
||||
mem@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
|
||||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
memory-streams@^0.1.0:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/memory-streams/-/memory-streams-0.1.3.tgz#d9b0017b4b87f1d92f55f2745c9caacb1dc93ceb"
|
||||
|
@ -6382,7 +6556,7 @@ methods@~1.1.2:
|
|||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
|
||||
micromatch@^2.1.5, micromatch@^2.3.7:
|
||||
micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7:
|
||||
version "2.3.11"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
|
||||
dependencies:
|
||||
|
@ -6443,6 +6617,12 @@ mimic-fn@^1.0.0:
|
|||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||
|
||||
min-document@^2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
|
||||
dependencies:
|
||||
dom-walk "^0.1.0"
|
||||
|
||||
minimalistic-assert@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||
|
@ -6530,6 +6710,10 @@ morgan@^1.8.1:
|
|||
on-finished "~2.3.0"
|
||||
on-headers "~1.0.1"
|
||||
|
||||
mousetrap@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.1.tgz#2a085f5c751294c75e7e81f6ec2545b29cbf42d9"
|
||||
|
||||
mout@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mout/-/mout-1.1.0.tgz#0b29d41e6a80fa9e2d4a5be9d602e1d9d02177f6"
|
||||
|
@ -6579,6 +6763,10 @@ natural-compare@^1.4.0:
|
|||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
|
||||
ncp@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
|
||||
|
||||
negotiator@0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
|
||||
|
@ -7151,6 +7339,12 @@ pinkie@^2.0.0:
|
|||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||
|
||||
pkg-dir@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
|
||||
dependencies:
|
||||
find-up "^2.1.0"
|
||||
|
||||
pluralize@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
|
||||
|
@ -7187,6 +7381,13 @@ preserve@^0.2.0:
|
|||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
||||
pretender@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pretender/-/pretender-2.0.0.tgz#5adae189f1d5b25f86113f9225df25bed54f4072"
|
||||
dependencies:
|
||||
fake-xml-http-request "^2.0.0"
|
||||
route-recognizer "^0.3.3"
|
||||
|
||||
prettier@^1.10.2:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.0.tgz#d26fc5894b9230de97629b39cae225b503724ce8"
|
||||
|
@ -7749,12 +7950,52 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
|||
hash-base "^3.0.0"
|
||||
inherits "^2.0.1"
|
||||
|
||||
rollup@^0.41.4:
|
||||
rollup-plugin-commonjs@^9.1.0:
|
||||
version "9.1.3"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.1.3.tgz#37bfbf341292ea14f512438a56df8f9ca3ba4d67"
|
||||
dependencies:
|
||||
estree-walker "^0.5.1"
|
||||
magic-string "^0.22.4"
|
||||
resolve "^1.5.0"
|
||||
rollup-pluginutils "^2.0.1"
|
||||
|
||||
rollup-plugin-memory@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-memory/-/rollup-plugin-memory-2.0.0.tgz#0a8ac6b57fa0e714f89a15c3ac82bc93f89c47c5"
|
||||
|
||||
rollup-plugin-node-resolve@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz#c26d110a36812cbefa7ce117cadcd3439aa1c713"
|
||||
dependencies:
|
||||
builtin-modules "^2.0.0"
|
||||
is-module "^1.0.0"
|
||||
resolve "^1.1.6"
|
||||
|
||||
rollup-pluginutils@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.1.1.tgz#50289c94f8d7426647b14dc9fbb93643645a8d09"
|
||||
dependencies:
|
||||
estree-walker "^0.5.2"
|
||||
micromatch "^2.3.11"
|
||||
tosource "^1.0.0"
|
||||
|
||||
rollup@^0.41.4, rollup@^0.41.6:
|
||||
version "0.41.6"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.41.6.tgz#e0d05497877a398c104d816d2733a718a7a94e2a"
|
||||
dependencies:
|
||||
source-map-support "^0.4.0"
|
||||
|
||||
rollup@^0.58.1:
|
||||
version "0.58.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.58.2.tgz#2feddea8c0c022f3e74b35c48e3c21b3433803ce"
|
||||
dependencies:
|
||||
"@types/estree" "0.0.38"
|
||||
"@types/node" "*"
|
||||
|
||||
route-recognizer@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.3.tgz#1d365e27fa6995e091675f7dc940a8c00353bd29"
|
||||
|
||||
rsvp@^3.0.14, rsvp@^3.0.16, rsvp@^3.0.17, rsvp@^3.0.18, rsvp@^3.0.21, rsvp@^3.0.6, rsvp@^3.1.0, rsvp@^3.2.1, rsvp@^3.3.3, rsvp@^3.5.0:
|
||||
version "3.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a"
|
||||
|
@ -8684,6 +8925,10 @@ to-regex@^3.0.1, to-regex@^3.0.2:
|
|||
regex-not "^1.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
tosource@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tosource/-/tosource-1.0.0.tgz#42d88dd116618bcf00d6106dd5446f3427902ff1"
|
||||
|
||||
tough-cookie@~2.3.0, tough-cookie@~2.3.3:
|
||||
version "2.3.4"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
|
||||
|
@ -8765,7 +9010,7 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
|
|||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376"
|
||||
|
||||
uglify-es@^3.1.3:
|
||||
uglify-es@^3.0.17, uglify-es@^3.1.3:
|
||||
version "3.3.9"
|
||||
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
|
||||
dependencies:
|
||||
|
@ -8812,6 +9057,10 @@ underscore@>=1.8.3:
|
|||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
|
||||
|
||||
unfetch@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-2.1.2.tgz#684fee4d8acdb135bdb26c0364c642fc326ca95b"
|
||||
|
||||
union-value@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
|
||||
|
@ -8891,6 +9140,13 @@ username@^1.0.1:
|
|||
dependencies:
|
||||
meow "^3.4.0"
|
||||
|
||||
username@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/username/-/username-3.0.0.tgz#b3dba982a72b4ce59d52f159fa1aeba266af5fc8"
|
||||
dependencies:
|
||||
execa "^0.7.0"
|
||||
mem "^1.1.0"
|
||||
|
||||
util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
|
@ -8941,6 +9197,10 @@ verror@1.10.0:
|
|||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
vlq@^0.2.2:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26"
|
||||
|
||||
vm-browserify@~0.0.1:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
|
||||
|
@ -9085,6 +9345,10 @@ xdg-basedir@^3.0.0:
|
|||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||
|
||||
xhr2@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f"
|
||||
|
||||
xmldom@^0.1.19:
|
||||
version "0.1.27"
|
||||
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9"
|
||||
|
@ -9101,6 +9365,10 @@ y18n@^3.2.0, y18n@^3.2.1:
|
|||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
|
||||
|
||||
yadda@*:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/yadda/-/yadda-1.4.0.tgz#75b87196f4a864c4131705131fbbc2df3367b58f"
|
||||
|
||||
yallist@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{"path":"github.com/Azure/go-autorest/autorest/to","checksumSHA1":"SbBb2GcJNm5GjuPKGL2777QywR4=","revision":"5cdef8c5dbd65369ef90f1b8d7ea2b7348ffc862","revisionTime":"2018-04-27T22:46:30Z"},
|
||||
{"path":"github.com/Azure/go-autorest/autorest/validation","checksumSHA1":"5UH4IFIB/98iowPCzzVs4M4MXiQ=","revision":"5cdef8c5dbd65369ef90f1b8d7ea2b7348ffc862","revisionTime":"2018-04-27T22:46:30Z"},
|
||||
{"path":"github.com/dgrijalva/jwt-go","checksumSHA1":"+TKtBzv23ywvmmqRiGEjUba4YmI=","revision":"dbeaa9332f19a944acb5736b4456cfcc02140e29","revisionTime":"2017-10-19T21:57:19Z"},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/azure","checksumSHA1":"F8qQlAbbXZWigahcusD+1FOgXuU=","revision":"c7c0eb3fc6b6f8024c39cb24f3fe254a72595dd4","revisionTime":"2018-04-27T00:43:23Z"},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/azure","checksumSHA1":"F8qQlAbbXZWigahcusD+1FOgXuU=","revision":"c7c0eb3fc6b6f8024c39cb24f3fe254a72595dd4","revisionTime":"2018-04-27T00:43:23Z"}
|
||||
],
|
||||
"rootPath": "github.com/hashicorp/go-discover/provider/azure"
|
||||
}
|
||||
|
|
|
@ -75,6 +75,12 @@ These metrics are used to monitor the health of specific Consul agents.
|
|||
<td>rejected requests</td>
|
||||
<td>counter</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>`consul.client.rpc.failed`</td>
|
||||
<td>This increments whenever a Consul agent in client mode makes an RPC request to a Consul server and fails.</td>
|
||||
<td>requests</td>
|
||||
<td>counter</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>`consul.client.api.catalog_register.<node>`</td>
|
||||
<td>This increments whenever a Consul agent receives a catalog register request.</td>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue