diff --git a/ui/src/__tests__/components/service/__snapshots__/ProviderTable.test.js.snap b/ui/src/__tests__/components/service/__snapshots__/ProviderTable.test.js.snap index bd71c6b31b7..ab55b869174 100644 --- a/ui/src/__tests__/components/service/__snapshots__/ProviderTable.test.js.snap +++ b/ui/src/__tests__/components/service/__snapshots__/ProviderTable.test.js.snap @@ -71,10 +71,16 @@ exports[`ProviderTable should render 1`] = ` vertical-align: text-bottom; } +.emotion-17 { + text-align: left; + padding: 5px 0 5px 15px; + vertical-align: middle; +} + +
+
+
@@ -147,7 +162,5 @@ exports[`ProviderTable should render error if allow api throws error 1`] = ` data-testid="error-message" name="red600" style="color: rgb(208, 17, 17); display: inline-block;" -> - Failed to fetch template details. - +/> `; diff --git a/ui/src/__tests__/components/service/__snapshots__/ServiceList.test.js.snap b/ui/src/__tests__/components/service/__snapshots__/ServiceList.test.js.snap index f136f7f918c..6146997a2a1 100644 --- a/ui/src/__tests__/components/service/__snapshots__/ServiceList.test.js.snap +++ b/ui/src/__tests__/components/service/__snapshots__/ServiceList.test.js.snap @@ -136,6 +136,7 @@ exports[`ServiceList should render add service modal after click 1`] = ` @@ -264,7 +265,7 @@ exports[`ServiceList should render add service modal after click 1`] = ` class="emotion-29" data-testid="icon" height="1.25em" - id="" + id="openhouse-providers" viewBox="0 0 1024 1024" width="1.25em" > @@ -451,6 +452,7 @@ exports[`ServiceList should render delete service modal error(other) after click
@@ -579,7 +581,7 @@ exports[`ServiceList should render delete service modal error(other) after click class="emotion-29" data-testid="icon" height="1.25em" - id="" + id="openhouse-providers" viewBox="0 0 1024 1024" width="1.25em" > @@ -756,6 +758,7 @@ exports[`ServiceList should render delete service modal error(refresh) after cli
@@ -884,7 +887,7 @@ exports[`ServiceList should render delete service modal error(refresh) after cli class="emotion-29" data-testid="icon" height="1.25em" - id="" + id="openhouse-providers" viewBox="0 0 1024 1024" width="1.25em" > @@ -1061,6 +1064,7 @@ exports[`ServiceList should render serviceList again after cancel delete 1`] = `
@@ -1189,7 +1193,7 @@ exports[`ServiceList should render serviceList again after cancel delete 1`] = ` class="emotion-29" data-testid="icon" height="1.25em" - id="" + id="openhouse-providers" viewBox="0 0 1024 1024" width="1.25em" > @@ -1344,6 +1348,7 @@ exports[`ServiceList should render serviceList again after confirm delete 1`] =
@@ -1525,6 +1530,7 @@ exports[`ServiceList should render with services 1`] = `
@@ -1653,7 +1659,7 @@ exports[`ServiceList should render with services 1`] = ` class="emotion-29" data-testid="icon" height="1.25em" - id="" + id="openhouse-providers" viewBox="0 0 1024 1024" width="1.25em" > @@ -1808,6 +1814,7 @@ exports[`ServiceList should render without services 1`] = `
diff --git a/ui/src/__tests__/components/service/__snapshots__/ServiceRow.test.js.snap b/ui/src/__tests__/components/service/__snapshots__/ServiceRow.test.js.snap index 91b2528fede..b781689f7ad 100644 --- a/ui/src/__tests__/components/service/__snapshots__/ServiceRow.test.js.snap +++ b/ui/src/__tests__/components/service/__snapshots__/ServiceRow.test.js.snap @@ -178,7 +178,7 @@ exports[`ServiceRow should render row 1`] = ` class="emotion-8" data-testid="icon" height="1.25em" - id="" + id="serviceName-providers" viewBox="0 0 1024 1024" width="1.25em" > diff --git a/ui/src/__tests__/pages/domain/[domain]/__snapshots__/service.test.js.snap b/ui/src/__tests__/pages/domain/[domain]/__snapshots__/service.test.js.snap index a87ecc08937..312aba3dbae 100644 --- a/ui/src/__tests__/pages/domain/[domain]/__snapshots__/service.test.js.snap +++ b/ui/src/__tests__/pages/domain/[domain]/__snapshots__/service.test.js.snap @@ -1361,6 +1361,7 @@ exports[`ServicePage should render 1`] = `
@@ -1489,7 +1490,7 @@ exports[`ServicePage should render 1`] = ` class="emotion-84" data-testid="icon" height="1.25em" - id="" + id="bastion-providers" viewBox="0 0 1024 1024" width="1.25em" > @@ -1612,7 +1613,7 @@ exports[`ServicePage should render 1`] = ` class="emotion-84" data-testid="icon" height="1.25em" - id="" + id="openhouse-providers" viewBox="0 0 1024 1024" width="1.25em" > diff --git a/ui/src/__tests__/spec/tests/services.spec.js b/ui/src/__tests__/spec/tests/services.spec.js index 04b0bce1b22..8a7487db740 100644 --- a/ui/src/__tests__/spec/tests/services.spec.js +++ b/ui/src/__tests__/spec/tests/services.spec.js @@ -14,6 +14,8 @@ * limitations under the License. */ +const serviceNameForProvidersTest = 'providers-error-test-service'; + describe('services screen tests', () => { it('when clicking help tooltip link, it should open a tab with athenz guide', async () => { // open browser @@ -81,4 +83,95 @@ describe('services screen tests', () => { let modalDeleteButton = await $('button*=Delete'); await modalDeleteButton.click(); }); + + it('when clicking "Allow" button on a provider without having appropriate authorisation, the error should be displayed to the right of the button', async () => { + // open browser + await browser.newUser(); + await browser.url(`/`); + // select domain + let domain = 'athenz.dev.functional-test'; + let testDomain = await $(`a*=${domain}`); + await browser.waitUntil(async () => await testDomain.isClickable()); + await testDomain.click(); + + // open Services + let servicesDiv = await $('div*=Services'); + await servicesDiv.click(); + + // use date and time when creating a service because if we recreate service with same name - it will reference + // previous provider allowances + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + const seconds = String(now.getSeconds()).padStart(2, '0'); + const dateTimeFormatted = `${year}-${month}-${day}-${hours}-${minutes}-${seconds}`; + + // add service + const serviceNameWithDate = `${serviceNameForProvidersTest}-${dateTimeFormatted}`; + let addServiceButton = await $('button*=Add Service'); + await addServiceButton.click(); + let serviceNameInput = await $('input[id="service-name"]'); + await serviceNameInput.addValue(serviceNameWithDate); + let submitButton = await $('button*=Submit'); + await submitButton.click(); + + // click Providers + let providersButton = await $(`.//*[local-name()="svg" and @id="${serviceNameWithDate + '-providers'}"]`); + await providersButton.click(); + + // click Azure provider + let awsProviderAllowButton = await $(`td[data-testid="provider-table"]`) + .$(`//td[text()="Azure VM launches instances for the service"]/following-sibling::td//button`); + await awsProviderAllowButton.click(); + + // warning should appear + let warning = await $(`td[data-testid="provider-table"]`) + .$(`//td[text()="Azure VM launches instances for the service"]/following-sibling::td//div[text()="Status: 404. Message: unknown domain - athenz.azure.provider"]`); + await expect(warning).toHaveText('Status: 404. Message: unknown domain - athenz.azure.provider'); + }); + + // after - runs after the last test in order of declaration + after(async() => { + // open browser + await browser.newUser(); + await browser.url(`/`); + // select domain + let domain = 'athenz.dev.functional-test'; + let testDomain = await $(`a*=${domain}`); + await browser.waitUntil(async () => await testDomain.isClickable()); + await testDomain.click(); + + // open Services + let servicesDiv = await $('div*=Services'); + await servicesDiv.click(); + + // wait until services table is populated with services + await browser.waitUntil(async () => { + const table = await $('//table[@id="services-table"]') + const rows = await table.$$('tr'); + return rows.length > 0; + }, { + timeout: 1000, + timeoutMsg: `Services table did not have a service containing ${serviceNameForProvidersTest}.` + }); + + // get all the services created during the test + let deleteButtons = await $$(`[id*="delete-service-${serviceNameForProvidersTest}"]`); + + // iteratively get delete buttons, pick only ones that match our ID and delete these + for (let i = 0; i < await deleteButtons.length; i++) { + const deleteButton = deleteButtons[i]; + const id = await deleteButton.getAttribute('id'); + await deleteButton.click(); + let deleteButtonOnModal = await $('button*=Delete'); + await deleteButtonOnModal.click(); + // wait until the notification about successful deletion disappears - so that next deletion can be done + let deletedDiv = await $('div*=Successfully deleted service'); + await deletedDiv.waitForDisplayed({ reverse: true, timeout: 10000 }); + console.log("DELETED SERVICE", id) + } + }); }) diff --git a/ui/src/components/service/ProviderTable.js b/ui/src/components/service/ProviderTable.js index cd2b3aab02d..14865e9f469 100644 --- a/ui/src/components/service/ProviderTable.js +++ b/ui/src/components/service/ProviderTable.js @@ -59,6 +59,12 @@ const TableHeadStyledRight = styled.th` border-right: none; `; +const TableHeadStyledError = styled.th` + padding-bottom: 5px; + padding: 5px 0 5px 15px; + border-right: none; +`; + const TdStyled = styled.td` padding: 20px; text-align: left; @@ -80,6 +86,12 @@ const ProviderTd = styled.td` word-break: break-all; `; +const ErrorTd = styled.td` + text-align: left; + padding: 5px 0 5px 15px; + vertical-align: middle; +`; + const AllowTd = styled.td` text-align: left; padding: 5px 0 5px 15px; @@ -96,6 +108,7 @@ class ProviderTable extends React.Component { constructor(props) { super(props); this.state = { + providerWithError: null, errorMessage: null, }; } @@ -107,9 +120,12 @@ class ProviderTable extends React.Component { this.props.service, provider, this.props._csrf - ) + ).then((data) => { + this.setState({ providerWithError: '' }); + }) .catch((err) => { this.setState({ + providerWithError: provider, errorMessage: RequestUtils.xhrErrorCheckHelper(err), }); }); @@ -117,72 +133,56 @@ class ProviderTable extends React.Component { render() { let providerContent = []; - if (this.state.errorMessage) { + providerContent = this.props.allProviders.map((provider) => { return ( - - - Failed to fetch template details. - - + + {provider.name} + + {this.props.provider[provider.id] === 'allow' ? ( + + + + ) : ( + + )} + + + + { + provider.id === this.state.providerWithError ? this.state.errorMessage : '' + } + + + ); - } else { - providerContent = this.props.allProviders.map((provider) => { - // if (this.props.provider[provider.id] === 'allow') { - return ( - - {provider.name} - - {this.props.provider[provider.id] === 'allow' ? ( - - - - ) : ( - - )} - - - ); - // } else if (this.props.provider[provider.id] === 'not') { - // let onAllow = this.onAllow.bind(this, provider.id); - // return ( - // - // {provider.name} - // - // - // ); - // } - // }); - }); - } + }); return ( - {this.state.errorMessage && ( -
- {this.state.errorMessage} -
- )}
ProviderStatus + { this.state.errorMessage ? + : '' + } {providerContent} diff --git a/ui/src/components/service/ServiceList.js b/ui/src/components/service/ServiceList.js index d6ce6392bbe..70018f844a6 100644 --- a/ui/src/components/service/ServiceList.js +++ b/ui/src/components/service/ServiceList.js @@ -223,7 +223,9 @@ class ServiceList extends React.Component { {addService} - + diff --git a/ui/src/components/service/ServiceRow.js b/ui/src/components/service/ServiceRow.js index e5ebe432eab..539082851a4 100644 --- a/ui/src/components/service/ServiceRow.js +++ b/ui/src/components/service/ServiceRow.js @@ -174,6 +174,7 @@ class ServiceRow extends React.Component {