From 88a540625ce76cf0f5bf5fc86507b81b7f262512 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 20 Jul 2018 20:39:14 +0100 Subject: [PATCH 001/103] Add test artifact upload --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index a901a5a3e9..deb225fa7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,16 @@ addons: target_paths: - e2e debug: true +deploy: + provider: s3 + access_key_id: + secure: "BFoXWuosoaTrPmNsrheLW7HEKMPDVBluZ7fmNAHaJM97n4pX6rtOMFVgO6NBMj8qzDy4AU//N33bxDGbVHqjRe9ATaSAaRDecGVXtGvRYh5SrvQ1PBs4Eksi8HlTIriWGr/iEQ7EXQFVVDp8OH+0pkEfL3vUeEIDI/N2S4tIAjcrXtpXftOcCxerA5XK+ztI2aoAixvnIbgZQAQaXQkI/5OJ88hpOHObiRwtHfRFHewIAly9+0/7f65G3Bqhs58I5z6GY1OEbmB25RdfujAH7uiinflLuTSUMSXIbHuLGXUb8UfL0isHgAoKC6fnxEkVrg0chJfoVTdvUoEIvb8ISaRsCKWmDBhpo8BLgiuS3thR9IGNhX5h3K9dHG8BlRWTV6nvuEwklfQ7FCE+kHEei9I/jSajgfaTDzFb9/EaD75pAqGF8uvWFI6uMkumiNRAEEdsUOz4oUpUhWzCmEeWu5a4EF/IDhywUO7g/1bhfZ7boWhMeyiGn4Z6TmFm8dwxbYBOZa+9V8A/A2yHZTxvuxxCcWnQiFc/LD/9GhWUZLO+g+9cw7bIBLjJl7XQSDchC6oFpfQGjTbHmdClRDtJfOdnPOi68tMaHUPPYgnHWqIoiW2EtbEKOIgmIC940t+b4YFDGH8VGfKDyOpMp/VqonylL6F//PDXaR3W9rZzCxk=" + secret_access_key: + secure: "j+XQTBcDKntmtCBcrsDi3E58mKBtFnu15ze/BgO8PsL65efS1GI+IA3JJOVbgr1t4jtR2BZ8dz/muK6MQaXT6/vMKcxIM/JTtHSrqOcS2HWn4eMVNhkaZt3tp+gTOw0y0u2DYij2Lv09b1qE9Knn026+KwlxR901PWn741DGLCqFUpm1lJXnH5rCaH2ghEObSWax6MmRRFizgF/struPSEpa+JVAB+5Q88bbJKKzXS0/ROUcydxVH3Q8tKplMR5RBSz+WCk+GyiMHf95ZNhn+p62A6HO7RzyUR5mvVwCWLo4tBHt+fAWURBAUPLT7Mk8ccBQoaTVdvdDENoK3N0IChZ/+z/UlZT9+v3tueYBhuVHc+YtygG3LfDD3NGsLxJ1jgthZ3MzrCRSPpx5wBK/GOmp2k7NaC2wnfO/vSeKsrN+g+QOM19j+AVqEWXNnje9HKAk2XtgP18ApZ23owLEEWVzMeIrain5MNC3gCemPpjs3Z0hiaWGxfnGSp+5GL8JXOY5LiafpAWEZbgWyrKGWfULWCpubAsmbHfXDqSOdyxNxBwOT8FRe3F1G+4OteYOPUJ05z2M7k23gO8OyUQpFxdNIrSihoFBLq4s0a3THl5hwmqSljh+Kda6IRiB58hCrmdQRtGLQ8FFg+TPFMVV9ZXgk+cIPg7M1cINe4AMG18=" + bucket: "e2e" + endpoint: + secure: "hfTvQqu7ODlUR8qqdMkt4TM5EM8hZi+muRbLCIffCOn927Mrw9GhYeFgSzNxxDa7YzU0eGjsTAF7uKPTZ5NfI5TAXsdyl/0ph3iCeoUAp3sdxSLYxS9tCw582saQZ6Gjv/m/5iKEY28neDssDyTii1VLaHwwm/98KBoR+D8DLObShmZxktxqTfPhIWOQfK5YRWKwAR5Rgh4OvieK8pb9WfA2xOARjvRyAiMTSzB3jVBWkZdk51uq9v0/NJZbl6UwReAG3K9egoo5BbZM6EWCbKoHgUKsNADFg9w+nLLWSonNilI0yvAEPjD7YeMAtHAbOFKEMhguKCAwSe+VTJ6+1MQ3mrmQTGdUcXRF1gvHTLjnT1+0OEtrkdmvHyq9/3Kt81IXsXN6j6+oFsjwJtg5HJSADSJ+tGBQ9YujenQTA0rPQS3Zy2Tlj2Lk7CONyMp5SbM2AW1YIP8NBFS9rymvsWrdNB0NS4vxb8RNyo2j4ZQIayCJYhMnPjOIReOtPqyu2nqeTU21DLGoknBibNE2Z9DgZ+7Eih0Gbt044HZZK88zgHshM1Oir3EpbJHEFbBZy94FDWO0VYgH8KdlDs8GpqbHmd/PmxCJTNPmqVOZWNwOvzfwx4tdMEa+W8nHGjZvcpeoqTw6KNqqQnI28gLiy2MEufBtL8MITILsYecRi/s=" + local_dir: e2e-reports before_install: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start From 98852c43110b31f9cbec51130a605780281c892f Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 21 Jul 2018 19:01:54 +0100 Subject: [PATCH 002/103] Test report upload --- .travis.yml | 4 ++++ deploy/ci/travis/upload-e2e-test-report.sh | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100755 deploy/ci/travis/upload-e2e-test-report.sh diff --git a/.travis.yml b/.travis.yml index deb225fa7f..77de8ead11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,11 +73,15 @@ jobs: - chmod +x ./deploy/ci/travis/run-e2e-tests.sh script: - "./deploy/ci/travis/run-e2e-tests.sh" + after_script: + - "./deploy/ci/travis/upload-e2e-test-report.sh" - stage: E2E tests quick before_script: - chmod +x ./deploy/ci/travis/run-e2e-tests.sh script: - "./deploy/ci/travis/run-e2e-tests.sh quick" + after_script: + - "./deploy/ci/travis/upload-e2e-test-report.sh" notifications: slack: secure: s5SFnFKwzfxLrjGR5lJ2AJG1FSWCKtHdQi8K2Kmx5ZhrYL/7P+KLc/ks18WnzCPoy705LbHCBSULcnWbLjqCpnkKxNjsFAyFl2nZZPxBjl2/mHpulbr3gmultDOrMDbmYL4oWPKBlxKResElz9nQwknlLWZ/L94AIx8zuMfRIWdEt1bJBDAQts4fx2D4cIEx0yZUq7JGAKjSiXKR9eDyMWFb+SWw6mvr5WtFM8uq35rPvRVEfm56LIgSuMUpVeYtnYiY2JP7W8iKX0gD+54wAiSXRZiQVCLJq606/TlJo7j8Na9Dn1Q5XDkX3b3XzcgmEZThoO1GFtv3yNYOVxv+50p2tSnc8CT0VEVOYOGJuz17AESZAYK+AyjEmeZmDiroj1czmIq8/ZYKbmvDYSZgGuDcSkQurX/6BPac6ra69WmSQmwv0tS3A/IzDw7X+CuC+3QubQ7GfaiVe25PUU+tRSEDM4PMUJY8QRF5Q+oeN5NjjWmJBqf/ic2TO2xTU1j+qysdqK34qIV1qyVcPMUIiYW+5ltH71qiy05TSvvfGS+oatRBMzINRl3zl2gOV1CKNU801XehRKCx9XDCw5NL1HSx5HD5psOyBRpAMYYBOqa+rv9VAza9MsfpslCoibg5rdrq4rZqqUgRhayNp/LKzlhe/g62+qbGNT+iFuHtB+Y= diff --git a/deploy/ci/travis/upload-e2e-test-report.sh b/deploy/ci/travis/upload-e2e-test-report.sh new file mode 100755 index 0000000000..55addd1740 --- /dev/null +++ b/deploy/ci/travis/upload-e2e-test-report.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -e + +echo "Stratos e2e test report upload" +echo "==============================" + +if [ -z "${AWS_ENDPOINT}" ]; then + echo "Need S3 endpoint URL" + exit 1 +fi + +curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip" +unzip awscli-bundle.zip +./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws + +echo "Uploading test report...." + +# Sync the E2E reports to S3 +aws --endpoint-url ${AWS_ENDPOINT} s3 sync ./e2e-reports s3://${S3_BUCKET} \ No newline at end of file From 3347b0eb59e6d4ab5bd2a5c40472793eeccb76e9 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sun, 22 Jul 2018 14:13:56 +0100 Subject: [PATCH 003/103] Artifact upload --- .travis.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 77de8ead11..5d419658e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,17 +13,8 @@ addons: target_paths: - e2e debug: true -deploy: - provider: s3 - access_key_id: - secure: "BFoXWuosoaTrPmNsrheLW7HEKMPDVBluZ7fmNAHaJM97n4pX6rtOMFVgO6NBMj8qzDy4AU//N33bxDGbVHqjRe9ATaSAaRDecGVXtGvRYh5SrvQ1PBs4Eksi8HlTIriWGr/iEQ7EXQFVVDp8OH+0pkEfL3vUeEIDI/N2S4tIAjcrXtpXftOcCxerA5XK+ztI2aoAixvnIbgZQAQaXQkI/5OJ88hpOHObiRwtHfRFHewIAly9+0/7f65G3Bqhs58I5z6GY1OEbmB25RdfujAH7uiinflLuTSUMSXIbHuLGXUb8UfL0isHgAoKC6fnxEkVrg0chJfoVTdvUoEIvb8ISaRsCKWmDBhpo8BLgiuS3thR9IGNhX5h3K9dHG8BlRWTV6nvuEwklfQ7FCE+kHEei9I/jSajgfaTDzFb9/EaD75pAqGF8uvWFI6uMkumiNRAEEdsUOz4oUpUhWzCmEeWu5a4EF/IDhywUO7g/1bhfZ7boWhMeyiGn4Z6TmFm8dwxbYBOZa+9V8A/A2yHZTxvuxxCcWnQiFc/LD/9GhWUZLO+g+9cw7bIBLjJl7XQSDchC6oFpfQGjTbHmdClRDtJfOdnPOi68tMaHUPPYgnHWqIoiW2EtbEKOIgmIC940t+b4YFDGH8VGfKDyOpMp/VqonylL6F//PDXaR3W9rZzCxk=" - secret_access_key: - secure: "j+XQTBcDKntmtCBcrsDi3E58mKBtFnu15ze/BgO8PsL65efS1GI+IA3JJOVbgr1t4jtR2BZ8dz/muK6MQaXT6/vMKcxIM/JTtHSrqOcS2HWn4eMVNhkaZt3tp+gTOw0y0u2DYij2Lv09b1qE9Knn026+KwlxR901PWn741DGLCqFUpm1lJXnH5rCaH2ghEObSWax6MmRRFizgF/struPSEpa+JVAB+5Q88bbJKKzXS0/ROUcydxVH3Q8tKplMR5RBSz+WCk+GyiMHf95ZNhn+p62A6HO7RzyUR5mvVwCWLo4tBHt+fAWURBAUPLT7Mk8ccBQoaTVdvdDENoK3N0IChZ/+z/UlZT9+v3tueYBhuVHc+YtygG3LfDD3NGsLxJ1jgthZ3MzrCRSPpx5wBK/GOmp2k7NaC2wnfO/vSeKsrN+g+QOM19j+AVqEWXNnje9HKAk2XtgP18ApZ23owLEEWVzMeIrain5MNC3gCemPpjs3Z0hiaWGxfnGSp+5GL8JXOY5LiafpAWEZbgWyrKGWfULWCpubAsmbHfXDqSOdyxNxBwOT8FRe3F1G+4OteYOPUJ05z2M7k23gO8OyUQpFxdNIrSihoFBLq4s0a3THl5hwmqSljh+Kda6IRiB58hCrmdQRtGLQ8FFg+TPFMVV9ZXgk+cIPg7M1cINe4AMG18=" - bucket: "e2e" - endpoint: - secure: "hfTvQqu7ODlUR8qqdMkt4TM5EM8hZi+muRbLCIffCOn927Mrw9GhYeFgSzNxxDa7YzU0eGjsTAF7uKPTZ5NfI5TAXsdyl/0ph3iCeoUAp3sdxSLYxS9tCw582saQZ6Gjv/m/5iKEY28neDssDyTii1VLaHwwm/98KBoR+D8DLObShmZxktxqTfPhIWOQfK5YRWKwAR5Rgh4OvieK8pb9WfA2xOARjvRyAiMTSzB3jVBWkZdk51uq9v0/NJZbl6UwReAG3K9egoo5BbZM6EWCbKoHgUKsNADFg9w+nLLWSonNilI0yvAEPjD7YeMAtHAbOFKEMhguKCAwSe+VTJ6+1MQ3mrmQTGdUcXRF1gvHTLjnT1+0OEtrkdmvHyq9/3Kt81IXsXN6j6+oFsjwJtg5HJSADSJ+tGBQ9YujenQTA0rPQS3Zy2Tlj2Lk7CONyMp5SbM2AW1YIP8NBFS9rymvsWrdNB0NS4vxb8RNyo2j4ZQIayCJYhMnPjOIReOtPqyu2nqeTU21DLGoknBibNE2Z9DgZ+7Eih0Gbt044HZZK88zgHshM1Oir3EpbJHEFbBZy94FDWO0VYgH8KdlDs8GpqbHmd/PmxCJTNPmqVOZWNwOvzfwx4tdMEa+W8nHGjZvcpeoqTw6KNqqQnI28gLiy2MEufBtL8MITILsYecRi/s=" - local_dir: e2e-reports before_install: +- pip install --user awscli - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - sleep 3 # give xvfb some time to start @@ -73,14 +64,12 @@ jobs: - chmod +x ./deploy/ci/travis/run-e2e-tests.sh script: - "./deploy/ci/travis/run-e2e-tests.sh" - after_script: - "./deploy/ci/travis/upload-e2e-test-report.sh" - stage: E2E tests quick before_script: - chmod +x ./deploy/ci/travis/run-e2e-tests.sh script: - "./deploy/ci/travis/run-e2e-tests.sh quick" - after_script: - "./deploy/ci/travis/upload-e2e-test-report.sh" notifications: slack: From 9708aadfd9cde7a344a94bcf3e92a4276b431242 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 31 Jul 2018 11:50:00 +0100 Subject: [PATCH 004/103] Add Metrics chart --- deploy/kubernetes/console/requirements.lock | 6 ++++++ deploy/kubernetes/console/requirements.yaml | 5 +++++ deploy/kubernetes/console/values.yaml | 4 +++- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 deploy/kubernetes/console/requirements.lock create mode 100644 deploy/kubernetes/console/requirements.yaml diff --git a/deploy/kubernetes/console/requirements.lock b/deploy/kubernetes/console/requirements.lock new file mode 100644 index 0000000000..dfaeaa993a --- /dev/null +++ b/deploy/kubernetes/console/requirements.lock @@ -0,0 +1,6 @@ +dependencies: +- name: metrics + repository: https://cloudfoundry-incubator.github.io/stratos + version: 2.0.0-metrics-dev-b5272b2d +digest: sha256:559d3e298b29a1ffc49ed536df54a6613f16dac31ce684312be4501d4ad07e66 +generated: 2018-07-31T11:49:11.224659675+01:00 diff --git a/deploy/kubernetes/console/requirements.yaml b/deploy/kubernetes/console/requirements.yaml new file mode 100644 index 0000000000..9a34cd32e2 --- /dev/null +++ b/deploy/kubernetes/console/requirements.yaml @@ -0,0 +1,5 @@ +dependencies: + - name: metrics + version: 2.0.0-metrics-dev-b5272b2d + repository: https://cloudfoundry-incubator.github.io/stratos + condition: metrics.enabled \ No newline at end of file diff --git a/deploy/kubernetes/console/values.yaml b/deploy/kubernetes/console/values.yaml index 9f28537310..40c95f2ebf 100644 --- a/deploy/kubernetes/console/values.yaml +++ b/deploy/kubernetes/console/values.yaml @@ -77,4 +77,6 @@ kube: password: email: default services: - loadbalanced: false \ No newline at end of file + loadbalanced: false +metrics: + enabled: false \ No newline at end of file From 6e964e9302b9745094d6615a117e5653929bd933 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Wed, 1 Aug 2018 15:00:38 +0100 Subject: [PATCH 005/103] Update requiremens --- deploy/kubernetes/console/requirements.lock | 6 +++--- deploy/kubernetes/console/requirements.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deploy/kubernetes/console/requirements.lock b/deploy/kubernetes/console/requirements.lock index dfaeaa993a..9ca357b07b 100644 --- a/deploy/kubernetes/console/requirements.lock +++ b/deploy/kubernetes/console/requirements.lock @@ -1,6 +1,6 @@ dependencies: - name: metrics repository: https://cloudfoundry-incubator.github.io/stratos - version: 2.0.0-metrics-dev-b5272b2d -digest: sha256:559d3e298b29a1ffc49ed536df54a6613f16dac31ce684312be4501d4ad07e66 -generated: 2018-07-31T11:49:11.224659675+01:00 + version: 2.0.0-metrics-dev +digest: sha256:82dc83e153f6c8115db7c2e0030c9d378bbeaa08bdddad97cf23a559aa9a12c8 +generated: 2018-08-01T15:00:06.437820149+01:00 diff --git a/deploy/kubernetes/console/requirements.yaml b/deploy/kubernetes/console/requirements.yaml index 9a34cd32e2..a3bd2b288a 100644 --- a/deploy/kubernetes/console/requirements.yaml +++ b/deploy/kubernetes/console/requirements.yaml @@ -1,5 +1,5 @@ dependencies: - name: metrics - version: 2.0.0-metrics-dev-b5272b2d + version: 2.0.0-metrics-dev repository: https://cloudfoundry-incubator.github.io/stratos - condition: metrics.enabled \ No newline at end of file + condition: metrics.enabled From 7d0f39a80981144a93568a2f84ba5cc12aaed1e9 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 2 Aug 2018 09:44:26 +0100 Subject: [PATCH 006/103] Add readme --- deploy/kubernetes/README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/deploy/kubernetes/README.md b/deploy/kubernetes/README.md index ec9482c9b5..b03559a474 100644 --- a/deploy/kubernetes/README.md +++ b/deploy/kubernetes/README.md @@ -351,3 +351,30 @@ Install ``` helm install stratos/console --namespace=console --name my-console --version 2.0.0-dev-9a5611dc ``` + +### Enabled Metrics + +[Stratos Metrics](https://github.com/suse/stratos-metrics) can be deployed as part of the Stratos helm chart. The following override file will configure the Metrics component to fetch metrics from a PCF Dev instance. + +Save the following to a file called `values.yaml` +``` +consoleVersion: 2.0.0 +metrics: + enabled: true + env: + CLUSTER_ADMIN_PASSWORD: admin + UAA_CF_IDENTITY_ZONE: uaa + DOMAIN: local.pcfdev.io + UAA_ADMIN_CLIENT_SECRET: admin-client-secret + UAA_HOST: uaa.local.pcfdev.io + UAA_PORT: 443 + DOPPLER_PORT: 443 + firehoseExporter: + noIdentityZone: true +``` + +Deploy Stratos with Metrics enabled +``` +$ helm install ./console -f values.yaml --name console --namespace stratos + +``` \ No newline at end of file From fca107665103ee63d041769ec44a5758be9a6c26 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 2 Aug 2018 11:03:45 +0100 Subject: [PATCH 007/103] Update helm chart --- deploy/kubernetes/README.md | 4 +++- deploy/kubernetes/console/requirements.lock | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/deploy/kubernetes/README.md b/deploy/kubernetes/README.md index b03559a474..942bb936ac 100644 --- a/deploy/kubernetes/README.md +++ b/deploy/kubernetes/README.md @@ -377,4 +377,6 @@ Deploy Stratos with Metrics enabled ``` $ helm install ./console -f values.yaml --name console --namespace stratos -``` \ No newline at end of file +``` + +The metrics endpoint will be available as `https://console-metrics-nginx`. This can registered as an endpoint in Stratos as a `Metrics` type. \ No newline at end of file diff --git a/deploy/kubernetes/console/requirements.lock b/deploy/kubernetes/console/requirements.lock index 9ca357b07b..fe6d63559e 100644 --- a/deploy/kubernetes/console/requirements.lock +++ b/deploy/kubernetes/console/requirements.lock @@ -3,4 +3,4 @@ dependencies: repository: https://cloudfoundry-incubator.github.io/stratos version: 2.0.0-metrics-dev digest: sha256:82dc83e153f6c8115db7c2e0030c9d378bbeaa08bdddad97cf23a559aa9a12c8 -generated: 2018-08-01T15:00:06.437820149+01:00 +generated: 2018-08-02T11:02:52.066583002+01:00 From 1f99b52898f5f9747bb5e97a5cdc8048602d3a71 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 2 Aug 2018 11:19:55 +0100 Subject: [PATCH 008/103] Update concourse pipelines --- deploy/ci/tasks/dev-releases/create-chart-helper.sh | 10 ++++++++++ deploy/ci/tasks/dev-releases/create-chart.yml | 1 + deploy/ci/tasks/dev-releases/create-nightly-chart.yml | 1 + 3 files changed, 12 insertions(+) diff --git a/deploy/ci/tasks/dev-releases/create-chart-helper.sh b/deploy/ci/tasks/dev-releases/create-chart-helper.sh index b93dbe0db7..dac1048775 100644 --- a/deploy/ci/tasks/dev-releases/create-chart-helper.sh +++ b/deploy/ci/tasks/dev-releases/create-chart-helper.sh @@ -30,6 +30,16 @@ setupAndPushChange() { } +updateHelmDependency() { + START_CWD=$(pwd) + cd ${STRATOS}/deploy/kubernetes/console + # Extract helm repo + HELM_REPO=$(cat requirements.yaml | grep repo | sed -e 's/.*repository:\s\(.*\)/\1/p' | head -1) + helm repo add repo ${HELM_REPO} + helm dependency update + cd ${START_CWD} +} + fetchImageTag() { echo "$(cat ${STRATOS}/deploy/ci/tasks/dev-releases/nightly-tag)-$(git rev-parse HEAD | head -c 8)" } diff --git a/deploy/ci/tasks/dev-releases/create-chart.yml b/deploy/ci/tasks/dev-releases/create-chart.yml index dc195af4e6..fbfbe2532a 100644 --- a/deploy/ci/tasks/dev-releases/create-chart.yml +++ b/deploy/ci/tasks/dev-releases/create-chart.yml @@ -31,6 +31,7 @@ run: patchHelmChart ${GIT_TAG} ${DOCKER_ORG} ${DOCKER_REGISTRY} ./console # Generate Helm package + updateHelmDependency helm package console cp console*.tgz ${ROOT_DIR}/helm-chart/console-helm-chart-${GIT_TAG}.tgz cd ${ROOT_DIR}/helm-chart/ diff --git a/deploy/ci/tasks/dev-releases/create-nightly-chart.yml b/deploy/ci/tasks/dev-releases/create-nightly-chart.yml index c868722a87..869cac2f90 100644 --- a/deploy/ci/tasks/dev-releases/create-nightly-chart.yml +++ b/deploy/ci/tasks/dev-releases/create-nightly-chart.yml @@ -30,6 +30,7 @@ run: patchHelmChart ${IMAGE_TAG} ${DOCKER_ORG} ${DOCKER_REGISTRY} ./console # Generate Helm package + updateHelmDependency helm package console cp console*.tgz ${ROOT_DIR}/helm-chart/console-helm-chart-v${IMAGE_TAG}.tgz cd ${ROOT_DIR}/helm-chart/ From a3a0631de82b78b3a84b83bf7340a9909add991e Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Wed, 8 Aug 2018 09:48:31 +0100 Subject: [PATCH 009/103] Update build.sh --- deploy/kubernetes/build.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deploy/kubernetes/build.sh b/deploy/kubernetes/build.sh index 1f09098c3e..d6bfc89430 100755 --- a/deploy/kubernetes/build.sh +++ b/deploy/kubernetes/build.sh @@ -168,6 +168,9 @@ buildPostflightJob buildMariaDb buildUI +# Fetch subcharts +helm dependency update + if [ ${CONCOURSE_BUILD:-"not-set"} == "not-set" ]; then # Patch Values.yaml file cp values.yaml.tmpl values.yaml From b2585506e96d9c1b65dc7e9e1c74a147916e982d Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 9 Aug 2018 18:19:22 +0100 Subject: [PATCH 010/103] Initial test --- .../components/list/list.component.html | 10 +- .../org-level/cf-org-level-e2e.spec.ts | 8 +- .../org-level/org-spaces-e2e.spec.ts | 118 ++++++++++++++++++ .../space-level/cf-space-level-e2e.spec.ts | 8 +- src/test-e2e/e2e.types.ts | 1 + src/test-e2e/helpers/cf-helpers.ts | 49 +++++--- src/test-e2e/po/list.po.ts | 16 ++- 7 files changed, 182 insertions(+), 28 deletions(-) create mode 100644 src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts diff --git a/src/frontend/app/shared/components/list/list.component.html b/src/frontend/app/shared/components/list/list.component.html index 6472c720b7..efe0070804 100644 --- a/src/frontend/app/shared/components/list/list.component.html +++ b/src/frontend/app/shared/components/list/list.component.html @@ -30,18 +30,18 @@
- + {{column.headerCell()}} + sort + + sort +
diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts index da5c0b414d..98860dd63f 100644 --- a/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts @@ -1,8 +1,8 @@ import { browser } from 'protractor'; -import { ApplicationE2eHelper } from '../../application/application-e2e-helpers'; import { e2e, E2ESetup } from '../../e2e'; import { E2EConfigCloudFoundry } from '../../e2e.types'; +import { CFHelpers } from '../../helpers/cf-helpers'; import { ConsoleUserType } from '../../helpers/e2e-helpers'; import { CfOrgLevelPage } from './cf-org-level-page.po'; @@ -12,7 +12,7 @@ describe('CF - Org Level - ', () => { let orgPage: CfOrgLevelPage; let e2eSetup: E2ESetup; let defaultCf: E2EConfigCloudFoundry; - let applicationE2eHelper: ApplicationE2eHelper; + let cfHelper: CFHelpers; function setup(user: ConsoleUserType) { e2eSetup = e2e.setup(ConsoleUserType.admin) @@ -22,7 +22,7 @@ describe('CF - Org Level - ', () => { .connectAllEndpoints(ConsoleUserType.user) .loginAs(user) .getInfo(); - applicationE2eHelper = new ApplicationE2eHelper(e2eSetup); + cfHelper = new CFHelpers(e2eSetup); } function testBreadcrumb() { @@ -43,7 +43,7 @@ describe('CF - Org Level - ', () => { function navToPage() { defaultCf = e2e.secrets.getDefaultCFEndpoint(); const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); - browser.wait(applicationE2eHelper.cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg).then((org => { + browser.wait(cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg).then((org => { orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, org.metadata.guid); orgPage.navigateTo(); orgPage.waitForPageOrChildPage(); diff --git a/src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts new file mode 100644 index 0000000000..e4a2268574 --- /dev/null +++ b/src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts @@ -0,0 +1,118 @@ +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 } 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 customAppLabel = E2EHelpers.e2eItemPrefix + (process.env.CUSTOM_APP_LABEL || process.env.USER) + '-org-spaces-test-'; + +fdescribe('Org Spaces - ', () => { + + let e2eSetup: E2ESetup; + let cfHelper: CFHelpers; + let defaultCf: E2EConfigCloudFoundry; + let orgPage: CfOrgLevelPage; + + const spaceNames = [ + E2EHelpers.createCustomName(customAppLabel + '1'), + E2EHelpers.createCustomName(customAppLabel + '2'), + E2EHelpers.createCustomName(customAppLabel + '3'), + ]; + + function setup() { + defaultCf = e2e.secrets.getDefaultCFEndpoint(); + const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); + browser.wait( + cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg) + .then((org: APIResource) => { + // Chain the creation of the spaces to ensure there's a nice sequential 'created_at' value + const promisesChain = spaceNames.reduce((promiseChain, name) => { + return promiseChain.then(() => { + // Ensure there's a gap so the 'created_at' is different + browser.sleep(1250); + return cfHelper.addSpaceIfMissingForEndpointUsers( + endpointGuid, + org.metadata.guid, + org.entity.name, + name, + defaultCf); + }); + }, promise.fullyResolved('')); + + return promisesChain.then(() => org.metadata.guid); + }) + .then(orgGuid => { + orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, orgGuid); + orgPage.navigateTo(); + orgPage.waitForPageOrChildPage(); + orgPage.loadingIndicator.waitUntilNotShown(); + orgPage.goToSpacesTab(); + }) + ); + } + + function tearDown() { + const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); + spaceNames.forEach(name => cfHelper.deleteSpaceIfExisting(endpointGuid, name)); + } + + + beforeAll(() => { + e2eSetup = e2e.setup(ConsoleUserType.admin) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .connectAllEndpoints(ConsoleUserType.admin) + .loginAs(ConsoleUserType.admin) + .getInfo(); + cfHelper = new CFHelpers(e2eSetup); + }); + + afterAll(() => { + tearDown(); + }); + + function testSortBy(sortFieldName: string) { + const spaceList = new ListComponent(); + expect(spaceList.isTableView()).toBeFalsy(); + + 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); + }); + } + + it('sort by name', () => { + + testSortBy('Name'); + }); + + fit('sort by creation', () => { + setup(); // TODO: RC sort out + + + testSortBy('Creation'); + }); + + +}); + +// TODO: RC empty list diff --git a/src/test-e2e/cloud-foundry/space-level/cf-space-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/space-level/cf-space-level-e2e.spec.ts index 9b0dc55128..0aba429c7b 100644 --- a/src/test-e2e/cloud-foundry/space-level/cf-space-level-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/space-level/cf-space-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 { CfSpaceLevelPage } from './cf-space-level-page.po'; @@ -12,7 +12,7 @@ describe('CF - Space Level - ', () => { let spacePage: CfSpaceLevelPage; 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 - Space Level - ', () => { .connectAllEndpoints(ConsoleUserType.user) .loginAs(user) .getInfo(); - applicationE2eHelper = new ApplicationE2eHelper(e2eSetup); + cfHelper = new CFHelpers(e2eSetup); } function testBreadcrumb() { @@ -46,7 +46,7 @@ describe('CF - Space Level - ', () => { function navToPage() { defaultCf = e2e.secrets.getDefaultCFEndpoint(); const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); - browser.wait(applicationE2eHelper.cfHelper.fetchSpace(endpointGuid, defaultCf.testSpace).then((space => { + browser.wait(cfHelper.fetchSpace(endpointGuid, defaultCf.testSpace).then((space => { spacePage = CfSpaceLevelPage.forEndpoint(endpointGuid, space.entity.organization_guid, space.metadata.guid); spacePage.navigateTo(); spacePage.waitForPageOrChildPage(); diff --git a/src/test-e2e/e2e.types.ts b/src/test-e2e/e2e.types.ts index f7acc561ea..4cb656466e 100644 --- a/src/test-e2e/e2e.types.ts +++ b/src/test-e2e/e2e.types.ts @@ -1,6 +1,7 @@ export interface E2ECred { username: string; password: string; + guid?: string; } export interface E2ECreds { diff --git a/src/test-e2e/helpers/cf-helpers.ts b/src/test-e2e/helpers/cf-helpers.ts index a85b06dd29..9e5ed0c82a 100644 --- a/src/test-e2e/helpers/cf-helpers.ts +++ b/src/test-e2e/helpers/cf-helpers.ts @@ -1,8 +1,10 @@ -import { CFResponse, APIResource } from '../../frontend/app/store/types/api.types'; -import { CFRequestHelpers } from './cf-request-helpers'; -import { e2e, E2ESetup } from '../e2e'; import { promise } from 'protractor'; + +import { APIResource } 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'; export class CFHelpers { @@ -12,18 +14,29 @@ export class CFHelpers { this.cfRequestHelper = new CFRequestHelpers(e2eSetup); } - addOrgIfMissingForEndpointUsers(guid: string, endpoint: E2EConfigCloudFoundry, testOrgName: string) { - 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); + private assignAdminAndUserGuids(cnsiGuid: string, endpoint: E2EConfigCloudFoundry): promise.Promise { + if (endpoint.creds.admin.guid && endpoint.creds.nonAdmin.guid) { + 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(); - return this.addOrgIfMissing(guid, testOrgName, testAdminUser.metadata.guid, testUser.metadata.guid); + endpoint.creds.nonAdmin.guid = testUser.metadata.guid; + endpoint.creds.admin.guid = testAdminUser.metadata.guid; }); } - private findUser(users: any, name: string) { + addOrgIfMissingForEndpointUsers(guid: string, endpoint: E2EConfigCloudFoundry, testOrgName: string) { + return this.assignAdminAndUserGuids(guid, endpoint).then(() => { + expect(endpoint.creds.nonAdmin.guid).not.toBeNull(); + expect(endpoint.creds.admin.guid).not.toBeNull(); + return this.addOrgIfMissing(guid, testOrgName, endpoint.creds.admin.guid, endpoint.creds.nonAdmin.guid); + }); + } + + private findUser(users: any, name: string): APIResource { return users.find(user => user && user.entity && user.entity.username === name); } @@ -52,7 +65,15 @@ export class CFHelpers { }); } - addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, adminGuid, userGuid) { + addSpaceIfMissingForEndpointUsers(cnsiGuid, orgGuid, orgName, spaceName, endpoint: E2EConfigCloudFoundry) { + return this.assignAdminAndUserGuids(cnsiGuid, endpoint).then(() => { + expect(endpoint.creds.nonAdmin.guid).not.toBeNull(); + return this.addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, endpoint.creds.nonAdmin.guid); + }); + } + + addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, userGuid) { + const cfRequestHelper = this.cfRequestHelper; return this.cfRequestHelper.sendCfGet(cnsiGuid, 'spaces?inline-relations-depth=1&include-relations=organization&q=name IN ' + spaceName) .then(function (json) { @@ -65,11 +86,11 @@ export class CFHelpers { }); } if (add) { - return this.cfRequestHelper.sendCfPost(cnsiGuid, 'pp/v1/proxy/v2/spaces', + return cfRequestHelper.sendCfPost(cnsiGuid, 'spaces', { name: spaceName, - manager_guids: [adminGuid], - developer_guids: [userGuid, adminGuid], + manager_guids: [], + developer_guids: [userGuid], organization_guid: orgGuid }); } diff --git a/src/test-e2e/po/list.po.ts b/src/test-e2e/po/list.po.ts index fa836b3f23..005e36c550 100644 --- a/src/test-e2e/po/list.po.ts +++ b/src/test-e2e/po/list.po.ts @@ -1,6 +1,8 @@ -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'; @@ -220,4 +222,16 @@ 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(); + } + } From 804a0b223b356fd3974418c13463ad9495fd77b8 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 10 Aug 2018 18:18:21 +0100 Subject: [PATCH 011/103] WIP --- .../org-level/org-spaces-e2e.spec.ts | 190 +++++++++++++----- .../organizations-spaces-e2e.spec.ts | 2 +- src/test-e2e/po/form.po.ts | 13 +- src/test-e2e/po/list.po.ts | 76 +++++-- src/test-e2e/po/paginator.po.ts | 35 ++-- 5 files changed, 226 insertions(+), 90 deletions(-) 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 index e4a2268574..0e865483f0 100644 --- 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 @@ -17,34 +17,57 @@ fdescribe('Org Spaces - ', () => { let cfHelper: CFHelpers; let defaultCf: E2EConfigCloudFoundry; let orgPage: CfOrgLevelPage; + const spaceList = new ListComponent(); + + function createSpaceNames(count: number): string[] { + const spaceNames = []; + for (let i = 0; i < count; i++) { + spaceNames.push(E2EHelpers.createCustomName(customAppLabel + i)); + } + return spaceNames; + } + + function chainCreateSpace(endpointGuid: string, 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); + }); + }, promise.fullyResolved('')); + } + + function concurrentCreateSpace(endpointGuid: string, org: APIResource, spaceNames: string[]): promise.Promise { + return promise.all(spaceNames.map(name => cfHelper.addSpaceIfMissingForEndpointUsers( + endpointGuid, + org.metadata.guid, + org.entity.name, + name, + defaultCf))); + } - const spaceNames = [ - E2EHelpers.createCustomName(customAppLabel + '1'), - E2EHelpers.createCustomName(customAppLabel + '2'), - E2EHelpers.createCustomName(customAppLabel + '3'), - ]; - function setup() { + + function setup(spaceNames: string[], orderImportant: boolean) { defaultCf = e2e.secrets.getDefaultCFEndpoint(); const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); browser.wait( cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg) .then((org: APIResource) => { + if (orderImportant) { + + } // Chain the creation of the spaces to ensure there's a nice sequential 'created_at' value - const promisesChain = spaceNames.reduce((promiseChain, name) => { - return promiseChain.then(() => { - // Ensure there's a gap so the 'created_at' is different - browser.sleep(1250); - return cfHelper.addSpaceIfMissingForEndpointUsers( - endpointGuid, - org.metadata.guid, - org.entity.name, - name, - defaultCf); - }); - }, promise.fullyResolved('')); - - return promisesChain.then(() => org.metadata.guid); + const promises = orderImportant ? + chainCreateSpace(endpointGuid, org, spaceNames) : + concurrentCreateSpace(endpointGuid, org, spaceNames); + + return promises.then(() => org.metadata.guid); }) .then(orgGuid => { orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, orgGuid); @@ -52,16 +75,16 @@ fdescribe('Org Spaces - ', () => { orgPage.waitForPageOrChildPage(); orgPage.loadingIndicator.waitUntilNotShown(); orgPage.goToSpacesTab(); + expect(spaceList.isTableView()).toBeFalsy(); }) ); } - function tearDown() { + function tearDown(spaceNames: string[]) { const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); spaceNames.forEach(name => cfHelper.deleteSpaceIfExisting(endpointGuid, name)); } - beforeAll(() => { e2eSetup = e2e.setup(ConsoleUserType.admin) .clearAllEndpoints() @@ -72,46 +95,117 @@ fdescribe('Org Spaces - ', () => { cfHelper = new CFHelpers(e2eSetup); }); - afterAll(() => { - tearDown(); - }); + describe('Single Page', () => { - function testSortBy(sortFieldName: string) { - const spaceList = new ListComponent(); - expect(spaceList.isTableView()).toBeFalsy(); + let spaceNames; - const sortFieldForm = spaceList.header.getSortFieldForm(); - sortFieldForm.fill({ 'sort-field': sortFieldName }); + 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]; - } + 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(spaceNames, true); + expect(spaceList.getTotalResults()).toBeLessThanOrEqual(9); + expect(spaceList.pagination.isPresent()).toBeFalsy(); }); - spaceList.header.toggleSortOrder(); + afterAll(() => tearDown(spaceNames)); - spaceList.cards.getCardsMetadata().then(cards => { - const newTitleOrder = cards.map(card => card.title); - expect(expectedTitleOrder).toEqual(newTitleOrder); + + it('sort by name', () => { + testSortBy('Name'); }); - } - it('sort by name', () => { + it('sort by creation', () => { + testSortBy('Creation'); + }); - testSortBy('Name'); - }); + 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); - fit('sort by creation', () => { - setup(); // TODO: RC sort out + // 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); + + // Check for zero cards + expect(spaceList.header.getSearchText()).toEqual(spaceToNotFind); + expect(spaceList.cards.getCardCount()).toBe(0); + }); + + it('single page pagination settings', () => { + + + + }); - testSortBy('Creation'); }); + fdescribe('Multi Page', () => { + + let spaceNames; + + beforeAll(() => { + spaceNames = createSpaceNames(11); + setup(spaceNames, false); + expect(spaceList.getTotalResults()).toBeGreaterThanOrEqual(spaceNames.length); + expect(spaceList.pagination.isPresent()).toBeTruthy(); + + }); + + afterAll(() => tearDown(spaceNames)); + + fit('Pagination', () => { + // Initial values + expect(spaceList.pagination.getPageSizeForm().getText('mat-select-1')).toEqual('9'); + expect(spaceList.pagination.getTotalResults()).toBeGreaterThan(9); + // lower - upper pages + // Buttons + // browser.sleep(20000); + 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(); + + }); + + }); }); 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..272422a4a6 100644 --- a/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts @@ -162,7 +162,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/po/form.po.ts b/src/test-e2e/po/form.po.ts index bb0dad3957..0591c22528 100644 --- a/src/test-e2e/po/form.po.ts +++ b/src/test-e2e/po/form.po.ts @@ -67,11 +67,14 @@ export class FormComponent extends Component { // 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]) => { + console.log(id); + const nameAtt = name || formcontrolname || id; + return nameAtt.toLowerCase() === ctrlName; }); })); expect(fields.count()).toBe(1); diff --git a/src/test-e2e/po/list.po.ts b/src/test-e2e/po/list.po.ts index 005e36c550..b2d39c8be1 100644 --- a/src/test-e2e/po/list.po.ts +++ b/src/test-e2e/po/list.po.ts @@ -4,7 +4,6 @@ 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; @@ -25,11 +24,14 @@ export class ListComponent extends Component { public header: ListHeaderComponent; + public pagination: ListPaginationComponent; + 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); } isTableView(): promise.Promise { @@ -49,10 +51,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) { @@ -126,7 +128,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(); @@ -165,21 +167,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 { @@ -189,8 +187,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 { @@ -235,3 +241,45 @@ export class ListHeaderComponent extends Component { } } + +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')); + } + + 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'))); + } + +} diff --git a/src/test-e2e/po/paginator.po.ts b/src/test-e2e/po/paginator.po.ts index 0bc9502cd0..31f1c81d30 100644 --- a/src/test-e2e/po/paginator.po.ts +++ b/src/test-e2e/po/paginator.po.ts @@ -1,26 +1,17 @@ -// -import { protractor, ElementFinder, ElementArrayFinder } from 'protractor/built'; -import { browser, element, by, promise } from 'protractor'; -import { Component } from './component.po'; +// // +// 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 { +// /** +// * Page Object for paginator component +// */ +// export class PaginatorComponent extends Component { - constructor() { - super(element(by.css('.mat-paginator'))); - } +// 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; - }); - } -} + +// } From bf2d78c2583cb54031361a806ab306e47874f95e Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 13 Aug 2018 13:50:13 +0100 Subject: [PATCH 012/103] Fix the edit of users by an admin at cf/org levels - Fix false warning for too many roles - Fix comparison (handle situation where there's no missing users) - Fix process of creating a pagination section from a parent. - Fetch org spaces.. - Org entity has spaces param - Populate space pagination section unsing org entity's spaces --- .../manage-users-modify.component.html | 2 +- .../manage-users-modify.component.ts | 8 +++++--- .../helpers/entity-relations/entity-relations.ts | 12 ++++++------ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/users/manage-users/manage-users-modify/manage-users-modify.component.html b/src/frontend/app/features/cloud-foundry/users/manage-users/manage-users-modify/manage-users-modify.component.html index 666397b387..0b0a2a5a70 100644 --- a/src/frontend/app/features/cloud-foundry/users/manage-users/manage-users-modify/manage-users-modify.component.html +++ b/src/frontend/app/features/cloud-foundry/users/manage-users/manage-users-modify/manage-users-modify.component.html @@ -2,7 +2,7 @@ Selected Users:
-
* Not all roles are known for user.
+
* Not all roles are known for user.
diff --git a/src/frontend/app/features/cloud-foundry/users/manage-users/manage-users-modify/manage-users-modify.component.ts b/src/frontend/app/features/cloud-foundry/users/manage-users/manage-users-modify/manage-users-modify.component.ts index 1be3529e91..2afbcb53a4 100644 --- a/src/frontend/app/features/cloud-foundry/users/manage-users/manage-users-modify/manage-users-modify.component.ts +++ b/src/frontend/app/features/cloud-foundry/users/manage-users/manage-users-modify/manage-users-modify.component.ts @@ -169,6 +169,7 @@ export class UsersRolesModifyComponent implements OnInit, OnDestroy { } const users$: Observable = this.store.select(selectUsersRolesPicked).pipe( + filter(users => !!users), distinctUntilChanged(), map(users => users.map(this.mapUser.bind(this))) ); @@ -190,9 +191,10 @@ export class UsersRolesModifyComponent implements OnInit, OnDestroy { private mapUser(user: CfUser): CfUserWithWarning { // If we're at the org level or lower we guarantee org roles. If we're at the space we guarantee space roles. - const showWarning = user.missingRoles && - (user.missingRoles.org.length && !this.activeRouteCfOrgSpace.orgGuid) || - (user.missingRoles.space.length && !this.activeRouteCfOrgSpace.spaceGuid); + + const showWarning = !!user.missingRoles && + ((user.missingRoles.org.length && !this.activeRouteCfOrgSpace.orgGuid) || + (user.missingRoles.space.length && !this.activeRouteCfOrgSpace.spaceGuid)); // Ensure we're in an object where the username is always populated (in some cases it's missing) const newUser = { ...user, 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], From 8aab3ee641362c1a016abe64c4552d510c8c2331 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 13 Aug 2018 14:27:36 +0100 Subject: [PATCH 013/103] Fix disable of org role pill cross - Org user cross should be disabled if there are other roles within the same organisation OR the organisation has space roles - Fix 1 - Enable cross of org user pill if the only org role is itself - Fix 2 - When checking if an organisation has space roles ignore spaces from other organisations --- .../list-types/cf-users/cf-permission-cell.ts | 8 ++++---- .../shared/data-services/cf-user.service.ts | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/frontend/app/shared/components/list/list-types/cf-users/cf-permission-cell.ts b/src/frontend/app/shared/components/list/list-types/cf-users/cf-permission-cell.ts index d78b85bbbc..f4f3536006 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-users/cf-permission-cell.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-users/cf-permission-cell.ts @@ -75,17 +75,17 @@ export abstract class CfPermissionCell extends TableCellCustom !can), - switchMap(can => { - if (!can) { + switchMap(hide => { + if (!hide) { if (perm.string === UserRoleLabels.org.short.users) { // If there are other roles than Org User, disable clear button return this.userEntity.pipe( filter(p => !!p), - map((entity: CfUser) => this.cfUserService.hasRolesInOrg(entity, perm.orgGuid)), + map((entity: CfUser) => this.cfUserService.hasRolesInOrg(entity, perm.orgGuid, true)), ); } } - return observableOf(can); + return observableOf(hide); }) ); return chipConfig; 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..dc2ecca44b 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,22 @@ export class CfUserService { } // Check space roles - if (this.populatedArray(user.audited_spaces) || - this.populatedArray(user.managed_spaces) || - this.populatedArray(user.spaces)) { + return this.hasSpaceRolesInOrg(user, orgGuid); + } + + 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 { + if (this.populatedArray(this.filterByOrg(orgGuid, user.audited_spaces)) || + this.populatedArray(this.filterByOrg(orgGuid, user.managed_spaces)) || + this.populatedArray(this.filterByOrg(orgGuid, user.spaces))) { return true; } - - return false; } getUserRoleInOrg = ( From 6fb32a1e533d3a52c3513503c40912f4ea13b77f Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Mon, 13 Aug 2018 14:31:40 +0100 Subject: [PATCH 014/103] Added endpoint logged in user's username and if they're an admin --- .../features/endpoints/endpoint-helpers.ts | 12 ++++++++ .../endpoint/endpoints-list-config.service.ts | 30 +++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/frontend/app/features/endpoints/endpoint-helpers.ts b/src/frontend/app/features/endpoints/endpoint-helpers.ts index 0a3f58b46b..cccbfe42a5 100644 --- a/src/frontend/app/features/endpoints/endpoint-helpers.ts +++ b/src/frontend/app/features/endpoints/endpoint-helpers.ts @@ -5,6 +5,18 @@ export function getFullEndpointApiUrl(endpoint: EndpointModel) { return endpoint && endpoint.api_endpoint ? `${endpoint.api_endpoint.Scheme}://${endpoint.api_endpoint.Host}` : 'Unknown'; } +export function getEndpointUsername(endpoint: EndpointModel) { + return endpoint && endpoint.user ? endpoint.user.name : '-'; +} + +export function getEndpointIsAdminString(endpoint: EndpointModel) { + if (!endpoint || !endpoint.user) { + return '-'; + } + return endpoint.user.admin ? 'Yes' : 'No'; +} + + export const DEFAULT_ENDPOINT_TYPE = 'cf'; export interface EndpointTypeHelper { diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts index 52b0aa84ea..3e68ed8051 100644 --- a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts @@ -8,7 +8,7 @@ import { CurrentUserPermissionsService } from '../../../../../core/current-user- import { ConnectEndpointDialogComponent, } from '../../../../../features/endpoints/connect-endpoint-dialog/connect-endpoint-dialog.component'; -import { getFullEndpointApiUrl, getNameForEndpointType } from '../../../../../features/endpoints/endpoint-helpers'; +import { getFullEndpointApiUrl, getNameForEndpointType, getEndpointUsername, getEndpointIsAdminString } from '../../../../../features/endpoints/endpoint-helpers'; import { DisconnectEndpoint, UnregisterEndpoint } from '../../../../../store/actions/endpoint.actions'; import { ShowSnackBar } from '../../../../../store/actions/snackBar.actions'; import { GetSystemInfo } from '../../../../../store/actions/system.actions'; @@ -71,6 +71,32 @@ export const endpointColumns: ITableColumn[] = [ }, cellFlex: '2' }, + { + columnId: 'username', + headerCell: () => 'Username', + cellDefinition: { + getValue: getEndpointUsername + }, + sort: { + type: 'sort', + orderKey: 'username', + field: 'user.name' + }, + cellFlex: '2' + }, + { + columnId: 'user-type', + headerCell: () => 'Admin', + cellDefinition: { + getValue: getEndpointIsAdminString + }, + sort: { + type: 'sort', + orderKey: 'user-type', + field: 'user.admin' + }, + cellFlex: '2' + }, { columnId: 'address', headerCell: () => 'Address', @@ -139,7 +165,7 @@ export class EndpointsListConfigService implements IListConfig { private listActionConnect: IListAction = { action: (item) => { - const dialogRef = this.dialog.open(ConnectEndpointDialogComponent, { + this.dialog.open(ConnectEndpointDialogComponent, { data: { name: item.name, guid: item.guid, From 6160f9c5b3cd880c7748e79f3cd9c11f727f4f78 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 13 Aug 2018 17:33:03 +0100 Subject: [PATCH 015/103] WIP --- .../org-level/org-spaces-e2e.spec.ts | 119 +++++++++++++----- src/test-e2e/po/form.po.ts | 5 +- src/test-e2e/po/list.po.ts | 8 ++ 3 files changed, 102 insertions(+), 30 deletions(-) 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 index 0e865483f0..93c191f5f5 100644 --- 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 @@ -9,7 +9,7 @@ import { ConsoleUserType, E2EHelpers } from '../../helpers/e2e-helpers'; import { ListComponent } from '../../po/list.po'; import { CfOrgLevelPage } from './cf-org-level-page.po'; -const customAppLabel = E2EHelpers.e2eItemPrefix + (process.env.CUSTOM_APP_LABEL || process.env.USER) + '-org-spaces-test-'; +const customOrgSpacesLabel = E2EHelpers.e2eItemPrefix + (process.env.CUSTOM_APP_LABEL || process.env.USER) + '-org-spaces-test-'; fdescribe('Org Spaces - ', () => { @@ -22,7 +22,7 @@ fdescribe('Org Spaces - ', () => { function createSpaceNames(count: number): string[] { const spaceNames = []; for (let i = 0; i < count; i++) { - spaceNames.push(E2EHelpers.createCustomName(customAppLabel + i)); + spaceNames.push(E2EHelpers.createCustomName(customOrgSpacesLabel + i)); } return spaceNames; } @@ -51,17 +51,12 @@ fdescribe('Org Spaces - ', () => { defaultCf))); } - - function setup(spaceNames: string[], orderImportant: boolean) { defaultCf = e2e.secrets.getDefaultCFEndpoint(); const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); browser.wait( cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg) .then((org: APIResource) => { - if (orderImportant) { - - } // Chain the creation of the spaces to ensure there's a nice sequential 'created_at' value const promises = orderImportant ? chainCreateSpace(endpointGuid, org, spaceNames) : @@ -69,17 +64,19 @@ fdescribe('Org Spaces - ', () => { return promises.then(() => org.metadata.guid); }) - .then(orgGuid => { - orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, orgGuid); - orgPage.navigateTo(); - orgPage.waitForPageOrChildPage(); - orgPage.loadingIndicator.waitUntilNotShown(); - orgPage.goToSpacesTab(); - expect(spaceList.isTableView()).toBeFalsy(); - }) + .then(orgGuid => navToOrgSpaces(endpointGuid, orgGuid)) ); } + function navToOrgSpaces(endpointGuid: string, orgGuid: string) { + orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, orgGuid); + orgPage.navigateTo(); + orgPage.waitForPageOrChildPage(); + orgPage.loadingIndicator.waitUntilNotShown(); + orgPage.goToSpacesTab(); + expect(spaceList.isTableView()).toBeFalsy(); + } + function tearDown(spaceNames: string[]) { const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); spaceNames.forEach(name => cfHelper.deleteSpaceIfExisting(endpointGuid, name)); @@ -95,7 +92,31 @@ fdescribe('Org Spaces - ', () => { cfHelper = new CFHelpers(e2eSetup); }); - describe('Single Page', () => { + describe('No Pages -', () => { + const orgName = customOrgSpacesLabel; + let endpointGuid; + beforeAll(() => { + defaultCf = e2e.secrets.getDefaultCFEndpoint(); + endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); + // Create a temporary org which will contain no spaces + browser.wait(cfHelper.addOrgIfMissingForEndpointUsers(endpointGuid, defaultCf, orgName)); + }); + + beforeEach(() => { + navToOrgSpaces(endpointGuid); + }); + + it('Should show message', () => { + + }); + + afterAll(() => { + const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); + cfHelper.deleteSpaceIfExisting(endpointGuid, orgName); + }); + }); + + describe('Single Page -', () => { let spaceNames; @@ -124,7 +145,7 @@ fdescribe('Org Spaces - ', () => { spaceNames = createSpaceNames(3); setup(spaceNames, true); expect(spaceList.getTotalResults()).toBeLessThanOrEqual(9); - expect(spaceList.pagination.isPresent()).toBeFalsy(); + expect(spaceList.pagination.isDisplayed()).toBeFalsy(); }); afterAll(() => tearDown(spaceNames)); @@ -170,14 +191,12 @@ fdescribe('Org Spaces - ', () => { }); it('single page pagination settings', () => { - - - + expect(spaceList.pagination.isDisplayed()).toBeFalsy(); }); }); - fdescribe('Multi Page', () => { + describe('Multi Page -', () => { let spaceNames; @@ -185,23 +204,67 @@ fdescribe('Org Spaces - ', () => { spaceNames = createSpaceNames(11); setup(spaceNames, false); expect(spaceList.getTotalResults()).toBeGreaterThanOrEqual(spaceNames.length); - expect(spaceList.pagination.isPresent()).toBeTruthy(); }); afterAll(() => tearDown(spaceNames)); - fit('Pagination', () => { - // Initial values - expect(spaceList.pagination.getPageSizeForm().getText('mat-select-1')).toEqual('9'); + 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); - // lower - upper pages - // Buttons - // browser.sleep(20000); + 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); + + afterEach(testStartingPosition); + + 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/po/form.po.ts b/src/test-e2e/po/form.po.ts index 0591c22528..32cfabade6 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; } /** @@ -61,6 +62,7 @@ export class FormComponent extends Component { clear: elm.clear, click: elm.click, tag: elm.getTagName(), + id: elm.getAttribute('id') }; } @@ -72,7 +74,6 @@ export class FormComponent extends Component { elm.getAttribute('formcontrolname'), elm.getAttribute('id') ]).then(([name, formcontrolname, id]) => { - console.log(id); const nameAtt = name || formcontrolname || id; return nameAtt.toLowerCase() === ctrlName; }); @@ -119,7 +120,7 @@ export class FormComponent extends Component { return this.getFieldsMapped().then(items => { const form = {}; items.forEach((item: FormItem) => { - const id = item.name || item.formControlName; + const id = item.name || item.formControlName || item.id; form[id.toLowerCase()] = item; }); return form; diff --git a/src/test-e2e/po/list.po.ts b/src/test-e2e/po/list.po.ts index b2d39c8be1..930c6ae106 100644 --- a/src/test-e2e/po/list.po.ts +++ b/src/test-e2e/po/list.po.ts @@ -262,6 +262,14 @@ export class ListPaginationComponent extends Component { 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()); } From 9f0a881c5afe54d87be7834bf8f3e865651b9f34 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Mon, 13 Aug 2018 18:13:37 +0100 Subject: [PATCH 016/103] WIP --- src/test-e2e/e2e.types.ts | 13 ++- .../create-service-instance-e2e.spec.ts | 8 +- ...reate-service-instance-private-e2e.spec.ts | 97 +++++++++++++++++++ ...-service-instance-space-scoped-e2e.spec.ts | 70 +++++++++++++ .../create-service-instance-stepper.po.ts | 4 +- .../marketplace/services-helper-e2e.ts | 22 +++-- .../marketplace/services-wall-e2e.spec.ts | 2 +- src/test-e2e/po/form.po.ts | 14 ++- 8 files changed, 209 insertions(+), 21 deletions(-) create mode 100644 src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts create mode 100644 src/test-e2e/marketplace/create-service-instance-space-scoped-e2e.spec.ts diff --git a/src/test-e2e/e2e.types.ts b/src/test-e2e/e2e.types.ts index f7acc561ea..066125cb16 100644 --- a/src/test-e2e/e2e.types.ts +++ b/src/test-e2e/e2e.types.ts @@ -15,11 +15,22 @@ export interface E2EEndpointConfig { creds: E2ECreds; } +export interface ServiceConfig { + invalidOrgName?: string; + invalidSpaceName?: string; + name: string; +} +export interface E2EServicesConfig { + 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/marketplace/create-service-instance-e2e.spec.ts b/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts index bfeec7e71e..fbeb95d857 100644 --- a/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts +++ b/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts @@ -29,7 +29,7 @@ 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.isActivePage(); @@ -67,7 +67,7 @@ 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(); @@ -83,7 +83,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 @@ -103,7 +103,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 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..f9f53bf295 --- /dev/null +++ b/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts @@ -0,0 +1,97 @@ +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'; + +fdescribe('Create Service Instance of Private Service', () => { + const createServiceInstance = new CreateServiceInstance(); + const servicesWall = new ServicesWallPage(); + let servicesHelperE2E: ServicesHelperE2E; + beforeAll(() => { + const e2eSetup = e2e.setup(ConsoleUserType.admin) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .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); + + servicesWall.isActivePage(); + + 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.isActivePage(); + + }); + + 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.isActivePage(); + + }); + + 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..ffb2494eac --- /dev/null +++ b/src/test-e2e/marketplace/create-service-instance-space-scoped-e2e.spec.ts @@ -0,0 +1,70 @@ +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'; + +fdescribe('Create Service Instance of Space Scoped Service', () => { + const createServiceInstance = new CreateServiceInstance(); + const servicesWall = new ServicesWallPage(); + let servicesHelperE2E: ServicesHelperE2E; + beforeAll(() => { + const e2eSetup = e2e.setup(ConsoleUserType.admin) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .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.isActivePage(); + + 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 1ac4e872e7..3af54cecb4 100644 --- a/src/test-e2e/marketplace/create-service-instance-stepper.po.ts +++ b/src/test-e2e/marketplace/create-service-instance-stepper.po.ts @@ -27,8 +27,8 @@ 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) => { diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index de425948ba..afb5c0641f 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -1,7 +1,7 @@ import { CFResponse } from '../../frontend/app/store/types/api.types'; import { E2ESetup, e2e } from '../e2e'; import { CFRequestHelpers } from '../helpers/cf-request-helpers'; -import { promise } from 'protractor'; +import { promise, browser } from 'protractor'; import { CFHelpers } from '../helpers/cf-helpers'; import { CreateServiceInstance } from './create-service-instance.po'; @@ -41,14 +41,14 @@ export class ServicesHelperE2E { ); } - createService = () => { + createService = (serviceName: string) => { // Select CF/Org/Space this.setCfOrgSpace(); this.createServiceInstance.stepper.next(); // Select Service - this.setServiceSelection(); + this.setServiceSelection(serviceName); this.createServiceInstance.stepper.next(); // Select Service Plan @@ -89,20 +89,22 @@ export class ServicesHelperE2E { expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy(); } - setServiceSelection = () => { + setServiceSelection = (serviceName: string, expectFailure = false) => { expect(this.createServiceInstance.stepper.canPrevious()).toBeTruthy(); expect(this.createServiceInstance.stepper.canNext()).toBeFalsy(); this.createServiceInstance.stepper.waitForStep('Select Service'); - this.createServiceInstance.stepper.setService(e2e.secrets.getDefaultCFEndpoint().testService); - expect(this.createServiceInstance.stepper.canNext()).toBeTruthy(); - expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy(); + this.createServiceInstance.stepper.setService(serviceName, expectFailure); + if (!expectFailure) { + expect(this.createServiceInstance.stepper.canNext()).toBeTruthy(); + expect(this.createServiceInstance.stepper.canCancel()).toBeTruthy(); + } } - setCfOrgSpace = () => { + setCfOrgSpace = (orgName: string = null, spaceName: string = null) => { 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); + 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 fb2a970efa..9191d4a010 100644 --- a/src/test-e2e/marketplace/services-wall-e2e.spec.ts +++ b/src/test-e2e/marketplace/services-wall-e2e.spec.ts @@ -23,7 +23,7 @@ describe('Service Instances Wall', () => { servicesHelperE2E = new ServicesHelperE2E(e2eSetup, new CreateServiceInstance()); createServiceInstance.navigateTo(); createServiceInstance.waitForPage(); - servicesHelperE2E.createService(); + servicesHelperE2E.createService(e2e.secrets.getDefaultCFEndpoint().services.publicService.name); }); beforeEach(() => { diff --git a/src/test-e2e/po/form.po.ts b/src/test-e2e/po/form.po.ts index bb0dad3957..3bdf4d4076 100644 --- a/src/test-e2e/po/form.po.ts +++ b/src/test-e2e/po/form.po.ts @@ -124,7 +124,7 @@ 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; @@ -145,13 +145,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; } }); From c7b1b992cbbdf38d981091bdd0ae92b72621a40b Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 14 Aug 2018 09:10:31 +0100 Subject: [PATCH 017/103] linting fix --- .../list-types/endpoint/endpoints-list-config.service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts index 3e68ed8051..39d29edf07 100644 --- a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts @@ -8,7 +8,12 @@ import { CurrentUserPermissionsService } from '../../../../../core/current-user- import { ConnectEndpointDialogComponent, } from '../../../../../features/endpoints/connect-endpoint-dialog/connect-endpoint-dialog.component'; -import { getFullEndpointApiUrl, getNameForEndpointType, getEndpointUsername, getEndpointIsAdminString } from '../../../../../features/endpoints/endpoint-helpers'; +import { + getFullEndpointApiUrl, + getNameForEndpointType, + getEndpointUsername, + getEndpointIsAdminString +} from '../../../../../features/endpoints/endpoint-helpers'; import { DisconnectEndpoint, UnregisterEndpoint } from '../../../../../store/actions/endpoint.actions'; import { ShowSnackBar } from '../../../../../store/actions/snackBar.actions'; import { GetSystemInfo } from '../../../../../store/actions/system.actions'; From 91fb0f16c55462a2f3bedf955d340db9ef4b1e8a Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 14 Aug 2018 11:26:18 +0100 Subject: [PATCH 018/103] Add test for 'no entities' list --- .../org-level/org-spaces-e2e.spec.ts | 31 +++++++++++-------- src/test-e2e/helpers/cf-helpers.ts | 29 +++++++++++------ src/test-e2e/helpers/cf-request-helpers.ts | 5 +-- src/test-e2e/po/list.po.ts | 13 ++++++++ 4 files changed, 54 insertions(+), 24 deletions(-) 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 index 93c191f5f5..6e6f54c8aa 100644 --- 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 @@ -2,16 +2,16 @@ 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 } from '../../e2e'; +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-'; +const customOrgSpacesLabel = E2EHelpers.e2eItemPrefix + (process.env.CUSTOM_APP_LABEL || process.env.USER) + '-org-spaces-test'; -fdescribe('Org Spaces - ', () => { +fdescribe('Org Spaces List- ', () => { let e2eSetup: E2ESetup; let cfHelper: CFHelpers; @@ -93,25 +93,27 @@ fdescribe('Org Spaces - ', () => { }); describe('No Pages -', () => { - const orgName = customOrgSpacesLabel; - let endpointGuid; + const orgName = E2EHelpers.createCustomName(customOrgSpacesLabel); + let endpointGuid, orgGuid; beforeAll(() => { defaultCf = e2e.secrets.getDefaultCFEndpoint(); endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); // Create a temporary org which will contain no spaces - browser.wait(cfHelper.addOrgIfMissingForEndpointUsers(endpointGuid, defaultCf, orgName)); + browser.wait(cfHelper.addOrgIfMissingForEndpointUsers(endpointGuid, defaultCf, orgName).then(res => orgGuid = res.metadata.guid)); }); beforeEach(() => { - navToOrgSpaces(endpointGuid); + navToOrgSpaces(endpointGuid, orgGuid); }); - it('Should show message', () => { - + 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(() => { - const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); cfHelper.deleteSpaceIfExisting(endpointGuid, orgName); }); }); @@ -185,9 +187,14 @@ fdescribe('Org Spaces - ', () => { const spaceToNotFind = 'sdfst4654324543224 s5d4x4g5g gdg4fdg 5fdg'; spaceList.header.setSearchText(spaceToNotFind); - // Check for zero cards 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', () => { @@ -271,5 +278,3 @@ fdescribe('Org Spaces - ', () => { }); }); - -// TODO: RC empty list diff --git a/src/test-e2e/helpers/cf-helpers.ts b/src/test-e2e/helpers/cf-helpers.ts index 9e5ed0c82a..39414164e5 100644 --- a/src/test-e2e/helpers/cf-helpers.ts +++ b/src/test-e2e/helpers/cf-helpers.ts @@ -1,10 +1,11 @@ import { promise } from 'protractor'; -import { APIResource } from '../../frontend/app/store/types/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'; +import { IOrganization, ISpace } from '../../frontend/app/core/cf-api.types'; export class CFHelpers { @@ -28,7 +29,11 @@ export class CFHelpers { }); } - addOrgIfMissingForEndpointUsers(guid: string, endpoint: E2EConfigCloudFoundry, testOrgName: string) { + addOrgIfMissingForEndpointUsers( + guid: string, + endpoint: E2EConfigCloudFoundry, + testOrgName: string + ): promise.Promise> { return this.assignAdminAndUserGuids(guid, endpoint).then(() => { expect(endpoint.creds.nonAdmin.guid).not.toBeNull(); expect(endpoint.creds.admin.guid).not.toBeNull(); @@ -40,21 +45,21 @@ export class CFHelpers { return users.find(user => user && user.entity && user.entity.username === name); } - addOrgIfMissing(cnsiGuid, orgName, adminGuid, userGuid) { + 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) { added = true; - return this.cfRequestHelper.sendCfPost(cnsiGuid, 'organizations', { name: orgName }); + return this.cfRequestHelper.sendCfPost>(cnsiGuid, 'organizations', { name: orgName }); } - return json; + return json.resources[0]; }).then(newOrg => { if (!added) { // No need to mess around with permissions, it exists already. return newOrg; } - const org = newOrg.resources ? newOrg.resources[0] as any : newOrg; - const orgGuid = org.metadata.guid; + // const org = newOrg.resources ? newOrg.resources[0] as any : newOrg; + const orgGuid = newOrg.metadata.guid; const p1 = this.cfRequestHelper.sendCfPut(cnsiGuid, 'organizations/' + orgGuid + '/users/' + adminGuid); const p2 = this.cfRequestHelper.sendCfPut(cnsiGuid, 'organizations/' + orgGuid + '/users/' + userGuid); // Add user to org users @@ -65,14 +70,20 @@ export class CFHelpers { }); } - addSpaceIfMissingForEndpointUsers(cnsiGuid, orgGuid, orgName, spaceName, endpoint: E2EConfigCloudFoundry) { + addSpaceIfMissingForEndpointUsers( + cnsiGuid, + orgGuid, + orgName, + spaceName, + endpoint: E2EConfigCloudFoundry + ): promise.Promise> { return this.assignAdminAndUserGuids(cnsiGuid, endpoint).then(() => { expect(endpoint.creds.nonAdmin.guid).not.toBeNull(); return this.addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, endpoint.creds.nonAdmin.guid); }); } - addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, userGuid) { + addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, userGuid): promise.Promise> { const cfRequestHelper = this.cfRequestHelper; return this.cfRequestHelper.sendCfGet(cnsiGuid, 'spaces?inline-relations-depth=1&include-relations=organization&q=name IN ' + spaceName) diff --git a/src/test-e2e/helpers/cf-request-helpers.ts b/src/test-e2e/helpers/cf-request-helpers.ts index 8fdf116f9c..393f8e8c11 100644 --- a/src/test-e2e/helpers/cf-request-helpers.ts +++ b/src/test-e2e/helpers/cf-request-helpers.ts @@ -33,8 +33,9 @@ export class CFRequestHelpers extends RequestHelpers { sendCfGet = (cfGuid: string, url: string): promise.Promise => this.sendCfRequest(cfGuid, url, 'GET').then(JSON.parse); - sendCfPost = (cfGuid: string, url: string, body: any): promise.Promise => - this.sendCfRequest(cfGuid, url, 'POST', body).then(JSON.parse) + sendCfPost(cfGuid: string, url: string, body: any): promise.Promise { + return this.sendCfRequest(cfGuid, url, 'POST', body).then(JSON.parse); + } sendCfPut = (cfGuid: string, url: string, body?: any): promise.Promise => this.sendCfRequest(cfGuid, url, 'PUT', body).then(JSON.parse) diff --git a/src/test-e2e/po/list.po.ts b/src/test-e2e/po/list.po.ts index 930c6ae106..747f0a85b9 100644 --- a/src/test-e2e/po/list.po.ts +++ b/src/test-e2e/po/list.po.ts @@ -26,12 +26,15 @@ export class ListComponent extends Component { 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 { @@ -291,3 +294,13 @@ export class ListPaginationComponent extends Component { } } + +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'))); + } +} From 4f112401fc91a855c43bc5ad6d44a298466c5534 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 14 Aug 2018 11:27:57 +0100 Subject: [PATCH 019/103] Remove fdescribe --- src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 6e6f54c8aa..1136788ab5 100644 --- 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 @@ -11,7 +11,7 @@ import { CfOrgLevelPage } from './cf-org-level-page.po'; const customOrgSpacesLabel = E2EHelpers.e2eItemPrefix + (process.env.CUSTOM_APP_LABEL || process.env.USER) + '-org-spaces-test'; -fdescribe('Org Spaces List- ', () => { +describe('Org Spaces List- ', () => { let e2eSetup: E2ESetup; let cfHelper: CFHelpers; From cba365fad30c5b0050028ee88a4002b054496081 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 14 Aug 2018 12:13:19 +0100 Subject: [PATCH 020/103] Remove artifacts addon --- .travis.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5d419658e6..6515d5133e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,6 @@ services: - docker addons: chrome: stable - artifacts: - paths: - - ./e2e-reports - target_paths: - - e2e - debug: true before_install: - pip install --user awscli - export DISPLAY=:99.0 From ea73bb27a5d82d7f790595470fc9f0270860a7ae Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 14 Aug 2018 13:32:24 +0100 Subject: [PATCH 021/103] Fetch space-scoped services when creating services in relevant cf/org --- .../service-catalog/services.service.ts | 2 +- .../services/services-wall.service.ts | 38 ++++++++++++++----- .../select-service.component.ts | 29 ++++++++++++-- .../store/actions/service-broker.actions.ts | 2 +- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/frontend/app/features/service-catalog/services.service.ts b/src/frontend/app/features/service-catalog/services.service.ts index 4c12ee5090..7e65381cb6 100644 --- a/src/frontend/app/features/service-catalog/services.service.ts +++ b/src/frontend/app/features/service-catalog/services.service.ts @@ -115,7 +115,7 @@ export class ServicesService { return getPaginationObservables>( { store: this.store, - action: new GetServiceBrokers(this.cfGuid, paginationKey), + action: new GetServiceBrokers(paginationKey, this.cfGuid), paginationMonitor: this.paginationMonitorFactory.create( paginationKey, entityFactory(serviceBrokerSchemaKey) diff --git a/src/frontend/app/features/services/services/services-wall.service.ts b/src/frontend/app/features/services/services/services-wall.service.ts index e7537e7349..25e719c418 100644 --- a/src/frontend/app/features/services/services/services-wall.service.ts +++ b/src/frontend/app/features/services/services/services-wall.service.ts @@ -3,13 +3,13 @@ import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { filter, map, publishReplay, refCount } from 'rxjs/operators'; -import { IService } from '../../../core/cf-api-svc.types'; -import { EntityServiceFactory } from '../../../core/entity-service-factory.service'; +import { IService, IServiceBroker } from '../../../core/cf-api-svc.types'; import { PaginationMonitorFactory } from '../../../shared/monitors/pagination-monitor.factory'; +import { GetServiceBrokers } from '../../../store/actions/service-broker.actions'; import { GetAllServices } from '../../../store/actions/service.actions'; import { GetServicesForSpace } from '../../../store/actions/space.actions'; import { AppState } from '../../../store/app-state'; -import { entityFactory, serviceSchemaKey } from '../../../store/helpers/entity-factory'; +import { entityFactory, serviceBrokerSchemaKey, serviceSchemaKey } from '../../../store/helpers/entity-factory'; import { createEntityRelationPaginationKey } from '../../../store/helpers/entity-relations/entity-relations.types'; import { getPaginationObservables } from '../../../store/reducers/pagination-reducer/pagination-reducer.helper'; import { APIResource } from '../../../store/types/api.types'; @@ -17,17 +17,15 @@ import { APIResource } from '../../../store/types/api.types'; @Injectable() export class ServicesWallService { services$: Observable[]>; + serviceBrokers$: Observable[]>; constructor( private store: Store, - private paginationMonitorFactory: PaginationMonitorFactory + private paginationMonitorFactory: PaginationMonitorFactory, ) { - this.services$ = this.initServicesObservable(); - } - - initServicesObservable = () => { const paginationKey = createEntityRelationPaginationKey(serviceSchemaKey); - return getPaginationObservables>( + + this.services$ = getPaginationObservables>( { store: this.store, action: new GetAllServices(paginationKey), @@ -38,6 +36,20 @@ export class ServicesWallService { }, true ).entities$; + + const brokerPaginationKey = createEntityRelationPaginationKey(serviceBrokerSchemaKey); + + this.serviceBrokers$ = getPaginationObservables>( + { + store: this.store, + action: new GetServiceBrokers(brokerPaginationKey), + paginationMonitor: this.paginationMonitorFactory.create( + brokerPaginationKey, + entityFactory(serviceBrokerSchemaKey) + ) + }, + true + ).entities$; } getServicesInCf = (cfGuid: string) => this.services$.pipe( @@ -48,6 +60,14 @@ export class ServicesWallService { refCount() ) + getServiceBrokersInCf = (cfGuid: string) => this.serviceBrokers$.pipe( + filter(p => !!p && p.length > 0), + map(s => s.filter(b => b.entity.cfGuid === cfGuid)), + filter(p => !!p), + publishReplay(1), + refCount() + ) + getSpaceServicePagKey(cfGuid: string, spaceGuid: string) { return createEntityRelationPaginationKey(serviceSchemaKey, `${cfGuid}-${spaceGuid}`); } diff --git a/src/frontend/app/shared/components/add-service-instance/select-service/select-service.component.ts b/src/frontend/app/shared/components/add-service-instance/select-service/select-service.component.ts index 9218592d10..54132c003b 100644 --- a/src/frontend/app/shared/components/add-service-instance/select-service/select-service.component.ts +++ b/src/frontend/app/shared/components/add-service-instance/select-service/select-service.component.ts @@ -1,8 +1,8 @@ import { AfterContentInit, Component, OnDestroy } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; -import { BehaviorSubject, Observable, of as observableOf, Subscription, combineLatest } from 'rxjs'; -import { filter, switchMap, tap, map } from 'rxjs/operators'; +import { BehaviorSubject, Observable, of as observableOf, Subscription, combineLatest, empty } from 'rxjs'; +import { filter, switchMap, combineLatest as combineLatestOperator, tap, map } from 'rxjs/operators'; import { IService } from '../../../../core/cf-api-svc.types'; import { ServicesWallService } from '../../../../features/services/services/services-wall.service'; @@ -62,11 +62,34 @@ export class SelectServiceComponent implements OnDestroy, AfterContentInit { fetching ? this.stepperForm.disable() : this.stepperForm.enable(); }) ); + + const spaceScopedServices = cfSpaceGuid$.pipe( + switchMap(([cfGuid, spaceGuid]) => { + return this.servicesWallService.getServiceBrokersInCf(cfGuid).pipe( + map(brokers => brokers.filter(b => b.entity.space_guid === spaceGuid)), + switchMap(b => { + if (b.length > 0) { + // There are space scoped brokers in the space + const brokerGuids = b.map(e => e.entity.guid); + return this.servicesWallService.services$.pipe( + map(p => p.filter(s => brokerGuids.indexOf(s.entity.service_broker_guid) !== -1)), + ); + } else { + return empty(); + } + }) + ); + }) + ); this.services$ = cfSpaceGuid$.pipe( tap(([cfGuid]) => this.cfGuid = cfGuid), switchMap(([cfGuid, spaceGuid]) => this.servicesWallService.getServicesInSpace(cfGuid, spaceGuid)), filter(p => !!p), - map(services => services.sort((a, b) => a.entity.label.localeCompare(b.entity.label))), + combineLatestOperator(spaceScopedServices), + map(([services, ssServices]) => { + const allServices = [].concat(services, ssServices); + return allServices.sort((a, b) => a.entity.label.localeCompare(b.entity.label)); + }), tap(services => { if (services.length === 1) { const guid = services[0].metadata.guid; diff --git a/src/frontend/app/store/actions/service-broker.actions.ts b/src/frontend/app/store/actions/service-broker.actions.ts index afbbdc64b4..40aba1c03e 100644 --- a/src/frontend/app/store/actions/service-broker.actions.ts +++ b/src/frontend/app/store/actions/service-broker.actions.ts @@ -7,8 +7,8 @@ import { getActions } from './action.helper'; export class GetServiceBrokers extends CFStartAction implements PaginationAction { constructor( - public endpointGuid: string, public paginationKey: string, + public endpointGuid: string = null, public includeRelations: string[] = [], public populateMissing = true ) { From c46530d6adf804faa68bc1a737077cd76c72aebe Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 14 Aug 2018 13:39:10 +0100 Subject: [PATCH 022/103] Non-sudo install of aws cli --- deploy/ci/travis/upload-e2e-test-report.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/ci/travis/upload-e2e-test-report.sh b/deploy/ci/travis/upload-e2e-test-report.sh index 55addd1740..5631a4d2ee 100755 --- a/deploy/ci/travis/upload-e2e-test-report.sh +++ b/deploy/ci/travis/upload-e2e-test-report.sh @@ -12,9 +12,9 @@ fi curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip" unzip awscli-bundle.zip -./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws +./awscli-bundle/install -b ~/bin/aws echo "Uploading test report...." # Sync the E2E reports to S3 -aws --endpoint-url ${AWS_ENDPOINT} s3 sync ./e2e-reports s3://${S3_BUCKET} \ No newline at end of file +~/bin/aws --endpoint-url ${AWS_ENDPOINT} s3 sync ./e2e-reports s3://${S3_BUCKET} From 3098a366400d17164155ac98917db9b2b67f4132 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 14 Aug 2018 13:48:03 +0100 Subject: [PATCH 023/103] Add tests for space-scoped services --- .../marketplace/create-service-instance-private-e2e.spec.ts | 4 ++-- .../create-service-instance-space-scoped-e2e.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 index f9f53bf295..ec28098da8 100644 --- a/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts +++ b/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts @@ -6,7 +6,7 @@ import { ServicesWallPage } from './services-wall.po'; import { MetaCard } from '../po/meta-card.po'; import { ServicesHelperE2E } from './services-helper-e2e'; -fdescribe('Create Service Instance of Private Service', () => { +describe('Create Service Instance of Private Service', () => { const createServiceInstance = new CreateServiceInstance(); const servicesWall = new ServicesWallPage(); let servicesHelperE2E: ServicesHelperE2E; @@ -80,7 +80,7 @@ fdescribe('Create Service Instance of Private Service', () => { // Select CF/Org/Space servicesHelperE2E.setCfOrgSpace(e2e.secrets.getDefaultCFEndpoint().services.privateService.invalidOrgName, - e2e.secrets.getDefaultCFEndpoint().services.privateService.invalidSpaceName); + e2e.secrets.getDefaultCFEndpoint().services.privateService.invalidSpaceName); createServiceInstance.stepper.next(); // Select Service 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 index ffb2494eac..69f85641b9 100644 --- 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 @@ -6,7 +6,7 @@ import { ServicesWallPage } from './services-wall.po'; import { MetaCard } from '../po/meta-card.po'; import { ServicesHelperE2E } from './services-helper-e2e'; -fdescribe('Create Service Instance of Space Scoped Service', () => { +describe('Create Service Instance of Space Scoped Service', () => { const createServiceInstance = new CreateServiceInstance(); const servicesWall = new ServicesWallPage(); let servicesHelperE2E: ServicesHelperE2E; @@ -53,7 +53,7 @@ fdescribe('Create Service Instance of Space Scoped Service', () => { // Select CF/Org/Space servicesHelperE2E.setCfOrgSpace(e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.invalidOrgName, - e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.invalidSpaceName); + e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.invalidSpaceName); createServiceInstance.stepper.next(); // Select Service From be4e1de336a917775dbf63a302d07b9b20009223 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 14 Aug 2018 15:02:30 +0100 Subject: [PATCH 024/103] Run tests as a `user` --- src/test-e2e/marketplace/create-service-instance-e2e.spec.ts | 3 ++- .../marketplace/create-service-instance-private-e2e.spec.ts | 3 ++- .../create-service-instance-space-scoped-e2e.spec.ts | 3 ++- src/test-e2e/marketplace/services-helper-e2e.ts | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) 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 fbeb95d857..9a56ed7a01 100644 --- a/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts +++ b/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts @@ -11,9 +11,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); servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance); }); 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 index ec28098da8..b43c43801d 100644 --- a/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts +++ b/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts @@ -11,9 +11,10 @@ describe('Create Service Instance of Private Service', () => { 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); servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance); }); 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 index 69f85641b9..d44c22f525 100644 --- 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 @@ -11,9 +11,10 @@ describe('Create Service Instance of Space Scoped Service', () => { 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); servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance); }); diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index afb5c0641f..10c5b68da9 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -101,7 +101,6 @@ export class ServicesHelperE2E { } setCfOrgSpace = (orgName: string = null, spaceName: string = null) => { - expect(this.createServiceInstance.stepper.canNext()).toBeFalsy(); 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); From 7a0c4bed49fcd3be01b8161076a18d17aa6d9937 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 14 Aug 2018 15:08:04 +0100 Subject: [PATCH 025/103] Fix marketplace tests --- .../marketplace/marketplace-summary-e2e.spec.ts | 7 +++++-- src/test-e2e/marketplace/marketplace-summary.po.ts | 10 +++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/test-e2e/marketplace/marketplace-summary-e2e.spec.ts b/src/test-e2e/marketplace/marketplace-summary-e2e.spec.ts index 09ac21c959..8be24d5cbb 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 02add641c3..d909ba1564 100644 --- a/src/test-e2e/marketplace/marketplace-summary.po.ts +++ b/src/test-e2e/marketplace/marketplace-summary.po.ts @@ -8,15 +8,19 @@ 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')) + return this.locator.element(by.css('.service-summary')); } getRecentInstances() { return this.locator.element(by.css('.recent-instances')); } + getAddServiceInstanceButton() { + return element(by.name('add-service-instance')); + } + } From 4c7457fee147122efce0bac6d370b616b8c6d170 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 14 Aug 2018 16:53:34 +0100 Subject: [PATCH 026/103] WIP --- ...create-public-service-instance-e2e.spec.ts | 162 ++++++++++++++++++ .../marketplace/services-helper-e2e.ts | 21 ++- 2 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 src/test-e2e/marketplace/marketplace-create-public-service-instance-e2e.spec.ts diff --git a/src/test-e2e/marketplace/marketplace-create-public-service-instance-e2e.spec.ts b/src/test-e2e/marketplace/marketplace-create-public-service-instance-e2e.spec.ts new file mode 100644 index 0000000000..11b7264501 --- /dev/null +++ b/src/test-e2e/marketplace/marketplace-create-public-service-instance-e2e.spec.ts @@ -0,0 +1,162 @@ +import { MarketplaceSummaryPage } from './marketplace-summary.po'; +import { ConsoleUserType } from '../helpers/e2e-helpers'; +import { e2e, E2ESetup } from '../e2e'; +import { browser } from 'protractor'; +import { ServicesHelperE2E } from './services-helper-e2e'; +import { CreateServiceInstance } from './create-service-instance.po'; + +fdescribe('Marketplace', () => { + let marketplaceSummaryPage: MarketplaceSummaryPage; + let cfGuid: string; + let serviceGuid: string; + let serviceName: string; + let setup: E2ESetup; + const createServiceInstance = new CreateServiceInstance(); + beforeAll(() => { + setup = e2e.setup(ConsoleUserType.admin) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .connectAllEndpoints(ConsoleUserType.admin); + + }); + + describe('Create Public Service Instance', () => { + let servicesHelperE2E: ServicesHelperE2E; + beforeAll((done) => { + init(setup, serviceName, serviceGuid, marketplaceSummaryPage, 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', () => { + 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(); + // Proceeed to create a service instance + servicesHelperE2E.createService(serviceName, true); + }); + }); + 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', () => { + beforeAll((done) => { + const getCfCnsi = servicesHelperE2E.cfRequestHelper.getCfCnsi(); + getCfCnsi.then(endpointModel => { + cfGuid = endpointModel.guid; + return servicesHelperE2E.fetchServices(cfGuid); + }).then(response => { + serviceName = e2e.secrets.getDefaultCFEndpoint().services.privateService.name; + const service = response.resources.find(e => e.entity.label === serviceName); + serviceGuid = service.metadata.guid; + marketplaceSummaryPage = new MarketplaceSummaryPage(cfGuid, serviceGuid); + 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', () => { + 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(); + // Proceeed to create a service instance + servicesHelperE2E.createService(serviceName, true); + }); + }); + 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', () => { + // beforeAll((done) => { + // const getCfCnsi = servicesHelperE2E.cfRequestHelper.getCfCnsi(); + // getCfCnsi.then(endpointModel => { + // cfGuid = endpointModel.guid; + // return servicesHelperE2E.fetchServices(cfGuid); + // }).then(response => { + // serviceName = e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.name; + // const service = response.resources.find(e => e.entity.label === serviceName); + // serviceGuid = service.metadata.guid; + // marketplaceSummaryPage = new MarketplaceSummaryPage(cfGuid, serviceGuid); + // 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', () => { + // 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(); + // // Proceeed to create a service instance + // servicesHelperE2E.createService(serviceName, true); + // }); + // }); + // 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 init( + setup: E2ESetup, + serviceName: string, +) { + const servicesHelperE2E = new ServicesHelperE2E(setup, new CreateServiceInstance()); + const getCfCnsi = servicesHelperE2E.cfRequestHelper.getCfCnsi(); + let cfGuid; + return getCfCnsi.then(endpointModel => { + cfGuid = endpointModel.guid; + return servicesHelperE2E.fetchServices(cfGuid); + }).then(response => { + serviceName = e2e.secrets.getDefaultCFEndpoint().services.publicService.name; + const service = response.resources.find(e => e.entity.label === serviceName); + const serviceGuid = service.metadata.guid; + const marketplaceSummaryPage = new MarketplaceSummaryPage(cfGuid, serviceGuid); + return { servicesHelperE2E, marketplaceSummaryPage }; + }); +} diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index 10c5b68da9..07b4522d33 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -41,15 +41,17 @@ export class ServicesHelperE2E { ); } - createService = (serviceName: string) => { + createService = (serviceName: string, marketplaceMode = false) => { // Select CF/Org/Space - this.setCfOrgSpace(); + this.setCfOrgSpace(null, null, marketplaceMode); this.createServiceInstance.stepper.next(); - // Select Service - this.setServiceSelection(serviceName); - this.createServiceInstance.stepper.next(); + if (!marketplaceMode) { + // Select Service + this.setServiceSelection(serviceName); + this.createServiceInstance.stepper.next(); + } // Select Service Plan this.setServicePlan(); @@ -65,6 +67,7 @@ export class ServicesHelperE2E { } + setServiceInstanceDetail = () => { this.createServiceInstance.stepper.waitForStep('Service Instance'); expect(this.createServiceInstance.stepper.canPrevious()).toBeTruthy(); @@ -100,8 +103,10 @@ export class ServicesHelperE2E { } } - setCfOrgSpace = (orgName: string = null, spaceName: string = null) => { - this.createServiceInstance.stepper.setCf(e2e.secrets.getDefaultCFEndpoint().name); + 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(); @@ -113,9 +118,11 @@ export class ServicesHelperE2E { let cfGuid: string; return getCfCnsi.then(endpointModel => { cfGuid = endpointModel.guid; + console.log(cfGuid); return this.fetchServicesInstances(cfGuid); }).then(response => { const services = response.resources; + console.log(services.map(e => e.entity.name)); const serviceInstance = services.filter(service => service.entity.name === serviceIntanceName)[0]; return this.deleteServiceInstance(cfGuid, serviceInstance.metadata.guid); }); From f86a2668f7e5febef8d784336cf25f5224a56cb6 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Wed, 15 Aug 2018 12:25:40 +0100 Subject: [PATCH 027/103] Create services --- ...create-public-service-instance-e2e.spec.ts | 162 ------------------ ...place-create-service-instances-e2e.spec.ts | 160 +++++++++++++++++ .../marketplace/services-helper-e2e.ts | 2 - 3 files changed, 160 insertions(+), 164 deletions(-) delete mode 100644 src/test-e2e/marketplace/marketplace-create-public-service-instance-e2e.spec.ts create mode 100644 src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts diff --git a/src/test-e2e/marketplace/marketplace-create-public-service-instance-e2e.spec.ts b/src/test-e2e/marketplace/marketplace-create-public-service-instance-e2e.spec.ts deleted file mode 100644 index 11b7264501..0000000000 --- a/src/test-e2e/marketplace/marketplace-create-public-service-instance-e2e.spec.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { MarketplaceSummaryPage } from './marketplace-summary.po'; -import { ConsoleUserType } from '../helpers/e2e-helpers'; -import { e2e, E2ESetup } from '../e2e'; -import { browser } from 'protractor'; -import { ServicesHelperE2E } from './services-helper-e2e'; -import { CreateServiceInstance } from './create-service-instance.po'; - -fdescribe('Marketplace', () => { - let marketplaceSummaryPage: MarketplaceSummaryPage; - let cfGuid: string; - let serviceGuid: string; - let serviceName: string; - let setup: E2ESetup; - const createServiceInstance = new CreateServiceInstance(); - beforeAll(() => { - setup = e2e.setup(ConsoleUserType.admin) - .clearAllEndpoints() - .registerDefaultCloudFoundry() - .connectAllEndpoints(ConsoleUserType.admin); - - }); - - describe('Create Public Service Instance', () => { - let servicesHelperE2E: ServicesHelperE2E; - beforeAll((done) => { - init(setup, serviceName, serviceGuid, marketplaceSummaryPage, 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', () => { - 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(); - // Proceeed to create a service instance - servicesHelperE2E.createService(serviceName, true); - }); - }); - 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', () => { - beforeAll((done) => { - const getCfCnsi = servicesHelperE2E.cfRequestHelper.getCfCnsi(); - getCfCnsi.then(endpointModel => { - cfGuid = endpointModel.guid; - return servicesHelperE2E.fetchServices(cfGuid); - }).then(response => { - serviceName = e2e.secrets.getDefaultCFEndpoint().services.privateService.name; - const service = response.resources.find(e => e.entity.label === serviceName); - serviceGuid = service.metadata.guid; - marketplaceSummaryPage = new MarketplaceSummaryPage(cfGuid, serviceGuid); - 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', () => { - 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(); - // Proceeed to create a service instance - servicesHelperE2E.createService(serviceName, true); - }); - }); - 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', () => { - // beforeAll((done) => { - // const getCfCnsi = servicesHelperE2E.cfRequestHelper.getCfCnsi(); - // getCfCnsi.then(endpointModel => { - // cfGuid = endpointModel.guid; - // return servicesHelperE2E.fetchServices(cfGuid); - // }).then(response => { - // serviceName = e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.name; - // const service = response.resources.find(e => e.entity.label === serviceName); - // serviceGuid = service.metadata.guid; - // marketplaceSummaryPage = new MarketplaceSummaryPage(cfGuid, serviceGuid); - // 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', () => { - // 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(); - // // Proceeed to create a service instance - // servicesHelperE2E.createService(serviceName, true); - // }); - // }); - // 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 init( - setup: E2ESetup, - serviceName: string, -) { - const servicesHelperE2E = new ServicesHelperE2E(setup, new CreateServiceInstance()); - const getCfCnsi = servicesHelperE2E.cfRequestHelper.getCfCnsi(); - let cfGuid; - return getCfCnsi.then(endpointModel => { - cfGuid = endpointModel.guid; - return servicesHelperE2E.fetchServices(cfGuid); - }).then(response => { - serviceName = e2e.secrets.getDefaultCFEndpoint().services.publicService.name; - const service = response.resources.find(e => e.entity.label === serviceName); - const serviceGuid = service.metadata.guid; - const marketplaceSummaryPage = new MarketplaceSummaryPage(cfGuid, serviceGuid); - return { servicesHelperE2E, marketplaceSummaryPage }; - }); -} 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..e303cbebbb --- /dev/null +++ b/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts @@ -0,0 +1,160 @@ +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'; + +fdescribe('Marketplace', () => { + let setup: E2ESetup; + const servicesWall = new ServicesWallPage(); + beforeAll(() => { + setup = e2e.setup(ConsoleUserType.admin) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .connectAllEndpoints(ConsoleUserType.admin); + }); + + 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(); + // Proceeed to create a service instance + servicesHelperE2E.createService(serviceName, true); + + servicesWall.isActivePage(); + + 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, +) { + const servicesHelperE2E = new ServicesHelperE2E(setup, new CreateServiceInstance()); + const getCfCnsi = servicesHelperE2E.cfRequestHelper.getCfCnsi(); + let cfGuid; + return getCfCnsi.then(endpointModel => { + cfGuid = endpointModel.guid; + return servicesHelperE2E.fetchServices(cfGuid); + }).then(response => { + serviceName = e2e.secrets.getDefaultCFEndpoint().services.publicService.name; + const service = response.resources.find(e => e.entity.label === serviceName); + const serviceGuid = service.metadata.guid; + const marketplaceSummaryPage = new MarketplaceSummaryPage(cfGuid, serviceGuid); + return { servicesHelper: servicesHelperE2E, summaryPage: marketplaceSummaryPage }; + }); +} + diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index 07b4522d33..e59ac18092 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -118,11 +118,9 @@ export class ServicesHelperE2E { let cfGuid: string; return getCfCnsi.then(endpointModel => { cfGuid = endpointModel.guid; - console.log(cfGuid); return this.fetchServicesInstances(cfGuid); }).then(response => { const services = response.resources; - console.log(services.map(e => e.entity.name)); const serviceInstance = services.filter(service => service.entity.name === serviceIntanceName)[0]; return this.deleteServiceInstance(cfGuid, serviceInstance.metadata.guid); }); From 637d8ff696d666f8251a9d086e80206e28ad442e Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Wed, 15 Aug 2018 13:06:48 +0100 Subject: [PATCH 028/103] Remove `fdescribe` --- .../marketplace-create-service-instances-e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index e303cbebbb..7917bf45b8 100644 --- a/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts +++ b/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts @@ -7,7 +7,7 @@ import { CreateServiceInstance } from './create-service-instance.po'; import { ServicesWallPage } from './services-wall.po'; import { MetaCard } from '../po/meta-card.po'; -fdescribe('Marketplace', () => { +describe('Marketplace', () => { let setup: E2ESetup; const servicesWall = new ServicesWallPage(); beforeAll(() => { From cb23095444a53cc4688b02c68adf9438fcf8a117 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Wed, 15 Aug 2018 14:49:57 +0100 Subject: [PATCH 029/103] Edit service tests --- .../edit-service-instance-e2e.spec.ts | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts 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..54d07aa79b --- /dev/null +++ b/src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts @@ -0,0 +1,70 @@ +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'; + +fdescribe('Edit Service Instance', () => { + const createServiceInstance = new CreateServiceInstance(); + const servicesWall = new ServicesWallPage(); + let servicesHelperE2E: ServicesHelperE2E; + beforeAll(() => { + const e2eSetup = e2e.setup(ConsoleUserType.user) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .connectAllEndpoints(ConsoleUserType.user) + .getInfo(); + servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance); + }); + + beforeEach(() => { + createServiceInstance.navigateTo(); + createServiceInstance.waitForPage(); + }); + + it('- should be able edit a service instance', () => { + + servicesHelperE2E.createService(e2e.secrets.getDefaultCFEndpoint().services.publicService.name); + + servicesWall.isActivePage(); + + 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 => { + return promise.all(cardTitles).then(titles => { + + for (let i = 0; i < titles.length; i++) { + if (titles[i] === serviceName) { + return servicesWall.serviceInstancesList.cards.getCard(i); + } + } + }); + }).then( (card: MetaCard) => { + card.openActionMenu().then(menu => { + menu.clickItem('Edit'); + browser.getCurrentUrl().then(url => { + expect(url.endsWith('edit')).toBeTruthy(); + }); + }); + }).catch(e => fail(e)); + + + }); + + + + afterAll((done) => { + servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done()); + }); +}); + + From de7ff3f52d20a2dd171972d8f890c49447b92096 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Wed, 15 Aug 2018 14:51:17 +0100 Subject: [PATCH 030/103] Fix typo --- .../marketplace/create-service-instance-private-e2e.spec.ts | 2 +- .../create-service-instance-space-scoped-e2e.spec.ts | 2 +- .../marketplace-create-service-instances-e2e.spec.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) 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 index b43c43801d..a43ff709cb 100644 --- a/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts +++ b/src/test-e2e/marketplace/create-service-instance-private-e2e.spec.ts @@ -91,7 +91,7 @@ describe('Create Service Instance of Private Service', () => { afterAll((done) => { - servicesHelperE2E.cleanupServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => 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 index d44c22f525..e464d1f97b 100644 --- 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 @@ -64,7 +64,7 @@ describe('Create Service Instance of Space Scoped Service', () => { afterAll((done) => { - servicesHelperE2E.cleanupServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done()); + servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done()); }); }); 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 index 7917bf45b8..1e9d67f330 100644 --- a/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts +++ b/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts @@ -44,7 +44,7 @@ describe('Marketplace', () => { 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()); + servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done()); }); }); @@ -75,7 +75,7 @@ describe('Create Private Service Instance', () => { 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()); + servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done()); }); }); @@ -106,7 +106,7 @@ describe('Create Private Service Instance', () => { 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()); + servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done()); }); }); }); From 0d98917021f597ec162111a90b4c25cd79a86a82 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 15 Aug 2018 18:30:27 +0100 Subject: [PATCH 031/103] Missed file + another travis typescript wonderfail --- src/test-e2e/e2e.types.ts | 1 + src/test-e2e/marketplace/services-helper-e2e.ts | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test-e2e/e2e.types.ts b/src/test-e2e/e2e.types.ts index 4cb656466e..9c5e62aed7 100644 --- a/src/test-e2e/e2e.types.ts +++ b/src/test-e2e/e2e.types.ts @@ -7,6 +7,7 @@ export interface E2ECred { export interface E2ECreds { admin: E2ECred; nonAdmin?: E2ECred; + guid: string; } export interface E2EEndpointConfig { diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index 5456d398b1..d1ea11fc51 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -132,9 +132,7 @@ export class ServicesHelperE2E { if (serviceInstance) { return this.deleteServiceInstance(cfGuid, serviceInstance.metadata.guid); } - const p = promise.defer(); - p.fulfill(createEmptyCfResponse()); - return p; + return promise.fullyResolved(createEmptyCfResponse()); }); } From d6d0768b5d1099f8deec80c2996a0105652d182d Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 16 Aug 2018 09:52:58 +0100 Subject: [PATCH 032/103] WIP --- .../create-service-instance-e2e.spec.ts | 4 ++-- .../create-service-instance-stepper.po.ts | 1 + ...tplace-create-service-instances-e2e.spec.ts | 18 +++++++++--------- .../marketplace/services-helper-e2e.ts | 4 ++++ src/test-e2e/po/form.po.ts | 7 +++++++ 5 files changed, 23 insertions(+), 11 deletions(-) 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 1bcbd67f21..99cb2cd41e 100644 --- a/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts +++ b/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts @@ -16,6 +16,7 @@ describe('Create Service Instance', () => { .clearAllEndpoints() .registerDefaultCloudFoundry() .connectAllEndpoints(ConsoleUserType.user) + .connectAllEndpoints(ConsoleUserType.admin) .getInfo(); servicesHelperE2E = new ServicesHelperE2E(e2eSetup, createServiceInstance); }); @@ -32,7 +33,6 @@ describe('Create Service Instance', () => { it('- should be able to to create a service instance', () => { servicesHelperE2E.createService(e2e.secrets.getDefaultCFEndpoint().services.publicService.name); - servicesWall.isActivePage(); const serviceName = servicesHelperE2E.serviceInstanceName; @@ -106,7 +106,7 @@ describe('Create Service Instance', () => { createServiceInstance.stepper.next(); // Select Service - servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.publicService.name); + servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.publicService.name); createServiceInstance.stepper.next(); // Select Service Plan 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 0ca5b2dbca..effb78e8b6 100644 --- a/src/test-e2e/marketplace/create-service-instance-stepper.po.ts +++ b/src/test-e2e/marketplace/create-service-instance-stepper.po.ts @@ -23,6 +23,7 @@ export class CreateServiceInstanceStepper extends StepperComponent { } setOrg = (orgName: string) => { + console.log('In setOrg ' + orgName) return this.getStepperForm().fill({ [this.orgFieldName]: orgName }); } 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 index 1e9d67f330..5340f9e39b 100644 --- a/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts +++ b/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts @@ -14,7 +14,8 @@ describe('Marketplace', () => { setup = e2e.setup(ConsoleUserType.admin) .clearAllEndpoints() .registerDefaultCloudFoundry() - .connectAllEndpoints(ConsoleUserType.admin); + .connectAllEndpoints(ConsoleUserType.admin) + .getInfo(); }); describe('Create Public Service Instance', () => { @@ -38,7 +39,7 @@ describe('Marketplace', () => { expect(marketplaceSummaryPage.getAddServiceInstanceButton().isPresent()).toBeTruthy(); }); - it('- should be able to create a new service instance', () => { + fit('- should be able to create a new service instance', () => { createService(marketplaceSummaryPage, servicesHelperE2E, serviceName, servicesWall); }); afterAll((done) => { @@ -119,6 +120,7 @@ function createService(marketplaceSummaryPage: MarketplaceSummaryPage, browser.getCurrentUrl().then(url => { expect(url.endsWith('create?isSpaceScoped=false')).toBeTruthy(); // Proceeed to create a service instance + console.log("Service NAme: " + serviceName + " is true: " + true) servicesHelperE2E.createService(serviceName, true); servicesWall.isActivePage(); @@ -144,16 +146,14 @@ function init( serviceName: string, ) { const servicesHelperE2E = new ServicesHelperE2E(setup, new CreateServiceInstance()); - const getCfCnsi = servicesHelperE2E.cfRequestHelper.getCfCnsi(); - let cfGuid; - return getCfCnsi.then(endpointModel => { - cfGuid = endpointModel.guid; - return servicesHelperE2E.fetchServices(cfGuid); - }).then(response => { + const defaultCf = e2e.secrets.getDefaultCFEndpoint(); + const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); + console.log('#############################' + endpointGuid + '##################') + 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; - const marketplaceSummaryPage = new MarketplaceSummaryPage(cfGuid, serviceGuid); + const marketplaceSummaryPage = new MarketplaceSummaryPage(endpointGuid, serviceGuid); return { servicesHelper: servicesHelperE2E, summaryPage: marketplaceSummaryPage }; }); } diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index 125bbbea4a..c21ef56516 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -48,6 +48,7 @@ export class ServicesHelperE2E { this.createServiceInstance.waitForPage(); // Select CF/Org/Space + console.log('In SetCfOrgSpace') this.setCfOrgSpace(null, null, marketplaceMode); this.createServiceInstance.stepper.next(); @@ -120,9 +121,12 @@ export class ServicesHelperE2E { } setCfOrgSpace = (orgName: string = null, spaceName: string = null, marketplaceMode = false) => { + + console.log('Marketplace: ' + marketplaceMode) if (!marketplaceMode) { this.createServiceInstance.stepper.setCf(e2e.secrets.getDefaultCFEndpoint().name); } + console.log('In setCfOrgSpacesdfad ' + e2e.secrets.getDefaultCFEndpoint().testOrg + ' ' + e2e.secrets.getDefaultCFEndpoint().testSpace) 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(); diff --git a/src/test-e2e/po/form.po.ts b/src/test-e2e/po/form.po.ts index 3bdf4d4076..87f30755d3 100644 --- a/src/test-e2e/po/form.po.ts +++ b/src/test-e2e/po/form.po.ts @@ -37,14 +37,17 @@ export class FormComponent extends Component { // Get metadata for all of the fields in the form getFields(): ElementArrayFinder { + console.log('At getFields') return this.locator.all(by.tagName('input, mat-select, textarea')); } getFieldsMapped(): promise.Promise { + console.log('At getFieldsMapped') return this.getFields().map(this.mapField); } mapField(elm: ElementFinder, index: number): FormItem | any { + console.log('In MapField') return { index: index, name: elm.getAttribute('name'), @@ -114,6 +117,7 @@ export class FormComponent extends Component { getControlsMap(): promise.Promise { return this.getFieldsMapped().then(items => { + console.log('getFieldsMapped') const form = {}; items.forEach((item: FormItem) => { const id = item.name || item.formControlName; @@ -125,7 +129,10 @@ export class FormComponent extends Component { // Fill the form fields in the specified object fill(fields: { [fieldKey: string]: string | boolean }, expectFailure = false): promise.Promise { + + console.log('Fill') return this.getControlsMap().then(ctrls => { + console.log(fields) Object.keys(fields).forEach(field => { const ctrl = ctrls[field] as FormItem; const value = fields[field]; From 3d73a0a6f0aef77d3d88dcecf22088ac4fb56c9d Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 16 Aug 2018 11:34:25 +0100 Subject: [PATCH 033/103] Fixes following merge --- .../application-delete-e2e.spec.ts | 6 ++- .../application/application-e2e-helpers.ts | 9 ++--- src/test-e2e/helpers/cf-helpers.ts | 40 ++++++------------- 3 files changed, 21 insertions(+), 34 deletions(-) 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-e2e-helpers.ts b/src/test-e2e/application/application-e2e-helpers.ts index 9830fccb83..d829fabf0b 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/helpers/cf-helpers.ts b/src/test-e2e/helpers/cf-helpers.ts index 4639c48ee5..2249889ed7 100644 --- a/src/test-e2e/helpers/cf-helpers.ts +++ b/src/test-e2e/helpers/cf-helpers.ts @@ -50,12 +50,12 @@ export class CFHelpers { 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. @@ -78,35 +78,24 @@ export class CFHelpers { orgName, spaceName, endpoint: E2EConfigCloudFoundry - ): promise.Promise> { + ): promise.Promise> { return this.assignAdminAndUserGuids(cnsiGuid, endpoint).then(() => { expect(endpoint.creds.nonAdmin.guid).not.toBeNull(); return this.addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, endpoint.creds.nonAdmin.guid); }); } - addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, userGuid): promise.Promise> { + addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, userGuid): promise.Promise> { const cfRequestHelper = this.cfRequestHelper; - 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; + return this.fetchSpace(cnsiGuid, orgGuid, spaceName) + .then(function (space) { + return space ? space : cfRequestHelper.sendCfPost>(cnsiGuid, 'spaces', + { + name: spaceName, + manager_guids: [], + developer_guids: [userGuid], + organization_guid: orgGuid }); - } - if (add) { - return cfRequestHelper.sendCfPost(cnsiGuid, 'spaces', - { - name: spaceName, - manager_guids: [], - developer_guids: [userGuid], - organization_guid: orgGuid - }); - } }); } @@ -151,9 +140,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); }); } From 0226e21858319cc35402e102e9e77f1a9ca5edd0 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 16 Aug 2018 12:55:40 +0100 Subject: [PATCH 034/103] Allow default url to be overridden for `CreateServiceInstance` --- .../marketplace/create-service-instance.po.ts | 6 +- ...place-create-service-instances-e2e.spec.ts | 101 +++++++++--------- .../marketplace/services-helper-e2e.ts | 27 ++--- src/test-e2e/po/form.po.ts | 6 -- 4 files changed, 69 insertions(+), 71 deletions(-) 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/marketplace-create-service-instances-e2e.spec.ts b/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts index 5340f9e39b..e5ef44d0c8 100644 --- a/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts +++ b/src/test-e2e/marketplace/marketplace-create-service-instances-e2e.spec.ts @@ -12,16 +12,16 @@ describe('Marketplace', () => { const servicesWall = new ServicesWallPage(); beforeAll(() => { setup = e2e.setup(ConsoleUserType.admin) - .clearAllEndpoints() - .registerDefaultCloudFoundry() - .connectAllEndpoints(ConsoleUserType.admin) - .getInfo(); + .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; + const serviceName = e2e.secrets.getDefaultCFEndpoint().services.publicService.name; beforeAll((done) => { init(setup, serviceName).then(res => { servicesHelperE2E = res.servicesHelper; @@ -30,60 +30,60 @@ describe('Marketplace', () => { }); }); - beforeEach(() => { - marketplaceSummaryPage.navigateTo(); - marketplaceSummaryPage.waitForPage(); - }); + beforeEach(() => { + marketplaceSummaryPage.navigateTo(); + marketplaceSummaryPage.waitForPage(); + }); - it('- should have an Add Service Instance button', () => { - expect(marketplaceSummaryPage.getAddServiceInstanceButton().isPresent()).toBeTruthy(); - }); + it('- should have an Add Service Instance button', () => { + expect(marketplaceSummaryPage.getAddServiceInstanceButton().isPresent()).toBeTruthy(); + }); - fit('- 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()); + 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(); + 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(); + beforeEach(() => { + marketplaceSummaryPage.navigateTo(); + marketplaceSummaryPage.waitForPage(); - }); - it('- should have an Add Service Instance button', () => { - expect(marketplaceSummaryPage.getAddServiceInstanceButton().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', () => { - 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()); + 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; + const serviceName = e2e.secrets.getDefaultCFEndpoint().services.spaceScopedService.name; beforeAll((done) => { init(setup, serviceName).then(res => { servicesHelperE2E = res.servicesHelper; @@ -120,7 +120,6 @@ function createService(marketplaceSummaryPage: MarketplaceSummaryPage, browser.getCurrentUrl().then(url => { expect(url.endsWith('create?isSpaceScoped=false')).toBeTruthy(); // Proceeed to create a service instance - console.log("Service NAme: " + serviceName + " is true: " + true) servicesHelperE2E.createService(serviceName, true); servicesWall.isActivePage(); @@ -144,15 +143,19 @@ function createService(marketplaceSummaryPage: MarketplaceSummaryPage, function init( setup: E2ESetup, serviceName: string, + spaceScoped = false ) { - const servicesHelperE2E = new ServicesHelperE2E(setup, new CreateServiceInstance()); const defaultCf = e2e.secrets.getDefaultCFEndpoint(); const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); - console.log('#############################' + endpointGuid + '##################') + + 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/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index c21ef56516..55250c1f47 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -16,9 +16,15 @@ 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; + } + } + + setCreateServiceInstance = (createServiceInstance: CreateServiceInstance) => { + this.createServiceInstance = createServiceInstance; } fetchServices = (cfGuid: string): promise.Promise => { @@ -48,16 +54,15 @@ export class ServicesHelperE2E { this.createServiceInstance.waitForPage(); // Select CF/Org/Space - console.log('In SetCfOrgSpace') - this.setCfOrgSpace(null, null, marketplaceMode); + this.setCfOrgSpace(null, null, marketplaceMode); this.createServiceInstance.stepper.next(); // Select Service - if (!marketplaceMode) { - // Select Service - this.setServiceSelection(serviceName); - this.createServiceInstance.stepper.next(); - } + if (!marketplaceMode) { + // Select Service + this.setServiceSelection(serviceName); + this.createServiceInstance.stepper.next(); + } // Select Service Plan this.setServicePlan(); @@ -122,11 +127,9 @@ export class ServicesHelperE2E { setCfOrgSpace = (orgName: string = null, spaceName: string = null, marketplaceMode = false) => { - console.log('Marketplace: ' + marketplaceMode) if (!marketplaceMode) { this.createServiceInstance.stepper.setCf(e2e.secrets.getDefaultCFEndpoint().name); } - console.log('In setCfOrgSpacesdfad ' + e2e.secrets.getDefaultCFEndpoint().testOrg + ' ' + e2e.secrets.getDefaultCFEndpoint().testSpace) 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(); @@ -145,9 +148,7 @@ export class ServicesHelperE2E { if (serviceInstance) { return this.deleteServiceInstance(cfGuid, serviceInstance.metadata.guid); } - const p = promise.defer(); - p.fulfill(createEmptyCfResponse()); - return p; + return promise.fullyResolved(createEmptyCfResponse); }); } diff --git a/src/test-e2e/po/form.po.ts b/src/test-e2e/po/form.po.ts index 87f30755d3..7c42a20ce9 100644 --- a/src/test-e2e/po/form.po.ts +++ b/src/test-e2e/po/form.po.ts @@ -37,17 +37,14 @@ export class FormComponent extends Component { // Get metadata for all of the fields in the form getFields(): ElementArrayFinder { - console.log('At getFields') return this.locator.all(by.tagName('input, mat-select, textarea')); } getFieldsMapped(): promise.Promise { - console.log('At getFieldsMapped') return this.getFields().map(this.mapField); } mapField(elm: ElementFinder, index: number): FormItem | any { - console.log('In MapField') return { index: index, name: elm.getAttribute('name'), @@ -117,7 +114,6 @@ export class FormComponent extends Component { getControlsMap(): promise.Promise { return this.getFieldsMapped().then(items => { - console.log('getFieldsMapped') const form = {}; items.forEach((item: FormItem) => { const id = item.name || item.formControlName; @@ -130,9 +126,7 @@ export class FormComponent extends Component { // Fill the form fields in the specified object fill(fields: { [fieldKey: string]: string | boolean }, expectFailure = false): promise.Promise { - console.log('Fill') return this.getControlsMap().then(ctrls => { - console.log(fields) Object.keys(fields).forEach(field => { const ctrl = ctrls[field] as FormItem; const value = fields[field]; From 24b598517847091f94d56f78cd28cd7a9d5b0ec7 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 16 Aug 2018 13:21:28 +0100 Subject: [PATCH 035/103] Improve time taken to execute tests, increase timeout - Increase minimum timeout to execute any before,after,it - Reduce time taken to create test spaces - Reduce time taken to delete test spaces --- protractor.conf.js | 7 +- .../app/test-framework/spec-helper.spec.ts | 6 +- .../org-level/org-spaces-e2e.spec.ts | 80 ++++++++++--------- src/test-e2e/helpers/cf-helpers.ts | 49 ++++++------ 4 files changed, 75 insertions(+), 67 deletions(-) diff --git a/protractor.conf.js b/protractor.conf.js index dc5824ff71..5efd5c6d43 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -35,8 +35,11 @@ try { process.exit(1); } +// This is the maximum amount of time ALL before/after/it's must execute in +const timeout = 60000 + exports.config = { - allScriptsTimeout: 11000, + allScriptsTimeout: timeout, specs: [ './src/test-e2e/**/*-e2e.spec.ts', ], @@ -53,7 +56,7 @@ exports.config = { framework: 'jasmine', jasmineNodeOpts: { showColors: true, - defaultTimeoutInterval: 30000, + defaultTimeoutInterval: timeout, print: function () {} }, params: secrets, diff --git a/src/frontend/app/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/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts index 1136788ab5..6d9e48c26e 100644 --- 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 @@ -13,11 +13,14 @@ const customOrgSpacesLabel = E2EHelpers.e2eItemPrefix + (process.env.CUSTOM_APP_ describe('Org Spaces List- ', () => { - let e2eSetup: E2ESetup; 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 = []; @@ -27,7 +30,7 @@ describe('Org Spaces List- ', () => { return spaceNames; } - function chainCreateSpace(endpointGuid: string, org: APIResource, spaceNames: string[]): promise.Promise { + 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 @@ -37,38 +40,45 @@ describe('Org Spaces List- ', () => { org.metadata.guid, org.entity.name, name, - defaultCf); + defaultCf, + true); }); }, promise.fullyResolved('')); } - function concurrentCreateSpace(endpointGuid: string, org: APIResource, spaceNames: string[]): promise.Promise { + 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))); + defaultCf, + true))); } - function setup(spaceNames: string[], orderImportant: boolean) { + function setup(orgName: string, spaceNames: string[], orderImportant: boolean) { defaultCf = e2e.secrets.getDefaultCFEndpoint(); - const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); + endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); + browser.wait( - cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg) + cfHelper.addOrgIfMissingForEndpointUsers(endpointGuid, defaultCf, orgName) .then((org: APIResource) => { - // Chain the creation of the spaces to ensure there's a nice sequential 'created_at' value + 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(endpointGuid, org, spaceNames) : - concurrentCreateSpace(endpointGuid, org, spaceNames); + chainCreateSpace(org, spaceNames) : + concurrentCreateSpace(org, spaceNames); return promises.then(() => org.metadata.guid); }) - .then(orgGuid => navToOrgSpaces(endpointGuid, orgGuid)) + .then(navToOrgSpaces) ); } - function navToOrgSpaces(endpointGuid: string, orgGuid: string) { + function navToOrgSpaces() { orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, orgGuid); orgPage.navigateTo(); orgPage.waitForPageOrChildPage(); @@ -77,13 +87,13 @@ describe('Org Spaces List- ', () => { expect(spaceList.isTableView()).toBeFalsy(); } - function tearDown(spaceNames: string[]) { - const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); - spaceNames.forEach(name => cfHelper.deleteSpaceIfExisting(endpointGuid, name)); + function tearDown(orgName: string) { + expect(orgName).not.toBeNull(); + cfHelper.deleteOrgIfExisting(endpointGuid, orgName); } beforeAll(() => { - e2eSetup = e2e.setup(ConsoleUserType.admin) + const e2eSetup = e2e.setup(ConsoleUserType.admin) .clearAllEndpoints() .registerDefaultCloudFoundry() .connectAllEndpoints(ConsoleUserType.admin) @@ -93,18 +103,12 @@ describe('Org Spaces List- ', () => { }); describe('No Pages -', () => { - const orgName = E2EHelpers.createCustomName(customOrgSpacesLabel); - let endpointGuid, orgGuid; + const orgName = E2EHelpers.createCustomName(customOrgSpacesLabel) + '-no-pages'; beforeAll(() => { - defaultCf = e2e.secrets.getDefaultCFEndpoint(); - endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); - // Create a temporary org which will contain no spaces - browser.wait(cfHelper.addOrgIfMissingForEndpointUsers(endpointGuid, defaultCf, orgName).then(res => orgGuid = res.metadata.guid)); + setup(orgName, [], false); }); - beforeEach(() => { - navToOrgSpaces(endpointGuid, orgGuid); - }); + beforeEach(navToOrgSpaces); it('Should show no entities message', () => { expect(spaceList.isDisplayed()).toBeTruthy(); @@ -113,12 +117,11 @@ describe('Org Spaces List- ', () => { expect(spaceList.cards.getCardCount()).toBe(0); }); - afterAll(() => { - cfHelper.deleteSpaceIfExisting(endpointGuid, orgName); - }); + afterAll(() => tearDown(orgName)); }); describe('Single Page -', () => { + const orgName = E2EHelpers.createCustomName(customOrgSpacesLabel) + '-1-page'; let spaceNames; @@ -145,13 +148,12 @@ describe('Org Spaces List- ', () => { beforeAll(() => { spaceNames = createSpaceNames(3); - setup(spaceNames, true); + setup(orgName, spaceNames, true); expect(spaceList.getTotalResults()).toBeLessThanOrEqual(9); expect(spaceList.pagination.isDisplayed()).toBeFalsy(); - }); - - afterAll(() => tearDown(spaceNames)); + }, timeAllowed); + afterAll(() => tearDown(orgName), timeAllowed); it('sort by name', () => { testSortBy('Name'); @@ -204,17 +206,17 @@ describe('Org Spaces List- ', () => { }); describe('Multi Page -', () => { + const orgName = E2EHelpers.createCustomName(customOrgSpacesLabel) + '-multi-page'; let spaceNames; beforeAll(() => { spaceNames = createSpaceNames(11); - setup(spaceNames, false); + setup(orgName, spaceNames, false); expect(spaceList.getTotalResults()).toBeGreaterThanOrEqual(spaceNames.length); + }, timeAllowed); - }); - - afterAll(() => tearDown(spaceNames)); + afterAll(() => tearDown(orgName), timeAllowed); function testStartingPosition() { // General expects for all tests in this section @@ -232,9 +234,9 @@ describe('Org Spaces List- ', () => { expect(spaceList.pagination.getNavLastPage().getComponent().isEnabled()).toBeTruthy(); } - beforeEach(testStartingPosition); + beforeEach(testStartingPosition, timeAllowed); - afterEach(testStartingPosition); + afterEach(testStartingPosition, timeAllowed); it('Initial Pagination Values', () => { }); diff --git a/src/test-e2e/helpers/cf-helpers.ts b/src/test-e2e/helpers/cf-helpers.ts index 2249889ed7..638d85f6cc 100644 --- a/src/test-e2e/helpers/cf-helpers.ts +++ b/src/test-e2e/helpers/cf-helpers.ts @@ -77,25 +77,23 @@ export class CFHelpers { orgGuid, orgName, spaceName, - endpoint: E2EConfigCloudFoundry + endpoint: E2EConfigCloudFoundry, + skipExistsCheck = false, ): promise.Promise> { return this.assignAdminAndUserGuids(cnsiGuid, endpoint).then(() => { expect(endpoint.creds.nonAdmin.guid).not.toBeNull(); - return this.addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, endpoint.creds.nonAdmin.guid); + return skipExistsCheck ? + this.baseAddSpace(cnsiGuid, orgGuid, orgName, spaceName, endpoint.creds.nonAdmin.guid) : + this.addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, endpoint.creds.nonAdmin.guid); + }); } addSpaceIfMissing(cnsiGuid, orgGuid, orgName, spaceName, userGuid): promise.Promise> { - const cfRequestHelper = this.cfRequestHelper; + const that = this; return this.fetchSpace(cnsiGuid, orgGuid, spaceName) .then(function (space) { - return space ? space : cfRequestHelper.sendCfPost>(cnsiGuid, 'spaces', - { - name: spaceName, - manager_guids: [], - developer_guids: [userGuid], - organization_guid: orgGuid - }); + return space ? space : that.baseAddSpace(cnsiGuid, orgGuid, orgName, spaceName, userGuid); }); } @@ -106,23 +104,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); } }); } @@ -175,6 +167,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); } From 0535e745efaf5de64e0aeb23a26987873b6563cd Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 16 Aug 2018 13:44:52 +0100 Subject: [PATCH 036/103] Missed file... --- protractor.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protractor.conf.js b/protractor.conf.js index 5efd5c6d43..dee671d815 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -56,7 +56,7 @@ exports.config = { framework: 'jasmine', jasmineNodeOpts: { showColors: true, - defaultTimeoutInterval: timeout, + defaultTimeoutInterval: 30000, print: function () {} }, params: secrets, From a48cfefb5cfc027267ed3cf8a337e08a2635c67e Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 16 Aug 2018 14:30:35 +0100 Subject: [PATCH 037/103] Edit Service Instance E2E tests --- .../create-service-instance-stepper.po.ts | 1 - .../edit-service-instance-e2e.spec.ts | 89 ++++++++++++------- .../marketplace/services-helper-e2e.ts | 19 +++- 3 files changed, 74 insertions(+), 35 deletions(-) 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 effb78e8b6..0ca5b2dbca 100644 --- a/src/test-e2e/marketplace/create-service-instance-stepper.po.ts +++ b/src/test-e2e/marketplace/create-service-instance-stepper.po.ts @@ -23,7 +23,6 @@ export class CreateServiceInstanceStepper extends StepperComponent { } setOrg = (orgName: string) => { - console.log('In setOrg ' + orgName) return this.getStepperForm().fill({ [this.orgFieldName]: orgName }); } diff --git a/src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts b/src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts index 54d07aa79b..9ae9f0b7d1 100644 --- a/src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts +++ b/src/test-e2e/marketplace/edit-service-instance-e2e.spec.ts @@ -6,65 +6,94 @@ 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'; -fdescribe('Edit Service Instance', () => { +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(() => { - createServiceInstance.navigateTo(); - createServiceInstance.waitForPage(); + 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.isActivePage(); 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 => { - return promise.all(cardTitles).then(titles => { - - for (let i = 0; i < titles.length; i++) { - if (titles[i] === serviceName) { - return servicesWall.serviceInstancesList.cards.getCard(i); - } - } - }); - }).then( (card: MetaCard) => { - card.openActionMenu().then(menu => { - menu.clickItem('Edit'); - browser.getCurrentUrl().then(url => { - expect(url.endsWith('edit')).toBeTruthy(); - }); - }); - }).catch(e => fail(e)); + 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.isActivePage(); + const editedServiceName = servicesHelperE2E.serviceInstanceName; + getCardWithTitle(servicesWall, editedServiceName).then((card: MetaCard) => { + expect(card).toBeDefined(); + }).catch(e => fail(e)); + }); - afterAll((done) => { - servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done()); + it('- should be able to delete service instance', () => { + + servicesWall.isActivePage(); + 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/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index 55250c1f47..af03d04845 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -23,6 +23,9 @@ export class ServicesHelperE2E { } } + addPrefixToServiceName = (prefix: string) => { + this.serviceInstanceName = `${prefix}-${this.serviceInstanceName}`; + } setCreateServiceInstance = (createServiceInstance: CreateServiceInstance) => { this.createServiceInstance = createServiceInstance; } @@ -90,10 +93,14 @@ export class ServicesHelperE2E { .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); } @@ -106,10 +113,14 @@ export class ServicesHelperE2E { 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(); } From d6209a53a0acb3ce5c13f06795e828672c6d802b Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 16 Aug 2018 14:34:08 +0100 Subject: [PATCH 038/103] Remove debug statement --- src/test-e2e/marketplace/create-service-instance-stepper.po.ts | 1 - 1 file changed, 1 deletion(-) 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 effb78e8b6..0ca5b2dbca 100644 --- a/src/test-e2e/marketplace/create-service-instance-stepper.po.ts +++ b/src/test-e2e/marketplace/create-service-instance-stepper.po.ts @@ -23,7 +23,6 @@ export class CreateServiceInstanceStepper extends StepperComponent { } setOrg = (orgName: string) => { - console.log('In setOrg ' + orgName) return this.getStepperForm().fill({ [this.orgFieldName]: orgName }); } From fd7514668ffb0a9b1c574ff1a2c833ed0b792f18 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 16 Aug 2018 15:31:22 +0100 Subject: [PATCH 039/103] Revert "Fetch space-scoped services when creating services in relevant cf/org" This reverts commit ea73bb27a5d82d7f790595470fc9f0270860a7ae. --- .../service-catalog/services.service.ts | 2 +- .../services/services-wall.service.ts | 38 +++++-------------- .../select-service.component.ts | 29 ++------------ .../store/actions/service-broker.actions.ts | 2 +- 4 files changed, 14 insertions(+), 57 deletions(-) diff --git a/src/frontend/app/features/service-catalog/services.service.ts b/src/frontend/app/features/service-catalog/services.service.ts index 7e65381cb6..4c12ee5090 100644 --- a/src/frontend/app/features/service-catalog/services.service.ts +++ b/src/frontend/app/features/service-catalog/services.service.ts @@ -115,7 +115,7 @@ export class ServicesService { return getPaginationObservables>( { store: this.store, - action: new GetServiceBrokers(paginationKey, this.cfGuid), + action: new GetServiceBrokers(this.cfGuid, paginationKey), paginationMonitor: this.paginationMonitorFactory.create( paginationKey, entityFactory(serviceBrokerSchemaKey) diff --git a/src/frontend/app/features/services/services/services-wall.service.ts b/src/frontend/app/features/services/services/services-wall.service.ts index 25e719c418..e7537e7349 100644 --- a/src/frontend/app/features/services/services/services-wall.service.ts +++ b/src/frontend/app/features/services/services/services-wall.service.ts @@ -3,13 +3,13 @@ import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { filter, map, publishReplay, refCount } from 'rxjs/operators'; -import { IService, IServiceBroker } from '../../../core/cf-api-svc.types'; +import { IService } from '../../../core/cf-api-svc.types'; +import { EntityServiceFactory } from '../../../core/entity-service-factory.service'; import { PaginationMonitorFactory } from '../../../shared/monitors/pagination-monitor.factory'; -import { GetServiceBrokers } from '../../../store/actions/service-broker.actions'; import { GetAllServices } from '../../../store/actions/service.actions'; import { GetServicesForSpace } from '../../../store/actions/space.actions'; import { AppState } from '../../../store/app-state'; -import { entityFactory, serviceBrokerSchemaKey, serviceSchemaKey } from '../../../store/helpers/entity-factory'; +import { entityFactory, serviceSchemaKey } from '../../../store/helpers/entity-factory'; import { createEntityRelationPaginationKey } from '../../../store/helpers/entity-relations/entity-relations.types'; import { getPaginationObservables } from '../../../store/reducers/pagination-reducer/pagination-reducer.helper'; import { APIResource } from '../../../store/types/api.types'; @@ -17,15 +17,17 @@ import { APIResource } from '../../../store/types/api.types'; @Injectable() export class ServicesWallService { services$: Observable[]>; - serviceBrokers$: Observable[]>; constructor( private store: Store, - private paginationMonitorFactory: PaginationMonitorFactory, + private paginationMonitorFactory: PaginationMonitorFactory ) { - const paginationKey = createEntityRelationPaginationKey(serviceSchemaKey); + this.services$ = this.initServicesObservable(); + } - this.services$ = getPaginationObservables>( + initServicesObservable = () => { + const paginationKey = createEntityRelationPaginationKey(serviceSchemaKey); + return getPaginationObservables>( { store: this.store, action: new GetAllServices(paginationKey), @@ -36,20 +38,6 @@ export class ServicesWallService { }, true ).entities$; - - const brokerPaginationKey = createEntityRelationPaginationKey(serviceBrokerSchemaKey); - - this.serviceBrokers$ = getPaginationObservables>( - { - store: this.store, - action: new GetServiceBrokers(brokerPaginationKey), - paginationMonitor: this.paginationMonitorFactory.create( - brokerPaginationKey, - entityFactory(serviceBrokerSchemaKey) - ) - }, - true - ).entities$; } getServicesInCf = (cfGuid: string) => this.services$.pipe( @@ -60,14 +48,6 @@ export class ServicesWallService { refCount() ) - getServiceBrokersInCf = (cfGuid: string) => this.serviceBrokers$.pipe( - filter(p => !!p && p.length > 0), - map(s => s.filter(b => b.entity.cfGuid === cfGuid)), - filter(p => !!p), - publishReplay(1), - refCount() - ) - getSpaceServicePagKey(cfGuid: string, spaceGuid: string) { return createEntityRelationPaginationKey(serviceSchemaKey, `${cfGuid}-${spaceGuid}`); } diff --git a/src/frontend/app/shared/components/add-service-instance/select-service/select-service.component.ts b/src/frontend/app/shared/components/add-service-instance/select-service/select-service.component.ts index 54132c003b..9218592d10 100644 --- a/src/frontend/app/shared/components/add-service-instance/select-service/select-service.component.ts +++ b/src/frontend/app/shared/components/add-service-instance/select-service/select-service.component.ts @@ -1,8 +1,8 @@ import { AfterContentInit, Component, OnDestroy } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; -import { BehaviorSubject, Observable, of as observableOf, Subscription, combineLatest, empty } from 'rxjs'; -import { filter, switchMap, combineLatest as combineLatestOperator, tap, map } from 'rxjs/operators'; +import { BehaviorSubject, Observable, of as observableOf, Subscription, combineLatest } from 'rxjs'; +import { filter, switchMap, tap, map } from 'rxjs/operators'; import { IService } from '../../../../core/cf-api-svc.types'; import { ServicesWallService } from '../../../../features/services/services/services-wall.service'; @@ -62,34 +62,11 @@ export class SelectServiceComponent implements OnDestroy, AfterContentInit { fetching ? this.stepperForm.disable() : this.stepperForm.enable(); }) ); - - const spaceScopedServices = cfSpaceGuid$.pipe( - switchMap(([cfGuid, spaceGuid]) => { - return this.servicesWallService.getServiceBrokersInCf(cfGuid).pipe( - map(brokers => brokers.filter(b => b.entity.space_guid === spaceGuid)), - switchMap(b => { - if (b.length > 0) { - // There are space scoped brokers in the space - const brokerGuids = b.map(e => e.entity.guid); - return this.servicesWallService.services$.pipe( - map(p => p.filter(s => brokerGuids.indexOf(s.entity.service_broker_guid) !== -1)), - ); - } else { - return empty(); - } - }) - ); - }) - ); this.services$ = cfSpaceGuid$.pipe( tap(([cfGuid]) => this.cfGuid = cfGuid), switchMap(([cfGuid, spaceGuid]) => this.servicesWallService.getServicesInSpace(cfGuid, spaceGuid)), filter(p => !!p), - combineLatestOperator(spaceScopedServices), - map(([services, ssServices]) => { - const allServices = [].concat(services, ssServices); - return allServices.sort((a, b) => a.entity.label.localeCompare(b.entity.label)); - }), + map(services => services.sort((a, b) => a.entity.label.localeCompare(b.entity.label))), tap(services => { if (services.length === 1) { const guid = services[0].metadata.guid; diff --git a/src/frontend/app/store/actions/service-broker.actions.ts b/src/frontend/app/store/actions/service-broker.actions.ts index 40aba1c03e..afbbdc64b4 100644 --- a/src/frontend/app/store/actions/service-broker.actions.ts +++ b/src/frontend/app/store/actions/service-broker.actions.ts @@ -7,8 +7,8 @@ import { getActions } from './action.helper'; export class GetServiceBrokers extends CFStartAction implements PaginationAction { constructor( + public endpointGuid: string, public paginationKey: string, - public endpointGuid: string = null, public includeRelations: string[] = [], public populateMissing = true ) { From 9b05bc1dda7e3c6c0b5581b5e7c5a647034fc468 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 16 Aug 2018 15:37:40 +0100 Subject: [PATCH 040/103] linting --- src/test-e2e/marketplace/create-service-instance-e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 99cb2cd41e..6cb2dcb9cc 100644 --- a/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts +++ b/src/test-e2e/marketplace/create-service-instance-e2e.spec.ts @@ -106,7 +106,7 @@ describe('Create Service Instance', () => { createServiceInstance.stepper.next(); // Select Service - servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.publicService.name); + servicesHelperE2E.setServiceSelection(e2e.secrets.getDefaultCFEndpoint().services.publicService.name); createServiceInstance.stepper.next(); // Select Service Plan From b907d82021a3c13f0eaacf562d50798e8a57201c Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 16 Aug 2018 17:36:36 +0100 Subject: [PATCH 041/103] Use tick+text as per multi cf card view, apply same ux to cf summary tile - Show nowt for metrics endpoint (no user) --- .../features/endpoints/endpoint-helpers.ts | 8 ----- .../card-cf-info/card-cf-info.component.html | 4 ++- .../card-cf-info/card-cf-info.component.ts | 4 --- .../table-cell/table-cell.component.ts | 6 +++- .../endpoint/endpoints-list-config.service.ts | 6 ++-- ...able-cell-endpoint-is-admin.component.html | 3 ++ ...able-cell-endpoint-is-admin.component.scss | 1 + ...e-cell-endpoint-is-admin.component.spec.ts | 32 +++++++++++++++++++ .../table-cell-endpoint-is-admin.component.ts | 11 +++++++ 9 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.html create mode 100644 src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.scss create mode 100644 src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.spec.ts create mode 100644 src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.ts diff --git a/src/frontend/app/features/endpoints/endpoint-helpers.ts b/src/frontend/app/features/endpoints/endpoint-helpers.ts index cccbfe42a5..43d460173d 100644 --- a/src/frontend/app/features/endpoints/endpoint-helpers.ts +++ b/src/frontend/app/features/endpoints/endpoint-helpers.ts @@ -9,14 +9,6 @@ export function getEndpointUsername(endpoint: EndpointModel) { return endpoint && endpoint.user ? endpoint.user.name : '-'; } -export function getEndpointIsAdminString(endpoint: EndpointModel) { - if (!endpoint || !endpoint.user) { - return '-'; - } - return endpoint.user.admin ? 'Yes' : 'No'; -} - - export const DEFAULT_ENDPOINT_TYPE = 'cf'; export interface EndpointTypeHelper { diff --git a/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.html b/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.html index a152cb77de..c930955d41 100644 --- a/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.html +++ b/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.html @@ -12,7 +12,9 @@ diff --git a/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.ts b/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.ts index 694808f07f..a4555e8bde 100644 --- a/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.ts +++ b/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.ts @@ -37,10 +37,6 @@ export class CardCfInfoComponent implements OnInit, OnDestroy { return `${apiEndpoint.Scheme}://${apiEndpoint.Host}${path}`; } - isAdmin(user) { - return user && user.admin ? 'Yes' : 'No'; - } - ngOnDestroy(): void { this.subs.forEach(s => s.unsubscribe()); } diff --git a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts index 62f77b0d5b..ea0a1ed503 100644 --- a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts @@ -1,3 +1,6 @@ +import { + TableCellEndpointIsAdminComponent, +} from '../../list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component'; import { Component, ComponentFactoryResolver, @@ -141,7 +144,8 @@ export const listTableCells = [ TableCellRequestMonitorIconComponent, TableCellSpaceNameComponent, TableCellAppCfOrgSpaceHeaderComponent, - TableCellAppCfOrgSpaceComponent + TableCellAppCfOrgSpaceComponent, + TableCellEndpointIsAdminComponent ]; @Component({ diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts index 39d29edf07..185e6fd776 100644 --- a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts @@ -12,7 +12,6 @@ import { getFullEndpointApiUrl, getNameForEndpointType, getEndpointUsername, - getEndpointIsAdminString } from '../../../../../features/endpoints/endpoint-helpers'; import { DisconnectEndpoint, UnregisterEndpoint } from '../../../../../store/actions/endpoint.actions'; import { ShowSnackBar } from '../../../../../store/actions/snackBar.actions'; @@ -34,6 +33,7 @@ import { TableCellEndpointStatusComponent } from './table-cell-endpoint-status/t import { map, pairwise } from 'rxjs/operators'; import { combineLatest, Observable } from 'rxjs'; +import { TableCellEndpointIsAdminComponent } from './table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component'; function getEndpointTypeString(endpoint: EndpointModel): string { @@ -92,9 +92,7 @@ export const endpointColumns: ITableColumn[] = [ { columnId: 'user-type', headerCell: () => 'Admin', - cellDefinition: { - getValue: getEndpointIsAdminString - }, + cellComponent: TableCellEndpointIsAdminComponent, sort: { type: 'sort', orderKey: 'user-type', diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.html b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.html new file mode 100644 index 0000000000..63aafdd572 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.html @@ -0,0 +1,3 @@ + + +- diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.scss b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.scss new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.scss @@ -0,0 +1 @@ + diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.spec.ts b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.spec.ts new file mode 100644 index 0000000000..4bd0ba9880 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.spec.ts @@ -0,0 +1,32 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CoreModule } from '../../../../../../core/core.module'; +import { EndpointModel } from '../../../../../../store/types/endpoint.types'; +import { TableCellEndpointIsAdminComponent } from './table-cell-endpoint-is-admin.component'; + + +describe('TableCellEndpointIsAdminComponent', () => { + let component: TableCellEndpointIsAdminComponent<{}>; + let fixture: ComponentFixture>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TableCellEndpointIsAdminComponent], + imports: [ + CoreModule + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TableCellEndpointIsAdminComponent); + component = fixture.componentInstance; + component.row = {} as EndpointModel; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.ts b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.ts new file mode 100644 index 0000000000..0624a37b3d --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +import { TableCellCustom } from '../../../list.types'; + +/* tslint:disable:no-access-missing-member https://github.com/mgechev/codelyzer/issues/191*/ +@Component({ + selector: 'app-table-cell-endpoint-is-admin', + templateUrl: './table-cell-endpoint-is-admin.component.html', + styleUrls: ['./table-cell-endpoint-is-admin.component.scss'] +}) +export class TableCellEndpointIsAdminComponent extends TableCellCustom { } From 8ad2ee69eaf8e03b8f696982e448889b8cdc90e3 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 16 Aug 2018 17:47:58 +0100 Subject: [PATCH 042/103] Fix endpoint sort when there are metrics endpoints --- .../list/data-sources-controllers/local-filtering-sorting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/app/shared/components/list/data-sources-controllers/local-filtering-sorting.ts b/src/frontend/app/shared/components/list/data-sources-controllers/local-filtering-sorting.ts index 7c90014f6f..1b421ebbc1 100644 --- a/src/frontend/app/shared/components/list/data-sources-controllers/local-filtering-sorting.ts +++ b/src/frontend/app/shared/components/list/data-sources-controllers/local-filtering-sorting.ts @@ -95,7 +95,7 @@ function getValue(obj, fieldArray: string[], index = 0, castToString = false): s } return obj; } - if (typeof obj[field] === 'undefined') { + if (!obj[field]) { return ''; } return getValue(obj[field], fieldArray, ++index); From e492cbb98e27b983657ad6ce1fb3fb18096f433a Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 17 Aug 2018 09:58:05 +0100 Subject: [PATCH 043/103] Fix build --- src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 272422a4a6..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(); From 310a34487aeab617791e791dcc42d79f93c7a274 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 17 Aug 2018 10:12:51 +0100 Subject: [PATCH 044/103] Fix unit tests --- .../cards/card-cf-info/card-cf-info.component.spec.ts | 5 +++-- .../table-cell-endpoint-is-admin.component.spec.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.spec.ts b/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.spec.ts index b6cb7bafca..c4f35c6e89 100644 --- a/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.spec.ts +++ b/src/frontend/app/shared/components/cards/card-cf-info/card-cf-info.component.spec.ts @@ -1,9 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { - generateTestCfEndpointService, BaseTestModulesNoShared, + generateTestCfEndpointService, } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { BooleanIndicatorComponent } from '../../boolean-indicator/boolean-indicator.component'; import { MetadataItemComponent } from '../../metadata-item/metadata-item.component'; import { CardCfInfoComponent } from './card-cf-info.component'; @@ -13,7 +14,7 @@ describe('CardCfInfoComponent', () => { beforeEach( async(() => { TestBed.configureTestingModule({ - declarations: [CardCfInfoComponent, MetadataItemComponent], + declarations: [CardCfInfoComponent, MetadataItemComponent, BooleanIndicatorComponent], imports: [...BaseTestModulesNoShared], providers: [generateTestCfEndpointService()] }).compileComponents(); diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.spec.ts b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.spec.ts index 4bd0ba9880..a0404e9707 100644 --- a/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.spec.ts +++ b/src/frontend/app/shared/components/list/list-types/endpoint/table-cell-endpoint-is-admin/table-cell-endpoint-is-admin.component.spec.ts @@ -2,6 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CoreModule } from '../../../../../../core/core.module'; import { EndpointModel } from '../../../../../../store/types/endpoint.types'; +import { BooleanIndicatorComponent } from '../../../../boolean-indicator/boolean-indicator.component'; import { TableCellEndpointIsAdminComponent } from './table-cell-endpoint-is-admin.component'; @@ -11,7 +12,7 @@ describe('TableCellEndpointIsAdminComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [TableCellEndpointIsAdminComponent], + declarations: [TableCellEndpointIsAdminComponent, BooleanIndicatorComponent], imports: [ CoreModule ] From c10eb1276365a09bd0a6c5f77855bb3366046ef9 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Fri, 17 Aug 2018 10:47:29 +0100 Subject: [PATCH 045/103] Fix typo --- src/test-e2e/marketplace/services-helper-e2e.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index 8c564f4535..baf5ca96c1 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -51,7 +51,7 @@ export class ServicesHelperE2E { ); } -eateService = (serviceName: string, marketplaceMode = false) => { + createService = (serviceName: string, marketplaceMode = false) => { this.createServiceInstance.waitForPage(); // Select CF/Org/Space From 043d0f0787b3317634bef90c98573bb506f917e3 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Mon, 20 Aug 2018 09:25:50 +0100 Subject: [PATCH 046/103] WIP --- package.json | 3 +- src/test-e2e/e2e.types.ts | 1 + .../create-service-instance-stepper.po.ts | 5 + ...ate-service-instances-bind-app-e2e.spec.ts | 126 ++++++++++++++++++ .../marketplace/services-helper-e2e.ts | 11 +- src/test-e2e/marketplace/services-wall.po.ts | 24 +++- src/test-e2e/po/meta-card.po.ts | 13 ++ 7 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 src/test-e2e/marketplace/create-service-instances-bind-app-e2e.spec.ts diff --git a/package.json b/package.json index dc63e926de..f92ae27da7 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,8 @@ "@types/request": "^2.47.0", "codecov": "^3.0.2", "codelyzer": "~3.1.1", - "delete": "^0.3.2", + "url-parse": "^1.4.3" +, "delete": "^0.3.2", "fs-extra": "^3.0.1", "gulp": "^4.0.0", "jasmine-core": "~3.1.0", diff --git a/src/test-e2e/e2e.types.ts b/src/test-e2e/e2e.types.ts index 066125cb16..b88c7b328a 100644 --- a/src/test-e2e/e2e.types.ts +++ b/src/test-e2e/e2e.types.ts @@ -21,6 +21,7 @@ export interface ServiceConfig { name: string; } export interface E2EServicesConfig { + bindApp: string; publicService: ServiceConfig; privateService: ServiceConfig; spaceScopedService: ServiceConfig; 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 20c8a505de..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(); @@ -37,6 +38,10 @@ export class CreateServiceInstanceStepper extends StepperComponent { 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-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..780d52ab19 --- /dev/null +++ b/src/test-e2e/marketplace/create-service-instances-bind-app-e2e.spec.ts @@ -0,0 +1,126 @@ +import { ElementFinder, promise, by } 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'; + +fdescribe('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; + } + return isCorrectCard; + }).length).toBe(1); + }); + }).catch(e => fail(e)); + + + }); + + it('- should have correct number in services card view', () => { + servicesWall.navigateTo(); + servicesWall.waitForPage(); + const servicesSecrets = e2e.secrets.getDefaultCFEndpoint().services; + 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)); + }); + + 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; + const serviceInstanceRow = servicesWall.serviceInstancesList.table.getRows().then( + (rows: ElementFinder[]) => { + return rows.map((row: ElementFinder) => { + return row.all(by.css('.app-table__cell')).then( + }); + }); + } + ) + + ; + 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)); + }); + + // it('- should have correct number in list view', () => { + + // }); + // it('- should display binding in App services view', () => { + + // }); + // it('- should be able to edit binding in App services view', () => { + + // }); + // it('- should be able to edit binding in App services view', () => { + + // }); + + // it('- should be able to delete binding in App services view', () => { + + // }); + + afterAll((done) => { + servicesHelperE2E.cleanUpServiceInstance(servicesHelperE2E.serviceInstanceName).then(() => done()); + }); +}); + + diff --git a/src/test-e2e/marketplace/services-helper-e2e.ts b/src/test-e2e/marketplace/services-helper-e2e.ts index baf5ca96c1..da41037891 100644 --- a/src/test-e2e/marketplace/services-helper-e2e.ts +++ b/src/test-e2e/marketplace/services-helper-e2e.ts @@ -51,7 +51,7 @@ export class ServicesHelperE2E { ); } - createService = (serviceName: string, marketplaceMode = false) => { + createService = (serviceName: string, marketplaceMode = false, bindApp: string = null) => { this.createServiceInstance.waitForPage(); // Select CF/Org/Space @@ -72,7 +72,7 @@ export class ServicesHelperE2E { // Bind App this.createServiceInstance.stepper.isBindAppStepDisabled().then(bindAppDisabled => { if (!bindAppDisabled) { - this.setBindApp(); + this.setBindApp(bindApp); this.createServiceInstance.stepper.next(); } @@ -99,9 +99,12 @@ export class ServicesHelperE2E { this.createServiceInstance.stepper.setServiceName(this.serviceInstanceName); } - 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(); 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/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(); } From 8a77e856f7f83a366378d36ca6cd7978070bb7b4 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Mon, 20 Aug 2018 10:19:19 +0100 Subject: [PATCH 047/103] App Binding tests --- ...ate-service-instances-bind-app-e2e.spec.ts | 51 +++++++------------ 1 file changed, 18 insertions(+), 33 deletions(-) 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 index 780d52ab19..acef395dec 100644 --- 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 @@ -1,4 +1,4 @@ -import { ElementFinder, promise, by } from 'protractor'; +import { ElementFinder, promise, by, browser } from 'protractor'; import { e2e } from '../e2e'; import { ConsoleUserType } from '../helpers/e2e-helpers'; @@ -7,7 +7,7 @@ import { CreateServiceInstance } from './create-service-instance.po'; import { ServicesHelperE2E } from './services-helper-e2e'; import { ServicesWallPage } from './services-wall.po'; -fdescribe('Create Service Instance with binding', () => { +describe('Create Service Instance with binding', () => { const createServiceInstance = new CreateServiceInstance(); const servicesWall = new ServicesWallPage(); let servicesHelperE2E: ServicesHelperE2E; @@ -31,7 +31,7 @@ fdescribe('Create Service Instance with binding', () => { expect(createServiceInstance.isActivePage()).toBeTruthy(); }); - it('- should be able to to create a service instance with binding', () => { + 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); @@ -81,42 +81,27 @@ fdescribe('Create Service Instance with binding', () => { // Switch to list view servicesWall.serviceInstancesList.header.getCardListViewToggleButton().click(); expect(servicesWall.serviceInstancesList.isTableView()).toBeTruthy(); - - const servicesSecrets = e2e.secrets.getDefaultCFEndpoint().services; - const serviceInstanceRow = servicesWall.serviceInstancesList.table.getRows().then( - (rows: ElementFinder[]) => { - return rows.map((row: ElementFinder) => { - return row.all(by.css('.app-table__cell')).then( - }); - }); - } - ) - - ; - 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)); - }); - - // it('- should have correct number in list view', () => { - - // }); - // it('- should display binding in App services view', () => { - // }); - // it('- should be able to edit binding in App services view', () => { + // Filter for name + servicesWall.serviceInstancesList.header.setSearchText(servicesHelperE2E.serviceInstanceName) + .then(() => { + servicesWall.serviceInstancesList.table.getRows().then((rows: ElementFinder[]) => { + expect(rows.length).toBe(1); - // }); - // it('- should be able to edit binding in App services view', () => { + const attachedApps = rows[0].element(by.css('.mat-column-attachedApps')); - // }); + expect(attachedApps.getText()).toBe(servicesSecrets.bindApp); - // it('- should be able to delete binding in App services view', () => { + // 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()); From 9a98e463862eee02ee4a1a27e4fa9fe42c9a7ec2 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 20 Aug 2018 13:54:30 +0100 Subject: [PATCH 048/103] Increase max time --- protractor.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protractor.conf.js b/protractor.conf.js index 969e4fb37c..e784e474f8 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -36,7 +36,7 @@ try { } // This is the maximum amount of time ALL before/after/it's must execute in -const timeout = 30000 +const timeout = 40000 exports.config = { allScriptsTimeout: timeout, From 5f173feef61f612c25afe1066ad65e2e10e761a9 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 20 Aug 2018 15:05:53 +0100 Subject: [PATCH 049/103] Ensure we wait for the delete org to complete before moving on - Previously failed to delete.. and then failed to connect to endpoint in next e2e test --- src/test-e2e/cloud-foundry/org-level/org-spaces-e2e.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 6d9e48c26e..39b5b745b9 100644 --- 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 @@ -11,7 +11,7 @@ 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- ', () => { +describe('Org Spaces List -', () => { let cfHelper: CFHelpers; let defaultCf: E2EConfigCloudFoundry; @@ -89,7 +89,7 @@ describe('Org Spaces List- ', () => { function tearDown(orgName: string) { expect(orgName).not.toBeNull(); - cfHelper.deleteOrgIfExisting(endpointGuid, orgName); + browser.wait(cfHelper.deleteOrgIfExisting(endpointGuid, orgName)); } beforeAll(() => { From f1bdb674eeb6f35b50a5d7e713d2e057ec97d2d7 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 21 Aug 2018 10:20:55 +0100 Subject: [PATCH 050/103] Sleep after creating bindin --- .../marketplace/create-service-instances-bind-app-e2e.spec.ts | 1 + 1 file changed, 1 insertion(+) 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 index acef395dec..8de8a93ef1 100644 --- 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 @@ -61,6 +61,7 @@ describe('Create Service Instance with binding', () => { }); it('- should have correct number in services card view', () => { + browser.sleep(1000); servicesWall.navigateTo(); servicesWall.waitForPage(); const servicesSecrets = e2e.secrets.getDefaultCFEndpoint().services; From 45e4b99550dc129dd6312ab541cafa70be8ff33f Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 21 Aug 2018 12:17:55 +0100 Subject: [PATCH 051/103] bump up default wait --- protractor.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protractor.conf.js b/protractor.conf.js index 765a3fb99c..7d838ca94f 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -53,7 +53,7 @@ exports.config = { framework: 'jasmine', jasmineNodeOpts: { showColors: true, - defaultTimeoutInterval: 30000, + defaultTimeoutInterval: 40000, print: function () {} }, params: secrets, From 2dd05b96ad361b8934314a03351e784bff3686ea Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 21 Aug 2018 13:02:17 +0100 Subject: [PATCH 052/103] Bump up scrip ttimeout --- protractor.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protractor.conf.js b/protractor.conf.js index 7d838ca94f..01f11b2f76 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -36,7 +36,7 @@ try { } exports.config = { - allScriptsTimeout: 30000, + allScriptsTimeout: 40000, specs: [ './src/test-e2e/**/*-e2e.spec.ts', ], From 989be341c1e66c85e10fb9adfa7f3c8787e68053 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 21 Aug 2018 13:15:43 +0100 Subject: [PATCH 053/103] Merge two tests --- ...ate-service-instances-bind-app-e2e.spec.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) 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 index 8de8a93ef1..e788957ff9 100644 --- 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 @@ -51,6 +51,12 @@ describe('Create Service Instance with binding', () => { 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); @@ -60,19 +66,6 @@ describe('Create Service Instance with binding', () => { }); - it('- should have correct number in services card view', () => { - browser.sleep(1000); - servicesWall.navigateTo(); - servicesWall.waitForPage(); - const servicesSecrets = e2e.secrets.getDefaultCFEndpoint().services; - 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)); - }); - it('- should have correct number in list view', () => { servicesWall.navigateTo(); servicesWall.waitForPage(); From ed2c5ee462d6910d2c43cf91c89bb6f9cbd873a8 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 21 Aug 2018 14:46:56 +0100 Subject: [PATCH 054/103] Add logging for debugging --- .../create-service-instances-bind-app-e2e.spec.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 index e788957ff9..eda1b67122 100644 --- 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 @@ -48,9 +48,14 @@ describe('Create Service Instance with binding', () => { }).then(cardTitles => { promise.all(cardTitles).then(titles => { expect(titles.filter((t, idx) => { - const isCorrectCard = t === serviceName; + const isCorrectCard = (t === serviceName); if (isCorrectCard) { cardIdx = idx; + /* tslint:disable */ + console.log('IS Correct card: ' + idx) + console.log('Service Name is: ' + serviceName) + /* tslint:enable */ + const card = servicesWall.serviceInstancesList.cards.getCard(cardIdx); card.getMetaCardItems().then(metaCardRows => { expect(metaCardRows[1].value).toBe(servicesSecrets.publicService.name); From abc2ddfefdc10d33fd8896549cf9451d0c853ed6 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 21 Aug 2018 15:18:06 +0100 Subject: [PATCH 055/103] Pin tests for debugging --- .../marketplace/create-service-instances-bind-app-e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index eda1b67122..f8da672f5a 100644 --- 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 @@ -7,7 +7,7 @@ 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', () => { +fdescribe('Create Service Instance with binding', () => { const createServiceInstance = new CreateServiceInstance(); const servicesWall = new ServicesWallPage(); let servicesHelperE2E: ServicesHelperE2E; From e7e9547f3c830732fb3f8f323da0a2409365f847 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Tue, 21 Aug 2018 16:10:50 +0100 Subject: [PATCH 056/103] Remove debugging code --- .../create-service-instances-bind-app-e2e.spec.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 index f8da672f5a..44b54ea5c6 100644 --- 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 @@ -7,7 +7,7 @@ import { CreateServiceInstance } from './create-service-instance.po'; import { ServicesHelperE2E } from './services-helper-e2e'; import { ServicesWallPage } from './services-wall.po'; -fdescribe('Create Service Instance with binding', () => { +describe('Create Service Instance with binding', () => { const createServiceInstance = new CreateServiceInstance(); const servicesWall = new ServicesWallPage(); let servicesHelperE2E: ServicesHelperE2E; @@ -51,10 +51,6 @@ fdescribe('Create Service Instance with binding', () => { const isCorrectCard = (t === serviceName); if (isCorrectCard) { cardIdx = idx; - /* tslint:disable */ - console.log('IS Correct card: ' + idx) - console.log('Service Name is: ' + serviceName) - /* tslint:enable */ const card = servicesWall.serviceInstancesList.cards.getCard(cardIdx); card.getMetaCardItems().then(metaCardRows => { From cd756553811237107fb641a70c988817fe62d115 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 22 Aug 2018 16:06:08 +0100 Subject: [PATCH 057/103] Go lint fixes --- src/jetstream/main.go | 2 -- src/jetstream/plugins/cfappssh/app_ssh.go | 8 +++--- src/jetstream/plugins/cfappssh/main.go | 25 ++++++++++--------- .../plugins/cloudfoundryhosting/main.go | 19 ++++++++++---- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/jetstream/main.go b/src/jetstream/main.go index 7ec918fb08..0de87a5e53 100644 --- a/src/jetstream/main.go +++ b/src/jetstream/main.go @@ -733,7 +733,6 @@ func (p *portalProxy) registerRoutes(e *echo.Echo, addSetupMiddleware *setupMidd if err == nil { routePlugin.AddAdminGroupRoutes(adminGroup) } - } adminGroup.POST("/unregister", p.unregisterCluster) @@ -748,7 +747,6 @@ func (p *portalProxy) registerRoutes(e *echo.Echo, addSetupMiddleware *setupMidd 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/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..d5d4e1f2bc 100644 --- a/src/jetstream/plugins/cfappssh/main.go +++ b/src/jetstream/plugins/cfappssh/main.go @@ -7,38 +7,39 @@ import ( "github.com/labstack/echo" ) -type CFAppSsh struct { +// CFAppSSH - Plugin to allow SSH into an application instance +type CFAppSSH struct { portalProxy interfaces.PortalProxy } 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!") +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!") +func (CFAppSSH *CFAppSSH) GetEndpointPlugin() (interfaces.EndpointPlugin, error) { + return nil, errors.New("Not implemented") } -func (cfAppSsh *CFAppSsh) GetRoutePlugin() (interfaces.RoutePlugin, error) { - return cfAppSsh, nil +func (CFAppSSH *CFAppSSH) GetRoutePlugin() (interfaces.RoutePlugin, error) { + return CFAppSSH, nil } -func (cfAppSsh *CFAppSsh) AddAdminGroupRoutes(echoGroup *echo.Group) { +func (CFAppSSH *CFAppSSH) AddAdminGroupRoutes(echoGroup *echo.Group) { // no-op } -func (cfAppSsh *CFAppSsh) AddSessionGroupRoutes(echoGroup *echo.Group) { +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 { +func (CFAppSSH *CFAppSSH) Init() error { return nil } diff --git a/src/jetstream/plugins/cloudfoundryhosting/main.go b/src/jetstream/plugins/cloudfoundryhosting/main.go index 947a2c4b3e..0f92fba317 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 - Plugin to configure Stratos when hosted in Cloud Foundry type CFHosting struct { portalProxy interfaces.PortalProxy endpointType string } +// Init - Initialize plugin func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) { return &CFHosting{portalProxy: portalProxy}, nil } +// GetMiddlewarePlugin - Get the middleware plugin to be added to Stratos 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 - Get the endpoint plugin to be added to Stratos (not needed by this plugin) func (ch *CFHosting) GetEndpointPlugin() (interfaces.EndpointPlugin, error) { - return nil, errors.New("Not implemented!") + return nil, errors.New("Not implemented") } +// GetRoutePlugin - Get the route plugin to be added to Stratos (not needed by this plugin) func (ch *CFHosting) GetRoutePlugin() (interfaces.RoutePlugin, error) { - return nil, errors.New("Not implemented!") + return nil, errors.New("Not implemented") } +// Init - Main plugin init method called by Stratos during start up 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) { @@ -100,9 +107,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) { @@ -182,6 +189,7 @@ func (ch *CFHosting) Init() error { return nil } +// EchoMiddleware - Echo server middleware provided by this plugin func (ch *CFHosting) EchoMiddleware(h echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { @@ -210,6 +218,7 @@ func (ch *CFHosting) EchoMiddleware(h echo.HandlerFunc) echo.HandlerFunc { } } +// SessionEchoMiddleware - 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 { From a8b05fe6ac97da90334035046b81d585720962b0 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 23 Aug 2018 11:55:33 +0100 Subject: [PATCH 058/103] Add goreportcard to readme. Fix first set of lint issues in plugins. --- README.md | 5 +- .../plugins/cfapppush/app-repository.go | 10 ++++ src/jetstream/plugins/cfapppush/deploy.go | 52 +++++++++---------- src/jetstream/plugins/cfapppush/main.go | 14 +++-- .../plugins/cfapppush/pushapp/pushapp.go | 10 +++- src/jetstream/plugins/cfappssh/main.go | 10 ++-- src/jetstream/plugins/cloudfoundry/main.go | 7 +++ .../plugins/cloudfoundryhosting/main.go | 16 +++--- .../plugins/metrics/cloud_foundry.go | 4 +- src/jetstream/plugins/metrics/main.go | 10 +++- src/jetstream/plugins/userinfo/main.go | 13 +++-- src/jetstream/plugins/userinfo/user_info.go | 8 +-- 12 files changed, 104 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index de50318cde..4a953fbf71 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # Stratos   - - + + [![GitHub release](https://img.shields.io/github/release/cloudfoundry-incubator/stratos.svg)](https://github.com/cloudfoundry-incubator/stratos/releases/latest) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/cloudfoundry-incubator/stratos/blob/master/LICENSE) [![slack.cloudfoundry.org](https://slack.cloudfoundry.org/badge.svg)](https://cloudfoundry.slack.com/messages/C80EP4Y57/) + Stratos is an Open Source Web-based UI (Console) for managing Cloud Foundry. It allows users and administrators to both manage applications running in the Cloud Foundry cluster and perform cluster management tasks. If you are looking for the V1 version of Stratos, you can find it in the [master](https://github.com/cloudfoundry-incubator/stratos/tree/master) branch. 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..3cbd12a283 100644 --- a/src/jetstream/plugins/cfapppush/deploy.go +++ b/src/jetstream/plugins/cfapppush/deploy.go @@ -65,16 +65,16 @@ 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) } func (cfAppPush *CFAppPush) deploy(echoContext echo.Context) error { - cnsiGUID := echoContext.Param("cnsiGuid") - orgGuid := echoContext.Param("orgGuid") - spaceGuid := echoContext.Param("spaceGuid") + cnsiGUID := echoContext.Param("cnsiGUID") + orgGUID := echoContext.Param("orgGUID") + spaceGUID := echoContext.Param("spaceGUID") spaceName := echoContext.QueryParam("space") orgName := echoContext.QueryParam("org") @@ -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/main.go b/src/jetstream/plugins/cfappssh/main.go index d5d4e1f2bc..259940b5ff 100644 --- a/src/jetstream/plugins/cfappssh/main.go +++ b/src/jetstream/plugins/cfappssh/main.go @@ -12,34 +12,38 @@ type CFAppSSH struct { portalProxy interfaces.PortalProxy } +// Init creates a new CFAppSSH func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) { return &CFAppSSH{portalProxy: portalProxy}, nil } +// GetMiddlewarePlugin gets the middleware plugin for this plugin func (CFAppSSH *CFAppSSH) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, 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") } +// GetRoutePlugin gets the route plugin for this plugin func (CFAppSSH *CFAppSSH) GetRoutePlugin() (interfaces.RoutePlugin, error) { return CFAppSSH, nil - } +// AddAdminGroupRoutes adds the admin routes for this plugin to the Echo server func (CFAppSSH *CFAppSSH) AddAdminGroupRoutes(echoGroup *echo.Group) { // no-op } +// 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) } +// Init performs plugin initialization func (CFAppSSH *CFAppSSH) Init() error { - return nil } diff --git a/src/jetstream/plugins/cloudfoundry/main.go b/src/jetstream/plugins/cloudfoundry/main.go index d59e9678d6..2c078d6686 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,10 +162,12 @@ 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) diff --git a/src/jetstream/plugins/cloudfoundryhosting/main.go b/src/jetstream/plugins/cloudfoundryhosting/main.go index 0f92fba317..4645dbb159 100644 --- a/src/jetstream/plugins/cloudfoundryhosting/main.go +++ b/src/jetstream/plugins/cloudfoundryhosting/main.go @@ -28,18 +28,18 @@ const ( ForceEndpointDashboard = "FORCE_ENDPOINT_DASHBOARD" ) -// CFHosting - Plugin to configure Stratos when hosted in Cloud Foundry +// CFHosting is a plugin to configure Stratos when hosted in Cloud Foundry type CFHosting struct { portalProxy interfaces.PortalProxy endpointType string } -// Init - Initialize plugin +// Init creates a new CFHosting plugin func Init(portalProxy interfaces.PortalProxy) (interfaces.StratosPlugin, error) { return &CFHosting{portalProxy: portalProxy}, nil } -// GetMiddlewarePlugin - Get the middleware plugin to be added to Stratos +// GetMiddlewarePlugin gets the middleware plugin for this plugin func (ch *CFHosting) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) { if config.IsSet(VCapApplication) { return ch, nil @@ -47,17 +47,17 @@ func (ch *CFHosting) GetMiddlewarePlugin() (interfaces.MiddlewarePlugin, error) return nil, errors.New("Not running as a Cloud Foundry application") } -// GetEndpointPlugin - Get the endpoint plugin to be added to Stratos (not needed by this plugin) +// GetEndpointPlugin gets the endpoint plugin for this plugin func (ch *CFHosting) GetEndpointPlugin() (interfaces.EndpointPlugin, error) { return nil, errors.New("Not implemented") } -// GetRoutePlugin - Get the route plugin to be added to Stratos (not needed by this plugin) +// GetRoutePlugin gets the route plugin for this plugin func (ch *CFHosting) GetRoutePlugin() (interfaces.RoutePlugin, error) { return nil, errors.New("Not implemented") } -// Init - Main plugin init method called by Stratos during start up +// 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) { @@ -189,7 +189,7 @@ func (ch *CFHosting) Init() error { return nil } -// EchoMiddleware - Echo server middleware provided by this plugin +// 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 { @@ -218,7 +218,7 @@ func (ch *CFHosting) EchoMiddleware(h echo.HandlerFunc) echo.HandlerFunc { } } -// SessionEchoMiddleware - Echo server session middleware provided by this plugin +// 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 { From c9e57d5641e7b2c355fabd38633fe222bdf9bfbe Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 23 Aug 2018 12:08:10 +0100 Subject: [PATCH 059/103] Update traviks to run golint and to update the go report card --- .travis.yml | 9 +++++++-- deploy/ci/travis/create-pr-branch.sh | 0 deploy/ci/travis/run-e2e-tests.sh | 0 deploy/ci/travis/update-go-report-card.sh | 11 +++++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) mode change 100644 => 100755 deploy/ci/travis/create-pr-branch.sh mode change 100644 => 100755 deploy/ci/travis/run-e2e-tests.sh create mode 100755 deploy/ci/travis/update-go-report-card.sh diff --git a/.travis.yml b/.travis.yml index 0e324ea037..acfd0adfca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,8 @@ cache: stages: - Frontend Lint - Frontend Unit Tests -- Backend +- Backend Lint +- Backend Unit Tests - name: E2E tests if: type != pull_request OR commit_message =~ /\[e2e\-full\]/ - name: E2E tests quick @@ -51,7 +52,11 @@ jobs: script: - npm test - npm run codecov - - stage: Backend + - stage: Backend Lint + script: + - go lint src/jetstream/... + - ./deploy/ci/travis/update-go-report-card.sh + - stage: Backend Unit Tests before_script: - curl -sL -o ~/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme - chmod +x ~/bin/gimme diff --git a/deploy/ci/travis/create-pr-branch.sh b/deploy/ci/travis/create-pr-branch.sh old mode 100644 new mode 100755 diff --git a/deploy/ci/travis/run-e2e-tests.sh b/deploy/ci/travis/run-e2e-tests.sh old mode 100644 new mode 100755 diff --git a/deploy/ci/travis/update-go-report-card.sh b/deploy/ci/travis/update-go-report-card.sh new file mode 100755 index 0000000000..8293c3404f --- /dev/null +++ b/deploy/ci/travis/update-go-report-card.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Only update if this is running against v2-master +if [ "$TRAVIS_BRANCH" == "v2-master" ] && [ "$TRAVIS_EVENT_TYPE" != "pull_request" ]; then + echo "Updating GO Report Card for: github.com/${TRAVIS_REPO_SLUG}" + curl -m 300 -d "repo=github.com/${TRAVIS_REPO_SLUG}" https://goreportcard.com/checks +fi + +# Always return success - any failure is down to the goreportcad site not being available +# Don't fail our build for this reason +exit 0 \ No newline at end of file From 6c9d2351d299542143b12bbfc651c4b9ed180069 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 23 Aug 2018 12:55:42 +0100 Subject: [PATCH 060/103] Ensure we have go and dep installed --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index acfd0adfca..76725592ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,6 +53,11 @@ jobs: - npm test - npm run codecov - stage: Backend Lint + before_script: + - curl -sL -o ~/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme + - chmod +x ~/bin/gimme + - eval "$(gimme 1.9)" + - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh script: - go lint src/jetstream/... - ./deploy/ci/travis/update-go-report-card.sh From 4cbefab9ef1accf7b84bccab3dfd122663df841c Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 23 Aug 2018 13:03:59 +0100 Subject: [PATCH 061/103] Fix lint --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index acfd0adfca..a741b929c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,8 +53,12 @@ jobs: - npm test - npm run codecov - stage: Backend Lint + before_script: + - curl -sL -o ~/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme + - chmod +x ~/bin/gimme + - eval "$(gimme 1.9)" script: - - go lint src/jetstream/... + - golint src/jetstream/... - ./deploy/ci/travis/update-go-report-card.sh - stage: Backend Unit Tests before_script: From 7729eaad2889ad499094e7fa8468a3314b5e80f0 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 23 Aug 2018 14:44:04 +0100 Subject: [PATCH 062/103] Ensure golint is installed --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index a741b929c7..2b6396e096 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,6 +57,7 @@ jobs: - curl -sL -o ~/bin/gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme - chmod +x ~/bin/gimme - eval "$(gimme 1.9)" + - go get -u golang.org/x/lint/golint script: - golint src/jetstream/... - ./deploy/ci/travis/update-go-report-card.sh From a7e44f9bac9aa22b497edf5c72285f8fe85f0044 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 24 Aug 2018 10:32:35 +0100 Subject: [PATCH 063/103] Harden the service wall instance card - Ensure we only show the template, which requires serviceInstanceEntity, when we have it - Removed ServicesWallService provider, it's not used (ctor never called) - Removed/Sorted imports --- .../service-catalog/services.service.ts | 2 +- .../services/services-wall.service.ts | 1 - .../add-service-instance.component.ts | 4 ++-- .../create-service-instance-helper.service.ts | 9 ++++++--- .../list/list-cards/card/card.component.ts | 6 ++++-- .../service-instance-card.component.html | 12 +++++------ .../service-instance-card.component.ts | 20 +++++++------------ 7 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/frontend/app/features/service-catalog/services.service.ts b/src/frontend/app/features/service-catalog/services.service.ts index 4c12ee5090..8b1cb08410 100644 --- a/src/frontend/app/features/service-catalog/services.service.ts +++ b/src/frontend/app/features/service-catalog/services.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngrx/store'; import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; -import { combineLatest, filter, first, map, publishReplay, refCount, switchMap, tap } from 'rxjs/operators'; +import { combineLatest, filter, first, map, publishReplay, refCount, switchMap } from 'rxjs/operators'; import { IService, diff --git a/src/frontend/app/features/services/services/services-wall.service.ts b/src/frontend/app/features/services/services/services-wall.service.ts index e7537e7349..72cfdcf6d2 100644 --- a/src/frontend/app/features/services/services/services-wall.service.ts +++ b/src/frontend/app/features/services/services/services-wall.service.ts @@ -4,7 +4,6 @@ import { Observable } from 'rxjs'; import { filter, map, publishReplay, refCount } from 'rxjs/operators'; import { IService } from '../../../core/cf-api-svc.types'; -import { EntityServiceFactory } from '../../../core/entity-service-factory.service'; import { PaginationMonitorFactory } from '../../../shared/monitors/pagination-monitor.factory'; import { GetAllServices } from '../../../store/actions/service.actions'; import { GetServicesForSpace } from '../../../store/actions/space.actions'; diff --git a/src/frontend/app/shared/components/add-service-instance/add-service-instance/add-service-instance.component.ts b/src/frontend/app/shared/components/add-service-instance/add-service-instance/add-service-instance.component.ts index 5280cbe810..e5ad35d822 100644 --- a/src/frontend/app/shared/components/add-service-instance/add-service-instance/add-service-instance.component.ts +++ b/src/frontend/app/shared/components/add-service-instance/add-service-instance/add-service-instance.component.ts @@ -12,13 +12,13 @@ import { getIdFromRoute } from '../../../../features/cloud-foundry/cf.helpers'; import { servicesServiceFactoryProvider } from '../../../../features/service-catalog/service-catalog.helpers'; import { GetApplication } from '../../../../store/actions/application.actions'; import { + ResetCreateServiceInstanceOrgAndSpaceState, ResetCreateServiceInstanceState, SetCreateServiceInstance, SetCreateServiceInstanceCFDetails, SetCreateServiceInstanceServiceGuid, - SetServiceInstanceGuid, - ResetCreateServiceInstanceOrgAndSpaceState, SetCreateServiceInstanceServicePlan, + SetServiceInstanceGuid, } from '../../../../store/actions/create-service-instance.actions'; import { GetServiceInstance } from '../../../../store/actions/service-instances.actions'; import { GetAllAppsInSpace, GetSpace } from '../../../../store/actions/space.actions'; diff --git a/src/frontend/app/shared/components/add-service-instance/create-service-instance-helper.service.ts b/src/frontend/app/shared/components/add-service-instance/create-service-instance-helper.service.ts index ca95250d0a..859ac56d56 100644 --- a/src/frontend/app/shared/components/add-service-instance/create-service-instance-helper.service.ts +++ b/src/frontend/app/shared/components/add-service-instance/create-service-instance-helper.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Inject } from '@angular/core'; +import { Inject } from '@angular/core'; import { Store } from '@ngrx/store'; import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { filter, first, map, publishReplay, refCount, share, switchMap } from 'rxjs/operators'; @@ -25,13 +25,16 @@ import { spaceSchemaKey, spaceWithOrgKey, } from '../../../store/helpers/entity-factory'; -import { createEntityRelationKey, createEntityRelationPaginationKey } from '../../../store/helpers/entity-relations/entity-relations.types'; +import { + createEntityRelationKey, + createEntityRelationPaginationKey, +} from '../../../store/helpers/entity-relations/entity-relations.types'; import { getPaginationObservables } from '../../../store/reducers/pagination-reducer/pagination-reducer.helper'; import { selectCreateServiceInstanceServicePlan } from '../../../store/selectors/create-service-instance.selectors'; import { APIResource } from '../../../store/types/api.types'; import { QParam } from '../../../store/types/pagination.types'; -import { PaginationMonitorFactory } from '../../monitors/pagination-monitor.factory'; import { CF_GUID } from '../../entity.tokens'; +import { PaginationMonitorFactory } from '../../monitors/pagination-monitor.factory'; export class CreateServiceInstanceHelper { servicePlanVisibilities$: Observable[]>; diff --git a/src/frontend/app/shared/components/list/list-cards/card/card.component.ts b/src/frontend/app/shared/components/list/list-cards/card/card.component.ts index 2c446c3a57..f8ed487253 100644 --- a/src/frontend/app/shared/components/list/list-cards/card/card.component.ts +++ b/src/frontend/app/shared/components/list/list-cards/card/card.component.ts @@ -25,8 +25,10 @@ import { import { CfServiceCardComponent } from '../../list-types/cf-services/cf-service-card/cf-service-card.component'; import { CfSpaceCardComponent } from '../../list-types/cf-spaces/cf-space-card/cf-space-card.component'; import { CfStacksCardComponent } from '../../list-types/cf-stacks/cf-stacks-card/cf-stacks-card.component'; -import { TableCellCustom, CardCell } from '../../list.types'; -import { ServiceInstanceCardComponent } from '../../list-types/services-wall/service-instance-card/service-instance-card.component'; +import { + ServiceInstanceCardComponent, +} from '../../list-types/services-wall/service-instance-card/service-instance-card.component'; +import { CardCell } from '../../list.types'; export const listCards = [ CardAppComponent, diff --git a/src/frontend/app/shared/components/list/list-types/services-wall/service-instance-card/service-instance-card.component.html b/src/frontend/app/shared/components/list/list-types/services-wall/service-instance-card/service-instance-card.component.html index 81492e2307..483a0ddb5a 100644 --- a/src/frontend/app/shared/components/list/list-types/services-wall/service-instance-card/service-instance-card.component.html +++ b/src/frontend/app/shared/components/list/list-types/services-wall/service-instance-card/service-instance-card.component.html @@ -1,6 +1,6 @@ - + -
{{ serviceInstanceEntity?.entity.name }}
+
{{ serviceInstanceEntity.entity.name }}
Space @@ -14,20 +14,20 @@ Service Plan - {{ serviceInstanceEntity?.entity.service_plan?.entity.name}} + {{ serviceInstanceEntity.entity.service_plan?.entity.name}} Applications Attached - {{ serviceInstanceEntity?.entity.service_bindings.length }} + {{ serviceInstanceEntity.entity.service_bindings.length }} Last Updated - {{ serviceInstanceEntity?.entity.last_operation?.updated_at | date:'medium' }} + {{ serviceInstanceEntity.entity.last_operation?.updated_at | date:'medium' }} Dashboard -
+