diff --git a/src/frontend/app/shared/components/stratos-title/stratos-title.component.html b/src/frontend/app/shared/components/stratos-title/stratos-title.component.html
index 9889020327..98052af571 100644
--- a/src/frontend/app/shared/components/stratos-title/stratos-title.component.html
+++ b/src/frontend/app/shared/components/stratos-title/stratos-title.component.html
@@ -1,4 +1,4 @@
-
+
diff --git a/src/frontend/app/shared/components/stratos-title/stratos-title.component.scss b/src/frontend/app/shared/components/stratos-title/stratos-title.component.scss
index 030f10ffff..be2b80bd6e 100644
--- a/src/frontend/app/shared/components/stratos-title/stratos-title.component.scss
+++ b/src/frontend/app/shared/components/stratos-title/stratos-title.component.scss
@@ -9,6 +9,7 @@
width: 240px;
}
&__header {
+ display: none;
font-size: 28px;
font-weight: 500;
letter-spacing: 2px;
diff --git a/src/frontend/app/shared/components/stratos-title/stratos-title.component.ts b/src/frontend/app/shared/components/stratos-title/stratos-title.component.ts
index 5d1fff8cb3..ea9bc48f49 100644
--- a/src/frontend/app/shared/components/stratos-title/stratos-title.component.ts
+++ b/src/frontend/app/shared/components/stratos-title/stratos-title.component.ts
@@ -1,15 +1,8 @@
-import { Component, OnInit } from '@angular/core';
+import { Component } from '@angular/core';
@Component({
selector: 'app-stratos-title',
templateUrl: './stratos-title.component.html',
styleUrls: ['./stratos-title.component.scss']
})
-export class StratosTitleComponent implements OnInit {
-
- constructor() { }
-
- ngOnInit() {
- }
-
-}
+export class StratosTitleComponent { }
diff --git a/src/frontend/app/shared/data-services/cf-user.service.ts b/src/frontend/app/shared/data-services/cf-user.service.ts
index 9d6118bcf8..20fd4b073c 100644
--- a/src/frontend/app/shared/data-services/cf-user.service.ts
+++ b/src/frontend/app/shared/data-services/cf-user.service.ts
@@ -193,13 +193,20 @@ export class CfUserService {
}
// Check space roles
- if (this.populatedArray(user.audited_spaces) ||
- this.populatedArray(user.managed_spaces) ||
- this.populatedArray(user.spaces)) {
- return true;
- }
+ return this.hasSpaceRolesInOrg(user, orgGuid);
+ }
- return false;
+ private filterByOrg(orgGuid: string, array?: Array
>): Array> {
+ return array ? array.filter(space => space.entity.organization_guid === orgGuid) : null;
+ }
+
+ /**
+ * Helper to determine if user has space roles in an organization
+ */
+ hasSpaceRolesInOrg(user: CfUser, orgGuid: string): boolean {
+ return this.populatedArray(this.filterByOrg(orgGuid, user.audited_spaces)) ||
+ this.populatedArray(this.filterByOrg(orgGuid, user.managed_spaces)) ||
+ this.populatedArray(this.filterByOrg(orgGuid, user.spaces));
}
getUserRoleInOrg = (
diff --git a/src/frontend/app/store/helpers/entity-relations/entity-relations.ts b/src/frontend/app/store/helpers/entity-relations/entity-relations.ts
index 431c451239..5dcd8a0eda 100644
--- a/src/frontend/app/store/helpers/entity-relations/entity-relations.ts
+++ b/src/frontend/app/store/helpers/entity-relations/entity-relations.ts
@@ -130,15 +130,15 @@ function createEntityWatcher(store, paramAction, guid: string): Observable;
- const paramAction = createAction(config);
+ const paramAction = action || createAction(config);
// We've got the value already, ensure we create a pagination section for them
let response: NormalizedResponse;
const guids = childEntitiesAsGuids(childEntitiesAsArray);
- const safeEewEntities = newEntities || {};
- const entities = pick(safeEewEntities[childRelation.entityKey], guids as [string]) ||
+ const safeEntities = newEntities || {};
+ const entities = pick(safeEntities[childRelation.entityKey], guids as [string]) ||
pick(allEntities[childRelation.entityKey], guids as [string]);
response = {
entities: {
@@ -511,10 +511,10 @@ export function populatePaginationFromParent(store: Store, action: Pag
// Yes? Let's create the action that will populate the pagination section with the value
const config: HandleRelationsConfig = {
store,
- action: null,
+ action,
allEntities,
allPagination: {},
- newEntities: {},
+ newEntities: entity.entity[paramName],
apiResponse: null,
parentEntities: null,
entities: entity.entity[paramName],
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/frontend/assets/stratos-logo.png b/src/frontend/assets/stratos-logo.png
new file mode 100755
index 0000000000..ebfb800741
Binary files /dev/null and b/src/frontend/assets/stratos-logo.png differ
diff --git a/src/jetstream/main.go b/src/jetstream/main.go
index 8b9406e0ae..615d985fa1 100644
--- a/src/jetstream/main.go
+++ b/src/jetstream/main.go
@@ -746,7 +746,6 @@ func (p *portalProxy) registerRoutes(e *echo.Echo, addSetupMiddleware *setupMidd
if err == nil {
routePlugin.AddAdminGroupRoutes(adminGroup)
}
-
}
adminGroup.POST("/unregister", p.unregisterCluster)
@@ -757,12 +756,10 @@ func (p *portalProxy) registerRoutes(e *echo.Echo, addSetupMiddleware *setupMidd
e.Use(p.setStaticCacheContentMiddleware)
log.Debug("Add URL Check Middleware")
e.Use(p.urlCheckMiddleware)
- e.Use(middleware.Gzip())
- e.Static("/", staticDir)
+ e.Group("", middleware.Gzip()).Static("/", staticDir)
e.SetHTTPErrorHandler(getUICustomHTTPErrorHandler(staticDir, e.DefaultHTTPErrorHandler))
log.Info("Serving static UI resources")
}
-
}
// Custom error handler to let Angular app handle application URLs (catches non-backend 404 errors)
diff --git a/src/jetstream/plugins/cfapppush/app-repository.go b/src/jetstream/plugins/cfapppush/app-repository.go
index de42818731..f75e5749fe 100644
--- a/src/jetstream/plugins/cfapppush/app-repository.go
+++ b/src/jetstream/plugins/cfapppush/app-repository.go
@@ -7,12 +7,14 @@ import (
"github.com/gorilla/websocket"
)
+// RepositoryIntercept allows us to intercept application creation within the push process
type RepositoryIntercept struct {
target applications.Repository
msgSender DeployAppMessageSender
clientWebsocket *websocket.Conn
}
+// NewRepositoryIntercept creates a new RepositoryIntercept based on the supplied parameters
func NewRepositoryIntercept(target applications.Repository, msgSender DeployAppMessageSender, clientWebsocket *websocket.Conn) (repo RepositoryIntercept) {
repo.target = target
repo.msgSender = msgSender
@@ -24,6 +26,7 @@ func (repo RepositoryIntercept) sendAppData(app models.Application) {
repo.msgSender.SendEvent(repo.clientWebsocket, APP_GUID_NOTIFY, app.GUID)
}
+// Create proxies the Create method from models.Application, notifying the application
func (repo RepositoryIntercept) Create(params models.AppParams) (models.Application, error) {
app, err := repo.target.Create(params)
if err == nil {
@@ -32,10 +35,12 @@ func (repo RepositoryIntercept) Create(params models.AppParams) (models.Applicat
return app, err
}
+// GetApp proxies the GetApp method from models.Application
func (repo RepositoryIntercept) GetApp(appGUID string) (app models.Application, apiErr error) {
return repo.target.GetApp(appGUID)
}
+// Read proxies the Read method from models.Application, notifying the application
func (repo RepositoryIntercept) Read(name string) (app models.Application, apiErr error) {
app, err := repo.target.Read(name)
if err == nil {
@@ -44,22 +49,27 @@ func (repo RepositoryIntercept) Read(name string) (app models.Application, apiEr
return app, err
}
+// ReadFromSpace proxies the ReadFromSpace method from models.Application
func (repo RepositoryIntercept) ReadFromSpace(name string, spaceGUID string) (app models.Application, apiErr error) {
return repo.target.ReadFromSpace(name, spaceGUID)
}
+// Update proxies the Update method from models.Application
func (repo RepositoryIntercept) Update(appGUID string, params models.AppParams) (updatedApp models.Application, apiErr error) {
return repo.target.Update(appGUID, params)
}
+// Delete proxies the Delete method from models.Application
func (repo RepositoryIntercept) Delete(appGUID string) (apiErr error) {
return repo.target.Delete(appGUID)
}
+// ReadEnv proxies the ReadEnv method from models.Application
func (repo RepositoryIntercept) ReadEnv(guid string) (*models.Environment, error) {
return repo.target.ReadEnv(guid)
}
+// CreateRestageRequest proxies the CreateRestageRequest method from models.Application
func (repo RepositoryIntercept) CreateRestageRequest(guid string) error {
return repo.target.CreateRestageRequest(guid)
}
diff --git a/src/jetstream/plugins/cfapppush/deploy.go b/src/jetstream/plugins/cfapppush/deploy.go
index 11f5c90c48..96fe1e73e7 100644
--- a/src/jetstream/plugins/cfapppush/deploy.go
+++ b/src/jetstream/plugins/cfapppush/deploy.go
@@ -65,7 +65,7 @@ const (
stratosProjectKey = "STRATOS_PROJECT"
)
-// Interface for sending a message over a web socket
+// DeployAppMessageSender is the interface for sending a message over a web socket
type DeployAppMessageSender interface {
SendEvent(clientWebSocket *websocket.Conn, event MessageType, data string)
}
@@ -73,8 +73,8 @@ type DeployAppMessageSender interface {
func (cfAppPush *CFAppPush) deploy(echoContext echo.Context) error {
cnsiGUID := echoContext.Param("cnsiGuid")
- orgGuid := echoContext.Param("orgGuid")
- spaceGuid := echoContext.Param("spaceGuid")
+ orgGUID := echoContext.Param("orgGuid")
+ spaceGUID := echoContext.Param("spaceGuid")
spaceName := echoContext.QueryParam("space")
orgName := echoContext.QueryParam("org")
@@ -115,7 +115,7 @@ func (cfAppPush *CFAppPush) deploy(echoContext echo.Context) error {
case SOURCE_FOLDER:
sourceEnvVarMetadata, appDir, err = getFolderSource(clientWebSocket, tempDir, msg)
case SOURCE_GITURL:
- sourceEnvVarMetadata, appDir, err = getGitUrlSource(clientWebSocket, tempDir, msg)
+ sourceEnvVarMetadata, appDir, err = getGitURLSource(clientWebSocket, tempDir, msg)
default:
err = errors.New("Unsupported source type; don't know how to get the source for the application")
}
@@ -143,7 +143,7 @@ func (cfAppPush *CFAppPush) deploy(echoContext echo.Context) error {
socketWriter := &SocketWriter{
clientWebSocket: clientWebSocket,
}
- pushConfig, err := cfAppPush.getConfigData(echoContext, cnsiGUID, orgGuid, spaceGuid, spaceName, orgName, clientWebSocket)
+ pushConfig, err := cfAppPush.getConfigData(echoContext, cnsiGUID, orgGUID, spaceGUID, spaceName, orgName, clientWebSocket)
if err != nil {
log.Warnf("Failed to initialise config due to error %+v", err)
return err
@@ -306,8 +306,8 @@ func getFolderSource(clientWebSocket *websocket.Conn, tempDir string, msg Socket
DeploySource: info,
}
- marshalledJson, _ := json.Marshal(stratosProject)
- return string(marshalledJson), tempDir, nil
+ marshalledJSON, _ := json.Marshal(stratosProject)
+ return string(marshalledJSON), tempDir, nil
}
// Check the suffix of the file name and return an archiver that can handle that file type
@@ -355,11 +355,11 @@ func getGitHubSource(clientWebSocket *websocket.Conn, tempDir string, msg Socket
DeploySource: info,
}
- marshalledJson, _ := json.Marshal(stratosProject)
- return string(marshalledJson), tempDir, nil
+ marshalledJSON, _ := json.Marshal(stratosProject)
+ return string(marshalledJSON), tempDir, nil
}
-func getGitUrlSource(clientWebSocket *websocket.Conn, tempDir string, msg SocketMessage) (string, string, error) {
+func getGitURLSource(clientWebSocket *websocket.Conn, tempDir string, msg SocketMessage) (string, string, error) {
var (
err error
@@ -391,8 +391,8 @@ func getGitUrlSource(clientWebSocket *websocket.Conn, tempDir string, msg Socket
DeploySource: info,
}
- marshalledJson, _ := json.Marshal(stratosProject)
- return string(marshalledJson), tempDir, nil
+ marshalledJSON, _ := json.Marshal(stratosProject)
+ return string(marshalledJSON), tempDir, nil
}
func getMarshalledSocketMessage(data string, messageType MessageType) ([]byte, error) {
@@ -402,30 +402,29 @@ func getMarshalledSocketMessage(data string, messageType MessageType) ([]byte, e
Timestamp: time.Now().Unix(),
Type: messageType,
}
- marshalledJson, err := json.Marshal(messageStruct)
- return marshalledJson, err
-
+ marshalledJSON, err := json.Marshal(messageStruct)
+ return marshalledJSON, err
}
-func (cfAppPush *CFAppPush) getConfigData(echoContext echo.Context, cnsiGuid string, orgGuid string, spaceGuid string, spaceName string, orgName string, clientWebSocket *websocket.Conn) (*pushapp.CFPushAppConfig, error) {
+func (cfAppPush *CFAppPush) getConfigData(echoContext echo.Context, cnsiGUID string, orgGUID string, spaceGUID string, spaceName string, orgName string, clientWebSocket *websocket.Conn) (*pushapp.CFPushAppConfig, error) {
- cnsiRecord, err := cfAppPush.portalProxy.GetCNSIRecord(cnsiGuid)
+ cnsiRecord, err := cfAppPush.portalProxy.GetCNSIRecord(cnsiGUID)
if err != nil {
- log.Warnf("Failed to retrieve record for CNSI %s, error is %+v", cnsiGuid, err)
+ log.Warnf("Failed to retrieve record for CNSI %s, error is %+v", cnsiGUID, err)
sendErrorMessage(clientWebSocket, err, CLOSE_NO_CNSI)
return nil, err
}
- userId, err := cfAppPush.portalProxy.GetSessionStringValue(echoContext, "user_id")
+ userID, err := cfAppPush.portalProxy.GetSessionStringValue(echoContext, "user_id")
if err != nil {
log.Warnf("Failed to retrieve session user")
sendErrorMessage(clientWebSocket, err, CLOSE_NO_SESSION)
return nil, err
}
- cnsiTokenRecord, found := cfAppPush.portalProxy.GetCNSITokenRecord(cnsiGuid, userId)
+ cnsiTokenRecord, found := cfAppPush.portalProxy.GetCNSITokenRecord(cnsiGUID, userID)
if !found {
- log.Warnf("Failed to retrieve record for CNSI %s", cnsiGuid)
+ log.Warnf("Failed to retrieve record for CNSI %s", cnsiGUID)
sendErrorMessage(clientWebSocket, err, CLOSE_NO_CNSI_USERTOKEN)
return nil, errors.New("Failed to find token record")
}
@@ -439,9 +438,9 @@ func (cfAppPush *CFAppPush) getConfigData(echoContext echo.Context, cnsiGuid str
SkipSSLValidation: cnsiRecord.SkipSSLValidation,
AuthToken: cnsiTokenRecord.AuthToken,
RefreshToken: cnsiTokenRecord.RefreshToken,
- OrgGUID: orgGuid,
+ OrgGUID: orgGUID,
OrgName: orgName,
- SpaceGUID: spaceGuid,
+ SpaceGUID: spaceGUID,
SpaceName: spaceName,
}
@@ -556,8 +555,8 @@ func sendManifest(manifest Applications, clientWebSocket *websocket.Conn) error
if err != nil {
return err
}
- manifestJson := string(manifestBytes)
- message, _ := getMarshalledSocketMessage(manifestJson, MANIFEST)
+ manifestJSON := string(manifestBytes)
+ message, _ := getMarshalledSocketMessage(manifestJSON, MANIFEST)
clientWebSocket.WriteMessage(websocket.TextMessage, message)
return nil
@@ -573,6 +572,7 @@ func sendEvent(clientWebSocket *websocket.Conn, event MessageType) {
clientWebSocket.WriteMessage(websocket.TextMessage, msg)
}
+// SendEvent sends a message over the web socket
func (cfAppPush *CFAppPush) SendEvent(clientWebSocket *websocket.Conn, event MessageType, data string) {
msg, _ := getMarshalledSocketMessage(data, event)
clientWebSocket.WriteMessage(websocket.TextMessage, msg)
diff --git a/src/jetstream/plugins/cfapppush/main.go b/src/jetstream/plugins/cfapppush/main.go
index b31cf2500b..605db754c9 100644
--- a/src/jetstream/plugins/cfapppush/main.go
+++ b/src/jetstream/plugins/cfapppush/main.go
@@ -8,38 +8,44 @@ import (
"github.com/labstack/echo"
)
+// CFAppPush is a plugin to allow applications to be pushed to Cloud Foundry from Stratos
type CFAppPush struct {
portalProxy interfaces.PortalProxy
cfPush pushapp.CFPush
}
+// Init creates a new CFAppPush
func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) {
return &CFAppPush{portalProxy: portalProxy}, nil
}
+// GetMiddlewarePlugin gets the middleware plugin for this plugin
func (cfAppPush *CFAppPush) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) {
- return nil, errors.New("Not implemented!")
-
+ return nil, errors.New("Not implemented")
}
+// GetEndpointPlugin gets the endpoint plugin for this plugin
func (cfAppPush *CFAppPush) GetEndpointPlugin() (interfaces.EndpointPlugin, error) {
- return nil, errors.New("Not implemented!")
+ return nil, errors.New("Not implemented")
}
+// GetRoutePlugin gets the route plugin for this plugin
func (cfAppPush *CFAppPush) GetRoutePlugin() (interfaces.RoutePlugin, error) {
return cfAppPush, nil
-
}
+// AddAdminGroupRoutes adds the admin routes for this plugin to the Echo server
func (cfAppPush *CFAppPush) AddAdminGroupRoutes(echoGroup *echo.Group) {
// no-op
}
+// AddSessionGroupRoutes adds the session routes for this plugin to the Echo server
func (cfAppPush *CFAppPush) AddSessionGroupRoutes(echoGroup *echo.Group) {
// Deploy Endpoint
echoGroup.GET("/:cnsiGuid/:orgGuid/:spaceGuid/deploy", cfAppPush.deploy)
}
+// Init performs plugin initialization
func (cfAppPush *CFAppPush) Init() error {
return nil
}
diff --git a/src/jetstream/plugins/cfapppush/pushapp/pushapp.go b/src/jetstream/plugins/cfapppush/pushapp/pushapp.go
index ec6caf3de7..511fdb8709 100644
--- a/src/jetstream/plugins/cfapppush/pushapp/pushapp.go
+++ b/src/jetstream/plugins/cfapppush/pushapp/pushapp.go
@@ -34,12 +34,14 @@ import (
"code.cloudfoundry.org/cli/cf/flags"
)
+// CFPushApp abstracts the push functionality form the CLI library
type CFPushApp struct {
pushCommand *application.Push
flagContext flags.FlagContext
deps commandregistry.Dependency
}
+// CFPushAppConfig is the configuration used
type CFPushAppConfig struct {
AuthorizationEndpoint string
CFClient string
@@ -74,6 +76,8 @@ type CFPush interface {
GetDeps() commandregistry.Dependency
PatchApplicationRepository(repo applications.Repository)
}
+
+// PushError is the return error type from pushing
type PushError struct {
error
Type ErrorType
@@ -84,6 +88,7 @@ func (p *PushError) Error() string {
return fmt.Sprintf("Failed due to: %s", p.Err)
}
+// Constructor returns a CFPush based on the supplied config
func Constructor(config *CFPushAppConfig) CFPush {
pushCmd := &application.Push{}
@@ -210,6 +215,7 @@ func initialiseDependency(writer io.Writer, logger trace.Printer, envDialTimeout
}
+// Init initializes the push operation with the specified application directory and manifest path
func (c *CFPushApp) Init(appDir string, manifestPath string) error {
err := c.flagContext.Parse("-p", appDir, "-f", manifestPath)
@@ -219,15 +225,17 @@ func (c *CFPushApp) Init(appDir string, manifestPath string) error {
return nil
}
-// To install watcher
+// GetDeps is used to install watcher
func (c *CFPushApp) GetDeps() commandregistry.Dependency {
return c.deps
}
+// PatchApplicationRepository patches the repository locator so we can determine when the app has been created during push
func (c *CFPushApp) PatchApplicationRepository(appRepo applications.Repository) {
c.deps.RepoLocator = c.deps.RepoLocator.SetApplicationRepository(appRepo)
}
+// Push starts the actual push process
func (c *CFPushApp) Push() error {
c.pushCommand.SetDependency(c.deps, false)
diff --git a/src/jetstream/plugins/cfappssh/app_ssh.go b/src/jetstream/plugins/cfappssh/app_ssh.go
index b9e0a4ffa7..4549e7c2ed 100644
--- a/src/jetstream/plugins/cfappssh/app_ssh.go
+++ b/src/jetstream/plugins/cfappssh/app_ssh.go
@@ -35,13 +35,14 @@ var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
+// KeyCode - JSON object that is passed from the front-end to notify of a key press or a term resize
type KeyCode struct {
Key string `json:"key"`
Cols int `json:"cols"`
Rows int `json:"rows"`
}
-func (cfAppSsh *CFAppSsh) appSSH(c echo.Context) error {
+func (cfAppSsh *CFAppSSH) appSSH(c echo.Context) error {
// Need to get info for the endpoint
// Get the CNSI and app IDs from route parameters
cnsiGUID := c.Param("cnsiGuid")
@@ -158,6 +159,7 @@ func (cfAppSsh *CFAppSsh) appSSH(c echo.Context) error {
_, r, err := ws.ReadMessage()
if err != nil {
log.Error("Error reading message from web socket")
+ log.Warnf("%v+", err)
return err
}
@@ -173,9 +175,6 @@ func (cfAppSsh *CFAppSsh) appSSH(c echo.Context) error {
}
}
}
-
- // Web socket has closed
- return nil
}
func sendSSHError(format string, a ...interface{}) error {
@@ -241,6 +240,7 @@ func pumpStdout(ws *websocket.Conn, r io.Reader, done chan struct{}) {
}
}
+// ErrPreventRedirect - Error to indicate a redirect - used to make a redirect that we want to prevent later
var ErrPreventRedirect = errors.New("prevent-redirect")
func getSSHCode(authorizeEndpoint, clientID, token string, skipSSLValidation bool) (string, error) {
diff --git a/src/jetstream/plugins/cfappssh/main.go b/src/jetstream/plugins/cfappssh/main.go
index 0193c339fc..259940b5ff 100644
--- a/src/jetstream/plugins/cfappssh/main.go
+++ b/src/jetstream/plugins/cfappssh/main.go
@@ -7,38 +7,43 @@ import (
"github.com/labstack/echo"
)
-type CFAppSsh struct {
+// CFAppSSH - Plugin to allow SSH into an application instance
+type CFAppSSH struct {
portalProxy interfaces.PortalProxy
}
+// Init creates a new CFAppSSH
func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) {
- return &CFAppSsh{portalProxy: portalProxy}, nil
+ return &CFAppSSH{portalProxy: portalProxy}, nil
}
-func (cfAppSsh *CFAppSsh) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) {
- return nil, errors.New("Not implemented!")
-
+// GetMiddlewarePlugin gets the middleware plugin for this plugin
+func (CFAppSSH *CFAppSSH) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) {
+ return nil, errors.New("Not implemented")
}
-func (cfAppSsh *CFAppSsh) GetEndpointPlugin() (interfaces.EndpointPlugin, error) {
- return nil, errors.New("Not implemented!")
+// GetEndpointPlugin gets the endpoint plugin for this plugin
+func (CFAppSSH *CFAppSSH) GetEndpointPlugin() (interfaces.EndpointPlugin, error) {
+ return nil, errors.New("Not implemented")
}
-func (cfAppSsh *CFAppSsh) GetRoutePlugin() (interfaces.RoutePlugin, error) {
- return cfAppSsh, nil
-
+// GetRoutePlugin gets the route plugin for this plugin
+func (CFAppSSH *CFAppSSH) GetRoutePlugin() (interfaces.RoutePlugin, error) {
+ return CFAppSSH, nil
}
-func (cfAppSsh *CFAppSsh) AddAdminGroupRoutes(echoGroup *echo.Group) {
+// AddAdminGroupRoutes adds the admin routes for this plugin to the Echo server
+func (CFAppSSH *CFAppSSH) AddAdminGroupRoutes(echoGroup *echo.Group) {
// no-op
}
-func (cfAppSsh *CFAppSsh) AddSessionGroupRoutes(echoGroup *echo.Group) {
+// AddSessionGroupRoutes adds the session routes for this plugin to the Echo server
+func (CFAppSSH *CFAppSSH) AddSessionGroupRoutes(echoGroup *echo.Group) {
// Application SSH
- echoGroup.GET("/:cnsiGuid/apps/:appGuid/ssh/:appInstance", cfAppSsh.appSSH)
+ echoGroup.GET("/:cnsiGuid/apps/:appGuid/ssh/:appInstance", CFAppSSH.appSSH)
}
-func (cfAppSsh *CFAppSsh) Init() error {
-
+// Init performs plugin initialization
+func (CFAppSSH *CFAppSSH) Init() error {
return nil
}
diff --git a/src/jetstream/plugins/cloudfoundry/cf_websocket_streams.go b/src/jetstream/plugins/cloudfoundry/cf_websocket_streams.go
index 7f808e2590..3fed346cc9 100644
--- a/src/jetstream/plugins/cloudfoundry/cf_websocket_streams.go
+++ b/src/jetstream/plugins/cloudfoundry/cf_websocket_streams.go
@@ -39,6 +39,10 @@ func (c CloudFoundrySpecification) firehose(echoContext echo.Context) error {
return c.commonStreamHandler(echoContext, firehoseStreamHandler)
}
+func (c CloudFoundrySpecification) appFirehose(echoContext echo.Context) error {
+ return c.commonStreamHandler(echoContext, appFirehoseStreamHandler)
+}
+
func (c CloudFoundrySpecification) commonStreamHandler(echoContext echo.Context, bespokeStreamHandler func(echo.Context, *AuthorizedConsumer, *websocket.Conn) error) error {
ac, err := c.openNoaaConsumer(echoContext)
if err != nil {
@@ -243,3 +247,31 @@ func firehoseStreamHandler(echoContext echo.Context, ac *AuthorizedConsumer, cli
log.Infof("Firehose connected and streaming for CNSI: %s - subscription ID: %s", cnsiGUID, firehoseSubscriptionId)
return nil
}
+
+func appFirehoseStreamHandler(echoContext echo.Context, ac *AuthorizedConsumer, clientWebSocket *websocket.Conn) error {
+ log.Debug("appFirehoseStreamHandler")
+
+ // Get the CNSI and app IDs from route parameters
+ cnsiGUID := echoContext.Param("cnsiGuid")
+ appGUID := echoContext.Param("appGuid")
+
+ log.Infof("Received request for log stream for App ID: %s - in CNSI: %s", appGUID, cnsiGUID)
+
+ msgChan, errorChan := ac.consumer.Stream(appGUID, ac.authToken)
+
+ // Process the app stream
+ go drainErrors(errorChan)
+ go drainFirehoseEvents(msgChan, func(msg *events.Envelope) {
+ if jsonMsg, err := json.Marshal(msg); err != nil {
+ log.Errorf("Received unparsable message from Doppler %v, %v", jsonMsg, err)
+ } else {
+ err := clientWebSocket.WriteMessage(websocket.TextMessage, jsonMsg)
+ if err != nil {
+ log.Errorf("Error writing data to WebSocket, %v", err)
+ }
+ }
+ })
+
+ log.Infof("Now streaming for App ID: %s - on CNSI: %s", appGUID, cnsiGUID)
+ return nil
+}
diff --git a/src/jetstream/plugins/cloudfoundry/main.go b/src/jetstream/plugins/cloudfoundry/main.go
index d59e9678d6..758cb9ce83 100644
--- a/src/jetstream/plugins/cloudfoundry/main.go
+++ b/src/jetstream/plugins/cloudfoundry/main.go
@@ -15,6 +15,7 @@ import (
log "github.com/sirupsen/logrus"
)
+// CloudFoundrySpecification - Plugin to support Cloud Foundry endpoint type
type CloudFoundrySpecification struct {
portalProxy interfaces.PortalProxy
endpointType string
@@ -25,18 +26,22 @@ const (
CLIENT_ID_KEY = "CF_CLIENT"
)
+// Init creates a new CloudFoundrySpecification
func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) {
return &CloudFoundrySpecification{portalProxy: portalProxy, endpointType: EndpointType}, nil
}
+// GetEndpointPlugin gets the endpoint plugin for this plugin
func (c *CloudFoundrySpecification) GetEndpointPlugin() (interfaces.EndpointPlugin, error) {
return c, nil
}
+// GetRoutePlugin gets the route plugin for this plugin
func (c *CloudFoundrySpecification) GetRoutePlugin() (interfaces.RoutePlugin, error) {
return c, nil
}
+// GetMiddlewarePlugin gets the middleware plugin for this plugin
func (c *CloudFoundrySpecification) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) {
return nil, errors.New("Not implemented!")
}
@@ -157,16 +162,21 @@ func (c *CloudFoundrySpecification) fetchAutoRegisterEndpoint() (string, interfa
return cfAPI, cfCnsi, err
}
+// AddAdminGroupRoutes adds the admin routes for this plugin to the Echo server
func (c *CloudFoundrySpecification) AddAdminGroupRoutes(echoGroup *echo.Group) {
// no-op
}
+// AddSessionGroupRoutes adds the session routes for this plugin to the Echo server
func (c *CloudFoundrySpecification) AddSessionGroupRoutes(echoGroup *echo.Group) {
// Firehose Stream
echoGroup.GET("/:cnsiGuid/firehose", c.firehose)
// Applications Log Streams
echoGroup.GET("/:cnsiGuid/apps/:appGuid/stream", c.appStream)
+
+ // Application Stream
+ echoGroup.GET("/:cnsiGuid/apps/:appGuid/appFirehose", c.appFirehose)
}
func (c *CloudFoundrySpecification) Info(apiEndpoint string, skipSSLValidation bool) (interfaces.CNSIRecord, interface{}, error) {
diff --git a/src/jetstream/plugins/cloudfoundryhosting/main.go b/src/jetstream/plugins/cloudfoundryhosting/main.go
index 2352fb4804..09a554af71 100644
--- a/src/jetstream/plugins/cloudfoundryhosting/main.go
+++ b/src/jetstream/plugins/cloudfoundryhosting/main.go
@@ -19,6 +19,7 @@ import (
"github.com/cloudfoundry-incubator/stratos/src/jetstream/repository/interfaces"
)
+// Constants
const (
VCapApplication = "VCAP_APPLICATION"
CFApiURLOverride = "CF_API_URL"
@@ -27,15 +28,18 @@ const (
ForceEndpointDashboard = "FORCE_ENDPOINT_DASHBOARD"
)
+// CFHosting is a plugin to configure Stratos when hosted in Cloud Foundry
type CFHosting struct {
portalProxy interfaces.PortalProxy
endpointType string
}
+// Init creates a new CFHosting plugin
func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) {
return &CFHosting{portalProxy: portalProxy}, nil
}
+// GetMiddlewarePlugin gets the middleware plugin for this plugin
func (ch *CFHosting) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) {
if config.IsSet(VCapApplication) {
return ch, nil
@@ -43,14 +47,17 @@ func (ch *CFHosting) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error)
return nil, errors.New("Not running as a Cloud Foundry application")
}
+// GetEndpointPlugin gets the endpoint plugin for this plugin
func (ch *CFHosting) GetEndpointPlugin() (interfaces.EndpointPlugin, error) {
- return nil, errors.New("Not implemented!")
+ return nil, errors.New("Not implemented")
}
+// GetRoutePlugin gets the route plugin for this plugin
func (ch *CFHosting) GetRoutePlugin() (interfaces.RoutePlugin, error) {
- return nil, errors.New("Not implemented!")
+ return nil, errors.New("Not implemented")
}
+// Init performs plugin initialization
func (ch *CFHosting) Init() error {
// Determine if we are running CF by presence of env var "VCAP_APPLICATION" and configure appropriately
if config.IsSet(VCapApplication) {
@@ -103,9 +110,9 @@ func (ch *CFHosting) Init() error {
// Allow the URL to be overridden by an application environment variable
if config.IsSet(CFApiURLOverride) {
- apiUrl, _ := config.GetValue(CFApiURLOverride)
- appData.API = apiUrl
- log.Infof("Overriden CF API URL from environment variable %s", apiUrl)
+ apiURL, _ := config.GetValue(CFApiURLOverride)
+ appData.API = apiURL
+ log.Infof("Overriden CF API URL from environment variable %s", apiURL)
}
if config.IsSet(CFApiForceSecure) {
@@ -185,6 +192,7 @@ func (ch *CFHosting) Init() error {
return nil
}
+// EchoMiddleware is the Echo server middleware provided by this plugin
func (ch *CFHosting) EchoMiddleware(h echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
@@ -213,6 +221,7 @@ func (ch *CFHosting) EchoMiddleware(h echo.HandlerFunc) echo.HandlerFunc {
}
}
+// SessionEchoMiddleware is the Echo server session middleware provided by this plugin
// For cloud foundry session affinity
// Ensure we add a cookie named "JSESSIONID" for Cloud Foundry session affinity
func (ch *CFHosting) SessionEchoMiddleware(h echo.HandlerFunc) echo.HandlerFunc {
diff --git a/src/jetstream/plugins/metrics/cloud_foundry.go b/src/jetstream/plugins/metrics/cloud_foundry.go
index bfd01e82ba..d647ccb3f6 100644
--- a/src/jetstream/plugins/metrics/cloud_foundry.go
+++ b/src/jetstream/plugins/metrics/cloud_foundry.go
@@ -21,8 +21,8 @@ func (m *MetricsSpecification) getCloudFoundryAppMetrics(c echo.Context) error {
// Use the passthrough mechanism to get the App metadata from Cloud Foundry
appID := c.Param("appId")
prometheusOp := c.Param("op")
- appUrl, _ := url.Parse("/v2/apps/" + appID)
- responses, err := m.portalProxy.ProxyRequest(c, appUrl)
+ appURL, _ := url.Parse("/v2/apps/" + appID)
+ responses, err := m.portalProxy.ProxyRequest(c, appURL)
if err != nil {
return err
}
diff --git a/src/jetstream/plugins/metrics/main.go b/src/jetstream/plugins/metrics/main.go
index 3ee2a1200d..5c40122abe 100644
--- a/src/jetstream/plugins/metrics/main.go
+++ b/src/jetstream/plugins/metrics/main.go
@@ -15,6 +15,7 @@ import (
log "github.com/sirupsen/logrus"
)
+// MetricsSpecification is a plugin to support the metrics endpoint type
type MetricsSpecification struct {
portalProxy interfaces.PortalProxy
endpointType string
@@ -43,28 +44,32 @@ type EndpointMetricsRelation struct {
endpoint *interfaces.ConnectedEndpoint
}
+// Init creates a new MetricsSpecification
func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) {
return &MetricsSpecification{portalProxy: portalProxy, endpointType: EndpointType}, nil
}
+// GetEndpointPlugin gets the endpoint plugin for this plugin
func (m *MetricsSpecification) GetEndpointPlugin() (interfaces.EndpointPlugin, error) {
return m, nil
}
+// GetRoutePlugin gets the route plugin for this plugin
func (m *MetricsSpecification) GetRoutePlugin() (interfaces.RoutePlugin, error) {
return m, nil
}
+// GetMiddlewarePlugin gets the middleware plugin for this plugin
func (m *MetricsSpecification) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) {
return nil, errors.New("Not implemented!")
}
-// Metrics endpoints - admin
+// AddAdminGroupRoutes adds the admin routes for this plugin to the Echo server
func (m *MetricsSpecification) AddAdminGroupRoutes(echoContext *echo.Group) {
echoContext.GET("/metrics/cf/:op", m.getCloudFoundryMetrics)
}
-// Metrics API endpoints - non-admin
+// AddSessionGroupRoutes adds the session routes for this plugin to the Echo server
func (m *MetricsSpecification) AddSessionGroupRoutes(echoContext *echo.Group) {
echoContext.GET("/metrics/cf/app/:appId/:op", m.getCloudFoundryAppMetrics)
}
@@ -136,6 +141,7 @@ func (m *MetricsSpecification) Connect(ec echo.Context, cnsiRecord interfaces.CN
return tr, false, nil
}
+// Init performs plugin initialization
func (m *MetricsSpecification) Init() error {
return nil
}
diff --git a/src/jetstream/plugins/userinfo/main.go b/src/jetstream/plugins/userinfo/main.go
index cda53330a8..3dd7798c68 100644
--- a/src/jetstream/plugins/userinfo/main.go
+++ b/src/jetstream/plugins/userinfo/main.go
@@ -7,36 +7,43 @@ import (
"github.com/labstack/echo"
)
+// UserInfo is a plugin to fetch user info from the UAA
type UserInfo struct {
portalProxy interfaces.PortalProxy
}
+// Init creates a new UserInfo
func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) {
return &UserInfo{portalProxy: portalProxy}, nil
}
+// GetMiddlewarePlugin gets the middleware plugin for this plugin
func (userInfo *UserInfo) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) {
- return nil, errors.New("Not implemented!")
-
+ return nil, errors.New("Not implemented")
}
+// GetEndpointPlugin gets the endpoint plugin for this plugin
func (userInfo *UserInfo) GetEndpointPlugin() (interfaces.EndpointPlugin, error) {
- return nil, errors.New("Not implemented!")
+ return nil, errors.New("Not implemented")
}
+// GetRoutePlugin gets the route plugin for this plugin
func (userInfo *UserInfo) GetRoutePlugin() (interfaces.RoutePlugin, error) {
return userInfo, nil
}
+// AddAdminGroupRoutes adds the admin routes for this plugin to the Echo server
func (userInfo *UserInfo) AddAdminGroupRoutes(echoGroup *echo.Group) {
// no-op
}
+// AddSessionGroupRoutes adds the session routes for this plugin to the Echo server
func (userInfo *UserInfo) AddSessionGroupRoutes(echoGroup *echo.Group) {
// User Info
echoGroup.Any("/uaa/*", userInfo.uaa)
}
+// Init performs plugin initialization
func (userInfo *UserInfo) Init() error {
return nil
}
diff --git a/src/jetstream/plugins/userinfo/user_info.go b/src/jetstream/plugins/userinfo/user_info.go
index 90fde10e56..165d1ea1b7 100644
--- a/src/jetstream/plugins/userinfo/user_info.go
+++ b/src/jetstream/plugins/userinfo/user_info.go
@@ -52,7 +52,7 @@ func (userInfo *UserInfo) uaa(c echo.Context) error {
}
}
- statusCode, body, err := userInfo.doApiRequest(sessionUser, url, c.Request())
+ statusCode, body, err := userInfo.doAPIRequest(sessionUser, url, c.Request())
if err != nil {
return err
}
@@ -63,7 +63,7 @@ func (userInfo *UserInfo) uaa(c echo.Context) error {
if err != nil {
return err
}
- statusCode, body, err = userInfo.doApiRequest(sessionUser, url, c.Request())
+ statusCode, body, err = userInfo.doAPIRequest(sessionUser, url, c.Request())
if err != nil {
return err
}
@@ -88,9 +88,9 @@ func (userInfo *UserInfo) uaa(c echo.Context) error {
return nil
}
-func (userInfo *UserInfo) doApiRequest(sessionUser string, url string, echoReq engine.Request) (stausCode int, body []byte, err error) {
+func (userInfo *UserInfo) doAPIRequest(sessionUser string, url string, echoReq engine.Request) (stausCode int, body []byte, err error) {
// Proxy the request to the UAA on behalf of the user
- log.Debugf("doApiRequest: %s", url)
+ log.Debugf("doAPIRequest: %s", url)
tokenRec, err := userInfo.portalProxy.GetUAATokenRecord(sessionUser)
if err != nil {
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/cf-level/cf-top-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts
index c8fe13f7c5..e19c80b85a 100644
--- a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts
+++ b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts
@@ -48,7 +48,7 @@ describe('CF - Top Level - ', () => {
it('Summary Panel', () => {
expect(cfPage.waitForInstanceAddress().getValue()).toBe(defaultCf.url);
expect(cfPage.waitForUsername().getValue()).toBe(defaultCf.creds.admin.username);
- expect(cfPage.waitForAdministrator().getValue()).toBe('Yes');
+ expect(cfPage.waitForAdministrator().getBooleanIndicator().getLabel()).toBe('Yes');
});
it('Walk Tabs', () => {
@@ -82,7 +82,7 @@ describe('CF - Top Level - ', () => {
it('Summary Panel', () => {
expect(cfPage.waitForInstanceAddress().getValue()).toBe(defaultCf.url);
expect(cfPage.waitForUsername().getValue()).toBe(defaultCf.creds.nonAdmin.username);
- expect(cfPage.waitForAdministrator().getValue()).toBe('No');
+ expect(cfPage.waitForAdministrator().getBooleanIndicator().getLabel()).toBe('No');
});
it('Walk Tabs', () => {
diff --git a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-page.po.ts b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-page.po.ts
index 277c6bd6c5..bc89833641 100644
--- a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-page.po.ts
+++ b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-page.po.ts
@@ -1,7 +1,7 @@
import { browser, by, element, promise } from 'protractor';
import { ListComponent } from '../../po/list.po';
-import { MetaDataItemComponent } from '../../po/meta-data-time.po';
+import { MetaDataItemComponent } from '../../po/meta-data-item.po';
import { CFPage } from '../../po/cf-page.po';
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/cf-org-level-page.po.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts
index 7300e30125..adccc837fa 100644
--- a/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts
+++ b/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts
@@ -1,7 +1,7 @@
import { browser, by, element } from 'protractor';
import { ListComponent } from '../../po/list.po';
-import { MetaDataItemComponent } from '../../po/meta-data-time.po';
+import { MetaDataItemComponent } from '../../po/meta-data-item.po';
import { CFPage } from '../../po/cf-page.po';
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/cloud-foundry/space-level/cf-space-level-page.po.ts b/src/test-e2e/cloud-foundry/space-level/cf-space-level-page.po.ts
index 718f4eb3c7..56f92f3959 100644
--- a/src/test-e2e/cloud-foundry/space-level/cf-space-level-page.po.ts
+++ b/src/test-e2e/cloud-foundry/space-level/cf-space-level-page.po.ts
@@ -1,7 +1,7 @@
import { browser, by, element } from 'protractor';
import { ListComponent } from '../../po/list.po';
-import { MetaDataItemComponent } from '../../po/meta-data-time.po';
+import { MetaDataItemComponent } from '../../po/meta-data-item.po';
import { CFPage } from '../../po/cf-page.po';
diff --git a/src/test-e2e/e2e.types.ts b/src/test-e2e/e2e.types.ts
index f7acc561ea..b88c7b328a 100644
--- a/src/test-e2e/e2e.types.ts
+++ b/src/test-e2e/e2e.types.ts
@@ -15,11 +15,23 @@ export interface E2EEndpointConfig {
creds: E2ECreds;
}
+export interface ServiceConfig {
+ invalidOrgName?: string;
+ invalidSpaceName?: string;
+ name: string;
+}
+export interface E2EServicesConfig {
+ bindApp: string;
+ publicService: ServiceConfig;
+ privateService: ServiceConfig;
+ spaceScopedService: ServiceConfig;
+}
+
export interface E2EConfigCloudFoundry extends E2EEndpointConfig {
testOrg: string;
testSpace: string;
testDeployApp: string;
- testService: string;
+ services: E2EServicesConfig;
}
export interface E2EEndpointTypeConfig extends E2EEndpointConfig {
diff --git a/src/test-e2e/endpoints/endpoints.po.ts b/src/test-e2e/endpoints/endpoints.po.ts
index 479f21f908..60a6594d56 100644
--- a/src/test-e2e/endpoints/endpoints.po.ts
+++ b/src/test-e2e/endpoints/endpoints.po.ts
@@ -14,12 +14,14 @@ export class EndpointsTable extends ListTableComponent {
getEndpointData(row: ElementFinder) {
// Get all of the columns
- return row.all(by.tagName('app-table-cell')).map(col => col.getText()).then(data => {
+ return row.all(by.tagName('app-table-cell')).map(col => col.getText()).then((data: string[]) => {
return {
name: data[0],
connected: data[1] === 'cloud_done',
type: data[2],
- url: data[3]
+ user: data[3],
+ isAdmin: data[4].indexOf('Yes') !== -1,
+ url: data[5]
} as EndpointMetadata;
});
}
@@ -119,6 +121,8 @@ export interface EndpointMetadata {
name: string;
url: string;
type: string;
+ user: string;
+ isAdmin: boolean;
connected: boolean;
}
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/login/login-e2e.spec.ts b/src/test-e2e/login/login-e2e.spec.ts
index d2ffecc18d..c36a9b617e 100644
--- a/src/test-e2e/login/login-e2e.spec.ts
+++ b/src/test-e2e/login/login-e2e.spec.ts
@@ -16,7 +16,7 @@ describe('Login', () => {
it('- should reach log in page', () => {
expect(loginPage.isLoginPage()).toBeTruthy();
- expect(loginPage.getTitle()).toEqual('STRATOS');
+ expect(loginPage.getTitle()).toEqual('');
expect(loginPage.loginButton().isPresent()).toBeTruthy();
});
diff --git a/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts b/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts
index cba954e1e3..4dbb07e1e4 100644
--- a/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts
+++ b/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts
@@ -1,4 +1,4 @@
-import { ElementFinder, promise } from 'protractor';
+import { browser, ElementFinder, promise } from 'protractor';
import { e2e } from '../e2e';
import { ConsoleUserType } from '../helpers/e2e-helpers';
@@ -12,9 +12,10 @@ describe('Create Service Instance', () => {
const servicesWall = new ServicesWallPage();
let servicesHelperE2E: ServicesHelperE2E;
beforeAll(() => {
- const e2eSetup = e2e.setup(ConsoleUserType.admin)
+ const e2eSetup = e2e.setup(ConsoleUserType.user)
.clearAllEndpoints()
.registerDefaultCloudFoundry()
+ .connectAllEndpoints(ConsoleUserType.user)
.connectAllEndpoints(ConsoleUserType.admin)
.getInfo();
servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance);
@@ -30,8 +31,8 @@ describe('Create Service Instance', () => {
});
it('- should be able to to create a service instance', () => {
- servicesHelperE2E.createService();
+ servicesHelperE2E.createService(e2e.secrets.getDefaultCFEndpoint().services.publicService.name);
servicesWall.waitForPage();
const serviceName = servicesHelperE2E.serviceInstanceName;
@@ -57,7 +58,7 @@ describe('Create Service Instance', () => {
servicesHelperE2E.setCfOrgSpace();
createServiceInstance.stepper.cancel();
- servicesWall.isActivePage();
+ servicesWall.waitForPage();
});
@@ -68,12 +69,12 @@ describe('Create Service Instance', () => {
createServiceInstance.stepper.next();
// Select Service
- servicesHelperE2E.setServiceSelection();
+ servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.publicService.name);
createServiceInstance.stepper.next();
createServiceInstance.stepper.cancel();
- servicesWall.isActivePage();
+ servicesWall.waitForPage();
});
@@ -84,7 +85,7 @@ describe('Create Service Instance', () => {
createServiceInstance.stepper.next();
// Select Service
- servicesHelperE2E.setServiceSelection();
+ servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.publicService.name);
createServiceInstance.stepper.next();
// Select Service Plan
@@ -93,7 +94,7 @@ describe('Create Service Instance', () => {
createServiceInstance.stepper.cancel();
- servicesWall.isActivePage();
+ servicesWall.waitForPage();
});
@@ -103,7 +104,7 @@ describe('Create Service Instance', () => {
createServiceInstance.stepper.next();
// Select Service
- servicesHelperE2E.setServiceSelection();
+ servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.publicService.name);
createServiceInstance.stepper.next();
// Select Service Plan
@@ -119,7 +120,7 @@ describe('Create Service Instance', () => {
createServiceInstance.stepper.cancel();
- servicesWall.isActivePage();
+ servicesWall.waitForPage();
});
});
diff --git a/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts b/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts
new file mode 100644
index 0000000000..6290cf0423
--- /dev/null
+++ b/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts
@@ -0,0 +1,98 @@
+import { ConsoleUserType } from '../helpers/e2e-helpers';
+import { e2e } from '../e2e';
+import { ElementFinder, promise } from 'protractor';
+import { CreateServiceInstance } from './create-service-instance.po';
+import { ServicesWallPage } from './services-wall.po';
+import { MetaCard } from '../po/meta-card.po';
+import { ServicesHelperE2E } from './services-helper-e2e';
+
+describe('Create Service Instance of Private Service', () => {
+ const createServiceInstance = new CreateServiceInstance();
+ const servicesWall = new ServicesWallPage();
+ let servicesHelperE2E: ServicesHelperE2E;
+ beforeAll(() => {
+ const e2eSetup = e2e.setup(ConsoleUserType.user)
+ .clearAllEndpoints()
+ .registerDefaultCloudFoundry()
+ .connectAllEndpoints(ConsoleUserType.user)
+ .connectAllEndpoints(ConsoleUserType.admin);
+ servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance);
+ });
+
+ beforeEach(() => {
+ createServiceInstance.navigateTo();
+ createServiceInstance.waitForPage();
+ });
+
+ it('- should reach create service instance page', () => {
+ expect(createServiceInstance.isActivePage()).toBeTruthy();
+ });
+
+ it('- should be able to to create a service instance', () => {
+
+ servicesHelperE2E.createService(e2e.secrets.getDefaultCFEndpoint().services.privateService.name, false);
+
+ servicesWall.waitForPage();
+
+ const serviceName = servicesHelperE2E.serviceInstanceName;
+
+ servicesWall.serviceInstancesList.cards.getCards().then(
+ (cards: ElementFinder[]) => {
+ return cards.map(card => {
+ const metaCard = new MetaCard(card);
+ return metaCard.getTitle();
+ });
+ }).then(cardTitles => {
+ promise.all(cardTitles).then(titles => {
+ expect(titles.filter(t => t === serviceName).length).toBe(1);
+ });
+ }).catch(e => fail(e));
+
+
+ });
+
+ it('- should return user to Service summary when cancelled on CFOrgSpace selection', () => {
+
+ // Select CF/Org/Space
+ servicesHelperE2E.setCfOrgSpace();
+ createServiceInstance.stepper.cancel();
+
+ servicesWall.waitForPage();
+
+ });
+
+ it('- should return user to Service summary when cancelled on Service selection', () => {
+
+ // Select CF/Org/Space
+ servicesHelperE2E.setCfOrgSpace();
+ createServiceInstance.stepper.next();
+
+ // Select Service
+ servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.privateService.name);
+ createServiceInstance.stepper.next();
+
+ createServiceInstance.stepper.cancel();
+
+ servicesWall.waitForPage();
+
+ });
+
+ it('- should not show service plan if wrong org/space are selected', () => {
+
+ // Select CF/Org/Space
+ servicesHelperE2E.setCfOrgSpace(e2e.secrets.getDefaultCFEndpoint().services.privateService.invalidOrgName,
+ e2e.secrets.getDefaultCFEndpoint().services.privateService.invalidSpaceName);
+ createServiceInstance.stepper.next();
+
+ // Select Service
+ servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.privateService.name, true);
+
+ });
+
+
+ afterAll((done) => {
+ servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done());
+ });
+});
+
+
diff --git a/src/test-e2e/marketplace/create-service-instance-space-scoped-e2e.spec.ts b/src/test-e2e/marketplace/create-service-instance-space-scoped-e2e.spec.ts
new file mode 100644
index 0000000000..618f8ed16a
--- /dev/null
+++ b/src/test-e2e/marketplace/create-service-instance-space-scoped-e2e.spec.ts
@@ -0,0 +1,71 @@
+import { ConsoleUserType } from '../helpers/e2e-helpers';
+import { e2e } from '../e2e';
+import { ElementFinder, promise } from 'protractor';
+import { CreateServiceInstance } from './create-service-instance.po';
+import { ServicesWallPage } from './services-wall.po';
+import { MetaCard } from '../po/meta-card.po';
+import { ServicesHelperE2E } from './services-helper-e2e';
+
+describe('Create Service Instance of Space Scoped Service', () => {
+ const createServiceInstance = new CreateServiceInstance();
+ const servicesWall = new ServicesWallPage();
+ let servicesHelperE2E: ServicesHelperE2E;
+ beforeAll(() => {
+ const e2eSetup = e2e.setup(ConsoleUserType.user)
+ .clearAllEndpoints()
+ .registerDefaultCloudFoundry()
+ .connectAllEndpoints(ConsoleUserType.user)
+ .connectAllEndpoints(ConsoleUserType.admin);
+ servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance);
+ });
+
+ beforeEach(() => {
+ createServiceInstance.navigateTo();
+ createServiceInstance.waitForPage();
+ });
+
+ it('- should reach create service instance page', () => {
+ expect(createServiceInstance.isActivePage()).toBeTruthy();
+ });
+
+ it('- should be able to to create a service instance', () => {
+
+ servicesHelperE2E.createService(e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.name);
+ servicesWall.waitForPage();
+
+ const serviceName = servicesHelperE2E.serviceInstanceName;
+
+ servicesWall.serviceInstancesList.cards.getCards().then(
+ (cards: ElementFinder[]) => {
+ return cards.map(card => {
+ const metaCard = new MetaCard(card);
+ return metaCard.getTitle();
+ });
+ }).then(cardTitles => {
+ promise.all(cardTitles).then(titles => {
+ expect(titles.filter(t => t === serviceName).length).toBe(1);
+ });
+ }).catch(e => fail(e));
+
+
+ });
+
+ it('- should not show service plan if wrong org/space are selected', () => {
+
+ // Select CF/Org/Space
+ servicesHelperE2E.setCfOrgSpace(e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.invalidOrgName,
+ e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.invalidSpaceName);
+ createServiceInstance.stepper.next();
+
+ // Select Service
+ servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.name, true);
+
+ });
+
+
+ afterAll((done) => {
+ servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done());
+ });
+});
+
+
diff --git a/src/test-e2e/marketplace/create-service-instance-stepper.po.ts b/src/test-e2e/marketplace/create-service-instance-stepper.po.ts
index b24ec0e037..c49da8cb2a 100644
--- a/src/test-e2e/marketplace/create-service-instance-stepper.po.ts
+++ b/src/test-e2e/marketplace/create-service-instance-stepper.po.ts
@@ -9,6 +9,7 @@ export class CreateServiceInstanceStepper extends StepperComponent {
private spaceFieldName = 'space';
private serviceFieldName = 'service';
private serviceNameFieldName = 'name';
+ private bindApp = 'apps';
constructor() {
super();
@@ -29,14 +30,18 @@ export class CreateServiceInstanceStepper extends StepperComponent {
setSpace = (spaceName: string) => {
return this.getStepperForm().fill({ [this.spaceFieldName]: spaceName });
}
- setService = (serviceName: string) => {
- return this.getStepperForm().fill({ [this.serviceFieldName]: serviceName });
+ setService = (serviceName: string, expectFailure = false) => {
+ return this.getStepperForm().fill({ [this.serviceFieldName]: serviceName }, expectFailure);
}
setServiceName = (serviceInstanceName: string) => {
return this.getStepperForm().fill({ [this.serviceNameFieldName]: serviceInstanceName });
}
+ setBindApp = (bindAppName: string) => {
+ return this.getStepperForm().fill({ [this.bindApp]: bindAppName });
+ }
+
isBindAppStepDisabled = () => {
return this.isStepDisabled('Bind App (Optional)');
}
diff --git a/src/test-e2e/marketplace/create-service-instance.po.ts b/src/test-e2e/marketplace/create-service-instance.po.ts
index 280ffedea3..73af33eb49 100644
--- a/src/test-e2e/marketplace/create-service-instance.po.ts
+++ b/src/test-e2e/marketplace/create-service-instance.po.ts
@@ -8,8 +8,8 @@ export class CreateServiceInstance extends Page {
stepper = new CreateServiceInstanceStepper();
- constructor() {
- super('/services/new');
- }
+ constructor(url = '/services/new') {
+ super(url);
+ }
}
diff --git a/src/test-e2e/marketplace/create-service-instances-bind-app-e2e.spec.ts b/src/test-e2e/marketplace/create-service-instances-bind-app-e2e.spec.ts
new file mode 100644
index 0000000000..44b54ea5c6
--- /dev/null
+++ b/src/test-e2e/marketplace/create-service-instances-bind-app-e2e.spec.ts
@@ -0,0 +1,106 @@
+import { ElementFinder, promise, by, browser } from 'protractor';
+
+import { e2e } from '../e2e';
+import { ConsoleUserType } from '../helpers/e2e-helpers';
+import { MetaCard } from '../po/meta-card.po';
+import { CreateServiceInstance } from './create-service-instance.po';
+import { ServicesHelperE2E } from './services-helper-e2e';
+import { ServicesWallPage } from './services-wall.po';
+
+describe('Create Service Instance with binding', () => {
+ const createServiceInstance = new CreateServiceInstance();
+ const servicesWall = new ServicesWallPage();
+ let servicesHelperE2E: ServicesHelperE2E;
+ let cardIdx = -1;
+ beforeAll(() => {
+ const e2eSetup = e2e.setup(ConsoleUserType.user)
+ .clearAllEndpoints()
+ .registerDefaultCloudFoundry()
+ .connectAllEndpoints(ConsoleUserType.user)
+ .connectAllEndpoints(ConsoleUserType.admin)
+ .getInfo();
+ servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance);
+ });
+
+ beforeEach(() => {
+ createServiceInstance.navigateTo();
+ createServiceInstance.waitForPage();
+ });
+
+ it('- should reach create service instance page', () => {
+ expect(createServiceInstance.isActivePage()).toBeTruthy();
+ });
+
+ it('- should be able to to create a service instance with binding', () => {
+
+ const servicesSecrets = e2e.secrets.getDefaultCFEndpoint().services;
+ servicesHelperE2E.createService(servicesSecrets.publicService.name, false, servicesSecrets.bindApp);
+ servicesWall.waitForPage();
+
+ const serviceName = servicesHelperE2E.serviceInstanceName;
+
+ servicesWall.serviceInstancesList.cards.getCards().then(
+ (cards: ElementFinder[]) => {
+ return cards.map(card => {
+ const metaCard = new MetaCard(card);
+ return metaCard.getTitle();
+ });
+ }).then(cardTitles => {
+ promise.all(cardTitles).then(titles => {
+ expect(titles.filter((t, idx) => {
+ const isCorrectCard = (t === serviceName);
+ if (isCorrectCard) {
+ cardIdx = idx;
+
+ const card = servicesWall.serviceInstancesList.cards.getCard(cardIdx);
+ card.getMetaCardItems().then(metaCardRows => {
+ expect(metaCardRows[1].value).toBe(servicesSecrets.publicService.name);
+ expect(metaCardRows[2].value).toBe('shared');
+ expect(metaCardRows[3].value).toBe('1');
+ }).catch(e => fail(e));
+ }
+ return isCorrectCard;
+ }).length).toBe(1);
+ });
+ }).catch(e => fail(e));
+
+
+ });
+
+ it('- should have correct number in list view', () => {
+ servicesWall.navigateTo();
+ servicesWall.waitForPage();
+
+ expect(servicesWall.serviceInstancesList.isCardsView()).toBeTruthy();
+
+ // Switch to list view
+ servicesWall.serviceInstancesList.header.getCardListViewToggleButton().click();
+ expect(servicesWall.serviceInstancesList.isTableView()).toBeTruthy();
+ const servicesSecrets = e2e.secrets.getDefaultCFEndpoint().services;
+
+ // Filter for name
+ servicesWall.serviceInstancesList.header.setSearchText(servicesHelperE2E.serviceInstanceName)
+ .then(() => {
+ servicesWall.serviceInstancesList.table.getRows().then((rows: ElementFinder[]) => {
+ expect(rows.length).toBe(1);
+
+ const attachedApps = rows[0].element(by.css('.mat-column-attachedApps'));
+
+ expect(attachedApps.getText()).toBe(servicesSecrets.bindApp);
+
+ // Navigate to Apps
+ attachedApps.element(by.tagName('a')).click();
+
+ browser.getCurrentUrl().then(url => {
+ expect(url.endsWith('summary?breadcrumbs=service-wall')).toBeTruthy();
+ });
+ });
+ });
+ });
+
+ afterAll((done) => {
+ servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done());
+ });
+});
+
+
diff --git a/src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts b/src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts
new file mode 100644
index 0000000000..e4724cb56a
--- /dev/null
+++ b/src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts
@@ -0,0 +1,99 @@
+import { browser, ElementFinder, promise } from 'protractor';
+
+import { e2e } from '../e2e';
+import { ConsoleUserType } from '../helpers/e2e-helpers';
+import { MetaCard } from '../po/meta-card.po';
+import { CreateServiceInstance } from './create-service-instance.po';
+import { ServicesHelperE2E } from './services-helper-e2e';
+import { ServicesWallPage } from './services-wall.po';
+import { ConfirmDialogComponent } from '../po/confirm-dialog';
+
+describe('Edit Service Instance', () => {
+ const createServiceInstance = new CreateServiceInstance();
+ const servicesWall = new ServicesWallPage();
+ let servicesHelperE2E: ServicesHelperE2E;
+ const serviceNamePrefix = 'edited';
+ beforeAll(() => {
+ const e2eSetup = e2e.setup(ConsoleUserType.user)
+ .clearAllEndpoints()
+ .registerDefaultCloudFoundry()
+ .connectAllEndpoints(ConsoleUserType.user)
+ .connectAllEndpoints(ConsoleUserType.admin)
+ .getInfo();
+ servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance);
+ });
+
+ beforeEach(() => {
+ servicesWall.navigateTo();
+ servicesWall.waitForPage();
+ });
+
+ it('- should be able edit a service instance', () => {
+ createServiceInstance.navigateTo();
+ createServiceInstance.waitForPage();
+ servicesHelperE2E.createService(e2e.secrets.getDefaultCFEndpoint().services.publicService.name);
+
+ servicesWall.waitForPage();
+
+ const serviceName = servicesHelperE2E.serviceInstanceName;
+
+ getCardWithTitle(servicesWall, serviceName).then((card: MetaCard) => {
+ card.openActionMenu().then(menu => {
+ menu.clickItem('Edit');
+ browser.getCurrentUrl().then(url => {
+ expect(url.endsWith('edit')).toBeTruthy();
+ servicesHelperE2E.setServicePlan(true);
+ servicesHelperE2E.createServiceInstance.stepper.next();
+
+ servicesHelperE2E.addPrefixToServiceName(serviceNamePrefix);
+ servicesHelperE2E.setServiceInstanceDetail(true);
+ servicesHelperE2E.createServiceInstance.stepper.next();
+
+ });
+ });
+ }).catch(e => fail(e));
+ });
+
+ it('- should have edited service instance', () => {
+
+ servicesWall.waitForPage();
+ const editedServiceName = servicesHelperE2E.serviceInstanceName;
+ getCardWithTitle(servicesWall, editedServiceName).then((card: MetaCard) => {
+ expect(card).toBeDefined();
+ }).catch(e => fail(e));
+ });
+
+ it('- should be able to delete service instance', () => {
+
+ servicesWall.waitForPage();
+ const editedServiceName = servicesHelperE2E.serviceInstanceName;
+ getCardWithTitle(servicesWall, editedServiceName).then((card: MetaCard) => {
+ card.openActionMenu().then(menu => {
+ menu.clickItem('Delete');
+ const deleteDialog = new ConfirmDialogComponent();
+
+ expect(deleteDialog.isDisplayed()).toBeTruthy();
+ expect(deleteDialog.getTitle()).toEqual('Delete Service Instance');
+ deleteDialog.confirm();
+ });
+ }).catch(e => fail(e));
+ });
+
+});
+
+function getCardWithTitle(servicesWall: ServicesWallPage, serviceName: string) {
+ return servicesWall.serviceInstancesList.cards.getCards().then((cards: ElementFinder[]) => {
+ return cards.map(card => {
+ const metaCard = new MetaCard(card);
+ return metaCard.getTitle();
+ });
+ }).then(cardTitles => {
+ return promise.all(cardTitles).then(titles => {
+ for (let i = 0; i < titles.length; i++) {
+ if (titles[i] === serviceName) {
+ return servicesWall.serviceInstancesList.cards.getCard(i);
+ }
+ }
+ });
+ });
+}
diff --git a/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts b/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts
new file mode 100644
index 0000000000..f8790af2ba
--- /dev/null
+++ b/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts
@@ -0,0 +1,163 @@
+import { MarketplaceSummaryPage } from './marketplace-summary.po';
+import { ConsoleUserType } from '../helpers/e2e-helpers';
+import { e2e, E2ESetup } from '../e2e';
+import { browser, ElementFinder, promise } from 'protractor';
+import { ServicesHelperE2E } from './services-helper-e2e';
+import { CreateServiceInstance } from './create-service-instance.po';
+import { ServicesWallPage } from './services-wall.po';
+import { MetaCard } from '../po/meta-card.po';
+
+describe('Marketplace', () => {
+ let setup: E2ESetup;
+ const servicesWall = new ServicesWallPage();
+ beforeAll(() => {
+ setup = e2e.setup(ConsoleUserType.admin)
+ .clearAllEndpoints()
+ .registerDefaultCloudFoundry()
+ .connectAllEndpoints(ConsoleUserType.admin)
+ .getInfo();
+ });
+
+ describe('Create Public Service Instance', () => {
+ let servicesHelperE2E: ServicesHelperE2E;
+ let marketplaceSummaryPage: MarketplaceSummaryPage;
+ const serviceName = e2e.secrets.getDefaultCFEndpoint().services.publicService.name;
+ beforeAll((done) => {
+ init(setup, serviceName).then(res => {
+ servicesHelperE2E = res.servicesHelper;
+ marketplaceSummaryPage = res.summaryPage;
+ done();
+ });
+ });
+
+ beforeEach(() => {
+ marketplaceSummaryPage.navigateTo();
+ marketplaceSummaryPage.waitForPage();
+ });
+
+ it('- should have an Add Service Instance button', () => {
+ expect(marketplaceSummaryPage.getAddServiceInstanceButton().isPresent()).toBeTruthy();
+ });
+
+ it('- should be able to create a new service instance', () => {
+ createService(marketplaceSummaryPage, servicesHelperE2E, serviceName, servicesWall);
+ });
+ afterAll((done) => {
+ // Sleeping because the service instance may not be listed in the `get services` request
+ browser.sleep(1000);
+ servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done());
+ });
+ });
+
+ describe('Create Private Service Instance', () => {
+ let servicesHelperE2E: ServicesHelperE2E;
+ let marketplaceSummaryPage: MarketplaceSummaryPage;
+ const serviceName = e2e.secrets.getDefaultCFEndpoint().services.privateService.name;
+ beforeAll((done) => {
+ init(setup, serviceName).then(res => {
+ servicesHelperE2E = res.servicesHelper;
+ marketplaceSummaryPage = res.summaryPage;
+ done();
+ });
+ });
+
+ beforeEach(() => {
+ marketplaceSummaryPage.navigateTo();
+ marketplaceSummaryPage.waitForPage();
+
+ });
+ it('- should have an Add Service Instance button', () => {
+ expect(marketplaceSummaryPage.getAddServiceInstanceButton().isPresent()).toBeTruthy();
+ });
+
+ it('- should be able to create a new service instance', () => {
+ createService(marketplaceSummaryPage, servicesHelperE2E, serviceName, servicesWall);
+ });
+ afterAll((done) => {
+ // Sleeping because the service instance may not be listed in the `get services` request
+ browser.sleep(1000);
+ servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done());
+ });
+ });
+
+ describe('Create Space Scoped Service Instance', () => {
+ let servicesHelperE2E: ServicesHelperE2E;
+ let marketplaceSummaryPage: MarketplaceSummaryPage;
+ const serviceName = e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.name;
+ beforeAll((done) => {
+ init(setup, serviceName).then(res => {
+ servicesHelperE2E = res.servicesHelper;
+ marketplaceSummaryPage = res.summaryPage;
+ done();
+ });
+ });
+
+ beforeEach(() => {
+ marketplaceSummaryPage.navigateTo();
+ marketplaceSummaryPage.waitForPage();
+
+ });
+ it('- should have an Add Service Instance button', () => {
+ expect(marketplaceSummaryPage.getAddServiceInstanceButton().isPresent()).toBeTruthy();
+ });
+
+ it('- should be able to create a new service instance', () => {
+ createService(marketplaceSummaryPage, servicesHelperE2E, serviceName, servicesWall);
+ });
+ afterAll((done) => {
+ // Sleeping because the service instance may not be listed in the `get services` request
+ browser.sleep(1000);
+ servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done());
+ });
+ });
+});
+
+function createService(marketplaceSummaryPage: MarketplaceSummaryPage,
+ servicesHelperE2E: ServicesHelperE2E, serviceName: string, servicesWall: ServicesWallPage) {
+ const button = marketplaceSummaryPage.header.getIconButton('add');
+ expect(button).toBeDefined();
+ button.then(bt => bt.click());
+ browser.getCurrentUrl().then(url => {
+ expect(url.endsWith('create?isSpaceScoped=false')).toBeTruthy();
+ // Proceed to create a service instance
+ servicesHelperE2E.createService(serviceName, true);
+
+ servicesWall.waitForPage();
+
+ const createdServiceInstanceName = servicesHelperE2E.serviceInstanceName;
+
+ servicesWall.serviceInstancesList.cards.getCards().then(
+ (cards: ElementFinder[]) => {
+ return cards.map(card => {
+ const metaCard = new MetaCard(card);
+ return metaCard.getTitle();
+ });
+ }).then(cardTitles => {
+ promise.all(cardTitles).then(titles => {
+ expect(titles.filter(t => t === createdServiceInstanceName).length).toBe(1);
+ });
+ }).catch(e => fail(e));
+ });
+}
+
+function init(
+ setup: E2ESetup,
+ serviceName: string,
+ spaceScoped = false
+) {
+ const defaultCf = e2e.secrets.getDefaultCFEndpoint();
+ const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name);
+
+ const servicesHelperE2E = new ServicesHelperE2E(setup);
+ return servicesHelperE2E.fetchServices(endpointGuid).then(response => {
+ serviceName = e2e.secrets.getDefaultCFEndpoint().services.publicService.name;
+ const service = response.resources.find(e => e.entity.label === serviceName);
+ const serviceGuid = service.metadata.guid;
+ servicesHelperE2E.setCreateServiceInstance(
+ new CreateServiceInstance('/marketplace/' + endpointGuid + '/' + serviceGuid +
+ '/create?isSpaceScoped=' + (spaceScoped ? 'true' : 'false')));
+ const marketplaceSummaryPage = new MarketplaceSummaryPage(endpointGuid, serviceGuid);
+ return { servicesHelper: servicesHelperE2E, summaryPage: marketplaceSummaryPage };
+ });
+}
+
diff --git a/src/test-e2e/marketplace/marketplace-summary-e2e.spec.ts b/src/test-e2e/marketplace/marketplace-summary-e2e.spec.ts
index e00839bbec..ef1967c938 100644
--- a/src/test-e2e/marketplace/marketplace-summary-e2e.spec.ts
+++ b/src/test-e2e/marketplace/marketplace-summary-e2e.spec.ts
@@ -43,11 +43,14 @@ describe('Marketplace Summary', () => {
});
it('- should have a service summary card', () => {
- expect(marketplaceSummaryPage.getServiceSummaryCard().isPresent()).toBeFalsy();
+ expect(marketplaceSummaryPage.getServiceSummaryCard().isPresent()).toBeTruthy();
});
it('- should have a recent service instances card', () => {
- expect(marketplaceSummaryPage.getRecentInstances().isPresent()).toBeFalsy();
+ expect(marketplaceSummaryPage.getRecentInstances().isPresent()).toBeTruthy();
+ });
+ it('- should have an Add Service Instance button', () => {
+ expect(marketplaceSummaryPage.getAddServiceInstanceButton().isPresent()).toBeTruthy();
});
it('- should be able to create a new service instance', () => {
diff --git a/src/test-e2e/marketplace/marketplace-summary.po.ts b/src/test-e2e/marketplace/marketplace-summary.po.ts
index 2b62af1110..d909ba1564 100644
--- a/src/test-e2e/marketplace/marketplace-summary.po.ts
+++ b/src/test-e2e/marketplace/marketplace-summary.po.ts
@@ -8,8 +8,8 @@ export class MarketplaceSummaryPage extends Page {
constructor(cfGuid: string, serviceGuid: string) {
super(`/marketplace/${cfGuid}/${serviceGuid}/summary`);
- this.locator = element(by.css('summary'));
- }
+ this.locator = element(by.css('.summary'));
+ }
getServiceSummaryCard() {
return this.locator.element(by.css('.service-summary'));
@@ -19,4 +19,8 @@ export class MarketplaceSummaryPage extends Page {
return this.locator.element(by.css('.recent-instances'));
}
+ getAddServiceInstanceButton() {
+ return element(by.name('add-service-instance'));
+ }
+
}
diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts
index d1ea11fc51..da41037891 100644
--- a/src/test-e2e/marketplace/services-helper-e2e.ts
+++ b/src/test-e2e/marketplace/services-helper-e2e.ts
@@ -16,9 +16,18 @@ export class ServicesHelperE2E {
constructor(public e2eSetup: E2ESetup, createServiceInstance: CreateServiceInstance = null) {
this.cfRequestHelper = new CFRequestHelpers(e2eSetup);
this.cfHelper = new CFHelpers(e2eSetup);
- this.createServiceInstance = createServiceInstance;
const testTime = (new Date()).toISOString();
this.serviceInstanceName = `serviceInstance-${testTime}`;
+ if (!!createServiceInstance) {
+ this.createServiceInstance = createServiceInstance;
+ }
+ }
+
+ addPrefixToServiceName = (prefix: string) => {
+ this.serviceInstanceName = `${prefix}-${this.serviceInstanceName}`;
+ }
+ setCreateServiceInstance = (createServiceInstance: CreateServiceInstance) => {
+ this.createServiceInstance = createServiceInstance;
}
fetchServices = (cfGuid: string): promise.Promise => {
@@ -42,25 +51,28 @@ export class ServicesHelperE2E {
);
}
- createService = () => {
+ createService = (serviceName: string, marketplaceMode = false, bindApp: string = null) => {
this.createServiceInstance.waitForPage();
// Select CF/Org/Space
- this.setCfOrgSpace();
+ this.setCfOrgSpace(null, null, marketplaceMode);
this.createServiceInstance.stepper.next();
// Select Service
- this.setServiceSelection();
- this.createServiceInstance.stepper.next();
+ if (!marketplaceMode) {
+ // Select Service
+ this.setServiceSelection(serviceName);
+ this.createServiceInstance.stepper.next();
+ }
- // Select Service Plan
- this.setServicePlan();
- this.createServiceInstance.stepper.next();
+ // Select Service Plan
+ this.setServicePlan();
+ this.createServiceInstance.stepper.next();
// Bind App
this.createServiceInstance.stepper.isBindAppStepDisabled().then(bindAppDisabled => {
if (!bindAppDisabled) {
- this.setBindApp();
+ this.setBindApp(bindApp);
this.createServiceInstance.stepper.next();
}
@@ -69,53 +81,65 @@ export class ServicesHelperE2E {
this.createServiceInstance.stepper.next();
});
}
-
canBindAppStep = (): promise.Promise => {
return this.cfHelper.fetchDefaultSpaceGuid(true)
.then(spaceGuid => this.cfHelper.fetchAppsCountInSpace(this.cfHelper.cachedDefaultCfGuid, spaceGuid))
.then(totalAppsInSpace => !!totalAppsInSpace);
}
- setServiceInstanceDetail = () => {
+ setServiceInstanceDetail = (isEditServiceInstance = false) => {
this.createServiceInstance.stepper.waitForStep('Service Instance');
expect(this.createServiceInstance.stepper.canPrevious()).toBeTruthy();
- expect(this.createServiceInstance.stepper.canNext()).toBeFalsy();
+ if (!isEditServiceInstance) {
+ expect(this.createServiceInstance.stepper.canNext()).toBeFalsy();
+ } else {
+ expect(this.createServiceInstance.stepper.canNext()).toBeTruthy();
+ }
expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy();
this.createServiceInstance.stepper.setServiceName(this.serviceInstanceName);
- expect(this.createServiceInstance.stepper.canNext()).toBeTruthy();
}
- setBindApp = () => {
+ setBindApp = (bindApp: string = null) => {
this.createServiceInstance.stepper.waitForStep('Bind App (Optional)');
- // Optional step can be skipped
+
+ if (!!bindApp) {
+ this.createServiceInstance.stepper.setBindApp(bindApp);
+ }
expect(this.createServiceInstance.stepper.canPrevious()).toBeTruthy();
expect(this.createServiceInstance.stepper.canNext()).toBeTruthy();
expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy();
}
- setServicePlan = () => {
+ setServicePlan = (isEditServiceInstance = false) => {
this.createServiceInstance.stepper.waitForStep('Select Plan');
// Should have a plan auto-selected
- expect(this.createServiceInstance.stepper.canPrevious()).toBeTruthy();
+ if (!isEditServiceInstance) {
+ expect(this.createServiceInstance.stepper.canPrevious()).toBeTruthy();
+ } else {
+ expect(this.createServiceInstance.stepper.canPrevious()).toBeFalsy();
+ }
expect(this.createServiceInstance.stepper.canNext()).toBeTruthy();
expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy();
}
- setServiceSelection = () => {
- this.createServiceInstance.stepper.waitForStep('Select Service');
+ setServiceSelection = (serviceName: string, expectFailure = false) => {
expect(this.createServiceInstance.stepper.canPrevious()).toBeTruthy();
expect(this.createServiceInstance.stepper.canNext()).toBeFalsy();
- this.createServiceInstance.stepper.setService(e2e.secrets.getDefaultCFEndpoint().testService);
- expect(this.createServiceInstance.stepper.canNext()).toBeTruthy();
- expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy();
+ this.createServiceInstance.stepper.waitForStep('Select Service');
+ this.createServiceInstance.stepper.setService(serviceName, expectFailure);
+ if (!expectFailure) {
+ expect(this.createServiceInstance.stepper.canNext()).toBeTruthy();
+ expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy();
+ }
}
- setCfOrgSpace = () => {
- this.createServiceInstance.stepper.waitForStep('Cloud Foundry');
- expect(this.createServiceInstance.stepper.canNext()).toBeFalsy();
- this.createServiceInstance.stepper.setCf(e2e.secrets.getDefaultCFEndpoint().name);
- this.createServiceInstance.stepper.setOrg(e2e.secrets.getDefaultCFEndpoint().testOrg);
- this.createServiceInstance.stepper.setSpace(e2e.secrets.getDefaultCFEndpoint().testSpace);
+ setCfOrgSpace = (orgName: string = null, spaceName: string = null, marketplaceMode = false) => {
+
+ if (!marketplaceMode) {
+ this.createServiceInstance.stepper.setCf(e2e.secrets.getDefaultCFEndpoint().name);
+ }
+ this.createServiceInstance.stepper.setOrg(!!orgName ? orgName : e2e.secrets.getDefaultCFEndpoint().testOrg);
+ this.createServiceInstance.stepper.setSpace(!!spaceName ? spaceName : e2e.secrets.getDefaultCFEndpoint().testSpace);
expect(this.createServiceInstance.stepper.canNext()).toBeTruthy();
expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy();
}
diff --git a/src/test-e2e/marketplace/services-wall-e2e.spec.ts b/src/test-e2e/marketplace/services-wall-e2e.spec.ts
index 2bb2a21198..58e3f9bc53 100644
--- a/src/test-e2e/marketplace/services-wall-e2e.spec.ts
+++ b/src/test-e2e/marketplace/services-wall-e2e.spec.ts
@@ -27,7 +27,7 @@ describe('Service Instances Wall', () => {
// FIXME: To save time the service should be created via api call
createServiceInstance.navigateTo();
createServiceInstance.waitForPage();
- servicesHelperE2E.createService();
+ servicesHelperE2E.createService(e2e.secrets.getDefaultCFEndpoint().services.publicService.name);
});
});
diff --git a/src/test-e2e/marketplace/services-wall.po.ts b/src/test-e2e/marketplace/services-wall.po.ts
index 33ef779cf2..d77520d645 100644
--- a/src/test-e2e/marketplace/services-wall.po.ts
+++ b/src/test-e2e/marketplace/services-wall.po.ts
@@ -1,6 +1,17 @@
import { Page } from '../po/page.po';
import { ListComponent } from '../po/list.po';
-import { ElementArrayFinder } from 'protractor';
+import { ElementArrayFinder, promise, ElementFinder } from 'protractor';
+import { MetaCard } from '../po/meta-card.po';
+
+export interface ServiceInstance {
+ serviceInstanceName: promise.Promise;
+ spaceName: promise.Promise;
+ serviceName: promise.Promise;
+ planName: promise.Promise;
+ tags?: promise.Promise;
+ applicationsAttached?: promise.Promise;
+ creationDate?: promise.Promise;
+}
export class ServicesWallPage extends Page {
@@ -12,4 +23,15 @@ export class ServicesWallPage extends Page {
getServiceInstances = (): ElementArrayFinder => {
return this.serviceInstancesList.cards.getCards();
}
+
+ getServiceInstanceFromCard = (card: ElementFinder): promise.Promise => {
+ const metaCard = new MetaCard(card);
+ return metaCard.getMetaCardItems().then(items => ({
+ serviceInstanceName: metaCard.getTitle(),
+ spaceName: items[0].value,
+ serviceName: items[1].value,
+ planName: items[2].value,
+ applicationsAttached: items[3].value,
+ }));
+ }
}
diff --git a/src/test-e2e/po/boolean-indicator.po.ts b/src/test-e2e/po/boolean-indicator.po.ts
new file mode 100644
index 0000000000..ada21706e3
--- /dev/null
+++ b/src/test-e2e/po/boolean-indicator.po.ts
@@ -0,0 +1,23 @@
+import { by, ElementFinder, promise } from 'protractor';
+
+import { Component } from './component.po';
+
+
+export class BooleangIndicatorComponent extends Component {
+
+ /**
+ * Page Object for the Boolean Indicator component
+ */
+ constructor(parent: ElementFinder) {
+ super(parent.element(by.css('.boolean-indicator__container')));
+ }
+
+ getLabel(): promise.Promise {
+ return this.locator.element(by.css('div')).getText();
+ }
+
+ getIcon(): promise.Promise {
+ return this.locator.element(by.css('mat-icon')).getText();
+ }
+
+}
diff --git a/src/test-e2e/po/form.po.ts b/src/test-e2e/po/form.po.ts
index a1b263511e..3132dc13f0 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;
@@ -159,7 +163,8 @@ export class FormComponent extends Component {
}
// Fill the form fields in the specified object
- fill(fields: { [fieldKey: string]: string | boolean }): promise.Promise {
+ fill(fields: { [fieldKey: string]: string | boolean }, expectFailure = false): promise.Promise {
+
return this.getControlsMap().then(ctrls => {
Object.keys(fields).forEach(field => {
const ctrl = ctrls[field] as FormItem;
@@ -180,13 +185,21 @@ export class FormComponent extends Component {
ctrl.click();
ctrl.sendKeys(value);
ctrl.sendKeys(Key.RETURN);
- expect(this.getText(field)).toBe(value);
+ if (!expectFailure) {
+ expect(this.getText(field)).toBe(value);
+ } else {
+ expect(this.getText(field)).not.toBe(value);
+ }
break;
default:
ctrl.click();
ctrl.clear();
ctrl.sendKeys(value);
- expect(this.getText(field)).toBe(value);
+ if (!expectFailure) {
+ expect(this.getText(field)).toBe(value);
+ } else {
+ expect(this.getText(field)).not.toBe(value);
+ }
break;
}
});
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/meta-card.po.ts b/src/test-e2e/po/meta-card.po.ts
index 419bc2f597..5db54cff9d 100644
--- a/src/test-e2e/po/meta-card.po.ts
+++ b/src/test-e2e/po/meta-card.po.ts
@@ -2,6 +2,11 @@ import { Component } from './component.po';
import { ElementFinder, element, by, promise } from 'protractor';
import { MenuComponent } from './menu.po';
+export interface MetaCardItem {
+ key: promise.Promise;
+ value: promise.Promise;
+}
+
export class MetaCard extends Component {
constructor(private elementFinder: ElementFinder) {
@@ -21,6 +26,14 @@ export class MetaCard extends Component {
});
}
+ getMetaCardItems(): promise.Promise {
+ const metaCardRows = this.elementFinder.all(by.css('.meta-card-item-row'));
+ return metaCardRows.then((rows: ElementFinder[]) => rows.map( row => ({
+ key: row.element(by.css('.meta-card-item__key')).getText(),
+ value: row.element(by.css('.meta-card-item__value')).getText()
+ })));
+ }
+
click() {
return this.elementFinder.click();
}
diff --git a/src/test-e2e/po/meta-data-time.po.ts b/src/test-e2e/po/meta-data-item.po.ts
similarity index 68%
rename from src/test-e2e/po/meta-data-time.po.ts
rename to src/test-e2e/po/meta-data-item.po.ts
index f76628c65d..172064c132 100644
--- a/src/test-e2e/po/meta-data-time.po.ts
+++ b/src/test-e2e/po/meta-data-item.po.ts
@@ -1,6 +1,7 @@
import { by, ElementFinder, promise } from 'protractor';
import { Component } from './component.po';
+import { BooleangIndicatorComponent } from './boolean-indicator.po';
export class MetaDataItemComponent extends Component {
@@ -16,4 +17,8 @@ export class MetaDataItemComponent extends Component {
return this.locator.element(by.css('.metadata-item__value')).getText();
}
+ public getBooleanIndicator(): BooleangIndicatorComponent {
+ return new BooleangIndicatorComponent(this.locator.element(by.css('.metadata-item__value')));
+ }
+
}
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;
- });
- }
-
-}