diff --git a/deploy/ci/travis/run-e2e-tests.sh b/deploy/ci/travis/run-e2e-tests.sh
index b14a25cb2c..6a796b996d 100755
--- a/deploy/ci/travis/run-e2e-tests.sh
+++ b/deploy/ci/travis/run-e2e-tests.sh
@@ -91,7 +91,7 @@ fi
# Output backend log if the tests failed
if [ "${RUN_TYPE}" == "quick" ]; then
if [ $RESULT -ne 0 ]; then
- cat outputs/backend.log
+ cat src/jetstream/backend.log
fi
fi
diff --git a/protractor.conf.js b/protractor.conf.js
index 765a3fb99c..e784e474f8 100644
--- a/protractor.conf.js
+++ b/protractor.conf.js
@@ -35,8 +35,11 @@ try {
process.exit(1);
}
+// This is the maximum amount of time ALL before/after/it's must execute in
+const timeout = 40000
+
exports.config = {
- allScriptsTimeout: 30000,
+ allScriptsTimeout: timeout,
specs: [
'./src/test-e2e/**/*-e2e.spec.ts',
],
@@ -53,7 +56,7 @@ exports.config = {
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
- defaultTimeoutInterval: 30000,
+ defaultTimeoutInterval: timeout,
print: function () {}
},
params: secrets,
diff --git a/src/frontend/app/shared/components/list/list.component.html b/src/frontend/app/shared/components/list/list.component.html
index 6472c720b7..efe0070804 100644
--- a/src/frontend/app/shared/components/list/list.component.html
+++ b/src/frontend/app/shared/components/list/list.component.html
@@ -30,18 +30,18 @@
diff --git a/src/frontend/app/test-framework/spec-helper.spec.ts b/src/frontend/app/test-framework/spec-helper.spec.ts
index 8a17a567db..2142b8eddc 100644
--- a/src/frontend/app/test-framework/spec-helper.spec.ts
+++ b/src/frontend/app/test-framework/spec-helper.spec.ts
@@ -6,15 +6,15 @@ import { TestBed } from '@angular/core/testing';
* a global beforeEach so we don't have to add it to all the necessary
* spec files.
*/
-beforeEach( () => {
+beforeEach(() => {
TestBed.configureTestingModule({
- providers: [ { provide: APP_BASE_HREF, useValue: '/' } ]
+ providers: [{ provide: APP_BASE_HREF, useValue: '/' }]
});
});
/**
* Bump up the Jasmine timeout from 5 seconds
*/
-beforeAll( () => {
+beforeAll(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
diff --git a/src/jetstream/plugins/cfapppush/deploy.go b/src/jetstream/plugins/cfapppush/deploy.go
index 3cbd12a283..96fe1e73e7 100644
--- a/src/jetstream/plugins/cfapppush/deploy.go
+++ b/src/jetstream/plugins/cfapppush/deploy.go
@@ -72,9 +72,9 @@ type DeployAppMessageSender interface {
func (cfAppPush *CFAppPush) deploy(echoContext echo.Context) error {
- cnsiGUID := echoContext.Param("cnsiGUID")
- orgGUID := echoContext.Param("orgGUID")
- spaceGUID := echoContext.Param("spaceGUID")
+ cnsiGUID := echoContext.Param("cnsiGuid")
+ orgGUID := echoContext.Param("orgGuid")
+ spaceGUID := echoContext.Param("spaceGuid")
spaceName := echoContext.QueryParam("space")
orgName := echoContext.QueryParam("org")
diff --git a/src/test-e2e/application/application-delete-e2e.spec.ts b/src/test-e2e/application/application-delete-e2e.spec.ts
index ac4edb4bb9..9ccc220d6c 100644
--- a/src/test-e2e/application/application-delete-e2e.spec.ts
+++ b/src/test-e2e/application/application-delete-e2e.spec.ts
@@ -34,7 +34,8 @@ describe('Application Delete', function () {
// Delete tests for a simple app with no routes
describe('Simple App', () => {
beforeAll(() => {
- const endpointName = e2e.secrets.getDefaultCFEndpoint().name;
+ const defaultCf = e2e.secrets.getDefaultCFEndpoint();
+ const endpointName = defaultCf.name;
cfGuid = e2e.helper.getEndpointGuid(e2e.info, endpointName);
const testTime = (new Date()).toISOString();
testAppName = ApplicationE2eHelper.createApplicationName(testTime);
@@ -42,7 +43,8 @@ describe('Application Delete', function () {
cfGuid,
e2e.secrets.getDefaultCFEndpoint().testOrg,
e2e.secrets.getDefaultCFEndpoint().testSpace,
- testAppName
+ testAppName,
+ defaultCf
).then(appl => app = appl);
});
diff --git a/src/test-e2e/application/application-deploy-e2e.spec.ts b/src/test-e2e/application/application-deploy-e2e.spec.ts
index 6e45b40d95..cd12f6b290 100644
--- a/src/test-e2e/application/application-deploy-e2e.spec.ts
+++ b/src/test-e2e/application/application-deploy-e2e.spec.ts
@@ -8,6 +8,7 @@ import { SideNavigation, SideNavMenuItem } from '../po/side-nav.po';
import { ApplicationE2eHelper } from './application-e2e-helpers';
import { ApplicationSummary } from './application-summary.po';
+
const until = protractor.ExpectedConditions;
let nav: SideNavigation;
@@ -45,6 +46,7 @@ describe('Application Deploy', function () {
beforeEach(() => nav.goto(SideNavMenuItem.Applications));
it('Should deploy app from GitHub', () => {
+ const loggingPrefix = 'Application Deploy: Deploy from Github:';
expect(appWall.isActivePage()).toBeTruthy();
// Should be on deploy app modal
@@ -52,6 +54,7 @@ describe('Application Deploy', function () {
expect(deployApp.header.getTitleText()).toBe('Deploy');
// Check the steps
+ e2e.log(`${loggingPrefix} Checking Steps`);
deployApp.stepper.getStepNames().then(steps => {
expect(steps.length).toBe(4);
expect(steps[0]).toBe('Cloud Foundry');
@@ -59,6 +62,7 @@ describe('Application Deploy', function () {
expect(steps[2]).toBe('Source Config');
expect(steps[3]).toBe('Deploy');
});
+ e2e.log(`${loggingPrefix} Cf/Org/Space Step`);
expect(deployApp.stepper.getActiveStepName()).toBe('Cloud Foundry');
promise.all([
deployApp.stepper.getStepperForm().getText('cf'),
@@ -79,12 +83,15 @@ describe('Application Deploy', function () {
expect(deployApp.stepper.canNext()).toBeTruthy();
deployApp.stepper.next();
+ e2e.log(`${loggingPrefix} Source Step`);
expect(deployApp.stepper.getActiveStepName()).toBe('Source');
expect(deployApp.stepper.canNext()).toBeFalsy();
deployApp.stepper.getStepperForm().fill({ 'projectname': testApp });
deployApp.stepper.waitUntilCanNext('Next');
deployApp.stepper.next();
+
+ e2e.log(`${loggingPrefix} Source Config Step`);
expect(deployApp.stepper.getActiveStepName()).toBe('Source Config');
const commits = deployApp.getCommitList();
@@ -99,6 +106,8 @@ describe('Application Deploy', function () {
commits.selectRow(0);
expect(deployApp.stepper.canNext()).toBeTruthy();
+ e2e.log(`${loggingPrefix} Select a commit (selected)`);
+
// Turn off waiting for Angular - the web socket connection is kept open which means the tests will timeout
// waiting for angular if we don't turn off.
browser.waitForAngularEnabled(false);
@@ -106,12 +115,15 @@ describe('Application Deploy', function () {
// Press next to deploy the app
deployApp.stepper.next();
+ e2e.log(`${loggingPrefix} Deploying Step (wait)`);
// Wait until app summary button can be pressed
deployApp.stepper.waitUntilCanNext('Go to App Summary');
+ e2e.log(`${loggingPrefix} Deploying Step (after wait)`);
// Click next
deployApp.stepper.next();
+ e2e.log(`${loggingPrefix} Waiting For Application Summary Page`);
// Should be app summary
browser.wait(ApplicationSummary.detect()
.then(appSummary => {
@@ -119,7 +131,10 @@ describe('Application Deploy', function () {
appSummary.header.waitForTitleText(appName);
return appSummary.cfGuid;
})
- .then(cfGuid => applicationE2eHelper.deleteApplication(null, { appName })));
+ .then(cfGuid => {
+ e2e.log(`${loggingPrefix} Starting application delete`);
+ return applicationE2eHelper.deleteApplication(null, { appName });
+ }));
});
});
diff --git a/src/test-e2e/application/application-e2e-helpers.ts b/src/test-e2e/application/application-e2e-helpers.ts
index 82df7b56e2..7f69e3dd78 100644
--- a/src/test-e2e/application/application-e2e-helpers.ts
+++ b/src/test-e2e/application/application-e2e-helpers.ts
@@ -1,3 +1,4 @@
+import { E2EConfigCloudFoundry } from '../e2e.types';
import { browser, promise } from 'protractor';
import { IApp, IRoute, ISpace } from '../../frontend/app/core/cf-api.types';
@@ -159,12 +160,10 @@ export class ApplicationE2eHelper {
.catch(err => fail(`Failed to delete app or associated dependencies: ${err}`));
}
- createApp(cfGuid: string, orgName: string, spaceName: string, appName: string) {
+ createApp(cfGuid: string, orgName: string, spaceName: string, appName: string, endpoint: E2EConfigCloudFoundry) {
return browser.driver.wait(
- this.cfHelper.addOrgIfMissing(cfGuid, orgName)
- .then(org => {
- return this.cfHelper.fetchSpace(cfGuid, org.metadata.guid, spaceName);
- })
+ this.cfHelper.addOrgIfMissingForEndpointUsers(cfGuid, endpoint, orgName)
+ .then(org => this.cfHelper.addSpaceIfMissingForEndpointUsers(cfGuid, org.metadata.guid, org.entity.name, spaceName, endpoint))
.then(space => {
expect(space).not.toBeNull();
return promise.all([
diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts
index da5c0b414d..98860dd63f 100644
--- a/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts
+++ b/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts
@@ -1,8 +1,8 @@
import { browser } from 'protractor';
-import { ApplicationE2eHelper } from '../../application/application-e2e-helpers';
import { e2e, E2ESetup } from '../../e2e';
import { E2EConfigCloudFoundry } from '../../e2e.types';
+import { CFHelpers } from '../../helpers/cf-helpers';
import { ConsoleUserType } from '../../helpers/e2e-helpers';
import { CfOrgLevelPage } from './cf-org-level-page.po';
@@ -12,7 +12,7 @@ describe('CF - Org Level - ', () => {
let orgPage: CfOrgLevelPage;
let e2eSetup: E2ESetup;
let defaultCf: E2EConfigCloudFoundry;
- let applicationE2eHelper: ApplicationE2eHelper;
+ let cfHelper: CFHelpers;
function setup(user: ConsoleUserType) {
e2eSetup = e2e.setup(ConsoleUserType.admin)
@@ -22,7 +22,7 @@ describe('CF - Org Level - ', () => {
.connectAllEndpoints(ConsoleUserType.user)
.loginAs(user)
.getInfo();
- applicationE2eHelper = new ApplicationE2eHelper(e2eSetup);
+ cfHelper = new CFHelpers(e2eSetup);
}
function testBreadcrumb() {
@@ -43,7 +43,7 @@ describe('CF - Org Level - ', () => {
function navToPage() {
defaultCf = e2e.secrets.getDefaultCFEndpoint();
const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name);
- browser.wait(applicationE2eHelper.cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg).then((org => {
+ browser.wait(cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg).then((org => {
orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, org.metadata.guid);
orgPage.navigateTo();
orgPage.waitForPageOrChildPage();
diff --git a/src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts
new file mode 100644
index 0000000000..39b5b745b9
--- /dev/null
+++ b/src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts
@@ -0,0 +1,282 @@
+import { browser, promise } from 'protractor';
+
+import { IOrganization } from '../../../frontend/app/core/cf-api.types';
+import { APIResource } from '../../../frontend/app/store/types/api.types';
+import { e2e, E2ESetup, E2E } from '../../e2e';
+import { E2EConfigCloudFoundry } from '../../e2e.types';
+import { CFHelpers } from '../../helpers/cf-helpers';
+import { ConsoleUserType, E2EHelpers } from '../../helpers/e2e-helpers';
+import { ListComponent } from '../../po/list.po';
+import { CfOrgLevelPage } from './cf-org-level-page.po';
+
+const customOrgSpacesLabel = E2EHelpers.e2eItemPrefix + (process.env.CUSTOM_APP_LABEL || process.env.USER) + '-org-spaces-test';
+
+describe('Org Spaces List -', () => {
+
+ let cfHelper: CFHelpers;
+ let defaultCf: E2EConfigCloudFoundry;
+ let orgPage: CfOrgLevelPage;
+ const spaceList = new ListComponent();
+ let orgGuid: string;
+ let endpointGuid: string;
+
+ const timeAllowed = 60000;
+
+ function createSpaceNames(count: number): string[] {
+ const spaceNames = [];
+ for (let i = 0; i < count; i++) {
+ spaceNames.push(E2EHelpers.createCustomName(customOrgSpacesLabel + i));
+ }
+ return spaceNames;
+ }
+
+ function chainCreateSpace(org: APIResource
, spaceNames: string[]): promise.Promise {
+ return spaceNames.reduce((promiseChain, name) => {
+ return promiseChain.then(() => {
+ // Ensure there's a gap so that the 'created_at' is different
+ browser.sleep(1100);
+ return cfHelper.addSpaceIfMissingForEndpointUsers(
+ endpointGuid,
+ org.metadata.guid,
+ org.entity.name,
+ name,
+ defaultCf,
+ true);
+ });
+ }, promise.fullyResolved(''));
+ }
+
+ function concurrentCreateSpace(org: APIResource, spaceNames: string[]): promise.Promise {
+ return promise.all(spaceNames.map(name => cfHelper.addSpaceIfMissingForEndpointUsers(
+ endpointGuid,
+ org.metadata.guid,
+ org.entity.name,
+ name,
+ defaultCf,
+ true)));
+ }
+
+ function setup(orgName: string, spaceNames: string[], orderImportant: boolean) {
+ defaultCf = e2e.secrets.getDefaultCFEndpoint();
+ endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name);
+
+ browser.wait(
+ cfHelper.addOrgIfMissingForEndpointUsers(endpointGuid, defaultCf, orgName)
+ .then((org: APIResource) => {
+ orgGuid = org.metadata.guid;
+ if (!spaceNames || !spaceNames.length) {
+ return promise.fullyResolved(org);
+ }
+ // Chain the creation of the spaces to ensure there's a nice sequential 'created_at' value to be used for sort tests
+ const promises = orderImportant ?
+ chainCreateSpace(org, spaceNames) :
+ concurrentCreateSpace(org, spaceNames);
+
+ return promises.then(() => org.metadata.guid);
+ })
+ .then(navToOrgSpaces)
+ );
+ }
+
+ function navToOrgSpaces() {
+ orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, orgGuid);
+ orgPage.navigateTo();
+ orgPage.waitForPageOrChildPage();
+ orgPage.loadingIndicator.waitUntilNotShown();
+ orgPage.goToSpacesTab();
+ expect(spaceList.isTableView()).toBeFalsy();
+ }
+
+ function tearDown(orgName: string) {
+ expect(orgName).not.toBeNull();
+ browser.wait(cfHelper.deleteOrgIfExisting(endpointGuid, orgName));
+ }
+
+ beforeAll(() => {
+ const e2eSetup = e2e.setup(ConsoleUserType.admin)
+ .clearAllEndpoints()
+ .registerDefaultCloudFoundry()
+ .connectAllEndpoints(ConsoleUserType.admin)
+ .loginAs(ConsoleUserType.admin)
+ .getInfo();
+ cfHelper = new CFHelpers(e2eSetup);
+ });
+
+ describe('No Pages -', () => {
+ const orgName = E2EHelpers.createCustomName(customOrgSpacesLabel) + '-no-pages';
+ beforeAll(() => {
+ setup(orgName, [], false);
+ });
+
+ beforeEach(navToOrgSpaces);
+
+ it('Should show no entities message', () => {
+ expect(spaceList.isDisplayed()).toBeTruthy();
+ spaceList.empty.getDefault().waitUntilShown();
+ expect(spaceList.empty.getDefault().getComponent().getText()).toBe('There are no spaces');
+ expect(spaceList.cards.getCardCount()).toBe(0);
+ });
+
+ afterAll(() => tearDown(orgName));
+ });
+
+ describe('Single Page -', () => {
+ const orgName = E2EHelpers.createCustomName(customOrgSpacesLabel) + '-1-page';
+
+ let spaceNames;
+
+ function testSortBy(sortFieldName: string) {
+ const sortFieldForm = spaceList.header.getSortFieldForm();
+ sortFieldForm.fill({ 'sort-field': sortFieldName });
+
+ let expectedTitleOrder: string[];
+ spaceList.cards.getCardsMetadata().then(cards => {
+ const originalTitleOrder = cards.map(card => card.title);
+ expectedTitleOrder = new Array(originalTitleOrder.length);
+ for (let i = 0; i < originalTitleOrder.length; i++) {
+ expectedTitleOrder[originalTitleOrder.length - i - 1] = originalTitleOrder[i];
+ }
+ });
+
+ spaceList.header.toggleSortOrder();
+
+ spaceList.cards.getCardsMetadata().then(cards => {
+ const newTitleOrder = cards.map(card => card.title);
+ expect(expectedTitleOrder).toEqual(newTitleOrder);
+ });
+ }
+
+ beforeAll(() => {
+ spaceNames = createSpaceNames(3);
+ setup(orgName, spaceNames, true);
+ expect(spaceList.getTotalResults()).toBeLessThanOrEqual(9);
+ expect(spaceList.pagination.isDisplayed()).toBeFalsy();
+ }, timeAllowed);
+
+ afterAll(() => tearDown(orgName), timeAllowed);
+
+ it('sort by name', () => {
+ testSortBy('Name');
+ });
+
+ it('sort by creation', () => {
+ testSortBy('Creation');
+ });
+
+ it('text filter by existing', () => {
+ // Clear and check initial cards
+ spaceList.header.clearSearchText();
+ expect(spaceList.header.getSearchText()).toBeFalsy();
+ expect(spaceList.cards.getCardCount()).toBeGreaterThanOrEqual(spaceNames.length);
+
+ // Apply filter
+ const spaceToFind = spaceNames[2];
+ spaceList.header.setSearchText(spaceToFind);
+
+ // Check for single card
+ expect(spaceList.header.getSearchText()).toEqual(spaceToFind);
+ expect(spaceList.cards.getCardCount()).toBe(1);
+ expect(spaceList.cards.findCardByTitle(spaceToFind)).toBeDefined();
+ });
+
+ it('text filter by non-existing', () => {
+ // Clear and check initial cards
+ spaceList.header.clearSearchText();
+ expect(spaceList.header.getSearchText()).toBeFalsy();
+ expect(spaceList.cards.getCardCount()).toBeGreaterThanOrEqual(spaceNames.length);
+
+ // Apply filter
+ const spaceToNotFind = 'sdfst4654324543224 s5d4x4g5g gdg4fdg 5fdg';
+ spaceList.header.setSearchText(spaceToNotFind);
+
+ expect(spaceList.header.getSearchText()).toEqual(spaceToNotFind);
+
+ // Check for zero cards
+ expect(spaceList.cards.getCardCount()).toBe(0);
+
+ // Check for 'no spaces' message
+ spaceList.empty.getDefault().waitUntilShown();
+ expect(spaceList.empty.getDefault().getComponent().getText()).toBe('There are no spaces');
+ });
+
+ it('single page pagination settings', () => {
+ expect(spaceList.pagination.isDisplayed()).toBeFalsy();
+ });
+
+ });
+
+ describe('Multi Page -', () => {
+ const orgName = E2EHelpers.createCustomName(customOrgSpacesLabel) + '-multi-page';
+
+ let spaceNames;
+
+ beforeAll(() => {
+ spaceNames = createSpaceNames(11);
+ setup(orgName, spaceNames, false);
+ expect(spaceList.getTotalResults()).toBeGreaterThanOrEqual(spaceNames.length);
+ }, timeAllowed);
+
+ afterAll(() => tearDown(orgName), timeAllowed);
+
+ function testStartingPosition() {
+ // General expects for all tests in this section
+ expect(spaceList.getTotalResults()).toBeLessThan(80);
+ expect(spaceList.pagination.isPresent()).toBeTruthy();
+
+ expect(spaceList.cards.getCardCount()).toBe(9);
+ expect(spaceList.pagination.getPageSize()).toEqual('9');
+ expect(spaceList.pagination.getTotalResults()).toBeGreaterThan(9);
+ expect(spaceList.pagination.getTotalResults()).toBeLessThanOrEqual(18);
+
+ expect(spaceList.pagination.getNavFirstPage().getComponent().isEnabled()).toBeFalsy();
+ expect(spaceList.pagination.getNavPreviousPage().getComponent().isEnabled()).toBeFalsy();
+ expect(spaceList.pagination.getNavNextPage().getComponent().isEnabled()).toBeTruthy();
+ expect(spaceList.pagination.getNavLastPage().getComponent().isEnabled()).toBeTruthy();
+ }
+
+ beforeEach(testStartingPosition, timeAllowed);
+
+ afterEach(testStartingPosition, timeAllowed);
+
+ it('Initial Pagination Values', () => { });
+
+ it('Next and Previous Page', () => {
+ spaceList.pagination.getNavNextPage().getComponent().click();
+
+ expect(spaceList.pagination.getNavFirstPage().getComponent().isEnabled()).toBeTruthy();
+ expect(spaceList.pagination.getNavPreviousPage().getComponent().isEnabled()).toBeTruthy();
+ expect(spaceList.pagination.getNavNextPage().getComponent().isEnabled()).toBeFalsy();
+ expect(spaceList.pagination.getNavLastPage().getComponent().isEnabled()).toBeFalsy();
+
+ spaceList.pagination.getNavPreviousPage().getComponent().click();
+ });
+
+ it('Last and First Page', () => {
+ spaceList.pagination.getNavLastPage().getComponent().click();
+
+ expect(spaceList.pagination.getNavFirstPage().getComponent().isEnabled()).toBeTruthy();
+ expect(spaceList.pagination.getNavPreviousPage().getComponent().isEnabled()).toBeTruthy();
+ expect(spaceList.pagination.getNavNextPage().getComponent().isEnabled()).toBeFalsy();
+ expect(spaceList.pagination.getNavLastPage().getComponent().isEnabled()).toBeFalsy();
+
+ spaceList.pagination.getNavFirstPage().getComponent().click();
+ });
+
+ it('Change Page Size', () => {
+
+ spaceList.pagination.setPageSize('80');
+ expect(spaceList.cards.getCardCount()).toBeGreaterThan(9);
+
+ expect(spaceList.pagination.getNavFirstPage().getComponent().isEnabled()).toBeFalsy();
+ expect(spaceList.pagination.getNavPreviousPage().getComponent().isEnabled()).toBeFalsy();
+ expect(spaceList.pagination.getNavNextPage().getComponent().isEnabled()).toBeFalsy();
+ expect(spaceList.pagination.getNavLastPage().getComponent().isEnabled()).toBeFalsy();
+
+ spaceList.pagination.setPageSize('9');
+ expect(spaceList.cards.getCardCount()).toBe(9);
+
+ });
+
+ });
+
+});
diff --git a/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts b/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts
index 0e6d148374..9fc34eeb6d 100644
--- a/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts
+++ b/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts
@@ -44,11 +44,7 @@ describe('CF - Manage Organizations and Spaces', () => {
cloudFoundry.waitForPageOrChildPage();
});
- afterAll(() => {
- return cfHelper.deleteSpaceIfExisting(endpointGuid, testSpaceName).then(() =>
- cfHelper.deleteOrgIfExisting(endpointGuid, testOrgName)
- );
- });
+ afterAll(() => cfHelper.deleteOrgIfExisting(endpointGuid, testOrgName));
it('Should validate org name', () => {
const cardView = cloudFoundry.goToOrgView();
@@ -162,7 +158,7 @@ describe('CF - Manage Organizations and Spaces', () => {
space.openActionMenu().then(menu => {
menu.clickItem('Delete');
ConfirmDialogComponent.expectDialogAndConfirm('Delete', 'Delete Space');
- cardView.cards.getCardCound().then(c => {
+ cardView.cards.getCardCount().then(c => {
expect(c).toBe(0);
});
});
diff --git a/src/test-e2e/helpers/cf-helpers.ts b/src/test-e2e/helpers/cf-helpers.ts
index c162f02d58..c93430f292 100644
--- a/src/test-e2e/helpers/cf-helpers.ts
+++ b/src/test-e2e/helpers/cf-helpers.ts
@@ -1,7 +1,8 @@
import { promise } from 'protractor';
-import { IOrganization, IRoute } from '../../frontend/app/core/cf-api.types';
-import { APIResource } from '../../frontend/app/store/types/api.types';
+import { IOrganization, IRoute, ISpace } from '../../frontend/app/core/cf-api.types';
+import { APIResource, CFResponse } from '../../frontend/app/store/types/api.types';
+import { CfUser } from '../../frontend/app/store/types/user.types';
import { e2e, E2ESetup } from '../e2e';
import { E2EConfigCloudFoundry } from '../e2e.types';
import { CFRequestHelpers } from './cf-request-helpers';
@@ -12,38 +13,51 @@ export class CFHelpers {
cachedDefaultCfGuid: string;
cachedDefaultOrgGuid: string;
cachedDefaultSpaceGuid: string;
+ cachedAdminGuid: string;
+ cachedNonAdminGuid: string;
constructor(public e2eSetup: E2ESetup) {
this.cfRequestHelper = new CFRequestHelpers(e2eSetup);
}
+ private assignAdminAndUserGuids(cnsiGuid: string, endpoint: E2EConfigCloudFoundry): promise.Promise {
+ if (this.cachedAdminGuid && this.cachedNonAdminGuid) {
+ return promise.fullyResolved({});
+ }
+ return this.fetchUsers(cnsiGuid).then(users => {
+ const testUser = this.findUser(users, endpoint.creds.nonAdmin.username);
+ const testAdminUser = this.findUser(users, endpoint.creds.admin.username);
+ expect(testUser).toBeDefined();
+ expect(testAdminUser).toBeDefined();
+ this.cachedNonAdminGuid = testUser.metadata.guid;
+ this.cachedAdminGuid = testAdminUser.metadata.guid;
+ });
+ }
+
addOrgIfMissingForEndpointUsers(
guid: string,
endpoint: E2EConfigCloudFoundry,
testOrgName: string
): promise.Promise> {
- let testAdminUser, testUser;
- return this.fetchUsers(guid).then(users => {
- testUser = this.findUser(users, endpoint.creds.nonAdmin.username);
- testAdminUser = this.findUser(users, endpoint.creds.admin.username);
- expect(testUser).toBeDefined();
- expect(testAdminUser).toBeDefined();
- return this.addOrgIfMissing(guid, testOrgName, testAdminUser.metadata.guid, testUser.metadata.guid);
+ return this.assignAdminAndUserGuids(guid, endpoint).then(() => {
+ expect(this.cachedNonAdminGuid).not.toBeNull();
+ expect(this.cachedAdminGuid).not.toBeNull();
+ return this.addOrgIfMissing(guid, testOrgName, this.cachedAdminGuid, this.cachedNonAdminGuid);
});
}
- private findUser(users: any, name: string) {
+ private findUser(users: any, name: string): APIResource {
return users.find(user => user && user.entity && user.entity.username === name);
}
- addOrgIfMissing(cnsiGuid, orgName, adminGuid?: string, userGuid?: string): promise.Promise> {
+ addOrgIfMissing(cnsiGuid, orgName, adminGuid, userGuid): promise.Promise> {
let added;
- return this.cfRequestHelper.sendCfGet(cnsiGuid, 'organizations?q=name IN ' + orgName).then(json => {
- if (json.total_results === 0) {
+ return this.fetchOrg(cnsiGuid, orgName).then(org => {
+ if (!org) {
added = true;
return this.cfRequestHelper.sendCfPost>(cnsiGuid, 'organizations', { name: orgName });
}
- return json.resources[0];
+ return org;
}).then(newOrg => {
if (!added || !adminGuid || !userGuid) {
// No need to mess around with permissions, it exists already.
@@ -60,27 +74,28 @@ export class CFHelpers {
});
}
- addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, adminGuid, userGuid) {
- return this.cfRequestHelper.sendCfGet(cnsiGuid,
- 'spaces?inline-relations-depth=1&include-relations=organization&q=name IN ' + spaceName)
- .then(function (json) {
- let add = false;
- if (json.total_results === 0) {
- add = true;
- } else if (json.total_results > 0) {
- add = !!json.resources.find(r => {
- return r && r.entity && r.entity.organization && r.entity.organization.entity && r.entity.organization.entity.name === orgName;
- });
- }
- if (add) {
- return this.cfRequestHelper.sendCfPost(cnsiGuid, 'pp/v1/proxy/v2/spaces',
- {
- name: spaceName,
- manager_guids: [adminGuid],
- developer_guids: [userGuid, adminGuid],
- organization_guid: orgGuid
- });
- }
+ addSpaceIfMissingForEndpointUsers(
+ cnsiGuid,
+ orgGuid,
+ orgName,
+ spaceName,
+ endpoint: E2EConfigCloudFoundry,
+ skipExistsCheck = false,
+ ): promise.Promise> {
+ return this.assignAdminAndUserGuids(cnsiGuid, endpoint).then(() => {
+ expect(this.cachedNonAdminGuid).not.toBeNull();
+ return skipExistsCheck ?
+ this.baseAddSpace(cnsiGuid, orgGuid, orgName, spaceName, this.cachedNonAdminGuid) :
+ this.addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, this.cachedNonAdminGuid);
+
+ });
+ }
+
+ addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, userGuid): promise.Promise> {
+ const that = this;
+ return this.fetchSpace(cnsiGuid, orgGuid, spaceName)
+ .then(function (space) {
+ return space ? space : that.baseAddSpace(cnsiGuid, orgGuid, orgName, spaceName, userGuid);
});
}
@@ -91,23 +106,17 @@ export class CFHelpers {
}
deleteOrgIfExisting(cnsiGuid: string, orgName: string) {
- return this.cfRequestHelper.sendCfGet(cnsiGuid, 'organizations?q=name IN ' + orgName).then(json => {
- if (json.total_results > 0) {
- const org = json.resources[0];
- if (org) {
- return this.cfRequestHelper.sendCfDelete(cnsiGuid, 'organizations/' + org.metadata.guid);
- }
+ return this.fetchOrg(cnsiGuid, orgName).then(org => {
+ if (org) {
+ return this.cfRequestHelper.sendCfDelete(cnsiGuid, 'organizations/' + org.metadata.guid + '?recursive=true&async=false');
}
});
}
- deleteSpaceIfExisting(cnsiGuid: string, spaceName: string) {
- return this.cfRequestHelper.sendCfGet(cnsiGuid, 'spaces?q=name IN ' + spaceName).then(json => {
- if (json.total_results > 0) {
- const space = json.resources[0];
- if (space) {
- return this.cfRequestHelper.sendCfDelete(cnsiGuid, 'spaces/' + space.metadata.guid);
- }
+ deleteSpaceIfExisting(cnsiGuid: string, orgGuid: string, spaceName: string) {
+ return this.fetchSpace(cnsiGuid, orgGuid, spaceName).then(space => {
+ if (space) {
+ return this.cfRequestHelper.sendCfDelete(cnsiGuid, 'spaces/' + space.metadata.guid);
}
});
}
@@ -125,9 +134,6 @@ export class CFHelpers {
return org;
}
return null;
- }).catch(err => {
- e2e.log(`Failed to fetch organisation with name '${orgName}' from endpoint ${cnsiGuid}`);
- throw new Error(err);
});
}
@@ -163,6 +169,17 @@ export class CFHelpers {
return this.cfRequestHelper.sendCfDelete(cnsiGuid, 'apps/' + appGuid);
}
+ baseAddSpace(cnsiGuid, orgGuid, orgName, spaceName, userGuid): promise.Promise> {
+ const cfRequestHelper = this.cfRequestHelper;
+ return cfRequestHelper.sendCfPost>(cnsiGuid, 'spaces',
+ {
+ name: spaceName,
+ manager_guids: [],
+ developer_guids: [userGuid],
+ organization_guid: orgGuid
+ });
+ }
+
fetchAppRoutes(cnsiGuid: string, appGuid: string): promise.Promise[]> {
return this.cfRequestHelper.sendCfGet(cnsiGuid, `apps/${appGuid}/routes`).then(res => res.resources);
}
diff --git a/src/test-e2e/po/form.po.ts b/src/test-e2e/po/form.po.ts
index a1b263511e..dab7232428 100644
--- a/src/test-e2e/po/form.po.ts
+++ b/src/test-e2e/po/form.po.ts
@@ -24,6 +24,7 @@ export interface FormItem {
tag: string;
valid: string;
error: string;
+ id: string;
}
// Page Object for a form field
@@ -96,17 +97,20 @@ export class FormComponent extends Component {
clear: elm.clear,
click: elm.click,
tag: elm.getTagName(),
+ id: elm.getAttribute('id')
};
}
// Get the form field with the specified name or formcontrolname
getField(ctrlName: string): ElementFinder {
const fields = this.getFields().filter((elm => {
- return elm.getAttribute('name').then(name => {
- return elm.getAttribute('formcontrolname').then(formcontrolname => {
- const nameAtt = name || formcontrolname;
- return nameAtt.toLowerCase() === ctrlName;
- });
+ return promise.all([
+ elm.getAttribute('name'),
+ elm.getAttribute('formcontrolname'),
+ elm.getAttribute('id')
+ ]).then(([name, formcontrolname, id]) => {
+ const nameAtt = name || formcontrolname || id;
+ return nameAtt.toLowerCase() === ctrlName;
});
}));
expect(fields.count()).toBe(1);
@@ -151,7 +155,7 @@ export class FormComponent extends Component {
return this.getFieldsMapped().then(items => {
const form = {};
items.forEach((item: FormItem) => {
- const id = item.name || item.formControlName;
+ const id = item.name || item.formControlName || item.id;
form[id.toLowerCase()] = item;
});
return form;
diff --git a/src/test-e2e/po/list.po.ts b/src/test-e2e/po/list.po.ts
index 1f80c29f7b..ffaf384d49 100644
--- a/src/test-e2e/po/list.po.ts
+++ b/src/test-e2e/po/list.po.ts
@@ -1,8 +1,9 @@
-import { by, element, promise, browser, protractor, Key } from 'protractor';
+import { browser, by, element, Key, promise, protractor } from 'protractor';
import { ElementArrayFinder, ElementFinder } from 'protractor/built';
+
import { Component } from './component.po';
+import { FormComponent } from './form.po';
import { MetaCard } from './meta-card.po';
-import { PaginatorComponent } from './paginator.po';
const until = protractor.ExpectedConditions;
@@ -73,7 +74,7 @@ export class ListCardComponent extends Component {
super(locator);
}
- getCardCound() {
+ getCardCount() {
const noRows = this.locator.all(by.css('.no-rows'));
return noRows.count().then(rows => {
return rows === 1 ? 0 : this.getCards().count();
@@ -112,21 +113,17 @@ export class ListCardComponent extends Component {
export class ListHeaderComponent extends Component {
constructor(locator: ElementFinder) {
- super(locator);
- }
-
- getListHeader(): ElementFinder {
- return this.locator.element(by.css('.list-component__header'));
+ super(locator.element(by.css('.list-component__header')));
}
getFilterFormField(): ElementArrayFinder {
- return this.getListHeader()
+ return this.locator
.element(by.css('.list-component__header__left--multi-filters'))
.all(by.tagName('mat-form-field'));
}
getRightHeaderSection(): ElementFinder {
- return this.getListHeader().element(by.css('.list-component__header__right'));
+ return this.locator.element(by.css('.list-component__header__right'));
}
getSearchInputField(): ElementFinder {
@@ -136,8 +133,16 @@ export class ListHeaderComponent extends Component {
setSearchText(text: string): promise.Promise {
const searchField = this.getSearchInputField();
searchField.click();
- searchField.sendKeys(text);
- return searchField.sendKeys(Key.RETURN);
+ searchField.clear();
+ return searchField.sendKeys(text);
+ }
+
+ clearSearchText() {
+ const searchField = this.getSearchInputField();
+ searchField.click();
+ searchField.clear();
+ searchField.sendKeys('a');
+ searchField.sendKeys(Key.BACK_SPACE);
}
getSearchText(): promise.Promise {
@@ -169,6 +174,78 @@ export class ListHeaderComponent extends Component {
return this.getRightHeaderSection().element(by.css('#list-card-toggle'));
}
+ private findSortSection(): ElementFinder {
+ return this.locator.element(by.css('.list-component__header__right .sort'));
+ }
+
+ getSortFieldForm(): FormComponent {
+ return new FormComponent(this.findSortSection());
+ }
+
+ toggleSortOrder() {
+ this.findSortSection().element(by.css('button')).click();
+ }
+
+}
+
+export class ListPaginationComponent extends Component {
+ constructor(listComponent: ElementFinder) {
+ super(listComponent.element(by.tagName('.list-component__paginator')));
+ }
+
+ getTotalResults() {
+ return this.locator.element(by.css('.mat-paginator-range-label')).getText().then(label => {
+ const index = label.indexOf('of ');
+ if (index > 0) {
+ const value = label.substr(index + 3).trim();
+ return parseInt(value, 10);
+ }
+ return -1;
+ });
+ }
+
+ private findPageSizeSection(): ElementFinder {
+ return this.locator.element(by.css('.mat-paginator-page-size'));
+ }
+
+ getPageSize(): promise.Promise {
+ return this.getPageSizeForm().getText('mat-select-1');
+ }
+
+ setPageSize(pageSize): promise.Promise {
+ return this.getPageSizeForm().fill({ 'mat-select-1': pageSize });
+ }
+
+ getPageSizeForm(): FormComponent {
+ return new FormComponent(this.findPageSizeSection());
+ }
+
+ getNavFirstPage(): Component {
+ return new Component(this.locator.element(by.css('.mat-paginator-navigation-first')));
+ }
+
+ getNavLastPage(): Component {
+ return new Component(this.locator.element(by.css('.mat-paginator-navigation-last')));
+ }
+
+ getNavPreviousPage(): Component {
+ return new Component(this.locator.element(by.css('.mat-paginator-navigation-previous')));
+ }
+
+ getNavNextPage(): Component {
+ return new Component(this.locator.element(by.css('.mat-paginator-navigation-next')));
+ }
+
+}
+
+export class ListEmptyComponent extends Component {
+ constructor(listComponent: ElementFinder) {
+ super(listComponent.element(by.css('.list-component__no-entries')));
+ }
+
+ getDefault(): Component {
+ return new Component(element(by.css('.list-component__default-no-entries')));
+ }
}
/**
* Page Object for the List component
@@ -181,11 +258,17 @@ export class ListComponent extends Component {
public header: ListHeaderComponent;
+ public pagination: ListPaginationComponent;
+
+ public empty: ListEmptyComponent;
+
constructor(locator: ElementFinder = element(by.tagName('app-list'))) {
super(locator);
this.table = new ListTableComponent(locator);
this.cards = new ListCardComponent(locator);
this.header = new ListHeaderComponent(locator);
+ this.pagination = new ListPaginationComponent(locator);
+ this.empty = new ListEmptyComponent(locator);
}
isTableView(): promise.Promise {
@@ -205,10 +288,10 @@ export class ListComponent extends Component {
}
getTotalResults() {
- const paginator = new PaginatorComponent();
- return paginator.isDisplayed().then(havePaginator => {
+ // const paginator = new PaginatorComponent();
+ return this.pagination.isDisplayed().then(havePaginator => {
if (havePaginator) {
- return paginator.getTotalResults();
+ return this.pagination.getTotalResults();
}
return this.isCardsView().then(haveCardsView => {
if (haveCardsView) {
diff --git a/src/test-e2e/po/paginator.po.ts b/src/test-e2e/po/paginator.po.ts
deleted file mode 100644
index 0bc9502cd0..0000000000
--- a/src/test-e2e/po/paginator.po.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-import { protractor, ElementFinder, ElementArrayFinder } from 'protractor/built';
-import { browser, element, by, promise } from 'protractor';
-import { Component } from './component.po';
-
-/**
- * Page Object for paginator component
- */
-export class PaginatorComponent extends Component {
-
- constructor() {
- super(element(by.css('.mat-paginator')));
- }
-
- getTotalResults() {
- return this.locator.element(by.css('.mat-paginator-range-label')).getText().then(label => {
- const index = label.indexOf('of ');
- if (index > 0) {
- const value = label.substr(index + 3).trim();
- return parseInt(value, 10);
- }
- return -1;
- });
- }
-
-}