Merge pull request #8080 from hashicorp/ui-staging

ui: UI Release Merge (1.8-beta-3: ui-staging merge)
This commit is contained in:
John Cowen 2020-06-10 17:40:22 +01:00 committed by hashicorp-ci
parent d7d37bace4
commit daf283d315
41 changed files with 316 additions and 154 deletions

View File

@ -1,11 +1,93 @@
{{#if item.Kind}}
{{#if (has-block)}}
{{yield
(component 'consul-kind' item=item)
}}
{{else}}
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
<span>{{titleize (humanize item.Kind)}}</span>
</span>
{{/if}}
{{#let (titleize (humanize item.Kind)) as |Name|}}
{{#if (has-block)}}
{{yield
(component 'consul-kind' item=item withInfo=withInfo)
}}
{{else}}
{{#if withInfo}}
<dl class="tooltip-panel">
<dt>
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
<span>{{Name}}</span>
</span>
</dt>
<dd>
<MenuPanel @position="left">
<BlockSlot @name="header">
{{#if (eq item.Kind 'ingress-gateway')}}
Ingress gateways enable ingress traffic from services outside the Consul service mesh to services inside the Consul service mesh.
{{else if (eq item.Kind 'terminating-gateway')}}
Terminating gateways allow connect-enabled services in Consul service mesh to communicate with services outside the service mesh.
{{else}}
Mesh gateways enable routing of Connect traffic between different Consul datacenters.
{{/if}}
</BlockSlot>
<BlockSlot @name="menu">
<li role="separator">
{{#if (eq item.Kind 'ingress-gateway')}}
About Ingress gateways
{{else if (eq item.Kind 'terminating-gateway')}}
About Terminating gateways
{{else}}
About Mesh gateways
{{/if}}
</li>
{{#let (from-entries
(array 'ingress-gateway' '/consul/developer-mesh/ingress-gateways')
(array 'terminating-gateway' '/consul/developer-mesh/understand-terminating-gateways')
(array 'mesh-gateway' '/consul/developer-mesh/connect-gateways')
) as |link|}}
<li role="none" class="learn-link">
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_LEARN_URL') (get link item.Kind)}} rel="noopener noreferrer" target="_blank">
Learn guides
</a>
</li>
{{/let}}
{{#let (from-entries
(array 'ingress-gateway' '/connect/ingress_gateway.html')
(array 'terminating-gateway' '/connect/terminating_gateway.html')
(array 'mesh-gateway' '/connect/mesh_gateway.html')
) as |link|}}
<li role="none" class="docs-link">
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link item.Kind)}} rel="noopener noreferrer" target="_blank">
Documentation
</a>
</li>
<li role="separator">
Other gateway types
</li>
{{#if (not-eq item.Kind 'mesh-gateway')}}
<li role="none" class="docs-link">
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'mesh-gateway')}} rel="noopener noreferrer" target="_blank">
Mesh gateways
</a>
</li>
{{/if}}
{{#if (not-eq item.Kind 'terminating-gateway')}}
<li role="none" class="docs-link">
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'terminating-gateway')}} rel="noopener noreferrer" target="_blank">
Terminating gateways
</a>
</li>
{{/if}}
{{#if (not-eq item.Kind 'ingress-gateway')}}
<li role="none" class="docs-link">
<a tabindex="-1" role="menuitem" href={{concat (env 'CONSUL_DOCS_URL') (get link 'ingress-gateway')}} rel="noopener noreferrer" target="_blank">
Ingress gateways
</a>
</li>
{{/if}}
{{/let}}
</BlockSlot>
</MenuPanel>
</dd>
</dl>
{{else}}
<span data-test-kind={{item.Kind}} class="consul-kind gateway">
<span>{{Name}}</span>
</span>
{{/if}}
{{/if}}
{{/let}}
{{/if}}

View File

@ -1,9 +1,19 @@
{{yield}}
{{#if (gt items.length 0)}}
<ListCollection @items={{items}} class="consul-service-list" as |item index|>
<a data-test-service-name href={{href-to routeName item.Name}} class={{service/health-checks item}}>
{{#if (eq item.Kind 'terminating-gateway')}}
<a data-test-service-name href={{href-to "dc.services.show.services" item.Name}} class={{service/health-checks item}}>
{{item.Name}}
</a>
{{else if (eq item.Kind 'ingress-gateway')}}
<a data-test-service-name href={{href-to "dc.services.show.upstreams" item.Name}} class={{service/health-checks item}}>
{{item.Name}}
</a>
{{else}}
<a data-test-service-name href={{href-to "dc.services.show.instances" item.Name}} class={{service/health-checks item}}>
{{item.Name}}
</a>
{{/if}}
<ul>
{{#if (and nspace (env 'CONSUL_NSPACES_ENABLED'))}}
{{#if (not-eq item.Namespace nspace)}}

View File

@ -13,7 +13,7 @@
{{#if dc}}
<ul>
{{#if (and (env 'CONSUL_NSPACES_ENABLED') (gt nspaces.length 0))}}
<li data-test-nspace-menu>
<li class="nspaces" data-test-nspace-menu>
{{#if (and (eq nspaces.length 1) (not canManageNspaces)) }}
<span data-test-nspace-selected={{nspace.Name}}>{{nspace.Name}}</span>
{{ else }}

View File

@ -0,0 +1,15 @@
{{yield}}
<div class="menu-panel {{position}}">
<YieldSlot @name="controls">
{{yield}}
</YieldSlot>
{{#yield-slot name="header"}}
<div>{{yield}}</div>
{{else}}
{{/yield-slot}}
<ul role="menu" ...attributes>
<YieldSlot @name="menu">
{{yield}}
</YieldSlot>
</ul>
</div>

View File

@ -0,0 +1,7 @@
import Component from '@ember/component';
import Slotted from 'block-slots';
export default Component.extend(Slotted, {
tagName: '',
});

View File

@ -11,21 +11,22 @@
</YieldSlot>
</button>
</ToggleButton>
<div class={{position}}>
<input type="checkbox" id={{concat 'popover-menu-' guid '-'}} />
{{#each submenus as |sub|}}
<input type="checkbox" id={{concat 'popover-menu-' guid '-' sub}} />
{{/each}}
{{#yield-slot name='header'}}
<div>
{{yield}}
</div>
{{else}}
{{/yield-slot}}
<ul role="menu" id={{ariaControls}} aria-labelledby={{ariaLabelledBy}} aria-expanded={{ariaExpanded}}>
<MenuPanel @position={{position}} id={{ariaControls}} aria-labelledby={{ariaLabelledBy}} aria-expanded={{ariaExpanded}}>
<BlockSlot @name="controls">
<input type="checkbox" id={{concat 'popover-menu-' guid '-'}} />
{{#each submenus as |sub|}}
<input type="checkbox" id={{concat 'popover-menu-' guid '-' sub}} />
{{/each}}
</BlockSlot>
{{#if hasHeader}}
<BlockSlot @name="header">
{{#yield-slot name="header"}}{{yield}}{{else}}{{/yield-slot}}
</BlockSlot>
{{/if}}
<BlockSlot @name="menu">
<YieldSlot @name="menu" @params={{block-params (concat "popover-menu-" guid "-") send keypressClick this.toggle.click}}>
{{yield}}
</YieldSlot>
</ul>
</div>
</BlockSlot>
</MenuPanel>
</AriaMenu>

View File

@ -2,6 +2,7 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import Slotted from 'block-slots';
import { set } from '@ember/object';
export default Component.extend(Slotted, {
tagName: '',
@ -16,6 +17,9 @@ export default Component.extend(Slotted, {
this._super(...arguments);
this.guid = this.dom.guid(this);
},
willRender: function() {
set(this, 'hasHeader', this._isRegistered('header'));
},
actions: {
change: function(e) {
if (!e.target.checked) {

View File

@ -1,7 +0,0 @@
<input id={{concat 'sort-control-value-' elementId}} type="radio" value="Name" name={{concat 'sort-control-' name}} onchange={{action 'change'}} />
{{#if checked}}
<input id={{concat 'sort-control-direction-' elementId}} type="checkbox" onchange={{action 'change'}} />
{{/if}}
<label for={{if checked (concat 'sort-control-direction-' elementId) (concat 'sort-control-value-' elementId)}}>
{{yield}}
</label>

View File

@ -1,15 +0,0 @@
import Component from '@ember/component';
import { set } from '@ember/object';
export default Component.extend({
classNames: ['sort-control'],
direction: 'asc',
onchange: function() {},
actions: {
change: function(e) {
if (e.target.type === 'checkbox') {
set(this, 'direction', e.target.checked ? 'desc' : 'asc');
}
this.onchange({ target: { value: `${this.value}:${this.direction}` } });
},
},
});

View File

@ -0,0 +1,5 @@
import { helper } from '@ember/component/helper';
export default helper(function fromEntries(params /*, hash*/) {
return Object.fromEntries(params);
});

View File

@ -12,4 +12,5 @@ export default Model.extend({
// TODO: Are these required?
Services: hasMany('service'),
Nodes: hasMany('node'),
MeshEnabled: attr('boolean', { defaultValue: true }),
});

View File

@ -30,23 +30,7 @@ export default Route.extend({
.catch(function() {
return null;
}),
chain: this.chainRepo.findBySlug(params.name, dc, nspace).catch(function(e) {
const code = get(e, 'errors.firstObject.status');
// Currently we are specifically catching a 500, but we return null
// by default, so null for all errors.
// The extra code here is mainly for documentation purposes
// and for if we need to perform different actions based on the error code
// in the future
switch (code) {
case '500':
// connect is likely to be disabled
// we just return a null to hide the tab
// `Connect must be enabled in order to use this endpoint`
return null;
default:
return null;
}
}),
chain: this.chainRepo.findBySlug(params.name, dc, nspace),
proxies: this.proxyRepo.findAllBySlug(params.name, dc, nspace),
...model,
});

View File

@ -39,6 +39,9 @@ export default Service.extend({
});
}
},
peekOne: function(id) {
return this.store.peekRecord(this.getModelName(), id);
},
findAllByDatacenter: function(dc, nspace, configuration = {}) {
const query = {
dc: dc,

View File

@ -1,8 +1,31 @@
import { inject as service } from '@ember/service';
import { get, set } from '@ember/object';
import RepositoryService from 'consul-ui/services/repository';
const modelName = 'discovery-chain';
const ERROR_MESH_DISABLED = 'Connect must be enabled in order to use this endpoint';
export default RepositoryService.extend({
dcs: service('repository/dc'),
getModelName: function() {
return modelName;
},
findBySlug: function(slug, dc, nspace, configuration = {}) {
const datacenter = this.dcs.peekOne(dc);
if (datacenter !== null && !get(datacenter, 'MeshEnabled')) {
return Promise.resolve();
}
return this._super(...arguments).catch(e => {
const code = get(e, 'errors.firstObject.status');
const body = get(e, 'errors.firstObject.detail').trim();
switch (code) {
case '500':
if (datacenter !== null && body === ERROR_MESH_DISABLED) {
set(datacenter, 'MeshEnabled', false);
}
return;
default:
return;
}
});
},
});

View File

@ -36,8 +36,8 @@ const createProxy = function(repo, find, settings, cache, serialize = JSON.strin
// original find... with configuration now added
return repo[find](...args)
.then(res => {
if (!configuration.settings.enabled) {
// blocking isn't enabled, immediately close
if (!configuration.settings.enabled || typeof res === 'undefined') {
// blocking isn't enabled, or we got no data, immediately close
this.close();
}
return res;

View File

@ -4,6 +4,7 @@
@extend %user-select-none;
cursor: pointer;
white-space: nowrap;
text-decoration: none;
}
%button:disabled,
%internal-button:disabled {

View File

@ -1,6 +1,5 @@
%menu-panel {
position: absolute;
overflow: hidden;
}
%menu-panel [type='checkbox'] {
display: none;
@ -15,8 +14,14 @@
/* or be hardcoded */
/* min-height: 143px; */
}
%menu-panel [role='menuitem']::after {
float: right;
%menu-panel [role='menuitem'] {
display: flex;
justify-content: space-between;
}
%menu-panel [role='menuitem']:after {
@extend %as-pseudo;
display: block !important;
background-position: center right !important;
}
%menu-panel-sub-panel {
position: absolute;
@ -70,7 +75,7 @@
}
%menu-panel-header {
padding: 10px;
padding-left: 36px;
white-space: normal;
}
/* here the !important is only needed for what seems to be a difference */
/* with the CSS before and after compression */

View File

@ -22,12 +22,12 @@
border-top: $decor-border-100;
border-color: $gray-300;
}
%menu-panel-header {
background-color: $gray-050;
%menu-panel .docs-link a::after {
@extend %with-docs-mask, %as-pseudo;
}
%menu-panel-header::before {
@extend %with-info-circle-fill-color-icon, %as-pseudo;
font-size: 1.1em;
%menu-panel .learn-link a::after {
@extend %with-learn-mask, %as-pseudo;
}
%menu-panel .is-active > *::after {
@extend %with-check-plain-mask, %as-pseudo;

View File

@ -16,6 +16,7 @@
padding: 0 8px;
border-radius: $decor-radius-100;
height: 18px;
display: inline-block;
line-height: 0.7rem;
}
%reduced-pill > span {

View File

@ -12,6 +12,7 @@
%popover-menu + label > * {
@extend %toggle-button;
}
%more-popover-menu-panel,
%popover-menu-panel {
@extend %menu-panel;
}

View File

@ -11,7 +11,7 @@
@extend %toggle-button;
}
%more-popover-menu-panel {
@extend %menu-panel;
overflow: hidden;
width: 192px;
}
%more-popover-menu + label + div {

View File

@ -6,6 +6,8 @@
display: flex;
align-items: center;
white-space: nowrap;
position: relative;
z-index: 1;
}
%app-view-actions {
margin-left: auto;

View File

@ -33,6 +33,8 @@
@import './list-collection';
@import './grid-collection';
@import './popover-select';
@import './tooltip-panel';
@import './menu-panel';
/**/

View File

@ -9,7 +9,7 @@
@extend %with-learn-icon, %as-pseudo;
}
%main-nav-horizontal .feedback-link a::after {
@extend %with-logo-github-monochrome-icon, %as-pseudo;
@extend %with-logo-github-monochrome-mask, %as-pseudo;
}
%main-header-horizontal::before {
background-color: var(--swatch-brand-600, $black);

View File

@ -10,6 +10,15 @@
%secondary-nav {
@extend %main-nav-horizontal;
}
%primary-nav .nspaces .menu-panel > div {
background-color: $gray-050;
padding-left: 36px;
}
%primary-nav .nspaces .menu-panel > div::before {
@extend %with-info-circle-fill-mask, %as-pseudo;
color: $blue-500;
font-size: 1.1em;
}
%main-header-horizontal > div {
@extend %main-nav-horizontal-panel;

View File

@ -0,0 +1,3 @@
.menu-panel {
@extend %menu-panel;
}

View File

@ -0,0 +1,4 @@
@import './tooltip-panel/index';
.tooltip-panel {
@extend %tooltip-panel;
}

View File

@ -0,0 +1,2 @@
@import './skin';
@import './layout';

View File

@ -0,0 +1,21 @@
%tooltip-panel {
margin: 0 !important;
}
%tooltip-panel dd > div {
top: auto !important;
}
%tooltip-panel dt {
cursor: pointer;
}
%tooltip-panel dd {
display: none;
position: relative;
padding-top: 10px;
margin-bottom: -10px;
}
%tooltip-panel:hover dd {
display: block;
}
%tooltip-panel dd > div {
width: 250px;
}

View File

@ -0,0 +1,13 @@
/* This is the top chevron */
%tooltip-panel dd > div::before {
@extend %as-pseudo;
width: 12px;
height: 12px;
background: white;
border-top: 1px solid $gray-300;
border-right: 1px solid $gray-300;
transform: rotate(-45deg);
position: absolute;
left: 16px;
top: -7px;
}

View File

@ -34,7 +34,7 @@
{{#let (sort-by (comparator 'service' sort.selected.key) services) as |sorted|}}
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
<BlockSlot @name="set" as |filtered|>
<ConsulServiceList @routeName="dc.services.show" @items={{filtered}} @proxies={{proxies}}/>
<ConsulServiceList @items={{filtered}} @proxies={{proxies}}/>
</BlockSlot>
<BlockSlot @name="empty">
<EmptyState @allowLogin={{true}}>

View File

@ -14,7 +14,7 @@
{{ item.ID }}
</h1>
<ConsulExternalSource @item={{item}} />
<ConsulKind @item={{item}} />
<ConsulKind @item={{item}} @withInfo={{true}} />
</BlockSlot>
<BlockSlot @name="nav">
<dl>

View File

@ -1,11 +1,5 @@
<div class="tab-section">
<div role="tabpanel">
{{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}}
<section>
<h3>Proxy health checks</h3>
<HealthcheckList data-test-proxy-checks @items={{append proxy.ServiceChecks proxy.NodeChecks}} />
</section>
{{/if}}
{{#if (gt proxy.Proxy.Upstreams.length 0)}}
<section class="proxy-upstreams">
<h3> Upstreams</h3>
@ -103,5 +97,11 @@
</ul>
</section>
{{/if}}
{{#if (or (gt proxy.ServiceChecks.length 0) (gt proxy.NodeChecks.length 0))}}
<section>
<h3>Proxy health</h3>
<HealthcheckList data-test-proxy-checks @items={{append proxy.ServiceChecks proxy.NodeChecks}} />
</section>
{{/if}}
</div>
</div>

View File

@ -14,27 +14,27 @@
{{item.Service.Service}}
</h1>
<ConsulExternalSource @item={{item.Service}} />
<ConsulKind @item={{item.Service}} />
<ConsulKind @item={{item.Service}} @withInfo={{true}} />
</BlockSlot>
<BlockSlot @name="nav">
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
<TabNav @items={{
compact
(array
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
(if (eq item.Service.Kind 'terminating-gateway')
(if (eq item.Service.Kind 'terminating-gateway')
(hash label="Linked Services" href=(href-to "dc.services.show.services") selected=(is-href "dc.services.show.services"))
'')
(if (eq item.Service.Kind 'ingress-gateway')
(if (eq item.Service.Kind 'ingress-gateway')
(hash label="Upstreams" href=(href-to "dc.services.show.upstreams") selected=(is-href "dc.services.show.upstreams"))
'')
(if (not item.Service.Kind)
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
(if (not item.Service.Kind)
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
'')
(if chain
(if chain.Chain
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
'')
(if (not item.Service.Kind)
(if (not item.Service.Kind)
(hash label="Tags" href=(href-to "dc.services.show.tags") selected=(is-href "dc.services.show.tags"))
'')
)

View File

@ -57,7 +57,7 @@
"@glimmer/component": "^1.0.0",
"@glimmer/tracking": "^1.0.0",
"@hashicorp/consul-api-double": "^2.6.2",
"@hashicorp/ember-cli-api-double": "^3.0.2",
"@hashicorp/ember-cli-api-double": "^3.1.0",
"@xstate/fsm": "^1.4.0",
"babel-eslint": "^10.0.3",
"base64-js": "^1.3.0",

View File

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

View File

@ -17,13 +17,20 @@ Feature: dc / services / show-routing: Show Routing for Service
And the title should be "service-0 - Consul"
And I see routing on the tabs
Scenario: Given connect is disabled, the Routing tab should not display or error
Given 1 datacenter model with the value "dc1"
Given 2 datacenter models from yaml
---
- dc1
- dc2
---
And 1 node models
And 1 service model from yaml
And 2 service model from yaml
---
- Service:
Name: service-0
ID: service-0-with-id
- Service:
Name: service-1
ID: service-1-with-id
---
And the url "/v1/discovery-chain/service-0?dc=dc1&ns=@namespace" responds with from yaml
---
@ -37,4 +44,17 @@ Feature: dc / services / show-routing: Show Routing for Service
---
And I don't see routing on the tabs
And I don't see the "[data-test-error]" element
And I visit the service page for yaml
---
dc: dc2
service: service-1
---
And I see routing on the tabs
And I visit the service page for yaml
---
dc: dc1
service: service-0
---
Then a GET request wasn't made to "/v1/discovery-chain/service-0?dc=dc1&ns=@namespace"
And I don't see routing on the tabs
And I don't see the "[data-test-error]" element

View File

@ -1,47 +0,0 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | sort control', function(hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function() {
this.actions = {};
this.send = (actionName, ...args) => this.actions[actionName].apply(this, args);
});
test('it renders', async function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
await render(hbs`{{sort-control}}`);
assert.dom('*').hasText('');
// Template block usage:
await render(hbs`
{{#sort-control}}
template block text
{{/sort-control}}
`);
assert.dom('*').hasText('template block text');
});
test('it changes direction and calls onchange when clicked/activated', async function(assert) {
assert.expect(2);
let count = 0;
this.actions.change = e => {
if (count === 0) {
assert.equal(e.target.value, 'sort:desc');
} else {
assert.equal(e.target.value, 'sort:asc');
}
count++;
};
await render(hbs`{{sort-control checked=true value='sort' onchange=(action 'change')}}`);
const $label = this.$('label');
$label.trigger('click');
$label.trigger('click');
});
});

View File

@ -21,9 +21,11 @@ import assertForm from './steps/assertions/form';
const pluralize = function(str) {
return Inflector.inflector.pluralize(str);
};
const getLastNthRequest = function(arr) {
const getLastNthRequest = function(getRequests) {
return function(n, method) {
let requests = arr.slice(0).reverse();
let requests = getRequests()
.slice(0)
.reverse();
if (method) {
requests = requests.filter(function(item) {
return item.method === method;
@ -82,7 +84,7 @@ export default function(assert, library) {
})();
});
};
const lastNthRequest = getLastNthRequest(api.server.history);
const lastNthRequest = getLastNthRequest(() => api.server.history);
const create = function(number, name, value) {
// don't return a promise here as
// I don't need it to wait
@ -99,6 +101,7 @@ export default function(assert, library) {
return currentPage;
};
const setCurrentPage = function(page) {
api.server.clearHistory();
currentPage = page;
return page;
};

View File

@ -1,3 +1,4 @@
const not = `(n't| not)?`;
export default function(scenario, assert, lastNthRequest) {
// lastNthRequest should return a
// {
@ -21,12 +22,17 @@ export default function(scenario, assert, lastNthRequest) {
);
assert.equal(diff.size, 0, `Expected requests "${[...diff].join(', ')}"`);
})
.then('a $method request was made to "$endpoint"', function(method, url) {
.then(`a $method request was${not} made to "$endpoint"`, function(method, negative, url) {
const isNegative = typeof negative !== 'undefined';
const requests = lastNthRequest(null, method);
const request = requests.find(function(item) {
const request = requests.some(function(item) {
return method === item.method && url === item.url;
});
assert.ok(request, `Expected a ${method} request url to ${url}`);
if (isNegative) {
assert.notOk(request, `Didn't expect a ${method} request url to ${url}`);
} else {
assert.ok(request, `Expected a ${method} request url to ${url}`);
}
})
.then('a $method request was made to "$endpoint" with no body', function(method, url) {
const requests = lastNthRequest(null, method);

View File

@ -1215,10 +1215,10 @@
resolved "https://registry.yarnpkg.com/@hashicorp/consul-api-double/-/consul-api-double-2.15.2.tgz#e2c34a348b9959fcc95ffad797c1fed9644a41bd"
integrity sha512-VNdwsL3ut4SubCtwWfqX4prD9R/RczKtWUID6s6K9h1TCdzTgpZQhbb+gdzaYGqzCE3Mrw416JzclxVTIFIUFw==
"@hashicorp/ember-cli-api-double@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-3.0.2.tgz#4f66f22e4b54293c46fe16dc24568267526c8acd"
integrity sha512-NmcA+jBcBO8tzCfbqWzrQdHvTUeaj71Gdu9phxaULMtM9Zw7BZtHlvb2P1ivknGGw92w9Se50pDNjkB57ww22A==
"@hashicorp/ember-cli-api-double@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@hashicorp/ember-cli-api-double/-/ember-cli-api-double-3.1.0.tgz#ce228ac5c8a46c7a10112f5bc0fb782c47775b60"
integrity sha512-G8dDSewInFZOeD5sprdZPw7ZKUYlkJ9bJxPkEaMRPbC6ZN4ZHqeFWB1xXeq2ROtR07J6Xbs+BrFIE6GHTshpEg==
dependencies:
"@hashicorp/api-double" "^1.6.1"
array-range "^1.0.1"